Turn scheduled commands to multi-entry

This commit is contained in:
Yan Lin 2025-10-04 22:08:37 +02:00
parent 479535c294
commit 592bc8cb45
2 changed files with 88 additions and 73 deletions

View file

@ -16,11 +16,9 @@
downloadDir = "/mnt/storage/Media/web"; downloadDir = "/mnt/storage/Media/web";
}; };
# Scheduled YouTube downloads services.scheduled-commands.video-downloads = {
services.scheduled-commands = {
enable = true; enable = true;
serviceName = "video-downloads"; description = "Download web videos from favorite channels";
serviceDescription = "Download web videos from favorite channels";
interval = "*-*-* 08:00:00"; interval = "*-*-* 08:00:00";
randomDelay = "1h"; randomDelay = "1h";
commands = [ commands = [

View file

@ -5,93 +5,110 @@ with lib;
let let
cfg = config.services.scheduled-commands; cfg = config.services.scheduled-commands;
# Create wrapper script that sources user shell environment # Create wrapper script for a specific instance
commandScript = pkgs.writeScriptBin "${cfg.serviceName}-run" '' makeCommandScript = name: instanceCfg: pkgs.writeScriptBin "${name}-run" ''
#!${pkgs.zsh}/bin/zsh #!${pkgs.zsh}/bin/zsh
# Source user shell to get environment and functions # Source user shell to get environment and functions
source ${config.home.homeDirectory}/.zshrc source ${config.home.homeDirectory}/.zshrc
# Execute commands sequentially # Execute commands sequentially
${concatStringsSep "\n" cfg.commands} ${concatStringsSep "\n" instanceCfg.commands}
''; '';
# Filter for enabled instances
enabledInstances = filterAttrs (_: instanceCfg: instanceCfg.enable) cfg;
in in
{ {
options.services.scheduled-commands = { options.services.scheduled-commands = mkOption {
enable = mkEnableOption "scheduled command execution service"; type = types.attrsOf (types.submodule {
options = {
enable = mkEnableOption "this scheduled command instance";
commands = mkOption { commands = mkOption {
type = types.listOf types.str; type = types.listOf types.str;
default = []; default = [];
example = [ example = [
"echo 'Starting backup...'" "echo 'Starting backup...'"
"rsync -av /source /destination" "rsync -av /source /destination"
"echo 'Backup completed'" "echo 'Backup completed'"
]; ];
description = "List of shell commands to execute sequentially"; description = "List of shell commands to execute sequentially";
}; };
interval = mkOption { interval = mkOption {
type = types.str; type = types.str;
default = "daily"; default = "daily";
example = "*-*-* 08:00:00"; example = "*-*-* 08:00:00";
description = "Systemd timer schedule (OnCalendar format)"; description = "Systemd timer schedule (OnCalendar format)";
}; };
randomDelay = mkOption { randomDelay = mkOption {
type = types.str; type = types.str;
default = "0"; default = "0";
example = "1h"; example = "1h";
description = "Random delay before execution (e.g., '30m', '1h')"; description = "Random delay before execution (e.g., '30m', '1h')";
}; };
serviceName = mkOption { description = mkOption {
type = types.str; type = types.str;
default = "scheduled-commands"; default = "Execute scheduled commands";
example = "video-downloads"; example = "Download YouTube videos from subscriptions";
description = "Name for the systemd service and timer"; description = "Description for the systemd service";
}; };
};
serviceDescription = mkOption { });
type = types.str; default = {};
default = "Execute scheduled commands"; description = "Scheduled command execution services";
example = "Download YouTube videos from subscriptions";
description = "Description for the systemd service";
};
}; };
config = mkIf cfg.enable { config = mkMerge [
# Install the wrapper script # Install wrapper scripts for all enabled instances
home.packages = [ commandScript ]; {
home.packages = mapAttrsToList (name: instanceCfg:
makeCommandScript name instanceCfg
) enabledInstances;
}
systemd.user.services.${cfg.serviceName} = { # Create systemd services and timers for all enabled instances
Unit = { {
Description = cfg.serviceDescription; systemd.user.services = mapAttrs' (name: instanceCfg:
After = [ "network-online.target" ]; nameValuePair name {
}; Unit = {
Description = instanceCfg.description;
After = [ "network-online.target" ];
};
Service = { Service = {
Type = "oneshot"; Type = "oneshot";
ExecStart = "${commandScript}/bin/${cfg.serviceName}-run"; ExecStart = "${makeCommandScript name instanceCfg}/bin/${name}-run";
StandardOutput = "journal"; StandardOutput = "journal";
StandardError = "journal"; StandardError = "journal";
}; };
};
systemd.user.timers.${cfg.serviceName} = { Install = {
Unit = { WantedBy = mkForce [];
Description = "Timer for ${cfg.serviceDescription}"; };
}; }
) enabledInstances;
Timer = { systemd.user.timers = mapAttrs' (name: instanceCfg:
OnCalendar = cfg.interval; nameValuePair name {
Persistent = true; Unit = {
RandomizedDelaySec = cfg.randomDelay; Description = "Timer for ${instanceCfg.description}";
}; };
Install = { Timer = {
WantedBy = [ "timers.target" ]; OnCalendar = instanceCfg.interval;
}; Persistent = true;
}; RandomizedDelaySec = instanceCfg.randomDelay;
}; };
Install = {
WantedBy = [ "timers.target" ];
};
}
) enabledInstances;
}
];
} }