nix/packages/upgrader/default.nix
Daniel Flanagan f2a9ab0655
All checks were successful
/ build-host (map[host:beefcake]) (push) Successful in 55s
/ build-host (map[host:dragon]) (push) Successful in 1m15s
/ build-host (map[host:flipflop]) (push) Successful in 1m8s
/ build-host (map[host:foxtrot]) (push) Successful in 1m16s
/ build-host (map[host:router]) (push) Successful in 45s
/ build-devshell (push) Successful in 18s
/ flake-check (push) Successful in 4m14s
feat: add upgrader
2025-03-20 16:27:08 -05:00

106 lines
2.9 KiB
Nix

{
pkgs,
...
}:
pkgs.writeShellApplication {
name = "upgrader";
runtimeInputs = with pkgs; [
ripgrep
fzf
jq
gawk
];
text = ''
CHECK_TIMEOUT=120
usage() {
exit_code="''${1:-0}"
echo "upgrader TARGET_HOST"
echo " -h,--help Show help"
echo " --wait-and-rollback GEN Wait CHECK_TIMEOUT seconds and rollback to the boot generation [REMOTE]"
echo " --ignore-lock Ignore locks"
exit "$exit_code"
}
ilog() {
echo "$1" >> .upgrader.log
}
wait_and_rollback() {
[[ $# -lt 1 ]] && { printf "error: no GEN argument provided for --wait-and-rollback\n" >&2; usage 1; }
gen="$1"
shift
while ! pgrep nixos-rebuild &>/dev/null; do
sleep 1;
done
while ! pgrep nixos-rebuild &>/dev/null; do
sleep 1
done
for i in $(seq 0 '"$CHECK_TIMEOUT"'; do
sleep 1
done
sudo nix-env --switch-generation -p /nix/var/nix/profiles/system
sudo /nix/var/nix/profiles/system/bin/switch-to-configuration switch
exit
}
[[ $# -lt 1 ]] && { printf "error: no TARGET_HOST argument provided\n" >&2; usage 1; }
target_host="$1"
shift
ignore_locks=0
while [ "$#" -gt 0 ]; do
case "$1" in
--wait-and-rollback)
wait_and_rollback
--ignore-locks)
ignore_locks=1
break
;;
-h | --help | help)
usage 0
;;
*)
echo "error: unexpected argument '$1' (if you meant to pass this to the underlying command(s), use '-- $1')"
usage 1
esac
shift
done
ssh "$target_host" true
echo "SSH access to '$target_host' confirmed!"
# TODO: install upgrader on remote host and know how to call it
log() {
echo "$1"
ssh "$target_host" 'echo "$(date) $1" >> .upgrader.log'
}
remote_bg_job() {
ssh "$target_host" 'nohup sh -c "( ( '"$1"' ) & )"'
}
if [[ $ignore_locks == 0 ]] && ssh "$target_host" "stat .upgrader.lck &>/dev/null" &>/dev/null; then
log ".upgrader.lck exists on '$target_host', refusing to upgrade"
exit 1
fi
# TODO: handle boot-level failures?
boot_gen="$(ssh "$target_host" "cat /boot/loader/loader.conf | rg 'default\s*nixos-generation-(\d+)\.conf' -r '\$1'")"
log "Current Boot Generation for '$target_host': $boot_gen"
# TODO: start some kind of "waiter"
remote_bg_job 'remote_upgrader --wait-and-rollback'
log "Upgrading '$target_host' (may require sudo prompt)..."
ssh -t "$target_host" "touch .upgrader.lck; sudo nixos-rebuild test --flake git+https://git.lyte.dev/lytedev/nix"
ssh -t "$target_host" 'remote_upgrader --kill-waiter'
log 'Promoting current configuration to boot generation (may require sudo prompt)...'
ssh -t "$target_host" 'sudo /run/current-system/bin/switch-to-configuration switch'
'';
}