diff --git a/doc/manual/rl-next/faster-nix-flake-check.md b/doc/manual/rl-next/faster-nix-flake-check.md new file mode 100644 index 000000000..c195023c3 --- /dev/null +++ b/doc/manual/rl-next/faster-nix-flake-check.md @@ -0,0 +1,9 @@ +--- +synopsis: "`nix flake check` now skips derivations that can be substituted" +prs: [13574] +--- + +Previously, `nix flake check` would evaluate and build/substitute all +derivations. Now, it will skip downloading derivations that can be substituted. +This can drastically decrease the time invocations take in environments where +checks may already be cached (like in CI). diff --git a/src/nix/flake.cc b/src/nix/flake.cc index 3b1e2f5e4..18be64bba 100644 --- a/src/nix/flake.cc +++ b/src/nix/flake.cc @@ -786,8 +786,32 @@ struct CmdFlakeCheck : FlakeCommand } if (build && !drvPaths.empty()) { - Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", drvPaths.size())); - store->buildPaths(drvPaths); + // TODO: This filtering of substitutable paths is a temporary workaround until + // https://github.com/NixOS/nix/issues/5025 (union stores) is implemented. + // + // Once union stores are available, this code should be replaced with a proper + // union store configuration. Ideally, we'd use a union of multiple destination + // stores to preserve the current behavior where different substituters can + // cache different check results. + // + // For now, we skip building derivations whose outputs are already available + // via substitution, as `nix flake check` only needs to verify buildability, + // not actually produce the outputs. + auto missing = store->queryMissing(drvPaths); + // Only occurs if `drvPaths` contains a `DerivedPath::Opaque`, which should never happen + assert(missing.unknown.empty()); + + std::vector toBuild; + for (auto & path : missing.willBuild) { + toBuild.emplace_back( + DerivedPath::Built{ + .drvPath = makeConstantStorePathRef(path), + .outputs = OutputsSpec::All{}, + }); + } + + Activity act(*logger, lvlInfo, actUnknown, fmt("running %d flake checks", toBuild.size())); + store->buildPaths(toBuild); } if (hasErrors) throw Error("some errors were encountered during the evaluation"); diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh index 50a2b21c9..9a356c2ed 100755 --- a/tests/functional/flakes/check.sh +++ b/tests/functional/flakes/check.sh @@ -167,3 +167,23 @@ EOF if !isTestOnNixOS && $NIX_REMOTE != daemon; then expectStderr 100 nix flake check "$flakeDir" | grepQuiet 'builder failed with exit code 1' fi + +# Ensure non-substitutable (read: usually failed) checks are actually run +# https://github.com/NixOS/nix/pull/13574 +cp "$config_nix" $flakeDir/ +cat > $flakeDir/flake.nix <&1 && fail "nix flake check should have failed" || true) +echo "$checkRes" | grepQuiet -E "builder( for .*)? failed with exit code 1"