Add 'nix flake archive' command

This copies a flake and all its inputs recursively to a store (e.g. a
binary cache). This is intended to enable long-term reproducibility
for flakes. However this will also require #3253.

Example:

  $ nix flake archive --json --to file:///tmp/my-cache nixops
  {"path":"/nix/store/272igzkgl1gdzmabsjvb2kb2zqbphb3p-source","inputs":{"nixops-aws":{"path":"/nix/store/ybcykw13gr7iq1pzg18iyibbcv8k9q1v-source","inputs":{}},"nixops-hetzner":{"path":"/nix/store/6yn0205x3nz55w8ms3335p2841javz2d-source","inputs":{}},"nixpkgs":{"path":"/nix/store/li3lkr2ajrzphqqz3jj2avndnyd3i5lc-source","inputs":{}}}}

  $ ll /tmp/my-cache
  total 16
  -rw-r--r-- 1 eelco users 403 Jan 30 01:01 272igzkgl1gdzmabsjvb2kb2zqbphb3p.narinfo
  -rw-r--r-- 1 eelco users 403 Jan 30 01:01 6yn0205x3nz55w8ms3335p2841javz2d.narinfo
  -rw-r--r-- 1 eelco users 408 Jan 30 01:01 li3lkr2ajrzphqqz3jj2avndnyd3i5lc.narinfo
  drwxr-xr-x 2 eelco users   6 Jan 30 01:01 nar
  -rw-r--r-- 1 eelco users  21 Jan 30 01:01 nix-cache-info
  -rw-r--r-- 1 eelco users 404 Jan 30 01:01 ybcykw13gr7iq1pzg18iyibbcv8k9q1v.narinfo

Fixes #3336.
This commit is contained in:
Eelco Dolstra 2020-01-30 00:58:55 +01:00
parent b9f93e7386
commit 3c54e9ba01

View file

@ -11,6 +11,7 @@
#include "attr-path.hh"
#include "fetchers/fetchers.hh"
#include "fetchers/registry.hh"
#include "json.hh"
#include <nlohmann/json.hpp>
#include <queue>
@ -213,7 +214,7 @@ struct CmdFlakeInfo : FlakeCommand, MixJSON
}
};
struct CmdFlakeCheck : FlakeCommand, MixJSON
struct CmdFlakeCheck : FlakeCommand
{
bool build = true;
@ -604,6 +605,79 @@ struct CmdFlakeClone : FlakeCommand
}
};
struct CmdFlakeArchive : FlakeCommand, MixJSON, MixDryRun
{
std::string dstUri;
CmdFlakeArchive()
{
mkFlag()
.longName("to")
.labels({"store-uri"})
.description("URI of the destination Nix store")
.dest(&dstUri);
}
std::string description() override
{
return "copy a flake and all its inputs to a store";
}
Examples examples() override
{
return {
Example{
"To copy the dwarffs flake and its dependencies to a binary cache:",
"nix flake archive --to file:///tmp/my-cache dwarffs"
},
Example{
"To fetch the dwarffs flake and its dependencies to the local Nix store:",
"nix flake archive dwarffs"
},
Example{
"To print the store paths of the flake sources of NixOps without fetching them:",
"nix flake archive --json --dry-run nixops"
},
};
}
void run(nix::ref<nix::Store> store) override
{
auto flake = lockFlake();
auto jsonRoot = json ? std::optional<JSONObject>(std::cout) : std::optional<JSONObject>();
StorePathSet sources;
sources.insert(flake.flake.sourceInfo->storePath.clone());
if (jsonRoot)
jsonRoot->attr("path", store->printStorePath(flake.flake.sourceInfo->storePath));
std::function<void(const LockedInputs & inputs, std::optional<JSONObject> & jsonObj)> traverse;
traverse = [&](const LockedInputs & inputs, std::optional<JSONObject> & jsonObj)
{
auto jsonObj2 = jsonObj ? jsonObj->object("inputs") : std::optional<JSONObject>();
for (auto & input : inputs.inputs) {
auto jsonObj3 = jsonObj2 ? jsonObj2->object(input.first) : std::optional<JSONObject>();
if (!dryRun)
input.second.ref.input->fetchTree(store);
auto storePath = input.second.computeStorePath(*store);
if (jsonObj3)
jsonObj3->attr("path", store->printStorePath(storePath));
sources.insert(std::move(storePath));
traverse(input.second, jsonObj3);
}
};
traverse(flake.lockFile, jsonRoot);
if (!dryRun && !dstUri.empty()) {
ref<Store> dstStore = dstUri.empty() ? openStore() : openStore(dstUri);
copyPaths(store, dstStore, sources);
}
}
};
struct CmdFlake : virtual MultiCommand, virtual Command
{
CmdFlake()
@ -617,6 +691,7 @@ struct CmdFlake : virtual MultiCommand, virtual Command
{"pin", []() { return make_ref<CmdFlakePin>(); }},
{"init", []() { return make_ref<CmdFlakeInit>(); }},
{"clone", []() { return make_ref<CmdFlakeClone>(); }},
{"archive", []() { return make_ref<CmdFlakeArchive>(); }},
})
{
}