fedora-csb-system-manager
1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.nix-flake-updater;
12
13 # Use the nix-flake-update package
14 updateScript = pkgs.writeShellScript "nix-flake-update-wrapper" ''
15 export REPO_PATH="${cfg.repoPath}"
16 export FLAKE_PATH="${cfg.flakePath}"
17 export GIT_REMOTE="${cfg.gitRemote}"
18 export BRANCH_PREFIX="${cfg.branchPrefix}"
19 export NTFY_TOPIC="${cfg.ntfyTopic}"
20 export NTFY_SERVER="${cfg.ntfyServer}"
21 export BUILD_SYSTEMS="${toString cfg.buildSystems}"
22 export DRY_RUN="${toString cfg.dryRun}"
23 ${optionalString (cfg.ntfyTokenFile != null) ''export NTFY_TOKEN_FILE="${cfg.ntfyTokenFile}"''}
24
25 # Execute the packaged update script (already has tools in PATH)
26 exec ${pkgs.nix-flake-update}/bin/nix-flake-update
27 '';
28
29in
30{
31 options.services.nix-flake-updater = {
32 enable = mkEnableOption "automated Nix flake.lock updates";
33
34 repoPath = mkOption {
35 type = types.str;
36 example = "/home/user/nixos-config";
37 description = "Path to the git repository containing the flake";
38 };
39
40 flakePath = mkOption {
41 type = types.str;
42 default = cfg.repoPath;
43 example = "/home/user/nixos-config";
44 description = "Path to the flake (usually same as repoPath)";
45 };
46
47 gitRemote = mkOption {
48 type = types.str;
49 default = "origin";
50 description = "Git remote name to push to";
51 };
52
53 branchPrefix = mkOption {
54 type = types.str;
55 default = "flake-update-";
56 description = "Prefix for update branches";
57 };
58
59 buildSystems = mkOption {
60 type = types.listOf types.str;
61 default = [ ];
62 example = [
63 "aomi"
64 "sakhalin"
65 ];
66 description = "List of NixOS systems to build for verification";
67 };
68
69 schedule = mkOption {
70 type = types.str;
71 default = "weekly";
72 example = "Mon *-*-* 02:00:00";
73 description = "Systemd timer schedule (OnCalendar format or 'weekly'/'daily')";
74 };
75
76 ntfyTopic = mkOption {
77 type = types.str;
78 default = "nix-updates";
79 description = "ntfy topic for notifications";
80 };
81
82 ntfyServer = mkOption {
83 type = types.str;
84 default = "https://ntfy.sh";
85 example = "http://ntfy.sbr.pm";
86 description = "ntfy server URL";
87 };
88
89 ntfyTokenFile = mkOption {
90 type = types.nullOr types.path;
91 default = null;
92 description = "Path to file containing ntfy authentication token (optional)";
93 };
94
95 dryRun = mkOption {
96 type = types.bool;
97 default = false;
98 description = "If true, don't push to remote (testing mode)";
99 };
100
101 user = mkOption {
102 type = types.str;
103 default = "root";
104 description = "User to run the update as";
105 };
106
107 randomizedDelaySec = mkOption {
108 type = types.int;
109 default = 3600;
110 description = "Random delay in seconds before starting (0-value)";
111 };
112 };
113
114 config = mkIf cfg.enable {
115 systemd.services.nix-flake-updater = {
116 description = "Automated Nix flake.lock updater";
117
118 serviceConfig = {
119 Type = "oneshot";
120 User = cfg.user;
121 ExecStart = "${updateScript}";
122 Environment = ''"GIT_SSH_COMMAND=ssh -o ControlMaster=no"'';
123
124 # Security hardening
125 PrivateTmp = true;
126 ProtectSystem = "strict";
127 ProtectHome = "read-only";
128 ReadWritePaths = [
129 cfg.repoPath
130 "/var/log/nix-flake-updater"
131 # Worktree location (script creates worktrees in ~/tmp)
132 "/home/${cfg.user}/tmp"
133 # Nix cache for flake fetcher
134 "/home/${cfg.user}/.cache/nix"
135 ];
136 NoNewPrivileges = true;
137
138 # Logging
139 StandardOutput = "journal";
140 StandardError = "journal";
141 SyslogIdentifier = "nix-flake-updater";
142 };
143
144 # Don't fail if update fails (e.g., no changes, build failures)
145 unitConfig = {
146 SuccessExitStatus = "0 1";
147 };
148 };
149
150 systemd.timers.nix-flake-updater = {
151 description = "Timer for automated Nix flake.lock updates";
152 wantedBy = [ "timers.target" ];
153
154 timerConfig = {
155 OnCalendar = cfg.schedule;
156 RandomizedDelaySec = cfg.randomizedDelaySec;
157 Persistent = true;
158 };
159 };
160
161 # Ensure log directory exists
162 systemd.tmpfiles.rules = [
163 "d /var/log/nix-flake-updater 0750 ${cfg.user} ${config.users.users.${cfg.user}.group} -"
164 ];
165 };
166}