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