From 5fe6c537030ffc0353473e25998e81fb37980a98 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 28 Jul 2025 18:53:49 +0200 Subject: [PATCH] nix flake prefetch-inputs: Add This command fetches all inputs of a flake in parallel. Example runtime for $ chmod -R u+w /tmp/nix2; rm -rf /tmp/nix2; rm ~/.cache/nix/fetcher-cache-v3.sqlite*; rm -rf ~/.cache/nix/tarball-cache/ ~/.cache/nix/gitv3/; time nix flake prefetch-inputs --store /tmp/nix2 https://api.flakehub.com/f/pinned/informalsystems/cosmos.nix/0.3.0/018ce9ed-d0be-7ce5-81b6-a3c6e3ae1187/source.tar.gz with http-connections = 1: real 4m11.859s user 2m6.931s sys 0m25.619s and http-connections = 25 (the default): real 0m57.146s user 2m49.506s sys 0m36.008s --- src/nix/flake-prefetch-inputs.cc | 72 ++++++++++++++++++++++++++++++++ src/nix/flake-prefetch-inputs.md | 17 ++++++++ src/nix/meson.build | 1 + 3 files changed, 90 insertions(+) create mode 100644 src/nix/flake-prefetch-inputs.cc create mode 100644 src/nix/flake-prefetch-inputs.md diff --git a/src/nix/flake-prefetch-inputs.cc b/src/nix/flake-prefetch-inputs.cc new file mode 100644 index 000000000..096eaf539 --- /dev/null +++ b/src/nix/flake-prefetch-inputs.cc @@ -0,0 +1,72 @@ +#include "flake-command.hh" +#include "nix/fetchers/fetch-to-store.hh" +#include "nix/util/thread-pool.hh" +#include "nix/store/filetransfer.hh" +#include "nix/util/exit.hh" + +#include + +using namespace nix; +using namespace nix::flake; + +struct CmdFlakePrefetchInputs : FlakeCommand +{ + std::string description() override + { + return "fetch the inputs of a flake"; + } + + std::string doc() override + { + return +#include "flake-prefetch-inputs.md" + ; + } + + void run(nix::ref store) override + { + auto flake = lockFlake(); + + ThreadPool pool{fileTransferSettings.httpConnections}; + + struct State + { + std::set done; + }; + + Sync state_; + + std::atomic nrFailed{0}; + + std::function visit; + visit = [&](const Node & node) { + if (!state_.lock()->done.insert(&node).second) + return; + + if (auto lockedNode = dynamic_cast(&node)) { + try { + Activity act(*logger, lvlInfo, actUnknown, fmt("fetching '%s'", lockedNode->lockedRef)); + auto accessor = lockedNode->lockedRef.input.getAccessor(store).first; + fetchToStore( + fetchSettings, *store, accessor, FetchMode::Copy, lockedNode->lockedRef.input.getName()); + } catch (Error & e) { + printError("%s", e.what()); + nrFailed++; + } + } + + for (auto & [inputName, input] : node.inputs) { + if (auto inputNode = std::get_if<0>(&input)) + pool.enqueue(std::bind(visit, **inputNode)); + } + }; + + pool.enqueue(std::bind(visit, *flake.lockFile.root)); + + pool.process(); + + throw Exit(nrFailed ? 1 : 0); + } +}; + +static auto rCmdFlakePrefetchInputs = registerCommand2({"flake", "prefetch-inputs"}); diff --git a/src/nix/flake-prefetch-inputs.md b/src/nix/flake-prefetch-inputs.md new file mode 100644 index 000000000..a69f7d367 --- /dev/null +++ b/src/nix/flake-prefetch-inputs.md @@ -0,0 +1,17 @@ +R""( + +# Examples + +* Fetch the inputs of the `hydra` flake: + + ```console + # nix flake prefetch-inputs github:NixOS/hydra + ``` + +# Description + +Fetch the inputs of a flake. This ensures that they are already available for any subsequent evaluation of the flake. + +This operation is recursive: it will fetch not just the direct inputs of the top-level flake, but also transitive inputs. + +)"" diff --git a/src/nix/meson.build b/src/nix/meson.build index 586ee15c3..f1c79b002 100644 --- a/src/nix/meson.build +++ b/src/nix/meson.build @@ -78,6 +78,7 @@ nix_sources = [config_priv_h] + files( 'env.cc', 'eval.cc', 'flake.cc', + 'flake-prefetch-inputs.cc', 'formatter.cc', 'hash.cc', 'log.cc',