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
This commit is contained in:
Maximilian Bosch 2024-08-18 21:50:54 +02:00
parent e727dbc3a3
commit 040e783232
2 changed files with 70 additions and 2 deletions

View file

@ -342,8 +342,21 @@ static void updateOverrides(std::map<InputPath, FlakeInput> & overrideMap, const
for (auto & [id, input] : overrides) { for (auto & [id, input] : overrides) {
auto inputPath(inputPathPrefix); auto inputPath(inputPathPrefix);
inputPath.push_back(id); inputPath.push_back(id);
/* 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 // Do not override existing assignment from outer flake
overrideMap.insert({inputPath, input}); overrideMap.insert({inputPath, input});
}
updateOverrides(overrideMap, input.overrides, inputPath); updateOverrides(overrideMap, input.overrides, inputPath);
} }
} }

View file

@ -307,3 +307,58 @@ cat <<EOF > $flakeFollowsB/flake.nix
EOF EOF
nix flake update --flake $flakeFollowsA 2>&1 | grepQuiet "warning: input 'B/C' has an override for a non-existent input 'E'" 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 <<EOF > $flakeFollowsA/flake.nix
{
inputs = {
B.url = "path:$flakeFollowsB";
C = {
url = "path:$flakeFollowsC";
inputs.D.inputs.E.follows = "B";
};
};
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsB/flake.nix
{
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsC/flake.nix
{
inputs = {
D.url = "path:$flakeFollowsD";
};
outputs = _: {};
}
EOF
cat <<EOF > $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" ]]