From ca5752d4faecad8b87ba59892b9ca2a3d8350837 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 16 May 2023 11:21:50 -0400 Subject: [PATCH 1/4] Add another case to the `nix-collect-garbage -d` test --- tests/gc.sh | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/gc.sh b/tests/gc.sh index 95669e25c..9d92ad758 100644 --- a/tests/gc.sh +++ b/tests/gc.sh @@ -56,25 +56,34 @@ testCollectGarbageD () { clearProfiles # Run two `nix-env` commands, should create two generations of # the profile - nix-env -f ./user-envs.nix -i foo-1.0 - nix-env -f ./user-envs.nix -i foo-2.0pre1 - [[ $(nix-env --list-generations | wc -l) -eq 2 ]] + nix-env -f ./user-envs.nix -i foo-1.0 "$@" + nix-env -f ./user-envs.nix -i foo-2.0pre1 "$@" + [[ $(nix-env --list-generations "$@" | wc -l) -eq 2 ]] # Clear the profile history. There should be only one generation # left nix-collect-garbage -d - [[ $(nix-env --list-generations | wc -l) -eq 1 ]] + [[ $(nix-env --list-generations "$@" | wc -l) -eq 1 ]] } + # `nix-env` doesn't work with CA derivations, so let's ignore that bit if we're # using them if [[ -z "${NIX_TESTS_CA_BY_DEFAULT:-}" ]]; then testCollectGarbageD + # Run the same test, but forcing the profiles an arbitrary location. + rm ~/.nix-profile + ln -s $TEST_ROOT/blah ~/.nix-profile + testCollectGarbageD + # 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 - ln -s $NIX_STATE_DIR/profiles/per-user/me ~/.nix-profile - testCollectGarbageD + testCollectGarbageD --profile "$NIX_STATE_DIR/profiles/per-user/me" fi From d4a2ced9cb99253a277c1507baf001d51871842f Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 16 May 2023 11:25:45 -0400 Subject: [PATCH 2/4] Split out `nix-collect-garbage -d` test to new file Good for test parallelism, and separation of concerns (core GC vs profiles deleting). --- tests/gc.sh | 37 ------------------------------- tests/local.mk | 1 + tests/nix-collect-garbage-d.sh | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 37 deletions(-) create mode 100644 tests/nix-collect-garbage-d.sh diff --git a/tests/gc.sh b/tests/gc.sh index 9d92ad758..ad09a8b39 100644 --- a/tests/gc.sh +++ b/tests/gc.sh @@ -50,40 +50,3 @@ if test -e $outPath/foobar; then false; fi # Check that the store is empty. rmdir $NIX_STORE_DIR/.links rmdir $NIX_STORE_DIR - -## Test `nix-collect-garbage -d` -testCollectGarbageD () { - clearProfiles - # Run two `nix-env` commands, should create two generations of - # the profile - nix-env -f ./user-envs.nix -i foo-1.0 "$@" - nix-env -f ./user-envs.nix -i foo-2.0pre1 "$@" - [[ $(nix-env --list-generations "$@" | wc -l) -eq 2 ]] - - # Clear the profile history. There should be only one generation - # left - nix-collect-garbage -d - [[ $(nix-env --list-generations "$@" | wc -l) -eq 1 ]] -} - -# `nix-env` doesn't work with CA derivations, so let's ignore that bit if we're -# using them -if [[ -z "${NIX_TESTS_CA_BY_DEFAULT:-}" ]]; then - testCollectGarbageD - - # Run the same test, but forcing the profiles an arbitrary location. - rm ~/.nix-profile - ln -s $TEST_ROOT/blah ~/.nix-profile - testCollectGarbageD - - # 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 - testCollectGarbageD --profile "$NIX_STATE_DIR/profiles/per-user/me" -fi diff --git a/tests/local.mk b/tests/local.mk index 9e340e2e2..2da47d243 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -16,6 +16,7 @@ nix_tests = \ flakes/flake-in-submodule.sh \ ca/gc.sh \ gc.sh \ + nix-collect-garbage-d.sh \ remote-store.sh \ legacy-ssh-store.sh \ lang.sh \ diff --git a/tests/nix-collect-garbage-d.sh b/tests/nix-collect-garbage-d.sh new file mode 100644 index 000000000..bf30f8938 --- /dev/null +++ b/tests/nix-collect-garbage-d.sh @@ -0,0 +1,40 @@ +source common.sh + +clearStore + +## Test `nix-collect-garbage -d` + +# TODO make `nix-env` doesn't work with CA derivations, and make +# `ca/nix-collect-garbage-d.sh` wrapper. + +testCollectGarbageD () { + clearProfiles + # Run two `nix-env` commands, should create two generations of + # the profile + nix-env -f ./user-envs.nix -i foo-1.0 "$@" + nix-env -f ./user-envs.nix -i foo-2.0pre1 "$@" + [[ $(nix-env --list-generations "$@" | wc -l) -eq 2 ]] + + # Clear the profile history. There should be only one generation + # left + nix-collect-garbage -d + [[ $(nix-env --list-generations "$@" | wc -l) -eq 1 ]] +} + +testCollectGarbageD + +# Run the same test, but forcing the profiles an arbitrary location. +rm ~/.nix-profile +ln -s $TEST_ROOT/blah ~/.nix-profile +testCollectGarbageD + +# 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 +testCollectGarbageD --profile "$NIX_STATE_DIR/profiles/per-user/me" From b55f26c65f4ca0e80bd0f3c6436333f054acf6d6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 16 May 2023 12:41:39 -0400 Subject: [PATCH 3/4] Improve `nix-env --delete-generations` docs Co-authored-by: Valentin Gagarin --- .../command-ref/nix-env/delete-generations.md | 59 ++++++++++++++++--- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md index 92cb7f0d9..d828a5b9e 100644 --- a/doc/manual/src/command-ref/nix-env/delete-generations.md +++ b/doc/manual/src/command-ref/nix-env/delete-generations.md @@ -9,14 +9,39 @@ # Description This operation deletes the specified generations of the current profile. -The generations can be a list of generation numbers, the special value -`old` to delete all non-current generations, a value such as `30d` to -delete all generations older than the specified number of days (except -for the generation that was active at that point in time), or a value -such as `+5` to keep the last `5` generations ignoring any newer than -current, e.g., if `30` is the current generation `+5` will delete -generation `25` and all older generations. Periodically deleting old -generations is important to make garbage collection effective. + +*generations* can be a one of the following: + +- `...`:\ + A list of generation numbers, each one a separate command-line argument. + + Delete exactly the profile generations given by their generation number. + Deleting the current generation is not allowed. + +- The special value `old` + + Delete all generations older than the current one. + +- `d`:\ + The last *days* days + + *Example*: `30d` + + Delete all generations older than *days* days. + The generation that was active at that point in time is excluded, and will not be deleted. + +- `+`:\ + The last *count* generations up to the present + + *Example*: `+5` + + Keep the last *count* generations, along with any newer than current. + +Periodically deleting old generations is important to make garbage collection +effective. +The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion. + +[store object]: @docroot@/glossary.md#gloss-store-object {{#include ./opt-common.md}} @@ -28,19 +53,35 @@ generations is important to make garbage collection effective. # Examples +## Delete explicit generation numbers + ```console $ nix-env --delete-generations 3 4 8 ``` +Delete the generations numbered 3, 4, and 8, so long as the current active generation is not any of those. + +## Keep most-recent by count count + ```console $ nix-env --delete-generations +5 ``` +Suppose `30` is the current generation, and we currently have generations numbered `20` through `32`. + +Then this command will delete generations `20` through `25` (`<= 30 - 5`), +and keep generations `26` through `31` (`> 30 - 5`). + +## Keep most-recent in days + ```console $ nix-env --delete-generations 30d ``` +This command will delete all generations older than 30 days, except for the generation that was active 30 days ago (if it currently exists). + +## Delete all older + ```console $ nix-env --profile other_profile --delete-generations old ``` - From 5b7e285727e23135a65597bcaab6d043d6d61ed6 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 16 May 2023 10:32:38 -0400 Subject: [PATCH 4/4] Improve `nix-collect-garbage` docs Co-authored-by: Valentin Gagarin --- .../src/command-ref/nix-collect-garbage.md | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md index 51db5fc67..a679ceaf7 100644 --- a/doc/manual/src/command-ref/nix-collect-garbage.md +++ b/doc/manual/src/command-ref/nix-collect-garbage.md @@ -1,6 +1,6 @@ # Name -`nix-collect-garbage` - delete unreachable store paths +`nix-collect-garbage` - delete unreachable [store objects] # Synopsis @@ -8,17 +8,57 @@ # Description -The command `nix-collect-garbage` is mostly an alias of [`nix-store ---gc`](@docroot@/command-ref/nix-store/gc.md), that is, it deletes all -unreachable paths in the Nix store to clean up your system. However, -it provides two additional options: `-d` (`--delete-old`), which -deletes all old generations of all profiles in `/nix/var/nix/profiles` -by invoking `nix-env --delete-generations old` on all profiles (of -course, this makes rollbacks to previous configurations impossible); -and `--delete-older-than` *period*, where period is a value such as -`30d`, which deletes all generations older than the specified number -of days in all profiles in `/nix/var/nix/profiles` (except for the -generations that were active at that point in time). +The command `nix-collect-garbage` is mostly an alias of [`nix-store --gc`](@docroot@/command-ref/nix-store/gc.md). +That is, it deletes all unreachable [store objects] in the Nix store to clean up your system. + +However, it provides two additional options, +[`--delete-old`](#opt-delete-old) and [`--delete-older-than`](#opt-delete-older-than), +which also delete old [profiles], allowing potentially more [store objects] to be deleted because profiles are also garbage collection roots. +These options are the equivalent of running +[`nix-env --delete-generations`](@docroot@/command-ref/nix-env/delete-generations.md) +with various augments on multiple profiles, +prior to running `nix-collect-garbage` (or just `nix-store --gc`) without any flags. + +> **Note** +> +> Deleting previous configurations makes rollbacks to them impossible. + +These flags should be used with care, because they potentially delete generations of profiles used by other users on the system. + +## Locations searched for profiles + +`nix-collect-garbage` cannot know about all profiles; that information doesn't exist. +Instead, it looks in a few locations, and acts on all profiles it finds there: + +1. The default profile locations as specified in the [profiles] section of the manual. + +2. > **NOTE** + > + > Not stable; subject to change + > + > Do not rely on this functionality; it just exists for migration purposes and is may change in the future. + > These deprecated paths remain a private implementation detail of Nix. + + `$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`. + + With the exception of `$NIX_STATE_DIR/profiles/per-user/root` and `$NIX_STATE_DIR/profiles/default`, these directories are no longer used by other commands. + `nix-collect-garbage` looks there anyways in order to clean up profiles from older versions of Nix. + +# Options + +These options are for deleting old [profiles] prior to deleting unreachable [store objects]. + +- [`--delete-old`](#opt-delete-old) / `-d`\ + Delete all old generations of profiles. + + This is the equivalent of invoking `nix-env --delete-generations old` on each found profile. + +- [`--delete-older-than`](#opt-delete-older-than) *period*\ + Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time). + *period* is a value such as `30d`, which would mean 30 days. + + This is the equivalent of invoking [`nix-env --delete-generations `](@docroot@/command-ref/nix-env/delete-generations.md#generations-days) on each found profile. + See the documentation of that command for additional information about the *period* argument. {{#include ./opt-common.md}} @@ -32,3 +72,6 @@ generations of each profile, do ```console $ nix-collect-garbage -d ``` + +[profiles]: @docroot@/command-ref/files/profiles.md +[store objects]: @docroot@/glossary.md#gloss-store-object