+# nix-profile-gc: More gently remove old profiles
+# Copyright (C) 2022 Scott Worley <scottworley@scottworley.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
{ lib, config, pkgs, ... }:
let
inherit (lib) escapeShellArg;
dryRun = lib.mkOption {
description = "Say what would have been deleted rather than actually deleting profiles";
type = lib.types.bool;
- default = true;
+ default = false;
};
keepLast = lib.mkOption {
description = ''
awhile (so keepLatest won't protect them) generates a bunch of broken profiles (so
keepLast won't protect them) while trying to get up to date.
- This is approximate and has a useful granularity of an hour
- (config.systemd.timers.profile-gc-log-active.timerConfig.OnActiveSec).
- Do not set less than this.
+ This threshold is approximate, see activeMeasurementGranularity.
+ Do not set less than activeMeasurementGranularity!
'';
- # We admonish the user "Do not set less than this." and check it at runtime rather
- # than verifying this with an assertion now because parsing these durations at
- # configuration-time requires import-from-derivation, which we want to avoid. :(
+ # We admonish the user "Do not set less than activeMeasurementGranularity!" and check
+ # it at runtime rather than verifying this with an assertion at evaluation time because
+ # parsing these durations at evaluation-time requires import-from-derivation, which we
+ # want to avoid. :(
type = lib.types.str;
default = "5 days";
};
+ activeMeasurementGranularity = lib.mkOption {
+ description = ''
+ How often to make a note of the currently-active profiles. This is the useful
+ granularity and minimum value of activeThreshold.
+ '';
+ default = "1 hour";
+ };
keepLatest = lib.mkOption {
description = ''
Keep all profiles younger than this duration (systemd.time format).
fi
alive_threshold="$(< ${parse-duration cfg.activeThreshold})"
- alive_loginterval="$(< ${parse-duration config.systemd.timers.profile-gc-log-active.timerConfig.OnActiveSec})"
+ alive_loginterval="$(< ${parse-duration cfg.activeMeasurementGranularity})"
if (( alive_threshold < alive_loginterval ));then
echo "Liveness threshold is too low. Not doing any profile garbage collection." >&2
exit 0
}
declare -A active_targets
- while read target;do
+ while read -r target;do
active_targets[$target]=1
done < <(
verbose_topn ${cfg.logdir}/active-system "" ${escapeShellArg cfg.keepLastActiveSystem}
now=$(${pkgs.coreutils}/bin/date +%s)
age_threshold="$(< ${parse-duration cfg.keepLatest})"
- while read profile;do
+ while read -r profile;do
echo "Contemplating profiles for $profile:" >&2
unset active
declare -A active
- while read p;do
- active[$p]=1
+ while read -r pname;do
+ active[$pname]=1
done < <(verbose_topn ${cfg.logdir}/active-profiles "$profile" ${escapeShellArg cfg.keepLastActive})
current=$(${pkgs.coreutils}/bin/readlink "$profile")
currentgen=''${current%-link}
echo "(Disregarding unrelated profile $p)" >&2
continue
fi
- if [[ "$p" == "$current" ]];then
+ pname=$(${pkgs.coreutils}/bin/basename "$p")
+ if [[ "$pname" == "$current" ]];then
echo "Keeeping current profile $p" >&2
continue
fi
- if [[ "''${active_targets[$(${pkgs.coreutils}/bin/readlink "$p")]}" ]];then
+ if [[ "''${active_targets[$(${pkgs.coreutils}/bin/readlink "$p")]:-}" ]];then
echo "Keeeping active system/boot profile $p" >&2
continue
fi
- if [[ "''${active[$p]}" ]];then
+ if [[ "''${active[$pname]:-}" ]];then
echo "Keeeping active profile $p" >&2
continue
fi
rm "$p"
''}
done
- done < <(${pkgs.findutils}/bin/find ''${NIX_STATE_DIR:-/nix/var/nix}/profiles/ -type l -not -name '*[0-9]-link')
+ done < <(${pkgs.findutils}/bin/find "''${NIX_STATE_DIR:-/nix/var/nix}/profiles/" -type l -not -name '*[0-9]-link')
'';
systemd.timers.profile-gc-log-active = {
wantedBy = [ "timers.target" ];
- timerConfig.OnActiveSec = "1 hour";
+ timerConfig.OnActiveSec = cfg.activeMeasurementGranularity;
+ timerConfig.OnUnitActiveSec = cfg.activeMeasurementGranularity;
};
systemd.services.profile-gc-log-active = {
description =