diff options
| author | tv <tv@krebsco.de> | 2026-11-12 18:24:47 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-13 03:23:49 +0100 |
| commit | 333144080f4a03f1adc1eb7ded0a9d3ae1bfa9ee (patch) | |
| tree | 2bd8f7b80c3bfc2ad2ff59e393005d06b5e732c7 /tests/scenarios.nix | |
| parent | 9359951b4470542049ceb63ad04b7526a4c6c9e8 (diff) | |
populate: draw inputs by rewriting lock files, add materialize and patches
Sources drawing inputs from other sources get the lock file of their
flake rewritten with nix flake lock --override-input, so the flake in
the source itself uses the other sources, and needs no generated flake
to be found. file and git sources get their lock file rewritten in
place. flake and flake-input sources get delivered as a copy with the
rewritten lock file, which gets added to the store with nix-store
--add-fixed, and the target path links to that. With rewriteLock set
to false, and for symlink sources, the source stays as it is and gets
a generated flake in .populate/flakes/<name>, which writeDeploy's flake
parameter uses.
flake and flake-input sources get two more options:
- patches: patches on the deploying machine, which get uploaded to
.populate/patches/<name> and applied in order with patch -p1 on the
target, in the directory the target path shows.
- materialize: the target path becomes a writable copy instead of a
link into the store.
Sources that get patched, rewritten, or materialized get delivered to
.populate/derived/<name>/original, which also roots the delivered store
path, and the flakes step derives the target path from there. Copies
get made again only if what they derive from changes, which a key in
.populate/derived/<name> records.
The matrices of the file, flake, flake-input, and git tests cover both
ways of drawing inputs, those of flake and flake-input materialize, the
deploy test a source with a rewritten lock file, and features and
edge cases run the scenarios drawing inputs both ways. New features
cover patches, a patch that doesn't apply, patches in a subdirectory,
reusing copies, and materializing.
Diffstat (limited to 'tests/scenarios.nix')
| -rw-r--r-- | tests/scenarios.nix | 384 |
1 files changed, 295 insertions, 89 deletions
diff --git a/tests/scenarios.nix b/tests/scenarios.nix index f8e103e..2815c3f 100644 --- a/tests/scenarios.nix +++ b/tests/scenarios.nix @@ -64,6 +64,21 @@ let alt = { alt.file = "/fixtures/alt"; }; + # How a source draws inputs from other sources: not at all, by rewriting + # the lock file of its flake, or by a generated flake. + inputModes = [ "none" "rewrite" "generated" ]; + + # The options of a source drawing inputs in mode. + drawInputs = mode: inputs: optionalAttrs (mode != "none") ({ + inherit inputs; + } // optionalAttrs (mode == "generated") { + rewriteLock = false; + }); + + # The flake of source x that has its inputs drawn in mode. + flakeOf = mode: + if mode == "generated" then ''"$T"/.populate/flakes/x'' else ''"$T"/x''; + # Builds a scenario from a name, a target kind, and phases with sources. # Isolated scenarios start with no target directories and a collected store # on both machines, so no other scenario's store paths or roots stand in for @@ -150,16 +165,22 @@ let targetKinds; # Checks that the source at $T/x is a link to a store path, or to the - # subdirectory sub therein, and evaluates the attribute attr of the flake - # there, through the link, or of its generated flake, after collecting - # garbage. - checkFlake = { attr, expected, subdir ? false, generated ? false }: '' + # subdirectory sub therein, or a writable directory if materialized, and + # evaluates the attribute attr of the flake there, or of its generated + # flake, after collecting garbage. + checkFlake = { + attr, expected, subdir ? false, generated ? false, materialized ? false, + }: (if materialized then '' + test -d "$T"/x && ! test -L "$T"/x || fail "x isn't a directory" + test -w "$T"/x/flake.nix || fail "flake.nix isn't writable" + '' else '' link=$(readlink "$T"/x) ${if subdir then '' expect_eq subdirectory "$(basename "$link")" sub link=$(dirname "$link") '' else ""} expect_eq "store directory" "$(dirname "$link")" /nix/store + '') + '' test ! -e "$T"/.populate/flake-uploads || fail "upload directory remains" gc expect_eq ${attr} "$(nix_eval ${ @@ -219,9 +240,8 @@ assert !(krops.isLocalTarget (targets.remote "/")); inherit (flags) deleteExcluded; exclude = optional (flags.exclude or false) "skip-ex"; filters = optional flags.filters { type = "exclude"; pattern = "skip-f"; }; - inputs = optionalAttrs flags.inputs { dep.follows = "alt"; }; - }; - } // optionalAttrs flags.inputs alt; + } // drawInputs flags.inputs { dep.follows = "alt"; }; + } // optionalAttrs (flags.inputs != "none") alt; check = let excluded = file: set: '' ${if !set then '' @@ -242,10 +262,10 @@ assert !(krops.isLocalTarget (targets.remote "/")); expect_eq stale "$(cat "$T"/x/stale)" stale ''} expect_eq same "$(cat "$T"/x/same)" ${if flags.checksum then "AAAA" else "BBBB"} - '' + optionalString flags.inputs '' + '' + optionalString (flags.inputs != "none") '' # Inputs not drawn from sources aren't delivered, so this needs the # network and can't collect garbage before. - expect_eq msg "$(nix_eval_online "$T"/.populate/flakes/x#msg)" top:alt,sub + expect_eq msg "$(nix_eval_online ${flakeOf flags.inputs}#msg)" top:alt,sub ''; }; }; @@ -254,7 +274,7 @@ assert !(krops.isLocalTarget (targets.remote "/")); checksum = [ false true ]; deleteExcluded = [ false true ]; filters = [ false true ]; - inputs = [ false true ]; + inputs = inputModes; } f ++ [ # The deprecated exclude option, once, because it warns. @@ -264,7 +284,7 @@ assert !(krops.isLocalTarget (targets.remote "/")); deleteExcluded = true; exclude = true; filters = false; - inputs = false; + inputs = "none"; } null; in args // { name = "file-local-exclude"; @@ -306,7 +326,8 @@ assert !(krops.isLocalTarget (targets.remote "/")); flake = matrix "flake" { kind = [ "git" "path" "subdir" "remote" ]; fetchOnTarget = [ false true ]; - inputs = [ false true ]; + inputs = inputModes; + materialize = [ false true ]; } (flags: ctx: let flake = { git = "/fixtures/top"; @@ -315,30 +336,32 @@ assert !(krops.isLocalTarget (targets.remote "/")); remote = "${url}/remote.tar.gz"; }.${flags.kind}; msg = { - git = "top:${if flags.inputs then "alt" else "dep+inner"},sub"; + git = "top:${if flags.inputs != "none" then "alt" else "dep+inner"},sub"; path = msg.git; - remote = "remote:${if flags.inputs then "alt" else "dep+inner"}"; + remote = "remote:${if flags.inputs != "none" then "alt" else "dep+inner"}"; }; in # The flake in a subdirectory has no inputs to draw from sources. - if flags.kind == "subdir" && flags.inputs then null else { + if flags.kind == "subdir" && flags.inputs != "none" then null else { isolate = true; phases = twice ({ sources = { x.flake = { url = flake; - inherit (flags) fetchOnTarget; - inputs = optionalAttrs flags.inputs { dep.follows = "alt"; }; - }; - } // optionalAttrs flags.inputs alt; + inherit (flags) fetchOnTarget materialize; + } // drawInputs flags.inputs { dep.follows = "alt"; }; + } // optionalAttrs (flags.inputs != "none") alt; } // (if copyingWithSudo flags ctx then { expect = "eval-error"; control = withFetchOnTarget; } else { check = if flags.kind == "subdir" then checkFlake { attr = "who"; expected = "sub"; subdir = true; + materialized = flags.materialize; } else checkFlake { - attr = "msg"; expected = msg.${flags.kind}; generated = flags.inputs; + attr = "msg"; expected = msg.${flags.kind}; + generated = flags.inputs == "generated"; + materialized = flags.materialize; }; })); }); @@ -346,10 +369,11 @@ assert !(krops.isLocalTarget (targets.remote "/")); flake-input = concatMap (name: matrix "flake-input-${lib.replaceStrings ["/"] ["."] name}" ({ fetchOnTarget = [ false true ]; + materialize = [ false true ]; } // optionalAttrs (name == "dep") { - inputs = [ false true ]; + inputs = inputModes; }) (flags: ctx: let - inputs = flags.inputs or false; + inputs = flags.inputs or "none"; in { isolate = true; phases = twice ({ @@ -357,10 +381,9 @@ assert !(krops.isLocalTarget (targets.remote "/")); x.flake-input = { url = "/fixtures/top"; inherit name; - inherit (flags) fetchOnTarget; - inputs = optionalAttrs inputs { inner.follows = "alt"; }; - }; - } // optionalAttrs inputs alt; + inherit (flags) fetchOnTarget materialize; + } // drawInputs inputs { inner.follows = "alt"; }; + } // optionalAttrs (inputs != "none") alt; } // (if copyingWithSudo flags ctx then { expect = "eval-error"; control = withFetchOnTarget; @@ -368,11 +391,17 @@ assert !(krops.isLocalTarget (targets.remote "/")); check = { dep = checkFlake { attr = "greeting"; - expected = if inputs then "dep+alt" else "dep+inner"; - generated = inputs; + expected = if inputs != "none" then "dep+alt" else "dep+inner"; + generated = inputs == "generated"; + materialized = flags.materialize; + }; + "dep/inner" = checkFlake { + attr = "who"; expected = "inner"; materialized = flags.materialize; + }; + sub = checkFlake { + attr = "who"; expected = "sub"; subdir = true; + materialized = flags.materialize; }; - "dep/inner" = checkFlake { attr = "who"; expected = "inner"; }; - sub = checkFlake { attr = "who"; expected = "sub"; subdir = true; }; }.${name}; })); })) @@ -382,7 +411,7 @@ assert !(krops.isLocalTarget (targets.remote "/")); shallow = [ false true ]; fetchAlways = [ false true ]; cleanExclude = [ false true ]; - inputs = [ false true ]; + inputs = inputModes; } (flags: _: let check = '' expect_eq HEAD "$(git -C "$T"/x rev-parse HEAD)" ${gitHead} @@ -390,11 +419,12 @@ assert !(krops.isLocalTarget (targets.remote "/")); if flags.shallow then "1" else "2" } test -e "$T"/x/two || fail "two missing" - '' + optionalString flags.inputs '' + '' + optionalString (flags.inputs == "generated") '' grep -q '"git+file:' "$T"/.populate/flakes/x/flake.nix || fail "the Git work tree isn't referred to as git+file:" + '' + optionalString (flags.inputs != "none") '' gc - expect_eq msg "$(nix_eval "$T"/.populate/flakes/x#msg)" git:alt + expect_eq msg "$(nix_eval ${flakeOf flags.inputs}#msg)" git:alt ''; sources = ref: { x.git = { @@ -402,9 +432,8 @@ assert !(krops.isLocalTarget (targets.remote "/")); inherit ref; inherit (flags) shallow fetchAlways; clean.exclude = optional flags.cleanExclude "keepme"; - inputs = optionalAttrs flags.inputs { dep.follows = "alt"; }; - }; - } // optionalAttrs flags.inputs alt; + } // drawInputs flags.inputs { dep.follows = "alt"; }; + } // optionalAttrs (flags.inputs != "none") alt; in { phases = [ # The older commit first, so deploying HEAD changes the work tree, and @@ -501,7 +530,7 @@ assert !(krops.isLocalTarget (targets.remote "/")); # writeDeploy's arguments to nixos-rebuild, which a stand-in records. deploy = matrix "deploy" { - flake = [ "none" "source" "generated" ]; + flake = [ "none" "source" "rewrite" "generated" ]; etcFlake = [ false true ]; buildTarget = [ false true ]; } (flags: ctx: let @@ -517,6 +546,7 @@ assert !(krops.isLocalTarget (targets.remote "/")); ++ { none = []; source = [ "--flake" "${build.path}/x" ]; + rewrite = [ "--flake" "${build.path}/x" ]; # Generated flakes as path:, which an enclosing Git work tree doesn't # capture. generated = [ "--flake" "path:${build.path}/.populate/flakes/x" ]; @@ -544,9 +574,8 @@ assert !(krops.isLocalTarget (targets.remote "/")); x.flake = { url = "/fixtures/top"; fetchOnTarget = ctx.targetKind == "sudo"; - inputs = optionalAttrs (flags.flake == "generated") { - dep.follows = "alt"; - }; + } // drawInputs (if flags.flake == "source" then "none" else flags.flake) { + dep.follows = "alt"; }; }; deploy = { @@ -572,68 +601,84 @@ assert !(krops.isLocalTarget (targets.remote "/")); } // args); copying = targetKind != "sudo"; fetchOnTarget = !copying; - in [ - # A generated flake draws an input from a source with a generated flake. - (s "chain" { + # A scenario for each way of drawing inputs, which f gets as mode, along + # with draw, which adds inputs drawn that way to a source's options, and + # flake, which gives the flake to evaluate for a source. + modes = name: f: map + (mode: s "${name}-${mode}" (f { + draw = drawInputs mode; + flake = n: + if mode == "generated" then ''"$T"/.populate/flakes/${n}'' else ''"$T"/${n}''; + inherit mode; + })) + [ "rewrite" "generated" ]; + in lib.flatten [ + # A source draws an input from a source that draws inputs itself. + (modes "chain" ({ draw, flake, ... }: { phases = twice { sources = alt // { - a.flake = { url = "/fixtures/top"; inherit fetchOnTarget; inputs.dep.follows = "b"; }; + a.flake = { url = "/fixtures/top"; inherit fetchOnTarget; } // draw { dep.follows = "b"; }; b.flake-input = { url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; - inputs.inner.follows = "alt"; - }; + } // draw { inner.follows = "alt"; }; }; check = '' gc - expect_eq msg "$(nix_eval "$T"/.populate/flakes/a#msg)" top:dep+alt,sub + expect_eq msg "$(nix_eval ${flake "a"}#msg)" top:dep+alt,sub ''; }; - }) - # An input of an input drawn from a source gets overridden at that source. - (s "nested" { + })) + # An input of an input drawn from a source gets drawn from another + # source. + (modes "nested" ({ draw, flake, ... }: { phases = twice { sources = alt // { x.flake = { url = "/fixtures/top"; inherit fetchOnTarget; - inputs.dep = { follows = "b"; inputs.inner.follows = "alt"; }; - }; + } // draw { dep = { follows = "b"; inputs.inner.follows = "alt"; }; }; b.flake-input = { url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; }; }; check = '' gc - expect_eq msg "$(nix_eval "$T"/.populate/flakes/x#msg)" top:dep+alt,sub + expect_eq msg "$(nix_eval ${flake "x"}#msg)" top:dep+alt,sub ''; }; - }) - # A generated flake gets locked again when a source it draws from - # changes. - (s "relock" (let - x.file = { path = "/fixtures/filesrc"; inputs.dep.follows = "a"; }; + })) + # The lock file gets rewritten or generated again when a source inputs + # get drawn from changes. + (modes "relock" ({ draw, flake, ... }: let + x.file = { path = "/fixtures/filesrc"; } // draw { dep.follows = "a"; }; in { phases = [ { sources = { inherit x; a.file = "/fixtures/alt"; }; check = '' - expect_eq msg "$(nix_eval_online "$T"/.populate/flakes/x#msg)" top:alt,sub + expect_eq msg "$(nix_eval_online ${flake "x"}#msg)" top:alt,sub ''; } { sources = { inherit x; a.file = "/fixtures/alt2"; }; check = '' - expect_eq msg "$(nix_eval_online "$T"/.populate/flakes/x#msg)" top:alt2,sub + expect_eq msg "$(nix_eval_online ${flake "x"}#msg)" top:alt2,sub ''; } ]; })) - # A generated flake gets removed once its source draws no inputs anymore. - (s "removal" { + # What drawing inputs needs gets removed once a source draws no inputs + # anymore. + (modes "removal" ({ draw, mode, ... }: let + state = { + rewrite = "derived/x/result"; + generated = "flakes/x/flake.lock"; + }.${mode}; + in { phases = [ { sources = alt // { - x.flake = { url = "/fixtures/top"; inherit fetchOnTarget; inputs.dep.follows = "alt"; }; + x.flake = { url = "/fixtures/top"; inherit fetchOnTarget; } // draw { dep.follows = "alt"; }; }; check = '' - test -e "$T"/.populate/flakes/x/flake.lock || fail "generated flake missing" + test -e "$T"/.populate/${state} || fail "${state} missing" ''; } { @@ -641,11 +686,158 @@ assert !(krops.isLocalTarget (targets.remote "/")); x.flake = { url = "/fixtures/top"; inherit fetchOnTarget; }; }; check = '' - test ! -e "$T"/.populate/flakes/x || fail "generated flake remains" + test ! -e "$T"/.populate/${dirOf state} || fail "${dirOf state} remains" + '' + checkFlake { attr = "msg"; expected = "top:dep+inner,sub"; }; + } + ]; + })) + # Patches get applied in order to a copy, which becomes a store path and + # gets made again only when the patches change. + (s "patches" (let + x = patches: { + x.flake-input = { + url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget patches; + }; + }; + both = x [ "/fixtures/patches/hello.patch" "/fixtures/patches/hello2.patch" ]; + in { + phases = [ + { + sources = both; + check = checkFlake { attr = "greeting"; expected = "hello2+inner"; }; + # The key changes whenever the copy gets made again, which + # the store path doesn't show, being the same. + mutate = '' + readlink "$T"/x > "$T".first + stat -c %y "$T"/.populate/derived/x/key > "$T".key + ''; + } + { + sources = both; + check = checkFlake { attr = "greeting"; expected = "hello2+inner"; } + '' + expect_eq "store path" "$(readlink "$T"/x)" "$(cat "$T".first)" + expect_eq "copy made at" "$(stat -c %y "$T"/.populate/derived/x/key)" "$(cat "$T".key)" + ''; + } + { + sources = x [ "/fixtures/patches/hello.patch" ]; + check = checkFlake { attr = "greeting"; expected = "hello+inner"; } + '' + test "$(readlink "$T"/x)" != "$(cat "$T".first)" || fail "store path unchanged" + expect_eq patches "$(ls "$T"/.populate/patches/x)" 0001-hello.patch + ''; + } + { + sources = x []; + check = checkFlake { attr = "greeting"; expected = "dep+inner"; } + '' + test ! -e "$T"/.populate/patches || fail "patches remain" + test ! -e "$T"/.populate/derived || fail "derived remains" ''; } ]; + })) + (s "patch-fails" { + phases = [{ + sources.x.flake-input = { + url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; + patches = [ "/fixtures/patches/bad.patch" ]; + }; + expect.fail = "error: patch bad.patch does not apply to source x"; + }]; }) + # Patches apply in the subdirectory the target path shows. + (s "patch-subdir" { + phases = twice { + sources.x.flake-input = { + url = "/fixtures/top"; name = "sub"; inherit fetchOnTarget; + patches = [ "/fixtures/patches/sub.patch" ]; + }; + check = checkFlake { attr = "who"; expected = "patched"; subdir = true; }; + }; + }) + # A copy with a rewritten lock file gets made again only when a source + # it draws inputs from changes. + (s "rewrite-cache" (let + x.flake-input = { + url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; + inputs.inner.follows = "a"; + }; + in { + phases = [ + { + sources = { inherit x; a.file = "/fixtures/alt"; }; + check = checkFlake { attr = "greeting"; expected = "dep+alt"; }; + # The key changes whenever the copy gets made again, which + # the store path doesn't show, being the same. + mutate = '' + readlink "$T"/x > "$T".first + stat -c %y "$T"/.populate/derived/x/key > "$T".key + ''; + } + { + sources = { inherit x; a.file = "/fixtures/alt"; }; + check = checkFlake { attr = "greeting"; expected = "dep+alt"; } + '' + expect_eq "store path" "$(readlink "$T"/x)" "$(cat "$T".first)" + expect_eq "copy made at" "$(stat -c %y "$T"/.populate/derived/x/key)" "$(cat "$T".key)" + ''; + } + { + sources = { inherit x; a.file = "/fixtures/alt2"; }; + check = checkFlake { attr = "greeting"; expected = "dep+alt2"; } + '' + test "$(readlink "$T"/x)" != "$(cat "$T".first)" || fail "store path unchanged" + ''; + } + ]; + })) + # A materialized input is a writable copy, patched and with a rewritten + # lock file, which gets made again only when what it derives from + # changes, and becomes a link again when not materialized anymore. + (s "materialize" (let + x = patches: alt // { + x.flake-input = { + url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget patches; + materialize = true; + inputs.inner.follows = "alt"; + }; + }; + hello = [ "/fixtures/patches/hello.patch" ]; + in { + phases = [ + { + sources = x hello; + check = '' + test ! -L "$T"/x || fail "x is a link" + test -w "$T"/x/flake.nix || fail "flake.nix isn't writable" + gc + expect_eq greeting "$(nix_eval "$T"/x#greeting)" hello+alt + ''; + mutate = '' + echo local > "$T"/x/local + ''; + } + { + sources = x hello; + check = '' + expect_eq local "$(cat "$T"/x/local)" local + ''; + } + { + sources = x (hello ++ [ "/fixtures/patches/hello2.patch" ]); + check = '' + test ! -e "$T"/x/local || fail "local remains" + gc + expect_eq greeting "$(nix_eval "$T"/x#greeting)" hello2+alt + ''; + } + { + sources = alt // { + x.flake-input = { url = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; }; + }; + check = checkFlake { attr = "greeting"; expected = "dep+inner"; } + '' + test ! -e "$T"/.populate/derived || fail "derived remains" + ''; + } + ]; + })) # Roots follow the delivered input. (s "retarget" { phases = [ @@ -780,7 +972,18 @@ assert !(krops.isLocalTarget (targets.remote "/")); roots = n: '' expect_eq roots "$(ls "$T"/.populate/flake-inputs/x 2>/dev/null | wc -l)" ${toString n} ''; - in [ + # A scenario for each way of drawing inputs, which f gets as mode, along + # with draw, which adds inputs drawn that way to a source's options, and + # flake, which gives the flake to evaluate for a source. + modes = name: f: map + (mode: s "${name}-${mode}" (f { + draw = drawInputs mode; + flake = n: + if mode == "generated" then ''"$T"/.populate/flakes/${n}'' else ''"$T"/${n}''; + inherit mode; + })) + [ "rewrite" "generated" ]; + in lib.flatten [ # The registry entry kropsremote, which fixtures.sh adds on the deploying # machine, points at ${url}/remote.tar.gz. The deploying machine has no # global registry, which it couldn't download. @@ -796,17 +999,19 @@ assert !(krops.isLocalTarget (targets.remote "/")); check = checkFlake { attr = "who"; expected = "sub"; subdir = true; }; }; }) - (s "subdir-inputs" { + (modes "subdir-inputs" ({ draw, mode, ... }: { phases = twice { sources = alt // { x.flake = { url = "git+file:/fixtures/monoin?dir=sub"; inherit fetchOnTarget; - inputs.dep.follows = "alt"; - }; + } // draw { dep.follows = "alt"; }; + }; + check = checkFlake { + attr = "msg"; expected = "monoin:alt"; subdir = true; + generated = mode == "generated"; }; - check = checkFlake { attr = "msg"; expected = "monoin:alt"; subdir = true; generated = true; }; }; - }) + })) (s "dirty" { phases = twice { sources.x.flake = { url = "/fixtures/dirty"; inherit fetchOnTarget; }; @@ -942,49 +1147,50 @@ assert !(krops.isLocalTarget (targets.remote "/")); } ]; })) - (s "override-generated" { + # An input of an input drawn from a source that draws inputs itself + # gets drawn from another source. + (modes "override" ({ draw, flake, ... }: { phases = twice { sources = { x.flake = { url = "/fixtures/top"; inherit fetchOnTarget; - inputs.dep = { follows = "b"; inputs.q.follows = "c2"; }; - }; - b.file = { path = "/fixtures/two"; inputs.p.follows = "c1"; }; + } // draw { dep = { follows = "b"; inputs.q.follows = "c2"; }; }; + b.file = { path = "/fixtures/two"; } // draw { p.follows = "c1"; }; c1.file = "/fixtures/c1"; c2.file = "/fixtures/c2"; }; check = '' - expect_eq msg "$(nix_eval_online "$T"/.populate/flakes/x#msg)" top:two:c1+c2,sub + expect_eq msg "$(nix_eval_online ${flake "x"}#msg)" top:two:c1+c2,sub ''; }; - }) - (s "nonflake-input" { + })) + (modes "nonflake-input" ({ draw, flake, ... }: { phases = twice { sources = { - x.file = { path = "/fixtures/usedata"; inputs.data.follows = "data"; }; + x.file = { path = "/fixtures/usedata"; } // draw { data.follows = "data"; }; data.file = "/fixtures/data"; }; check = '' - expect_eq msg "$(nix_eval "$T"/.populate/flakes/x#msg)" data:hello + expect_eq msg "$(nix_eval ${flake "x"}#msg)" data:hello ''; }; - }) + })) # The target directory in a Git work tree. - (s "gitdir" { + (modes "gitdir" ({ draw, flake, ... }: { prepare = '' mkdir -p "$T" git -C "$T" init -q ''; phases = twice { sources = alt // { - x.file = { path = "/fixtures/filesrc"; inputs.dep.follows = "alt"; }; + x.file = { path = "/fixtures/filesrc"; } // draw { dep.follows = "alt"; }; }; check = '' # Not just the path, which Nix takes for the enclosing work tree. - expect_eq msg "$(nix_eval_online path:"$T"/.populate/flakes/x#msg)" top:alt,sub + expect_eq msg "$(nix_eval_online path:${flake "x"}#msg)" top:alt,sub ''; }; - }) + })) # Output of the same length, which rsync's quick check doesn't notice. (s "pipe-change" { prepareDeployer = '' @@ -1011,17 +1217,17 @@ assert !(krops.isLocalTarget (targets.remote "/")); } ]; }) - (s "source-name" { + (modes "source-name" ({ draw, flake, ... }: { phases = twice { sources = { "alt-1.0".file = "/fixtures/alt"; - x.file = { path = "/fixtures/filesrc"; inputs.dep.follows = "alt-1.0"; }; + x.file = { path = "/fixtures/filesrc"; } // draw { dep.follows = "alt-1.0"; }; }; check = '' - expect_eq msg "$(nix_eval_online "$T"/.populate/flakes/x#msg)" top:alt,sub + expect_eq msg "$(nix_eval_online ${flake "x"}#msg)" top:alt,sub ''; }; - }) + })) # An upload an interrupted run left behind. (s "upload-leftover" { phases = [ |
