dot

vizie's NixOS desktop: Hyprland, CachyOS kernel and gaming tweaks

12a2b1d

Control centre: update the desktop or the server

By Gustaf Granström

The update tile is split in two, like the Network and Bluetooth tiles:
Desktop runs nixos-update as before, Server runs the new server-update,
which does the same for ~/git/srv: flake update in a throwaway worktree,
build the system and the VM tests, nvd against what the server runs, and
after a yes push and switch the server over SSH. It doesn't reboot the
server (it is the router) unless the kernel changed and you say so. Each
half shows its NixOS release and nixpkgs date, the server's read over SSH.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

6 files changed +189 −27

@@ -39,7 +39,7 @@
3939 font-weight: bold;
4040 }
4141
42-// ---- tiles (Network / Bluetooth, Update NixOS) -----------------------------------
42+// ---- tiles (Network / Bluetooth, Update NixOS: Desktop / Server) -----------------
4343
4444 .tile {
4545 background-color: $card;
@@ -51,10 +51,6 @@
5151 &.split {
5252 padding: 4px;
5353 }
54-}
55-
56-.tile-wrap:hover .tile {
57- background-color: $card-hover;
5854 }
5955
6056 .toggle {
@@ -77,8 +73,7 @@
7773 color: $dim;
7874 }
7975
80-.open-wrap:hover .chevron,
81-.tile-wrap:hover .chevron {
76+.open-wrap:hover .chevron {
8277 color: $fg;
8378 }
8479
@@ -90,12 +85,6 @@
9085
9186 label {
9287 font-size: 18px;
93- }
94-
95- // The NixOS snowflake sits a little left of centre in the font.
96- .nix {
97- font-size: 19px;
98- padding-left: 2px;
9988 }
10089
10190 &.on {
@@ -33,11 +33,16 @@
3333 :initial "0"
3434 "df --output=pcent / | tail -1 | tr -dc '0-9'")
3535
36-;; "26.11 · nixpkgs from 15 Oct", from nixos-version (26.11.20261015.fe13242).
36+;; "26.11 · 15 Oct" (release · nixpkgs date) of this desktop and of the server.
3737 (defpoll nixos
3838 :interval "10m"
3939 :initial ""
40- "v=$(nixos-version | cut -d' ' -f1); date -d \"$(cut -d. -f3 <<<\"$v\")\" \"+$(cut -d. -f1-2 <<<\"$v\") · nixpkgs from %-d %b\"")
40+ "bash $HOME/.config/waybar/eww/scripts/nixos-version.sh")
41+
42+(defpoll nixos_server
43+ :interval "10m"
44+ :initial ""
45+ "bash $HOME/.config/waybar/eww/scripts/nixos-version.sh granstrm")
4146
4247 ;; Last notifications from dunst's history. Only polled while the panel is open;
4348 ;; scripts/toggle.sh fills it once right before opening.
@@ -208,18 +213,27 @@
208213 (box :class "btn danger"
209214 (label :text "Confirm")))))))
210215
211-;; Updates and reboots in a kitty window (tools/nixos-update), asking before it
212-;; installs anything.
216+;; Update NixOS, in two halves like the split tiles: this desktop (tools/nixos-update,
217+;; reboots when done) and the server (tools/server-update, switches it over SSH). Each
218+;; runs in a kitty window and asks before it installs anything.
213219 (defwidget update []
214- (eventbox :class "tile-wrap" :cursor "pointer" :timeout "3s"
215- :onclick "${EWW_CMD} close control-center cc-closer; setsid -f kitty --title 'NixOS update' nixos-update"
216- (box :class "tile" :orientation "h" :space-evenly false :spacing 10
220+ (box :class "tile split" :orientation "h" :space-evenly true
221+ (update-half :icon "󰍹" :title "Desktop" :sub nixos
222+ :tooltip "Update NixOS on this desktop"
223+ :command "nixos-update")
224+ (update-half :icon "󰒋" :title "Server" :sub nixos_server
225+ :tooltip "Update NixOS on the server"
226+ :command "server-update")))
227+
228+(defwidget update-half [icon title sub tooltip command]
229+ (eventbox :class "open-wrap" :cursor "pointer" :timeout "3s" :hexpand true :tooltip tooltip
230+ :onclick "${EWW_CMD} close control-center cc-closer; setsid -f kitty --title '${tooltip}' ${command}"
231+ (box :class "open" :orientation "h" :space-evenly false :spacing 10
217232 (box :class "icon-circle" :space-evenly true
218- (label :class "nix" :text ""))
233+ (label :text icon))
219234 (box :orientation "v" :valign "center" :halign "start" :hexpand true :space-evenly false
220- (label :class "tile-title" :halign "start" :text "Update NixOS")
221- (label :class "tile-sub" :halign "start" :limit-width 34 :text nixos))
222- (label :class "chevron" :valign "center" :text "󰅂"))))
235+ (label :class "tile-title" :halign "start" :text title)
236+ (label :class "tile-sub" :halign "start" :limit-width 15 :text sub)))))
223237
224238 ;; ---------------------------------------------------------------------------
225239 ;; Notifications panel
@@ -1,0 +1,14 @@
1+#!/usr/bin/env bash
2+# "26.11 · 15 Oct" (release and nixpkgs date) from nixos-version (26.11.20261015.fe13242),
3+# for the update tile. With a host, asks it over SSH; "Unreachable" if that fails.
4+# nixos-version.sh [host]
5+
6+if [ -n "$1" ]; then
7+ v=$(ssh -o BatchMode=yes -o ConnectTimeout=5 "$1" nixos-version 2>/dev/null)
8+else
9+ v=$(nixos-version)
10+fi
11+v=${v%% *}
12+[ -n "$v" ] || { echo "Unreachable"; exit 0; }
13+IFS=. read -r major minor date _ <<<"$v"
14+date -d "$date" "+$major.$minor · %-d %b"
@@ -8,7 +8,7 @@
88 let
99 system = pkgs.stdenv.hostPlatform.system;
1010
11- # The control centre's "Update NixOS" tile (tools/nixos-update).
11+ # The "Desktop" half of the control centre's update tile (tools/nixos-update).
1212 nixos-update = pkgs.writeShellApplication {
1313 name = "nixos-update";
1414 runtimeInputs = with pkgs; [
@@ -19,6 +19,17 @@
1919 text = builtins.readFile ../../tools/nixos-update;
2020 };
2121
22+ # The same tile's "Server" half (tools/server-update): updates and deploys ~/git/srv.
23+ server-update = pkgs.writeShellApplication {
24+ name = "server-update";
25+ runtimeInputs = with pkgs; [
26+ git
27+ nvd
28+ openssh
29+ ];
30+ text = builtins.readFile ../../tools/server-update;
31+ };
32+
2233 # The Network tile's settings window, on its own: the networkmanagerapplet
2334 # package also has an autostart entry that would bring nm-applet back to the tray.
2435 nm-connection-editor = pkgs.runCommand "nm-connection-editor" { } ''
@@ -63,6 +74,7 @@
6374 bluez # bluetoothctl
6475 nm-connection-editor
6576 nixos-update
77+ server-update
6678 solaar # mouse battery fallback (mouse-battery.sh)
6779
6880 # Screenshots and screen recording (imsh-shot, imsh-cast; Super+Shift+S/F/R).
@@ -1,7 +1,7 @@
11 #!/usr/bin/env bash
22 # Updates NixOS from a throwaway worktree of this repo, so the checkout you edit in
3-# is left alone until the update is done. Started from the control centre's
4-# "Update NixOS" tile in a kitty window (installed as `nixos-update`,
3+# is left alone until the update is done. Started from the "Desktop" half of the
4+# control centre's update tile in a kitty window (installed as `nixos-update`,
55 # modules/home/packages.nix, which sets DOT_REPO).
66 #
77 # 1. a temporary worktree of main
@@ -1,0 +1,133 @@
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