dot
vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks
| 1 | #!/usr/bin/env bash |
| 2 | # Updates the server (granstrm, from the srv repo) the way nixos-update updates this |
| 3 | # desktop, from a throwaway worktree so the checkout you edit in is left alone. |
| 4 | # Started from the "Server" half of the control centre's update tile in a kitty |
| 5 | # window (installed as `server-update`, modules/home/packages.nix). |
| 6 | # |
| 7 | # 1. a temporary worktree of main |
| 8 | # 2. nix flake update, committed there |
| 9 | # 3. builds the server's system and the repo's VM tests (router, web, media), then |
| 10 | # nvd's list of what changed against the system the server runs |
| 11 | # 4. asks before going on; then pushes the commit, switches the server to it over |
| 12 | # SSH (nixos-rebuild switch --target-host) and brings your checkout's main up |
| 13 | # to date |
| 14 | # 5. if the kernel changed, asks whether to reboot the server now. It is the |
| 15 | # house router, so the internet drops while it reboots. |
| 16 | # |
| 17 | # If anything fails, the window stays open until you press Enter, and nothing on the |
| 18 | # server is changed. |
| 19 | set -E |
| 20 | |
| 21 | repo=${SRV_REPO:-$HOME/git/srv} |
| 22 | host=${SRV_HOST:-granstrm} |
| 23 | tmp="" |
| 24 | |
| 25 | step() { printf '\n\e[1;34m==> %s\e[0m\n' "$*"; } |
| 26 | note() { printf '\e[1;33m%s\e[0m\n' "$*"; } |
| 27 | wait_close() { read -rp "Press Enter to close. " _ || true; } |
| 28 | |
| 29 | die() { |
| 30 | trap - ERR |
| 31 | printf '\n\e[1;31m%s\e[0m\n' "$*" |
| 32 | wait_close |
| 33 | exit 1 |
| 34 | } |
| 35 | |
| 36 | cleanup() { |
| 37 | if [ -n "$tmp" ]; then |
| 38 | git -C "$repo" worktree remove --force "$tmp" 2>/dev/null || true |
| 39 | rm -rf "$tmp" |
| 40 | tmp="" |
| 41 | fi |
| 42 | } |
| 43 | |
| 44 | trap 'die "The update failed (see above). The server was not changed."' ERR |
| 45 | trap cleanup EXIT |
| 46 | |
| 47 | step "Fetching $repo" |
| 48 | git -C "$repo" worktree prune |
| 49 | git -C "$repo" fetch origin main |
| 50 | git -C "$repo" merge-base --is-ancestor origin/main main || |
| 51 | die "main in $repo is behind origin/main (or has diverged); pull first." |
| 52 | base=$(git -C "$repo" rev-parse main) |
| 53 | ahead=$(git -C "$repo" rev-list --count origin/main..main) |
| 54 | dirty=$(git -C "$repo" status --porcelain) |
| 55 | |
| 56 | tmp=$(mktemp -d -t srv-update.XXXXXX) |
| 57 | step "Creating a worktree of main in $tmp" |
| 58 | git -C "$repo" worktree add --detach "$tmp" main |
| 59 | cd "$tmp" || die "Could not enter $tmp." |
| 60 | |
| 61 | step "Updating flake inputs" |
| 62 | nix flake update --commit-lock-file |
| 63 | if [ "$(git rev-parse HEAD)" = "$base" ]; then |
| 64 | printf '\nEverything is already up to date.\n' |
| 65 | wait_close |
| 66 | exit 0 |
| 67 | fi |
| 68 | |
| 69 | step "Building the server's system" |
| 70 | nixos-rebuild build --flake "$tmp#$host" |
| 71 | |
| 72 | checks=$(nix eval --raw .#checks.x86_64-linux --apply 'c: builtins.concatStringsSep " " (builtins.attrNames c)') |
| 73 | step "Running the VM tests ($checks)" |
| 74 | for c in $checks; do |
| 75 | nix build --no-link ".#checks.x86_64-linux.$c" |
| 76 | done |
| 77 | |
| 78 | step "What changes" |
| 79 | git --no-pager log -1 --format=%b |
| 80 | running=$(ssh "$host" readlink -f /run/current-system) |
| 81 | # Usually still here from the last deploy; fetched from the server if not. |
| 82 | nix path-info "$running" >/dev/null 2>&1 || nix copy --from "ssh-ng://$host" "$running" |
| 83 | nvd diff "$running" "$tmp/result" |
| 84 | |
| 85 | echo |
| 86 | [ -z "$dirty" ] || note "Uncommitted changes in $repo are not part of this update." |
| 87 | [ "$ahead" -eq 0 ] || note "main has $ahead commit(s) not on GitHub yet; they are pushed and deployed too." |
| 88 | read -rp "Commit, push and switch the server? [y/N] " answer || answer="" |
| 89 | case $answer in |
| 90 | [yY]*) ;; |
| 91 | *) |
| 92 | echo "Nothing was changed." |
| 93 | sleep 1 |
| 94 | exit 0 |
| 95 | ;; |
| 96 | esac |
| 97 | |
| 98 | step "Pushing" |
| 99 | git push origin HEAD:main |
| 100 | |
| 101 | step "Switching the server" |
| 102 | nixos-rebuild switch --flake "$tmp#$host" --target-host "$host" |
| 103 | |
| 104 | step "Updating main in $repo" |
| 105 | commit=$(git rev-parse HEAD) |
| 106 | if [ "$(git -C "$repo" symbolic-ref --quiet --short HEAD)" = main ]; then |
| 107 | git -C "$repo" merge --ff-only "$commit" |
| 108 | else |
| 109 | git -C "$repo" update-ref refs/heads/main "$commit" "$base" |
| 110 | fi |
| 111 | cd "$repo" |
| 112 | cleanup |
| 113 | |
| 114 | # The same test NixOS's own auto-upgrade uses: a new kernel or initrd only takes |
| 115 | # effect after a reboot. |
| 116 | # shellcheck disable=SC2016 # expanded on the server |
| 117 | if ssh "$host" '[ "$(readlink /run/booted-system/{initrd,kernel,kernel-modules})" = "$(readlink /run/current-system/{initrd,kernel,kernel-modules})" ]'; then |
| 118 | printf '\nDone. No reboot needed.\n' |
| 119 | wait_close |
| 120 | exit 0 |
| 121 | fi |
| 122 | |
| 123 | echo |
| 124 | note "The kernel changed; it is used after the server's next reboot." |
| 125 | read -rp "Reboot the server now? The internet drops for a few minutes. [y/N] " answer || answer="" |
| 126 | case $answer in |
| 127 | [yY]*) |
| 128 | step "Rebooting the server" |
| 129 | ssh "$host" systemctl reboot || true |
| 130 | ;; |
| 131 | *) echo "Not rebooted. The new kernel is used after the next reboot." ;; |
| 132 | esac |
| 133 | wait_close |