1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 19:46:05 +01:00

restic: decouple helper script from linux-specific systemd logic

And make platform support explicit.
This commit is contained in:
Jess 2025-10-19 12:47:22 +13:00 committed by Austin Horstman
parent 12bca6d40a
commit c199de6cd8

View file

@ -43,6 +43,40 @@ let
gnugrep gnugrep
which which
]; ];
# Create the base restic environment variables for both systemd
# and the helper script
mkEnvironment =
backup:
lib.flatten [
"PATH=${backup.ssh-package}/bin"
(attrsToEnvs (
{
RESTIC_PROGRESS_FPS = backup.progressFps;
RESTIC_PASSWORD_FILE = backup.passwordFile;
RESTIC_REPOSITORY = backup.repository;
RESTIC_REPOSITORY_FILE = backup.repositoryFile;
}
// backup.rcloneOptions
))
];
inherit (pkgs.stdenv.hostPlatform) isLinux;
# Until we have launchd support (#7924), mark the options
# not used in the helper script as "linux exclusive"
linuxExclusive =
option:
option
// {
readOnly = pkgs.stdenv.hostPlatform.isDarwin;
description = option.description + ''
This option is only supported on linux.
'';
};
in in
{ {
options.services.restic = { options.services.restic = {
@ -50,7 +84,14 @@ in
backups = lib.mkOption { backups = lib.mkOption {
description = '' description = ''
Periodic backups to create with Restic. Backup configurations for Restic.
On Linux systems, a corresponding systemd user service
(and optionally a systemd timer for automatic scheduling)
will be created, along with a helper wrapper script.
On non-Linux platforms, only the helper wrapper script
will be created.
''; '';
type = lib.types.attrsOf ( type = lib.types.attrsOf (
lib.types.submodule ( lib.types.submodule (
@ -102,7 +143,8 @@ in
}; };
}; };
inhibitsSleep = lib.mkOption { inhibitsSleep = linuxExclusive (
lib.mkOption {
default = false; default = false;
type = lib.types.bool; type = lib.types.bool;
example = true; example = true;
@ -111,7 +153,8 @@ in
to block system idling so you may need to enable polkitd with to block system idling so you may need to enable polkitd with
{option}`security.polkit.enable`. {option}`security.polkit.enable`.
''; '';
}; }
);
repository = lib.mkOption { repository = lib.mkOption {
type = with lib.types; nullOr str; type = with lib.types; nullOr str;
@ -136,7 +179,8 @@ in
''; '';
}; };
paths = lib.mkOption { paths = linuxExclusive (
lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [ ];
description = '' description = ''
@ -148,9 +192,11 @@ in
"/var/lib/postgresql" "/var/lib/postgresql"
"/home/user/backup" "/home/user/backup"
]; ];
}; }
);
exclude = lib.mkOption { exclude = linuxExclusive (
lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [ ];
description = '' description = ''
@ -163,9 +209,11 @@ in
"/home/*/.cache" "/home/*/.cache"
".git" ".git"
]; ];
}; }
);
timerConfig = lib.mkOption { timerConfig = linuxExclusive (
lib.mkOption {
type = lib.types.nullOr unitType; type = lib.types.nullOr unitType;
default = { default = {
OnCalendar = "daily"; OnCalendar = "daily";
@ -180,9 +228,11 @@ in
RandomizedDelaySec = "5h"; RandomizedDelaySec = "5h";
Persistent = true; Persistent = true;
}; };
}; }
);
extraBackupArgs = lib.mkOption { extraBackupArgs = linuxExclusive (
lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [ ];
description = '' description = ''
@ -192,7 +242,8 @@ in
"--cleanup-cache" "--cleanup-cache"
"--exclude-file=/etc/nixos/restic-ignore" "--exclude-file=/etc/nixos/restic-ignore"
]; ];
}; }
);
extraOptions = lib.mkOption { extraOptions = lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
@ -206,15 +257,18 @@ in
]; ];
}; };
initialize = lib.mkOption { initialize = linuxExclusive (
lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
description = '' description = ''
Create the repository if it does not already exist. Create the repository if it does not already exist.
''; '';
}; }
);
pruneOpts = lib.mkOption { pruneOpts = linuxExclusive (
lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [ ];
description = '' description = ''
@ -232,26 +286,32 @@ in
"--keep-monthly 12" "--keep-monthly 12"
"--keep-yearly 75" "--keep-yearly 75"
]; ];
}; }
);
runCheck = lib.mkOption { runCheck = linuxExclusive (
lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = lib.length config.checkOpts > 0 || lib.length config.pruneOpts > 0; default = lib.length config.checkOpts > 0 || lib.length config.pruneOpts > 0;
defaultText = lib.literalExpression "lib.length config.checkOpts > 0 || lib.length config.pruneOpts > 0"; defaultText = lib.literalExpression "lib.length config.checkOpts > 0 || lib.length config.pruneOpts > 0";
description = "Whether to run 'restic check' with the provided `checkOpts` options."; description = "Whether to run 'restic check' with the provided `checkOpts` options.";
example = true; example = true;
}; }
);
checkOpts = lib.mkOption { checkOpts = linuxExclusive (
lib.mkOption {
type = with lib.types; listOf str; type = with lib.types; listOf str;
default = [ ]; default = [ ];
description = '' description = ''
A list of options for 'restic check'. A list of options for 'restic check'.
''; '';
example = [ "--with-cache" ]; example = [ "--with-cache" ];
}; }
);
dynamicFilesFrom = lib.mkOption { dynamicFilesFrom = linuxExclusive (
lib.mkOption {
type = with lib.types; nullOr str; type = with lib.types; nullOr str;
default = null; default = null;
description = '' description = ''
@ -260,23 +320,28 @@ in
are given to the '--files-from' option. are given to the '--files-from' option.
''; '';
example = "find /home/alice/git -type d -name .git"; example = "find /home/alice/git -type d -name .git";
}; }
);
backupPrepareCommand = lib.mkOption { backupPrepareCommand = linuxExclusive (
lib.mkOption {
type = with lib.types; nullOr str; type = with lib.types; nullOr str;
default = null; default = null;
description = '' description = ''
A script that must run before starting the backup process. A script that must run before starting the backup process.
''; '';
}; }
);
backupCleanupCommand = lib.mkOption { backupCleanupCommand = linuxExclusive (
lib.mkOption {
type = with lib.types; nullOr str; type = with lib.types; nullOr str;
default = null; default = null;
description = '' description = ''
A script that must run after finishing the backup process. A script that must run after finishing the backup process.
''; '';
}; }
);
createWrapper = lib.mkOption { createWrapper = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
@ -326,12 +391,16 @@ in
}; };
}; };
config = lib.mkIf cfg.enable { config = lib.mkIf cfg.enable (
lib.mkMerge [
{
assertions = lib.mapAttrsToList (n: v: { assertions = lib.mapAttrsToList (n: v: {
assertion = lib.xor (v.repository == null) (v.repositoryFile == null); assertion = lib.xor (v.repository == null) (v.repositoryFile == null);
message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set"; message = "services.restic.backups.${n}: exactly one of repository or repositoryFile should be set";
}) cfg.backups; }) cfg.backups;
}
(lib.mkIf isLinux {
systemd.user.services = lib.mapAttrs' ( systemd.user.services = lib.mapAttrs' (
name: backup: name: backup:
let let
@ -404,19 +473,7 @@ in
CacheDirectoryMode = "0700"; CacheDirectoryMode = "0700";
PrivateTmp = true; PrivateTmp = true;
Environment = [ Environment = mkEnvironment backup ++ [ "RESTIC_CACHE_DIR=%C" ];
"RESTIC_CACHE_DIR=%C"
"PATH=${backup.ssh-package}/bin"
]
++ attrsToEnvs (
{
RESTIC_PROGRESS_FPS = backup.progressFps;
RESTIC_PASSWORD_FILE = backup.passwordFile;
RESTIC_REPOSITORY = backup.repository;
RESTIC_REPOSITORY_FILE = backup.repositoryFile;
}
// backup.rcloneOptions
);
ExecStart = ExecStart =
lib.optional doBackup backupCmd lib.optional doBackup backupCmd
@ -476,7 +533,9 @@ in
}; };
} }
) cfg.backups; ) cfg.backups;
})
(lib.mkIf isLinux {
systemd.user.timers = lib.mapAttrs' ( systemd.user.timers = lib.mapAttrs' (
name: backup: name: backup:
lib.nameValuePair "restic-backups-${name}" { lib.nameValuePair "restic-backups-${name}" {
@ -486,12 +545,14 @@ in
Timer = backup.timerConfig; Timer = backup.timerConfig;
} }
) (lib.filterAttrs (_: v: v.timerConfig != null) cfg.backups); ) (lib.filterAttrs (_: v: v.timerConfig != null) cfg.backups);
})
{
home.packages = lib.mapAttrsToList ( home.packages = lib.mapAttrsToList (
name: backup: name: backup:
let let
serviceName = "restic-backups-${name}"; serviceName = "restic-backups-${name}";
backupService = config.systemd.user.services.${serviceName}; environment = mkEnvironment backup;
notPathVar = x: !(lib.hasPrefix "PATH" x); notPathVar = x: !(lib.hasPrefix "PATH" x);
extraOptions = lib.concatMap (arg: [ extraOptions = lib.concatMap (arg: [
"-o" "-o"
@ -523,16 +584,15 @@ in
''} ''}
# Set same environment variables as the systemd service # Set same environment variables as the systemd service
${lib.pipe backupService.Service.Environment [ ${lib.pipe environment [
(lib.filter notPathVar) (lib.filter notPathVar)
lib.concatLines lib.concatLines
]} ]}
# Override this as %C will not work
RESTIC_CACHE_DIR=$HOME/.cache/${serviceName} RESTIC_CACHE_DIR=$HOME/.cache/${serviceName}
PATH=${ PATH=${
lib.pipe backupService.Service.Environment [ lib.pipe environment [
(lib.filter (lib.hasPrefix "PATH=")) (lib.filter (lib.hasPrefix "PATH="))
lib.head lib.head
(lib.removePrefix "PATH=") (lib.removePrefix "PATH=")
@ -543,7 +603,9 @@ in
''; '';
} }
) (lib.filterAttrs (_: v: v.createWrapper) cfg.backups); ) (lib.filterAttrs (_: v: v.createWrapper) cfg.backups);
}; }
]
);
meta.maintainers = [ lib.maintainers.jess ]; meta.maintainers = [ lib.maintainers.jess ];
} }