NixOS Generations Trimmer: Difference between revisions
imported>Nix Page to help with generations cleanup |
imported>Nix m fix defaults help |
||
Line 139: | Line 139: | ||
Usage: | Usage: | ||
trim-generations.sh (defaults are: Keep-Gens= | trim-generations.sh (defaults are: Keep-Gens=10 Keep-Days=7 Profile=user) | ||
If you enter any parameters, you must enter all three. | If you enter any parameters, you must enter all three. | ||
Line 161: | Line 161: | ||
## Defaults | ## Defaults | ||
keepGens= | keepGensDef=10; keepDaysDef=7 | ||
keepGens=$keepGensDef; keepDays=$keepDaysDef | |||
## Usage | ## Usage | ||
usage () { | usage () { | ||
printf "Usage:\n\t trim-generations.sh (defaults are: Keep-Gens=$ | printf "Usage:\n\t trim-generations.sh (defaults are: Keep-Gens=$keepGensDef Keep-Days=$keepDaysDef Profile=user)\n\n" | ||
printf "If you enter any parameters, you must enter all three.\n\n" | printf "If you enter any parameters, you must enter all three.\n\n" | ||
printf "Example:\n\t trim-generations.sh 15 10 home-manager\n" | printf "Example:\n\t trim-generations.sh 15 10 home-manager\n" |
Revision as of 07:55, 26 September 2021
Overview
The normal options available in NixOS to cleanup old generations might be too limiting. The script below is interactive and smart.
By default (run with no arguments), the script keeps the last 7 days of generations and will prompt you to remove older generations if they are more than 10 generations behind the current one. So it always keeps at least 10 generations and always preserves generations from the last week.
You can specify the number of days to always keep generations, and you can specify the number of generations to always keep.
By default, it works on the default profile for the user running it.
Other profile options:
- If you are root, you can run on the
system
profile - If you are a normal user, you can run on either the
home-manager
orchannels
profiles too.
Example script output
Without parameters (defaults)
❯ ./trim-generations.sh
Keeping default: 10 generations OR 7 days, whichever is more
Operating on profile: /nix/var/nix/profiles/per-user/user/profile
oldest generation: 27
oldest generation created: 2021-09-24
minutes before now: 3918
hours before now: 65
days before now: 2
current generation: 33
current generation created: 2021-09-25
All generations are no more than 7 days older than current generation.
Oldest gen difference from current gen: 1
Nothing to do!
With parameters
❯ ./trim-generations.sh 3 0 home-manager
OK! Keep Gens = 3 Keep Days = 0
Operating on profile: /nix/var/nix/profiles/per-user/user/home-manager
oldest generation: 58
oldest generation created: 2021-09-24
minutes before now: 3922
hours before now: 65
days before now: 2
current generation: 65
current generation created: 2021-09-25
Something to do...
Found the following generation(s) to delete:
generation 58 2021-09-24, 2 day(s) old
generation 59 2021-09-25, 1 day(s) old
generation 60 2021-09-25, 1 day(s) old
generation 61 2021-09-25, 1 day(s) old
generation 62 2021-09-25, 1 day(s) old
Do you want to delete these? [Y/n]:
With parameters (not enough though)
Example if you accidentally don't specify all parameters / arguments.
❯ sudo ./trim-generations.sh 2 0
Error: Not enough arguments.
Usage:
trim-generations.sh (defaults are: Keep-Gens=10 Keep-Days=7 Profile=user)
If you enter any parameters, you must enter all three.
Example:
trim-generations.sh 15 10 home-manager
... this will work on the home-manager profile and keep all generations from the last 10 days, and keep at least 15 generations no matter how old.
Profile choices available: user, home-manager, channels, system (root only)
Run as root (default)
❯ sudo ./trim-generations.sh
Keeping default: 10 generations OR 7 days, whichever is more
Operating on profile: /nix/var/nix/profiles/default
oldest generation: 8
oldest generation created: 2021-09-04
minutes before now: 32725
hours before now: 545
days before now: 22
current generation: 8
current generation created: 2021-09-04
All generations are no more than 7 days older than current generation.
Oldest gen days difference from current gen: 0
Nothing to do!
Run as root (on system profile)
❯ sudo ./trim-generations.sh 2 0 system
OK! Keep Gens = 2 Keep Days = 0
Operating on profile: /nix/var/nix/profiles/system
oldest generation: 11
oldest generation created: 2021-09-22
minutes before now: 6807
hours before now: 113
days before now: 4
current generation: 12
current generation created: 2021-09-26
Oldest generation (11) is only 1 generations behind current (12). Nothing to do!
Run as root (wrong profile)
Example if you accidentally give wrong profile (no home-manager on root):
❯ sudo ./trim-generations.sh 3 0 home-manager
Error: Do not understand your third argument. Should be one of: (user / system)
Usage:
trim-generations.sh (defaults are: Keep-Gens=10 Keep-Days=7 Profile=user)
If you enter any parameters, you must enter all three.
Example:
trim-generations.sh 15 10 home-manager
... this will work on the home-manager profile and keep all generations from the last 10 days, and keep at least 15 generations no matter how old.
Profile choices available: user, home-manager, channels, system (root only)
Script (trim-generations.sh)
trim-generations.sh
#!/usr/bin/env bash
set -euo pipefail
## Defaults
keepGensDef=10; keepDaysDef=7
keepGens=$keepGensDef; keepDays=$keepDaysDef
## Usage
usage () {
printf "Usage:\n\t trim-generations.sh (defaults are: Keep-Gens=$keepGensDef Keep-Days=$keepDaysDef Profile=user)\n\n"
printf "If you enter any parameters, you must enter all three.\n\n"
printf "Example:\n\t trim-generations.sh 15 10 home-manager\n"
printf "... this will work on the home-manager profile and keep all generations from the last 10 days, and keep at least 15 generations no matter how old.\n"
printf "\nProfile choices available: \t user, home-manager, channels, system (root only)\n"
}
## Handle parameters (and change if root)
if [[ $EUID -ne 0 ]]; then
profile=$(readlink /home/$USER/.nix-profile)
else
profile="/nix/var/nix/profiles/default"
fi
if (( $# < 1 )); then
printf "Keeping default: 10 generations OR 7 days, whichever is more\n"
elif [[ $# -le 2 ]]; then
printf "\nError: Not enough arguments.\n\n" >&2
usage
exit 1
elif (( $# > 4)); then
printf "\nError: Too many arguments.\n\n" >&2
usage
exit 2
else
keepGens=$1; keepDays=$2;
(( keepGens < 1 )) && keepGens=1
(( keepDays < 0 )) && keepDays=0
if [[ $EUID -ne 0 ]]; then
if [[ $3 == "user" ]] || [[ $3 == "default" ]]; then
profile=$(readlink /home/$USER/.nix-profile)
elif [[ $3 == "home-manager" ]]; then
profile="/nix/var/nix/profiles/per-user/$USER/home-manager"
elif [[ $3 == "channels" ]]; then
profile="/nix/var/nix/profiles/per-user/$USER/channels"
else
printf "\nError: Do not understand your third argument. Should be one of: (user / home-manager/ channels)\n\n"
usage
exit 3
fi
else
if [[ $3 == "system" ]]; then
profile="/nix/var/nix/profiles/system"
elif [[ $3 == "user" ]] || [[ $3 == "default" ]]; then
profile="/nix/var/nix/profiles/default"
else
printf "\nError: Do not understand your third argument. Should be one of: (user / system)\n\n"
usage
exit 3
fi
fi
printf "OK! \t Keep Gens = $keepGens \t Keep Days = $keepDays\n\n"
fi
printf "Operating on profile: \t $profile\n\n"
## Runs at the end, to decide whether to delete profiles that match chosen parameters.
choose () {
local default="$1"
local prompt="$2"
local answer
read -p "$prompt" answer
[ -z "$answer" ] && answer="$default"
case "$answer" in
[yY1] ) #printf "answered yes!\n"
nix-env --delete-generations ${!gens[@]}
exit 0
;;
[nN0] ) printf "answered no! exiting\n"
exit 6;
;;
* ) printf "%b" "Unexpected answer '$answer'!" >&2
exit 7;
;;
esac
} # end of function choose
## Query nix-env for generations list
IFS=$'\n' nixGens=( $(nix-env --list-generations -p $profile | sed 's:^\s*::; s:\s*$::' | tr '\t' ' ' | tr -s ' ') )
timeNow=$(date +%s)
## Get info on oldest generation
IFS=' ' read -r -a oldestGenArr <<< "${nixGens[0]}"
oldestGen=${oldestGenArr[0]}
oldestDate=${oldestGenArr[1]}
printf "%-30s %s\n" "oldest generation:" $oldestGen
#oldestDate=${nixGens[0]:3:19}
printf "%-30s %s\n" "oldest generation created:" $oldestDate
oldestTime=$(date -d "$oldestDate" +%s)
oldestElapsedSecs=$((timeNow-oldestTime))
oldestElapsedMins=$((oldestElapsedSecs/60))
oldestElapsedHours=$((oldestElapsedMins/60))
oldestElapsedDays=$((oldestElapsedHours/24))
printf "%-30s %s\n" "minutes before now:" $oldestElapsedMins
printf "%-30s %s\n" "hours before now:" $oldestElapsedHours
printf "%-30s %s\n\n" "days before now:" $oldestElapsedDays
## Get info on current generation
for i in "${nixGens[@]}"; do
IFS=' ' read -r -a iGenArr <<< "$i"
genNumber=${iGenArr[0]}
genDate=${iGenArr[1]}
if [[ "$i" =~ current ]]; then
currentGen=$genNumber
printf "%-30s %s\n" "current generation:" $currentGen
currentDate=$genDate
printf "%-30s %s\n\n" "current generation created:" $currentDate
currentTime=$(date -d "$currentDate" +%s)
fi
done
## Compare oldest and current generations
timeBetweenOldestAndCurrent=$((currentTime-oldestTime))
elapsedDays=$((timeBetweenOldestAndCurrent/60/60/24))
generationsDiff=$((currentGen-oldestGen))
## Figure out what we should do, based on generations and options
if [[ elapsedDays -le keepDays ]]; then
printf "All generations are no more than $keepDays days older than current generation. \nOldest gen days difference from current gen: $elapsedDays \n\n\tNothing to do!\n"
exit 4;
elif [[ generationsDiff -lt keepGens ]]; then
printf "Oldest generation ($oldestGen) is only $generationsDiff generations behind current ($currentGen). \t Nothing to do!\n"
exit 5;
else
printf "\tSomething to do...\n"
declare -a gens
for i in "${nixGens[@]}"; do
IFS=' ' read -r -a iGenArr <<< "$i"
genNumber=${iGenArr[0]}
genDiff=$((currentGen-genNumber))
genDate=${iGenArr[1]}
genTime=$(date -d "$genDate" +%s)
elapsedSecs=$((timeNow-genTime))
genDaysOld=$((elapsedSecs/60/60/24))
if [[ genDaysOld -gt keepDays ]] && [[ genDiff -ge keepGens ]]; then
gens["$genNumber"]="$genDate, $genDaysOld day(s) old"
fi
done
printf "\nFound the following generation(s) to delete:\n"
for K in "${!gens[@]}"; do
printf "generation $K \t ${gens[$K]}\n"
done
printf "\n"
choose "y" "Do you want to delete these? [Y/n]: "
fi