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