Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions src/libflake/flake.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ static void parseFlakeInputAttr(

static FlakeInput parseFlakeInput(
EvalState & state,
std::string_view inputName,
Value * value,
const PosIdx pos,
const InputAttrPath & lockRootAttrPath,
Expand Down Expand Up @@ -155,8 +154,8 @@ static FlakeInput parseFlakeInput(
input.ref = parseFlakeRef(state.fetchSettings, *url, {}, true, input.isFlake, true);
}

if (!input.follows && !input.ref)
input.ref = FlakeRef::fromAttrs(state.fetchSettings, {{"type", "indirect"}, {"id", std::string(inputName)}});
if (input.ref && input.follows)
throw Error("flake input has both a flake reference and a follows attribute, at %s", state.positions[pos]);

return input;
}
Expand Down Expand Up @@ -185,7 +184,6 @@ static std::pair<std::map<FlakeId, FlakeInput>, fetchers::Attrs> parseFlakeInput
} else {
inputs.emplace(inputName,
parseFlakeInput(state,
inputName,
inputAttr.value,
inputAttr.pos,
lockRootAttrPath,
Expand Down Expand Up @@ -467,18 +465,27 @@ LockedFlake lockFlake(

/* Get the overrides (i.e. attributes of the form
'inputs.nixops.inputs.nixpkgs.url = ...'). */
for (auto & [id, input] : flakeInputs) {
std::function<void(const FlakeInput & input, const InputAttrPath & prefix)> addOverrides;
addOverrides = [&](const FlakeInput & input, const InputAttrPath & prefix)
{
for (auto & [idOverride, inputOverride] : input.overrides) {
auto inputAttrPath(inputAttrPathPrefix);
inputAttrPath.push_back(id);
auto inputAttrPath(prefix);
inputAttrPath.push_back(idOverride);
overrides.emplace(inputAttrPath,
OverrideTarget {
.input = inputOverride,
.sourcePath = sourcePath,
.parentInputAttrPath = inputAttrPathPrefix
});
if (inputOverride.ref || inputOverride.follows)
overrides.emplace(inputAttrPath,
OverrideTarget {
.input = inputOverride,
.sourcePath = sourcePath,
.parentInputAttrPath = inputAttrPathPrefix

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also be changed to prefix like line 472 above was?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct because it's the input attr path used to resolve relative path inputs. In an override, these should refer to the attr path of the overriding flake, which is inputAttrPathPrefix.

});
addOverrides(inputOverride, inputAttrPath);
}
};

for (auto & [id, input] : flakeInputs) {
auto inputAttrPath(inputAttrPathPrefix);
inputAttrPath.push_back(id);
addOverrides(input, inputAttrPath);
}

/* Check whether this input has overrides for a
Expand Down Expand Up @@ -534,7 +541,8 @@ LockedFlake lockFlake(
continue;
}

assert(input.ref);
if (!input.ref)
input.ref = FlakeRef::fromAttrs(state.fetchSettings, {{"type", "indirect"}, {"id", std::string(id)}});

auto overridenParentPath =
input.ref->input.isRelative()
Expand Down
71 changes: 71 additions & 0 deletions tests/functional/flakes/follow-paths.sh
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,74 @@ rm "$flakeFollowsCustomUrlA"/flake.lock
json=$(nix flake metadata "$flakeFollowsCustomUrlA" --override-input B/C "$flakeFollowsCustomUrlD" --json)
echo "$json" | jq .locks.nodes.C.original
[[ $(echo "$json" | jq -r .locks.nodes.C.original.path) = './flakeC' ]]

# Test deep overrides, e.g. `inputs.B.inputs.C.inputs.D.follows = ...`.

cat <<EOF > $flakeFollowsD/flake.nix
{ outputs = _: {}; }
EOF
cat <<EOF > $flakeFollowsC/flake.nix
{
inputs.D.url = "path:nosuchflake";
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsB/flake.nix
{
inputs.C.url = "path:$flakeFollowsC";
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsA/flake.nix
{
inputs.B.url = "path:$flakeFollowsB";
inputs.D.url = "path:$flakeFollowsD";
inputs.B.inputs.C.inputs.D.follows = "D";
outputs = _: {};
}
EOF

nix flake lock $flakeFollowsA

[[ $(jq -c .nodes.C.inputs.D $flakeFollowsA/flake.lock) = '["D"]' ]]

# Test overlapping flake follows: B has D follow C/D, while A has B/C follow C

cat <<EOF > $flakeFollowsC/flake.nix
{
inputs.D.url = "path:$flakeFollowsD";
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsB/flake.nix
{
inputs.C.url = "path:nosuchflake";
inputs.D.follows = "C/D";
outputs = _: {};
}
EOF
cat <<EOF > $flakeFollowsA/flake.nix
{
inputs.B.url = "path:$flakeFollowsB";
inputs.C.url = "path:$flakeFollowsC";
inputs.B.inputs.C.follows = "C";
outputs = _: {};
}
EOF

# bug was not triggered without recreating the lockfile
nix flake lock $flakeFollowsA --recreate-lock-file

[[ $(jq -c .nodes.B.inputs.D $flakeFollowsA/flake.lock) = '["B","C","D"]' ]]

# Check that you can't have both a flakeref and a follows attribute on an input.
cat <<EOF > $flakeFollowsB/flake.nix
{
inputs.C.url = "path:nosuchflake";
inputs.D.url = "path:nosuchflake";
inputs.D.follows = "C/D";
outputs = _: {};
}
EOF

expectStderr 1 nix flake lock $flakeFollowsA --recreate-lock-file | grepQuiet "flake input has both a flake reference and a follows attribute"