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
17 changes: 17 additions & 0 deletions doc/manual/rl-next/outpath-and-sourceinfo-fixes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
synopsis: Non-flake inputs now contain a `sourceInfo` attribute
issues: 13164
prs: 13170
---

Flakes have always a `sourceInfo` attribute which describes the source of the flake.
The `sourceInfo.outPath` is often identical to the flake's `outPath`, however it can differ when the flake is located in a subdirectory of its source.

Non-flake inputs (i.e. inputs with `flake = false`) can also be located at some path _within_ a wider source.
This usually happens when defining a relative path input within the same source as the parent flake, e.g. `inputs.foo.url = ./some-file.nix`.
Such relative inputs will now inherit their parent's `sourceInfo`.

This also means it is now possible to use `?dir=subdir` on non-flake inputs.

This iterates on the work done in 2.26 to improve relative path support ([#10089](https://github.com/NixOS/nix/pull/10089)),
and resolves a regression introduced in 2.28 relating to nested relative path inputs ([#13164](https://github.com/NixOS/nix/issues/13164)).
26 changes: 11 additions & 15 deletions src/libflake/call-flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -39,32 +39,28 @@ let
allNodes = mapAttrs (
key: node:
let
hasOverride = overrides ? ${key};
isRelative = node.locked.type or null == "path" && builtins.substring 0 1 node.locked.path != "/";

parentNode = allNodes.${getInputByPath lockFile.root node.parent};

flakeDir =
let
dir = overrides.${key}.dir or node.locked.path or "";
parentDir = parentNode.flakeDir;
in
if node ? parent then parentDir + ("/" + dir) else dir;

sourceInfo =
if overrides ? ${key} then
if hasOverride then
overrides.${key}.sourceInfo
else if node.locked.type == "path" && builtins.substring 0 1 node.locked.path != "/" then
else if isRelative then
parentNode.sourceInfo
// {
outPath = parentNode.sourceInfo.outPath + ("/" + flakeDir);
}
else
# FIXME: remove obsolete node.info.
# Note: lock file entries are always final.
fetchTreeFinal (node.info or { } // removeAttrs node.locked [ "dir" ]);

subdir = overrides.${key}.dir or node.locked.dir or "";

outPath = sourceInfo + ((if subdir == "" then "" else "/") + subdir);
outPath =
if !hasOverride && isRelative then
parentNode.outPath + (if node.locked.path == "" then "" else "/" + node.locked.path)
else
sourceInfo.outPath + (if subdir == "" then "" else "/" + subdir);

flake = import (outPath + "/flake.nix");

Expand Down Expand Up @@ -99,9 +95,9 @@ let
assert builtins.isFunction flake.outputs;
result
else
sourceInfo;
sourceInfo // { inherit sourceInfo outPath; };

inherit flakeDir sourceInfo;
inherit outPath sourceInfo;
}
) lockFile.nodes;

Expand Down
46 changes: 46 additions & 0 deletions tests/functional/flakes/non-flake-inputs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,20 @@ cat > "$flake3Dir/flake.nix" <<EOF
url = "$nonFlakeDir/README.md";
flake = false;
};
nonFlakeFile3 = {
url = "$nonFlakeDir?dir=README.md";
flake = false;
};
relativeNonFlakeFile = {
url = ./config.nix;
flake = false;
};
};

description = "Fnord";

outputs = inputs: rec {
inherit inputs;
packages.$system.xyzzy = inputs.flake2.packages.$system.bar;
packages.$system.sth = inputs.flake1.packages.$system.foo;
packages.$system.fnord =
Expand Down Expand Up @@ -88,6 +97,43 @@ mv "$flake2Dir.tmp" "$flake2Dir"
mv "$nonFlakeDir.tmp" "$nonFlakeDir"
nix build -o "$TEST_ROOT/result" flake3#xyzzy flake3#fnord

# Check non-flake inputs have a sourceInfo and an outPath
#
# This may look redundant, but the other checks below happen in a command
# substitution subshell, so failures there will not exit this shell
nix eval --raw flake3#inputs.nonFlake.outPath
nix eval --raw flake3#inputs.nonFlake.sourceInfo.outPath
nix eval --raw flake3#inputs.nonFlakeFile.outPath
nix eval --raw flake3#inputs.nonFlakeFile.sourceInfo.outPath
nix eval --raw flake3#inputs.nonFlakeFile2.outPath
nix eval --raw flake3#inputs.nonFlakeFile2.sourceInfo.outPath
nix eval --raw flake3#inputs.nonFlakeFile3.outPath
nix eval --raw flake3#inputs.nonFlakeFile3.sourceInfo.outPath
nix eval --raw flake3#inputs.relativeNonFlakeFile.outPath
nix eval --raw flake3#inputs.relativeNonFlakeFile.sourceInfo.outPath

# Check non-flake file inputs have the expected outPaths
[[
$(nix eval --raw flake3#inputs.nonFlake.outPath) \
= $(nix eval --raw flake3#inputs.nonFlake.sourceInfo.outPath)
]]
[[
$(nix eval --raw flake3#inputs.nonFlakeFile.outPath) \
= $(nix eval --raw flake3#inputs.nonFlakeFile.sourceInfo.outPath)
]]
[[
$(nix eval --raw flake3#inputs.nonFlakeFile2.outPath) \
= $(nix eval --raw flake3#inputs.nonFlakeFile2.sourceInfo.outPath)
]]
[[
$(nix eval --raw flake3#inputs.nonFlakeFile3.outPath) \
= $(nix eval --raw flake3#inputs.nonFlakeFile3.sourceInfo.outPath)/README.md
]]
[[
$(nix eval --raw flake3#inputs.relativeNonFlakeFile.outPath) \
= $(nix eval --raw flake3#inputs.relativeNonFlakeFile.sourceInfo.outPath)/config.nix
]]

# Make branch "removeXyzzy" where flake3 doesn't have xyzzy anymore
git -C "$flake3Dir" checkout -b removeXyzzy
rm "$flake3Dir/flake.nix"
Expand Down
43 changes: 43 additions & 0 deletions tests/functional/flakes/relative-paths.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,46 @@ EOF
# would fail:
nix eval .#ok
)

# https://github.com/NixOS/nix/issues/13164
mkdir -p "$TEST_ROOT/issue-13164/nested-flake1/nested-flake2"
(
cd "$TEST_ROOT/issue-13164"
git init
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
cat >flake.nix <<EOF
{
inputs.nestedFlake1.url = "path:./nested-flake1";
outputs = { self, nestedFlake1 }: {
inherit nestedFlake1;
};
}
EOF

cat >nested-flake1/flake.nix <<EOF
{
inputs.nestedFlake2.url = "path:./nested-flake2";

outputs = { self, nestedFlake2 }: {
name = "nestedFlake1";
inherit nestedFlake2;
};
}
EOF

cat >nested-flake1/nested-flake2/flake.nix <<EOF
{
outputs = { self }: {
name = "nestedFlake2";
};
}
EOF

git add .
git commit -m "Initial commit"

# I don't understand why two calls are necessary to reproduce the issue.
nix eval --json .#nestedFlake1.nestedFlake2 --no-eval-cache
nix eval --json .#nestedFlake1.nestedFlake2 --no-eval-cache
)
Loading