aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-12 20:25:15 +0000
committertv <tv@krebsco.de>2026-11-13 03:24:31 +0100
commitbde412b675588ec14bf069f7e244b6e34eb5fdff (patch)
treef6c9f70e58b80d5dd02a6a39dee98ea55344fa6b
parent32eeb3aeda1519acbc15c120693aebc95cbd5c38 (diff)
populate: draw inputs below drawn inputs at any depth
An input below an input that gets drawn from a source is an input of the flake in that source. If that flake draws the input on the way from another source itself, the path goes on in the flake of that one, and so forth. Populate follows the path one source deep only, so x.inputs.dep = { follows = "b"; inputs.p.inputs.z.follows = "c"; } has no effect if b draws p from a source: Nix ignores overrides below an input that follows another one, without warning. Populate follows such paths through the sources, for generated flakes as well as for rewritten lock files. Where the way leads through a generated flake, it names the generated flake's inputs, which are its source and the sources that one draws from. So a source with a rewritten lock file can also draw an input below one it draws from a source with a generated flake. Edge cases cover an input two sources deep, both ways of drawing inputs, and a source with a rewritten lock file drawing from one with a generated flake.
-rw-r--r--pkgs/populate/default.nix42
-rw-r--r--tests/scenarios.nix39
2 files changed, 65 insertions, 16 deletions
diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix
index ce17305..c707b7c 100644
--- a/pkgs/populate/default.nix
+++ b/pkgs/populate/default.nix
@@ -236,12 +236,34 @@ let
throw "krops: source ${name} draws inputs from missing source ${source}"
else
source;
+ # Where the input at path, a list of input names, of the flake in the
+ # source named name is in the flake that the source's inputs get drawn
+ # into: the flake itself, or its generated flake. On the way, inputs
+ # that get drawn from sources are the flakes of those sources, where
+ # drawing went on, so the path continues there. A generated flake has
+ # its source and the sources that one draws from as inputs, and nothing
+ # else, so the path names them there.
+ drawnPath = name: path: let
+ follows = flakeFollows sources.${name};
+ parents = filter
+ (prefix: any (x: x.path == prefix) follows)
+ (map (n: take n path) (range 1 (length path - 1)));
+ parent = last parents;
+ from = (findFirst (x: x.path == parent) null follows).source;
+ rest = drawnPath from (drop (length parent) path);
+ in
+ if generated ? ${name} then
+ if parents == [] then [ (inputName name) ] ++ path
+ else [ (inputName from) ] ++ rest
+ else
+ if parents == [] then path
+ else parent ++ rest;
# Sets the positional parameters to nix flake lock's arguments for
# drawing the inputs of the flake in source name from other sources.
overrideArgs = name: /* sh */ ''
set --
${concatMapStrings (x: /* sh */ ''
- set -- "$@" --override-input ${quote (concatStringsSep "/" x.path)} ${url (check name x.source)}
+ set -- "$@" --override-input ${quote (concatStringsSep "/" (drawnPath name x.path))} ${url (check name x.source)}
'') (flakeFollows sources.${name})}
'';
# Rewrites the lock file of the flake in a file or git source in place.
@@ -381,21 +403,9 @@ let
# Nix ignores overrides below an input that follows another input, so
# an input of an input drawn from a source gets overridden at that
# source.
- override = x: let
- drawnParents = filter
- (path: any (y: y.path == path) follows)
- (map (n: take n x.path) (range 1 (length x.path - 1)));
- parent = if drawnParents == [] then null else last drawnParents;
- base =
- if parent == null then name
- else (findFirst (y: y.path == parent) null follows).source;
- rest = if parent == null then x.path else drop (length parent) x.path;
- in
- "inputs.${toJSON (inputName base)}" +
- # A source with a generated flake has itself as input therein.
- optionalString (parent != null && generated ? ${base})
- ".inputs.${toJSON (inputName base)}" +
- concatMapStrings (name: ".inputs.${toJSON name}") rest +
+ override = x:
+ "inputs." +
+ concatMapStringsSep ".inputs." toJSON (drawnPath name x.path) +
".follows = ${toJSON (inputName x.source)};";
in /* sh */ ''
(
diff --git a/tests/scenarios.nix b/tests/scenarios.nix
index a7dff66..d4df931 100644
--- a/tests/scenarios.nix
+++ b/tests/scenarios.nix
@@ -1385,6 +1385,45 @@ assert !(krops.isLocalTarget (targets.remote "/"));
'';
};
}))
+ # The same one level deeper: the input gets drawn from a source below
+ # an input that the source in between draws from a source itself.
+ (modes "override-deep" ({ draw, flake, ... }: {
+ phases = twice {
+ sources = {
+ x.flake = {
+ url = "/fixtures/top"; inherit fetchOnTarget;
+ } // draw { dep = { follows = "b"; inputs.p.inputs.z.follows = "c2"; }; };
+ b.file = { path = "/fixtures/two"; } // draw { p.follows = "c3"; };
+ c2.file = "/fixtures/c2";
+ c3.file = "/fixtures/c3";
+ };
+ check = ''
+ expect_eq msg "$(nix_eval_online ${flake "x"}#msg)" 'top:two:c3(c2)+inner,sub'
+ '';
+ };
+ }))
+ # A source with a rewritten lock file draws an input from a source with
+ # a generated flake, and an input below it from another source.
+ (s "override-mixed" {
+ phases = twice {
+ sources = {
+ x.flake = {
+ url = "/fixtures/top"; inherit fetchOnTarget;
+ inputs.dep = { follows = "b"; inputs.q.follows = "c2"; };
+ };
+ b.file = {
+ path = "/fixtures/two";
+ rewriteLock = false;
+ inputs.p.follows = "c1";
+ };
+ c1.file = "/fixtures/c1";
+ c2.file = "/fixtures/c2";
+ };
+ check = ''
+ expect_eq msg "$(nix_eval_online "$T"/x#msg)" top:two:c1+c2,sub
+ '';
+ };
+ })
(modes "nonflake-input" ({ draw, flake, ... }: {
phases = twice {
sources = {