460 lines
13 KiB
Nix
460 lines
13 KiB
Nix
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
|
|
let
|
|
cfg = config.services.angrr;
|
|
direnvCfg = config.programs.direnv.angrr;
|
|
toml = pkgs.formats.toml { };
|
|
exampleSettings = {
|
|
temporary-root-policies = {
|
|
direnv = {
|
|
path-regex = "/\\.direnv/";
|
|
period = "14d";
|
|
};
|
|
result = {
|
|
path-regex = "/result[^/]*$";
|
|
period = "3d";
|
|
};
|
|
};
|
|
profile-policies = {
|
|
system = {
|
|
profile-paths = [ "/nix/var/nix/profiles/system" ];
|
|
keep-since = "14d";
|
|
keep-latest-n = 5;
|
|
keep-booted-system = true;
|
|
keep-current-system = true;
|
|
keep-n-per-bucket = [
|
|
{
|
|
bucket-window = "1 day";
|
|
bucket-amount = 7;
|
|
}
|
|
{
|
|
bucket-window = "1 week";
|
|
bucket-amount = 4;
|
|
}
|
|
];
|
|
};
|
|
user = {
|
|
enable = false;
|
|
profile-paths = [
|
|
"~/.local/state/nix/profiles/profile"
|
|
"/nix/var/nix/profiles/per-user/root/profile"
|
|
];
|
|
keep-since = "1d";
|
|
keep-latest-n = 1;
|
|
keep-booted-system = false;
|
|
keep-current-system = false;
|
|
};
|
|
};
|
|
};
|
|
settingsOptions = {
|
|
freeformType = toml.type;
|
|
options = {
|
|
owned-only = lib.mkOption {
|
|
type =
|
|
with lib.types;
|
|
enum [
|
|
"auto"
|
|
"true"
|
|
"false"
|
|
];
|
|
default = "auto";
|
|
description = ''
|
|
Only monitors owned symbolic link target of GC roots.
|
|
|
|
- "auto": behaves like true for normal users, false for root.
|
|
- "true": only monitor GC roots owned by the current user.
|
|
- "false": monitor all GC roots.
|
|
'';
|
|
};
|
|
temporary-root-policies = lib.mkOption {
|
|
type = with lib.types; attrsOf (submodule temporaryRootPolicyOptions);
|
|
default = { };
|
|
description = ''
|
|
Policies for temporary GC roots(e.g. result and direnv).
|
|
'';
|
|
};
|
|
profile-policies = lib.mkOption {
|
|
type = with lib.types; attrsOf (submodule profilePolicyOptions);
|
|
default = { };
|
|
description = ''
|
|
Profile GC root policies.
|
|
'';
|
|
};
|
|
touch = {
|
|
project-globs = lib.mkOption {
|
|
type = with lib.types; listOf str;
|
|
default = [
|
|
"!.git"
|
|
];
|
|
description = ''
|
|
List of glob patterns to include or exclude files when touching GC roots.
|
|
|
|
Only applied when `angrr touch` is invoked with the `--project` flag.
|
|
Patterns use an inverted gitignore-style semantics.
|
|
See <https://docs.rs/ignore/latest/ignore/overrides/struct.OverrideBuilder.html#method.add>.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
commonPolicyOptions = {
|
|
options = {
|
|
enable = lib.mkEnableOption "this angrr policy" // {
|
|
default = true;
|
|
example = false;
|
|
};
|
|
};
|
|
};
|
|
temporaryRootPolicyOptions = {
|
|
freeformType = toml.type;
|
|
imports = [ commonPolicyOptions ];
|
|
options = {
|
|
path-regex = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = ''
|
|
Regex pattern to match the GC root path.
|
|
'';
|
|
};
|
|
period = lib.mkOption {
|
|
type = with lib.types; nullOr str;
|
|
default = null;
|
|
description = ''
|
|
Retention period for the GC roots matched by this policy.
|
|
'';
|
|
};
|
|
priority = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 100;
|
|
description = ''
|
|
Priority of this policy.
|
|
|
|
Lower number means higher priority, if multiple policies monitor the
|
|
same path, the one with higher priority will be applied.
|
|
'';
|
|
};
|
|
filter = lib.mkOption {
|
|
type = with lib.types; nullOr (submodule filterOptions);
|
|
default = null;
|
|
description = ''
|
|
External filter program to further filter GC roots matched by this policy.
|
|
'';
|
|
};
|
|
ignore-prefixes = lib.mkOption {
|
|
type = with lib.types; nullOr (listOf str);
|
|
default = null;
|
|
description = ''
|
|
List of path prefixes to ignore.
|
|
|
|
If null is specified, angrr builtin settings will be used.
|
|
'';
|
|
};
|
|
ignore-prefixes-in-home = lib.mkOption {
|
|
type = with lib.types; nullOr (listOf str);
|
|
default = null;
|
|
description = ''
|
|
Path prefixes to ignore under home directory.
|
|
|
|
If null is specified, angrr builtin settings will be used.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
profilePolicyOptions = {
|
|
freeformType = toml.type;
|
|
imports = [ commonPolicyOptions ];
|
|
options = {
|
|
profile-paths = lib.mkOption {
|
|
type = with lib.types; listOf str;
|
|
description = ''
|
|
Paths to the Nix profile.
|
|
|
|
When angrr runs in owned-only mode, and the option begins with `~`,
|
|
it will be expanded to the home directory of the current user.
|
|
|
|
When angrr does not run in owned-only mode, and the option begins with `~`,
|
|
it will be expanded to the home of all users discovered respectively.
|
|
'';
|
|
};
|
|
keep-since = lib.mkOption {
|
|
type = with lib.types; nullOr str;
|
|
default = null;
|
|
description = ''
|
|
Retention period for the GC roots in this profile.
|
|
'';
|
|
};
|
|
keep-latest-n = lib.mkOption {
|
|
type = with lib.types; nullOr int;
|
|
default = null;
|
|
description = ''
|
|
Keep the latest N GC roots in this profile.
|
|
'';
|
|
};
|
|
keep-current-system = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to keep the current system generation. Only useful for system profiles.
|
|
'';
|
|
};
|
|
keep-booted-system = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = false;
|
|
description = ''
|
|
Whether to keep the last booted system generation. Only useful for system profiles.
|
|
'';
|
|
};
|
|
keep-n-per-bucket = lib.mkOption {
|
|
type = with lib.types; listOf (submodule keepNPerBucketOptions);
|
|
default = [ ];
|
|
description = ''
|
|
Specify a list of rules having n, bucket-window, and bucket-amount attributes.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
filterOptions = {
|
|
freeformType = toml.type;
|
|
options = {
|
|
program = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = ''
|
|
Path to the external filter program.
|
|
'';
|
|
};
|
|
arguments = lib.mkOption {
|
|
type = with lib.types; listOf str;
|
|
default = [ ];
|
|
description = ''
|
|
Extra command-line arguments pass to the external filter program.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
keepNPerBucketOptions = {
|
|
freeformType = toml.type;
|
|
options = {
|
|
n = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 1;
|
|
description = ''
|
|
Retain n generations every bucket-window duration for bucket-amount buckets.
|
|
'';
|
|
};
|
|
bucket-window = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = ''
|
|
The duration of the bucket window.
|
|
'';
|
|
};
|
|
bucket-amount = lib.mkOption {
|
|
type = lib.types.int;
|
|
default = 1;
|
|
description = ''
|
|
The number of buckets to keep.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
# toml.generate does not support null values, we need to filter them out first
|
|
filteredSettings = lib.filterAttrsRecursive (name: value: value != null) cfg.settings;
|
|
originalConfigFile = toml.generate "angrr.toml" filteredSettings;
|
|
validatedConfigFile = pkgs.runCommand "angrr-config.toml" { } ''
|
|
${lib.getExe cfg.package} validate --config "${originalConfigFile}" > $out
|
|
'';
|
|
|
|
configFileMigrationMsg = ''
|
|
This option has been removed since angrr 0.2.0.
|
|
Please use `services.angrr.settings` to configure retention policies through configuration file.
|
|
|
|
See <https://github.com/linyinfeng/angrr/tree/main?tab=readme-ov-file#nixos-module-usage> for a configuration example.
|
|
'';
|
|
in
|
|
{
|
|
meta.maintainers = pkgs.angrr.meta.maintainers;
|
|
imports = [
|
|
(lib.mkRemovedOptionModule [ "services" "angrr" "removeRoot" ] configFileMigrationMsg)
|
|
(lib.mkRemovedOptionModule [ "services" "angrr" "ownedOnly" ] configFileMigrationMsg)
|
|
];
|
|
options = {
|
|
services.angrr = {
|
|
enable = lib.mkEnableOption "angrr";
|
|
package = lib.mkPackageOption pkgs "angrr" { };
|
|
logLevel = lib.mkOption {
|
|
type =
|
|
with lib.types;
|
|
enum [
|
|
"off"
|
|
"error"
|
|
"warn"
|
|
"info"
|
|
"debug"
|
|
"trace"
|
|
];
|
|
default = "info";
|
|
description = ''
|
|
Set the log level of angrr.
|
|
'';
|
|
};
|
|
extraArgs = lib.mkOption {
|
|
type = with lib.types; listOf str;
|
|
default = [ ];
|
|
description = ''
|
|
Extra command-line arguments pass to angrr.
|
|
'';
|
|
};
|
|
period = lib.mkOption {
|
|
type = with lib.types; nullOr str;
|
|
default = null;
|
|
description = ''
|
|
If set, it configures {option}`services.angrr.settings` to a preset that
|
|
monitor .direnv, results, system, and user profiles,
|
|
retaining GC roots that are younger than the specified period.
|
|
'';
|
|
};
|
|
settings = lib.mkOption {
|
|
type = lib.types.submodule settingsOptions;
|
|
example = exampleSettings;
|
|
description = ''
|
|
Global configuration for angrr in TOML format.
|
|
'';
|
|
};
|
|
configFile = lib.mkOption {
|
|
type = with lib.types; nullOr path;
|
|
default = validatedConfigFile;
|
|
defaultText = "TOML file generated from {option}`services.angrr.settings`";
|
|
description = ''
|
|
Path to the angrr configuration file in TOML format.
|
|
|
|
If not set, the configuration generated from {option}`services.angrr.settings` will be used.
|
|
If specified, {option}`services.angrr.settings` will be ignored.
|
|
'';
|
|
};
|
|
enableNixGcIntegration = lib.mkOption {
|
|
type = lib.types.bool;
|
|
description = ''
|
|
Whether to enable nix-gc.service integration.
|
|
'';
|
|
};
|
|
timer = {
|
|
enable = lib.mkEnableOption "angrr timer";
|
|
dates = lib.mkOption {
|
|
type = lib.types.str;
|
|
default = "03:00";
|
|
description = ''
|
|
How often or when the retention policy is performed.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
programs.direnv.angrr = {
|
|
enable = lib.mkEnableOption "angrr direnv integration" // {
|
|
default = true;
|
|
example = false;
|
|
};
|
|
autoUse = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
example = false;
|
|
description = ''
|
|
Whether to automatically use angrr before loading .envrc.
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable (
|
|
lib.mkMerge [
|
|
{
|
|
assertions = [
|
|
{
|
|
assertion = cfg.enableNixGcIntegration -> config.nix.gc.automatic;
|
|
message = "angrr nix-gc.service integration requires `nix.gc.automatic = true`";
|
|
}
|
|
];
|
|
services.angrr.enableNixGcIntegration = lib.mkDefault config.nix.gc.automatic;
|
|
}
|
|
|
|
{
|
|
environment.etc."angrr/config.toml".source = cfg.configFile;
|
|
|
|
systemd.services.angrr = {
|
|
description = "Auto Nix GC Roots Retention";
|
|
script = ''
|
|
${lib.getExe cfg.package} run \
|
|
--log-level "${cfg.logLevel}" \
|
|
--no-prompt \
|
|
${lib.escapeShellArgs cfg.extraArgs}
|
|
'';
|
|
environment.ANGRR_LOG_STYLE = "systemd";
|
|
serviceConfig = {
|
|
Type = "oneshot";
|
|
};
|
|
};
|
|
|
|
environment.systemPackages = [ cfg.package ];
|
|
}
|
|
|
|
(lib.mkIf cfg.timer.enable {
|
|
systemd.timers.angrr = {
|
|
timerConfig = {
|
|
OnCalendar = cfg.timer.dates;
|
|
};
|
|
wantedBy = [ "timers.target" ];
|
|
};
|
|
})
|
|
|
|
(lib.mkIf cfg.enableNixGcIntegration {
|
|
systemd.services.angrr = {
|
|
wantedBy = [ "nix-gc.service" ];
|
|
before = [ "nix-gc.service" ];
|
|
};
|
|
})
|
|
|
|
(lib.mkIf (config.programs.direnv.enable && direnvCfg.enable) {
|
|
environment.etc."direnv/lib/angrr.sh".source = "${cfg.package}/share/direnv/lib/angrr.sh";
|
|
programs.direnv.direnvrcExtra = lib.mkIf direnvCfg.autoUse ''
|
|
_angrr_auto_use "$@"
|
|
'';
|
|
})
|
|
|
|
# When period is set, configure a preset retention policy
|
|
# Users can still override settings via services.angrr.settings
|
|
(lib.mkIf (cfg.period != null) {
|
|
services.angrr.settings = {
|
|
temporary-root-policies = {
|
|
direnv = {
|
|
path-regex = "/\\.direnv/";
|
|
period = cfg.period;
|
|
};
|
|
result = {
|
|
path-regex = "/result[^/]*$";
|
|
period = cfg.period;
|
|
};
|
|
};
|
|
profile-policies = {
|
|
system = {
|
|
profile-paths = [ "/nix/var/nix/profiles/system" ];
|
|
keep-since = cfg.period;
|
|
keep-booted-system = true;
|
|
keep-current-system = true;
|
|
};
|
|
user = {
|
|
profile-paths = [
|
|
"~/.local/state/nix/profiles/profile"
|
|
"/nix/var/nix/profiles/per-user/root/profile"
|
|
];
|
|
keep-since = cfg.period;
|
|
};
|
|
};
|
|
};
|
|
})
|
|
]
|
|
);
|
|
}
|