Add support for tarball flake inputs

For example,

  $ nix flake info https://github.com/edolstra/dwarffs/archive/master.tar.gz

Fixes #2929.
This commit is contained in:
Eelco Dolstra 2020-01-28 13:11:02 +01:00
parent c39c2503f7
commit 1af7b94c1d
4 changed files with 110 additions and 1 deletions

View file

@ -39,6 +39,10 @@ std::pair<Tree, std::shared_ptr<const Input>> Input::fetchTree(ref<Store> store)
if (input->narHash) if (input->narHash)
assert(input->narHash == tree.narHash); assert(input->narHash == tree.narHash);
if (narHash && narHash != input->narHash)
throw Error("NAR hash mismatch in input '%s', expected '%s', got '%s'",
to_string(), narHash->to_string(SRI), input->narHash->to_string(SRI));
return {std::move(tree), input}; return {std::move(tree), input};
} }

View file

@ -25,7 +25,7 @@ struct Tree
struct Input : std::enable_shared_from_this<Input> struct Input : std::enable_shared_from_this<Input>
{ {
std::string type; std::string type;
std::optional<Hash> narHash; std::optional<Hash> narHash; // FIXME: implement
virtual ~Input() { } virtual ~Input() { }

View file

@ -0,0 +1,91 @@
#include "fetchers.hh"
#include "download.hh"
#include "globals.hh"
#include "parse.hh"
#include "store-api.hh"
namespace nix::fetchers {
struct TarballInput : Input
{
ParsedURL url;
std::optional<Hash> hash;
bool operator ==(const Input & other) const override
{
auto other2 = dynamic_cast<const TarballInput *>(&other);
return
other2
&& to_string() == other2->to_string()
&& hash == other2->hash;
}
bool isImmutable() const override
{
return (bool) hash;
}
std::string to_string() const override
{
auto url2(url);
if (narHash)
url2.query.insert_or_assign("narHash", narHash->to_string(SRI));
return url2.to_string();
}
std::pair<Tree, std::shared_ptr<const Input>> fetchTreeInternal(nix::ref<Store> store) const override
{
CachedDownloadRequest request(url.to_string());
request.unpack = true;
request.getLastModified = true;
request.name = "source";
auto res = getDownloader()->downloadCached(store, request);
auto input = std::make_shared<TarballInput>(*this);
auto storePath = store->parseStorePath(res.storePath);
input->narHash = store->queryPathInfo(storePath)->narHash;
return {
Tree {
.actualPath = res.path,
.storePath = std::move(storePath),
.lastModified = *res.lastModified
},
input
};
}
};
struct TarballInputScheme : InputScheme
{
std::unique_ptr<Input> inputFromURL(const ParsedURL & url) override
{
if (url.scheme != "file" && url.scheme != "http" && url.scheme != "https") return nullptr;
if (!hasSuffix(url.path, ".zip")
&& !hasSuffix(url.path, ".tar")
&& !hasSuffix(url.path, ".tar.gz")
&& !hasSuffix(url.path, ".tar.xz")
&& !hasSuffix(url.path, ".tar.bz2"))
return nullptr;
auto input = std::make_unique<TarballInput>();
input->type = "tarball";
input->url = url;
auto narHash = url.query.find("narHash");
if (narHash != url.query.end()) {
// FIXME: require SRI hash.
input->narHash = Hash(narHash->second);
}
return input;
}
};
static auto r1 = OnStartup([] { registerInputScheme(std::make_unique<TarballInputScheme>()); });
}

View file

@ -573,3 +573,17 @@ nix flake info --flake-registry $registry --json hg+file://$flake5Dir
[[ $(nix flake info --flake-registry $registry --json hg+file://$flake5Dir | jq -e -r .revCount) = 1 ]] [[ $(nix flake info --flake-registry $registry --json hg+file://$flake5Dir | jq -e -r .revCount) = 1 ]]
nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-registries --no-allow-dirty nix build -o $TEST_ROOT/result hg+file://$flake5Dir --no-registries --no-allow-dirty
# Test tarball flakes
tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake5
nix build -o $TEST_ROOT/result file://$TEST_ROOT/flake.tar.gz
# Building with a tarball URL containing a SRI hash should also work.
url=$(nix flake info --json file://$TEST_ROOT/flake.tar.gz | jq -r .url)
[[ $url =~ sha256- ]]
nix build -o $TEST_ROOT/result $url
# Building with an incorrect SRI hash should fail.
nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ2Zz4DNHViCUrp6gTS7EE4+RMqFQtUfWF2UNUtJKS0=" 2>&1 | grep 'NAR hash mismatch'