]> git.scottworley.com Git - auto-upgrade-with-pinch/blame - modules/auto-upgrade.nix
Separate pull-updates snippet
[auto-upgrade-with-pinch] / modules / auto-upgrade.nix
CommitLineData
901670f5
SW
1{ config, lib, pkgs, ... }:
2with lib;
364c110c
SW
3let
4 cfg = config.system.autoUpgradeWithPinch;
9dbfef33
SW
5 pull-repo-snippet = ''
6 (
7 cd /etc/nixos
8 ${pkgs.git}/bin/git fetch
9 PATH="${pkgs.keyedgit cfg.keys}/bin:$PATH" \
10 ${pkgs.polite-merge}/bin/polite-merge --ff-only --verify-signatures
11 )
12 '';
13
364c110c 14 auto-upgrade-script = pkgs.writeShellScript "auto-upgrade" ''
2b58720b 15 ${pkgs.utillinux}/bin/flock /run/auto-upgrade-with-pinch ${
364c110c
SW
16 pkgs.writeShellScript "auto-upgrade-with-lock-held" ''
17 set -e
eb0fa99c
SW
18
19 in_tmpdir() {
20 d=$(mktemp -d)
21 pushd "$d"
22 "$@"
23 popd
24 rm -r "$d"
25 }
26
27 as_user() {
28 ${
29 if cfg.userEnvironment.enable then ''
4acf153c 30 /run/wrappers/bin/sudo -u ${escapeShellArg cfg.userEnvironment.user} "$@"
eb0fa99c
SW
31 '' else ''
32 :
33 ''
34 }
35 }
36
9dbfef33
SW
37 # Pull updates
38 ${pull-repo-snippet}
364c110c 39
fae44c38
SW
40 # Update channels
41 ${pkgs.pinch}/bin/pinch update /etc/nixos/channels
42
eb0fa99c
SW
43 # Build
44 in_tmpdir ${config.system.build.nixos-rebuild}/bin/nixos-rebuild build
b972908a 45 as_user nix-build --no-out-link '<nixpkgs>' -A ${
eb0fa99c
SW
46 escapeShellArg cfg.userEnvironment.package
47 }
48
49 # Install
50 ${config.system.build.nixos-rebuild}/bin/nixos-rebuild switch
51 as_user nix-env -f '<nixpkgs>' -riA ${
52 escapeShellArg cfg.userEnvironment.package
53 }
364c110c
SW
54 ''
55 }
56 '';
901670f5
SW
57in {
58 options = {
59 system.autoUpgradeWithPinch = {
60
61 enable = mkOption {
62 type = types.bool;
63 default = false;
64 description = ''
65 Whether to periodically upgrade NixOS to the latest version.
66 Presumes that /etc/nixos is a git repo with a remote and
67 contains a pinch file called "channels".
68 '';
69 };
70
71 dates = mkOption {
72 default = "04:40";
73 type = types.str;
74 description = ''
75 Specification (in the format described by
76 <citerefentry><refentrytitle>systemd.time</refentrytitle>
77 <manvolnum>7</manvolnum></citerefentry>) of the time at
78 which the update will occur.
79 '';
80 };
d8537205 81
9d0c0d71 82 keys = mkOption {
d8537205
SW
83 type = types.path;
84 description = ''
9d0c0d71
SW
85 File containing GPG keys that sign updates. Updates are only merged
86 if the commit at the tip of the remote branch is signed with one of
87 these keys.
d8537205
SW
88 '';
89 };
eb0fa99c
SW
90
91 userEnvironment = {
92 enable = mkOption {
93 type = types.bool;
94 default = false;
95 description = ''
96 Whether to update a user-environment as well. This update is done
97 with nix-env -riA. Note the -r! I.e., ALL OTHER PACKAGES INSTALLED
98 WITH nix-env WILL BE DELETED!
99
100 This presumes that you have configured an "entire user environment"
101 package as shown in
102 https://nixos.wiki/wiki/FAQ#How_can_I_manage_software_with_nix-env_like_with_configuration.nix.3F
103
104 To check if you're set up for this, run "nix-env --query". If it
105 only lists one package, you're good to go.
106 '';
107 };
108
109 user = mkOption {
110 type = types.str;
111 description = ''
112 The username of the user whose environment should be updated.
113 '';
114 };
115
116 package = mkOption {
117 type = types.str;
118 example = "nixos.userPackages";
119 description = ''
120 The name of the single package that is the user's entire environment.
121 '';
122 };
123
124 };
901670f5
SW
125 };
126 };
127
128 config = lib.mkIf cfg.enable {
364c110c
SW
129
130 security.sudo.extraRules = lib.mkAfter [{
131 groups = [ "users" ];
132 commands = [{
133 command = "${auto-upgrade-script}";
134 options = [ "NOPASSWD" "NOSETENV" ];
135 }];
136 }];
137 # NOSETENV above still allows through ~17 vars, including PATH. Block those
138 # as well:
139 security.sudo.extraConfig = ''
140 Defaults!${auto-upgrade-script} !env_check
141 Defaults!${auto-upgrade-script} !env_keep
142 '';
143
d8537205
SW
144 nixpkgs.overlays = [
145 (import ../overlays/keyedgit.nix)
146 (import ../overlays/pinch.nix)
5048e8ce 147 (import ../overlays/polite-merge.nix)
5aaf4680
SW
148 (self: super: {
149 auto-upgrade = super.writeShellScriptBin "auto-upgrade" ''
4acf153c 150 /run/wrappers/bin/sudo ${auto-upgrade-script}
5aaf4680
SW
151 '';
152 })
d8537205 153 ];
5aaf4680
SW
154
155 environment.systemPackages = [ pkgs.auto-upgrade ];
156
901670f5
SW
157 systemd.services.nixos-upgrade = {
158 description = "NixOS Upgrade";
159 restartIfChanged = false;
160 unitConfig.X-StopOnRemoval = false;
161 serviceConfig.Type = "oneshot";
162 environment = config.nix.envVars // {
163 inherit (config.environment.sessionVariables) NIX_PATH;
164 HOME = "/root";
165 } // config.networking.proxy.envVars;
166
167 path = with pkgs; [
168 config.nix.package.out
169 coreutils
170 git
171 gitMinimal
172 gnutar
173 gzip
901670f5
SW
174 xz.bin
175 ];
176
177 script = ''
178 set -e
8569b965
SW
179
180 # Chill for awhile before applying updates. If applying an update
181 # badly breaks things, we want a window in which an operator can
182 # intervene either to fix the problem or disable automatic updates.
183 sleep 2h
184
f43ffe15
SW
185 # Wait until outside business hours
186 now=$(date +%s)
187 day_of_week=$(date +%u)
188 business_start=$(date -d 8:00 +%s)
189 business_end=$( date -d 17:00 +%s)
190 if (( day_of_week <= 5 && now > business_start && now < business_end ));then
191 delay=$((business_end - now))
192 echo "Waiting $delay seconds so we don't upgrade during business hours" >&2
193 sleep "$delay"
194 fi
195
364c110c 196 ${auto-upgrade-script}
901670f5
SW
197 '';
198
199 startAt = cfg.dates;
200 };
eb0fa99c
SW
201
202 assertions = [{
203 assertion = cfg.userEnvironment.enable -> cfg.enable;
204 message =
205 "User environment upgrades cannot yet be enabled separately from system upgrades.";
206 }];
901670f5
SW
207 };
208}