Fix dry-run flag for nix-collect-garbage

`nix-collect-garbage --dry-run` previously elided the entire garbage
collection check, meaning that it would just exit the script without
printing anything.

This change makes the dry run flag instead set the GC action to
`gcReturnDead` rather than `gcDeleteDead`, and then continue with the
script. So if you set `--dry-run`, it will print the paths it *would*
have garbage collected, but not actually delete them.

I filed a bug for this: #432 but then realised I could give fixing it a go myself.

Change-Id: I062dbf1a80bbab192b5fd0b3a453a0b555ad16f2
This commit is contained in:
Quantum Jump 2024-07-05 20:22:53 +01:00
parent accfd8aa9d
commit 6e0ca02425
5 changed files with 88 additions and 5 deletions

View file

@ -96,6 +96,10 @@ puck:
forgejo: puck forgejo: puck
github: puckipedia github: puckipedia
quantumjump:
display_name: Quantum Jump
github: QuantumBJump
r-vdp: r-vdp:
github: r-vdp github: r-vdp

View file

@ -0,0 +1,29 @@
---
synopsis: Fix nix-collect-garbage --dry-run
issues: [fj#432]
cls: [1566]
category: Fixes
credits: [quantumjump]
---
`nix-collect-garbage --dry-run` did not previously give any output - it simply
exited without even checking to see what paths would be deleted.
```
$ nix-collect-garbage --dry-run
$
```
We updated the behaviour of the flag such that instead it prints out how many
paths it *would* delete, but doesn't actually delete them.
```
$ nix-collect-garbage --dry-run
finding garbage collector roots...
determining live/dead paths...
...
<nix store paths>
...
2670 store paths deleted, 0.00MiB freed
$
```

View file

@ -89,12 +89,21 @@ static int main_nix_collect_garbage(int argc, char * * argv)
// Run the actual garbage collector. // Run the actual garbage collector.
if (!dryRun) { if (!dryRun) {
options.action = GCOptions::gcDeleteDead;
} else {
options.action = GCOptions::gcReturnDead;
}
auto store = openStore(); auto store = openStore();
auto & gcStore = require<GcStore>(*store); auto & gcStore = require<GcStore>(*store);
options.action = GCOptions::gcDeleteDead;
GCResults results; GCResults results;
PrintFreed freed(true, results); PrintFreed freed(true, results);
gcStore.collectGarbage(options, results); gcStore.collectGarbage(options, results);
if (dryRun) {
// Only print results for dry run; when !dryRun, paths will be printed as they're deleted.
for (auto & i : results.paths) {
printInfo("%s", i);
}
} }
return 0; return 0;

View file

@ -73,6 +73,7 @@ functional_tests_scripts = [
'flakes/flake-in-submodule.sh', 'flakes/flake-in-submodule.sh',
'gc.sh', 'gc.sh',
'nix-collect-garbage-d.sh', 'nix-collect-garbage-d.sh',
'nix-collect-garbage-dry-run.sh',
'remote-store.sh', 'remote-store.sh',
'legacy-ssh-store.sh', 'legacy-ssh-store.sh',
'lang.sh', 'lang.sh',

View file

@ -0,0 +1,40 @@
source common.sh
clearStore
## Test `nix-collect-garbage --dry-run`
testCollectGarbageDryRun () {
clearProfiles
# Install then uninstall a package
# This should leave packages ready to be garbage collected.
nix-env -f ./user-envs.nix -i foo-1.0
nix-env -f ./user-envs.nix -e foo-1.0
nix-env --delete-generations old
[[ $(nix-store --gc --print-dead | wc -l) -eq 7 ]]
nix-collect-garbage --dry-run
[[ $(nix-store --gc --print-dead | wc -l) -eq 7 ]]
}
testCollectGarbageDryRun
# Run the same test, but forcing the profiles an arbitrary location.
rm ~/.nix-profile
ln -s $TEST_ROOT/blah ~/.nix-profile
testCollectGarbageDryRun
# Run the same test, but forcing the profiles at their legacy location under
# /nix/var/nix.
#
# Note that we *don't* use the default profile; `nix-collect-garbage` will
# need to check the legacy conditional unconditionally not just follow
# `~/.nix-profile` to pass this test.
#
# Regression test for #8294
rm ~/.nix-profile
testCollectGarbageDryRun --profile "$NIX_STATE_DIR/profiles/per-user/me"