diff options
| -rw-r--r-- | README.md | 17 | ||||
| -rw-r--r-- | pkgs/krops/default.nix | 11 | ||||
| -rw-r--r-- | pkgs/populate/default.nix | 41 | ||||
| -rw-r--r-- | tests/scenarios.nix | 53 |
4 files changed, 115 insertions, 7 deletions
@@ -147,6 +147,15 @@ Create the sentinel file (`/var/src/.populate`) before syncing the new source. Specifies which `nixos-rebuild` operation to perform. +### `prune` (optional, defaults to false) + +Remove everything from the target directory that isn't a source, except +`.populate`, after syncing the sources. This also removes the garbage +collector roots of the inputs of former `flake` and `flake-input` sources in +`.populate/flake-inputs/<name>`, so the garbage collector can collect what +they delivered. Don't use it if the target directory contains anything else +worth keeping, or if several deployments with different sources share it. + ### `useNixOutputMonitor` (optional, defaults to `"opportunistic"`) Specifies when to pipe `nixos-rebuild`'s output to @@ -190,6 +199,10 @@ below `/var/src`, and executes `NIX_PATH=/var/src nix-build -A system '<nixpkgs/ [see `writeDeploy`](#writeDeploy) +### `prune` (optional, defaults to false) + +[see `writeDeploy`](#writeDeploy) + ### `trace` (optional, defaults to false) run nix-build with `--show-trace` @@ -227,6 +240,10 @@ pkgs.krops.writeCommand "deploy-with-swap" { [see `writeDeploy`](#writeDeploy) +### `prune` (optional, defaults to false) + +[see `writeDeploy`](#writeDeploy) + ### `allocateTTY` (optional, defaults to false) whether the ssh session should do a pseudo-terminal allocation. diff --git a/pkgs/krops/default.nix b/pkgs/krops/default.nix index 75a4d14..dcda28f 100644 --- a/pkgs/krops/default.nix +++ b/pkgs/krops/default.nix @@ -80,6 +80,7 @@ in backup ? false, force ? false, allocateTTY ? false, + prune ? false, source, target }: let @@ -87,7 +88,7 @@ in in writers.writeDash name '' set -efu - ${populate { inherit backup force source; target = target'; }} + ${populate { inherit backup force prune source; target = target'; }} ${runShell target' { inherit allocateTTY; } (command target'.path)} ''; @@ -99,6 +100,7 @@ in flake ? null, force ? false, operation ? "switch", + prune ? false, source, target, useNixOutputMonitor ? "opportunistic" @@ -124,8 +126,8 @@ in writers.writeDash name '' set -efu ${lib.optionalString (buildTarget' != target') - (populate { inherit backup force source; target = buildTarget'; })} - ${populate { inherit backup force source; target = target'; }} + (populate { inherit backup force prune source; target = buildTarget'; })} + ${populate { inherit backup force prune source; target = target'; }} ${rebuild { flake = flake'; inherit useNixOutputMonitor; } ([ operation ] ++ lib.optionals crossDeploy [ @@ -142,6 +144,7 @@ in writeTest = name: { backup ? false, force ? false, + prune ? false, source, target, trace ? false @@ -151,7 +154,7 @@ in assert lib.isLocalTarget target'; writers.writeDash name '' set -efu - ${populate { inherit backup force source; target = target'; }} >&2 + ${populate { inherit backup force prune source; target = target'; }} >&2 NIX_PATH=${lib.escapeShellArg target'.path} \ ${nix}/bin/nix-build \ -A system \ diff --git a/pkgs/populate/default.nix b/pkgs/populate/default.nix index 5472b5d..553183e 100644 --- a/pkgs/populate/default.nix +++ b/pkgs/populate/default.nix @@ -169,6 +169,44 @@ let fi ''; + # Removes entries of the target directory that aren't sources, garbage + # collector roots of flake inputs of sources that aren't flake or + # flake-input sources, and uploads left behind by interrupted runs. + prune' = target: sources: let + pattern = names: concatMapStringsSep "|" quote names; + flakeNames = attrNames (filterAttrs + (_: source: elem source.type [ "flake" "flake-input" ]) + sources); + in runShell target /* sh */ '' + set -efu + cd ${quote target.path} + ls -A | while read -r name; do + case $name in + ${pattern (attrNames sources ++ [ ".populate" ])}) ;; + *) + >&2 printf 'pruning %s\n' ${quote target.path}/"$name" + rm -fR ./"$name" + ;; + esac + done + roots=.populate/flake-inputs + if test -d "$roots"; then + ls -A "$roots" | while read -r name; do + case $name in + ${optionalString (flakeNames != []) "${pattern flakeNames}) ;;"} + *) rm -fR "$roots"/"$name" ;; + esac + done + if test -z "$(ls -A "$roots")"; then + rmdir "$roots" + fi + fi + if test -e .populate/flake-uploads; then + chmod -R u+w .populate/flake-uploads + rm -fR .populate/flake-uploads + fi + ''; + pop.derivation = target: source: runShell target /* sh */ '' nix-build -E ${quote source.text} -o ${quote target.path} >&2 ''; @@ -761,7 +799,7 @@ let in -{ backup ? false, force ? false, source, target }: +{ backup ? false, force ? false, prune ? false, source, target }: writers.writeDash "populate.${target.host}" '' set -efu @@ -776,6 +814,7 @@ writers.writeDash "populate.${target.host}" '' ${optionalString backup (do-backup { inherit target; })} ${concatStringsSep "\n" (mapAttrsToList (populate target) source)} ${flakes target source} + ${optionalString prune (prune' target source)} # Stop the ssh master, if any source started one. if ${ssh' target} -q -O check ${quote target.host} 2>/dev/null; then diff --git a/tests/scenarios.nix b/tests/scenarios.nix index 22431a2..c9774bf 100644 --- a/tests/scenarios.nix +++ b/tests/scenarios.nix @@ -64,10 +64,10 @@ let path = "/var/krops/${name}"; target = targets.${targetKind} path; vars = "T=${path}\n"; - phase = { sources, expect ? "success", check ? "", mutate ? null }: let + phase = { sources, prune ? false, expect ? "success", check ? "", mutate ? null }: let command = populate { source = krops.evalSource [ sources ]; - inherit target; + inherit prune target; force = true; }; evaluated = builtins.tryEval (builtins.seq (toString command) true); @@ -480,6 +480,55 @@ in { } ]; }) + # Pruning removes everything sources don't deliver anymore, including + # garbage collector roots, and keeps everything else. + (s "prune" (let + sources = { + a.file = "/fixtures/alt"; + b.flake = { flake = "/fixtures/top"; inherit fetchOnTarget; }; + c.flake-input = { flake = "/fixtures/top"; name = "dep"; inherit fetchOnTarget; }; + d.symlink = "a"; + }; + in { + prepare = '' + mkdir -p "$T" + echo other > "$T"/other + ''; + phases = [ + { + inherit sources; + check = '' + test -e "$T"/.populate/flake-inputs/b || fail "roots of b missing" + test -e "$T"/.populate/flake-inputs/c || fail "roots of c missing" + ''; + } + { + # Without pruning, nothing gets removed. + sources = { inherit (sources) a; }; + check = '' + for name in b c d other .populate/flake-inputs/b .populate/flake-inputs/c; do + test -e "$T"/$name || fail "$name removed" + done + ''; + } + { + # b changes its type, so its roots go, while b stays. + sources = { inherit (sources) a; b.symlink = "a"; }; + prune = true; + check = '' + test -e "$T"/a/flake.nix || fail "a removed" + expect_eq b "$(readlink "$T"/b)" a + test -d "$T"/.populate || fail ".populate removed" + for name in c d other .populate/flake-inputs; do + test ! -e "$T"/$name || fail "$name not pruned" + done + if nix-store --gc --print-roots 2>/dev/null | grep -F "$T/"; then + fail "garbage collector roots remain" + fi + ''; + } + ]; + })) (s "cycle" { phases = [{ sources = { |
