ALPHA: wire is a tool to deploy nixos systems wire.althaea.zone/
at rollback-hacking 146 lines 4.2 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-or-later 2# Copyright 2024-2025 wire Contributors 3 4{ 5 pkgs, 6 lib, 7 config, 8 ... 9}: 10{ 11 config = { 12 systemd = { 13 paths = lib.mapAttrs' ( 14 _name: value: 15 lib.nameValuePair "${value.name}-key" { 16 description = "Monitor changes to ${value.path}. You should Require ${value.service} instead of this."; 17 pathConfig = { 18 PathExists = value.path; 19 PathChanged = value.path; 20 Unit = "${value.name}-key.service"; 21 }; 22 } 23 ) config.deployment.keys; 24 25 system.activationScripts.setup-wire-rollback.text = '' 26 mkdir -p /var/lib/wire-rollback 27 chmod 700 /var/lib/wire-rollback 28 ''; 29 30 services = { 31 wire-rollback = { 32 enable = config.deployment.rollback; 33 description = "Rolls back the NixOS profile if `/var/lib/wire-rollback/heartbeat` is not created in 30 34 seconds after this service starts."; 35 documentation = [ 36 "https://wire.althaea.zone/guides/rollback" 37 ]; 38 path = [ 39 pkgs.coreutils 40 ]; 41 wantedBy = [ "multi-user.target" ]; 42 script = '' 43 set -euo pipefail 44 45 goal=$(<"/var/lib/wire-rollback/goal") 46 47 case $goal in 48 "check" | "switch" | "boot" | "test" | "dry-activate") 49 echo "<5>using goal $goal" 50 ;; 51 *) 52 echo "<3>'$goal' is not a valid goal." 53 exit 1 54 ;; 55 esac 56 57 sleep 30 58 59 if [ -f "/var/lib/wire-rollback/heartbeat" ]; then 60 exit 0 61 fi 62 63 echo "<1>/var/lib/wire-rollback/heartbeat does not exist, rolling back system" 64 65 # set current system 66 nix-env --rollback --profile /nix/var/nix/profiles/system 67 # get the path to the system we are now rolling back to 68 system=$(readlink -f /nix/var/nix/profiles/system) 69 70 echo "<5>rolling back to $system" 71 72 # switch to the system using goal 73 "$system/bin/switch-to-configuration $goal" 74 ''; 75 unitConfig = { 76 ConditionPathExists = [ 77 "/var/lib/wire-rollback/goal" 78 "!/var/lib/wire-rollback/heartbeat" 79 ]; 80 }; 81 serviceConfig = { 82 Type = "oneshot"; 83 Restart = "no"; 84 StateDirectory = "wire-rollback"; 85 NotifyAccess = "all"; 86 RemainAfterExit = "yes"; 87 88 ExecStopPost = "${pkgs.coreutils}/bin/rm -f /var/lib/wire-rollback/goal"; 89 }; 90 }; 91 } 92 // (lib.mapAttrs' ( 93 _name: value: 94 lib.nameValuePair "${value.name}-key" { 95 description = "Service that requires ${value.path}"; 96 path = [ 97 pkgs.inotify-tools 98 pkgs.coreutils 99 ]; 100 script = '' 101 MSG="Key ${value.path} exists." 102 systemd-notify --ready --status="$MSG" 103 104 echo "waiting to fail if the key is removed..." 105 106 while inotifywait -e delete_self "${value.path}"; do 107 MSG="Key ${value.path} no longer exists." 108 109 systemd-notify --status="$MSG" 110 echo $MSG 111 112 exit 1 113 done 114 ''; 115 unitConfig = { 116 ConditionPathExists = value.path; 117 }; 118 serviceConfig = { 119 Type = "simple"; 120 Restart = "no"; 121 NotifyAccess = "all"; 122 RemainAfterExit = "yes"; 123 }; 124 } 125 ) config.deployment.keys); 126 }; 127 128 deployment = { 129 _keys = lib.mapAttrsToList ( 130 _: value: 131 value 132 // { 133 source = { 134 # Attach type to internally tag serde enum 135 t = builtins.replaceStrings [ "path" "string" "list" ] [ "Path" "String" "Command" ] ( 136 builtins.typeOf value.source 137 ); 138 c = value.source; 139 }; 140 } 141 ) config.deployment.keys; 142 143 _hostPlatform = config.nixpkgs.hostPlatform.system; 144 }; 145 }; 146}