From 040e7832323ca6ed59bd58620bca35a496f977b1 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Sun, 18 Aug 2024 21:50:54 +0200 Subject: [PATCH] flake: don't refetch unmodified inputs by recursive follows Closes #460 I managed to trigger the issue by having the following inputs (shortened): authentik-nix.url = "github:nix-community/authentik-nix"; authentik-nix.inputs.poetry2nix.inputs.nixpkgs.follows = "nixpkgs"; When evaluating this using nix-eval-jobs --flake .#hydraJobs I got the following error: error: cannot update unlocked flake input 'authentik-nix/poetry2nix' in pure mode The issue we have here is that `authentik-nix/poetry2nix` was written into the `overrideMap` which caused Nix to assume it's a new input and tried to refetch it (#460) or errored out in pure mode (nix-eval-jobs / Hydra). The testcase unfortunately only involves checking for the output log and makes sure that something *is* logged on the first fetch so that the test doesn't rot when the logging changes since I didn't manage to trigger the error above with the reproducer from #460. In fact, I only managed to trigger the `cannot update unlocked flake input` error in this context with `nix-eval-jobs`. Change-Id: Ifd00091eec9a0067ed4bb3e5765a15d027328807 --- src/libexpr/flake/flake.cc | 17 +++++++- tests/functional/flakes/follow-paths.sh | 55 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/libexpr/flake/flake.cc b/src/libexpr/flake/flake.cc index 7f0e5b1e9..ab13ba3be 100644 --- a/src/libexpr/flake/flake.cc +++ b/src/libexpr/flake/flake.cc @@ -342,8 +342,21 @@ static void updateOverrides(std::map & overrideMap, const for (auto & [id, input] : overrides) { auto inputPath(inputPathPrefix); inputPath.push_back(id); - // Do not override existing assignment from outer flake - overrideMap.insert({inputPath, input}); + + /* Given + * + * { inputs.hydra.inputs.nix-eval-jobs.inputs.lix.follows = "lix"; } + * + * then `nix-eval-jobs` doesn't have an override. + * It's neither replaced using follows nor by a different + * URL. Thus no need to add it to overrides and thus re-fetch + * it. + */ + if (input.ref || input.follows) { + // Do not override existing assignment from outer flake + overrideMap.insert({inputPath, input}); + } + updateOverrides(overrideMap, input.overrides, inputPath); } } diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh index e91ce87b1..1f824793b 100644 --- a/tests/functional/flakes/follow-paths.sh +++ b/tests/functional/flakes/follow-paths.sh @@ -307,3 +307,58 @@ cat < $flakeFollowsB/flake.nix EOF nix flake update --flake $flakeFollowsA 2>&1 | grepQuiet "warning: input 'B/C' has an override for a non-existent input 'E'" + +# Test for Nested follows cause flake interactions to update the nested input #460 +for letter in {A..E}; do + path="flakeFollows${letter}" + rm -f "${!path}"/flake.lock +done + +cat < $flakeFollowsA/flake.nix +{ + inputs = { + B.url = "path:$flakeFollowsB"; + C = { + url = "path:$flakeFollowsC"; + inputs.D.inputs.E.follows = "B"; + }; + }; + outputs = _: {}; +} +EOF + +cat < $flakeFollowsB/flake.nix +{ + outputs = _: {}; +} +EOF + +cat < $flakeFollowsC/flake.nix +{ + inputs = { + D.url = "path:$flakeFollowsD"; + }; + outputs = _: {}; +} +EOF + +cat < $flakeFollowsD/flake.nix +{ + inputs = { + E.url = "path:$flakeFollowsE"; + }; + outputs = _: {}; +} +EOF + +# Lockfiles are cleared, initially the dependency needs to be fetched. +out="$(nix --verbose flake show path:$flakeFollowsA 2>&1)" +echo "$out" +[[ "$out" = *$'\n'"fetching path input 'path:"*"/flakeD'"$'\n'* ]] + +# But on another flake command it doesn't. +out="$(nix --verbose flake show path:$flakeFollowsA 2>&1)" +[[ "$out" != *$'\n'"fetching path input 'path:"*"/flakeD'"$'\n'* ]] + +# Make sure the nested override is actually correct in this testcase. +[[ "$(nix flake metadata path:$flakeFollowsA --json | jq '.locks.nodes.D.inputs.E|.[]' -r)" = "B" ]]