Files
ssmithxandClaude Opus 5.5 76602eb3d2 Time sync, signed regdb, EEE off on LAN port; deploy-rpi fixes
- chronyd (makestep 1 -1) from a shepherd service + chrony account; the
  Pi has no RTC.  ntp doesn't cross-build, openntpd won't step late.
- wireless-regdb-signed: upstream regulatory.db + .p7s, so the kernel
  accepts it (country US instead of world 00).
- lan-eee-off one-shot: EEE between the Pi's PHY and the workstation's
  e1000e dropped ~90% of LAN frames.
- Drop console-font service (setfont fails on the Pi framebuffer).
- deploy-rpi.sh: ssh -n so the generation loop sees every generation.
- openwrt.scm: missing (guix git-download) import.

Generation 6 verified after a cold boot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:12:27 -04:00

99 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Update the running Pi over SSH without reflashing.
#
# ./deploy-rpi.sh build rpi4.scm, copy, switch, reboot
# ./deploy-rpi.sh --no-reboot ... activate only (services restart on reboot)
# ./deploy-rpi.sh switch N make existing generation N the default
# (roll back), activate, reboot
# ./deploy-rpi.sh list show the Pi's system generations
#
# The system is cross-built here (--target, like the image) and sent with
# `guix copy'; extlinux.conf is generated here too (scripts/extlinux-conf.scm),
# because `guix system reconfigure/switch-generation' on the Pi would need
# native aarch64 builds (PITFALLS #28). Old generations stay in the U-Boot
# menu: pick one from the console if a new one doesn't boot.
#
# Needs: ~/.ssh/config entry for $PI, /etc/guix/signing-key.* on this host,
# and that key in the Pi's guix-service-type authorized-keys (rpi4.scm).
set -euo pipefail
cd "$(dirname "$0")"
export PATH=$HOME/.local/bin:$PATH
export GUIX_PACKAGE_PATH="$PWD/packages:$HOME/channels/nonguix"
PI=${PI:-pirouter}
PROFILES=/var/guix/profiles
# -n: never read stdin (it would swallow `while read` loop input).
pi() { ssh -n -o BatchMode=yes "$PI" "$@"; }
# "N /gnu/store/…-system" for each generation, ascending.
generations() {
pi "cd $PROFILES && for l in system-*-link; do n=\${l#system-}; echo \"\${n%-link} \$(readlink \$l)\"; done" |
sort -n
}
current() { pi "readlink -f $PROFILES/system"; }
# Write extlinux.conf with generation $1 (store path) as default and every
# other generation as fallback entries, newest first; install it on the Pi.
install_bootcfg() {
local default=$1 tmp params=() n path conf
tmp=$(mktemp -d)
pi "cat $default/parameters" > "$tmp/default"
while read -r n path; do
[ "$path" = "$default" ] && continue
pi "cat $path/parameters" > "$tmp/$n"
params=("$path" "$tmp/$n" "${params[@]}") # newest first
done < <(generations)
conf=$(guix repl -L packages -L "$HOME/channels/nonguix" -- \
scripts/extlinux-conf.scm rpi4.scm \
"$default" "$tmp/default" "${params[@]}" | tail -1)
rm -rf "$tmp"
[ -f "$conf" ] || { echo "extlinux.conf generation failed" >&2; exit 1; }
scp -q -o BatchMode=yes "$conf" "$PI:/boot/extlinux/extlinux.conf.new"
pi "mv /boot/extlinux/extlinux.conf.new /boot/extlinux/extlinux.conf && sync"
echo "extlinux.conf: default $default, $(( ${#params[@]} / 2 )) fallback(s)"
}
# Point the system profile at generation $1, activate it.
switch_to() {
local n=$1 path
path=$(pi "readlink $PROFILES/system-$n-link")
install_bootcfg "$path"
pi "ln -sfn system-$n-link $PROFILES/system.new && mv -T $PROFILES/system.new $PROFILES/system"
echo "activating generation $n ($path)"
# activate.scm picks the system from GUIX_NEW_SYSTEM (set by reconfigure
# and at boot); without it, it re-activates /run/current-system.
pi "GUIX_NEW_SYSTEM=$path $path/activate" ||
echo "warning: activation reported errors" >&2
}
reboot_pi() {
echo "rebooting $PI"
pi "sync; (sleep 1; reboot) >/dev/null 2>&1 &" || true
}
case "${1:-deploy}" in
list)
cur=$(current)
generations | while read -r n path; do
echo "$n $path$( [ "$path" = "$cur" ] && echo ' (current)')"
done ;;
switch)
n=${2:?usage: deploy-rpi.sh switch N}
switch_to "$n"
[ "${3:-}" = --no-reboot ] || reboot_pi ;;
deploy|--no-reboot)
sys=$(guix system build --target=aarch64-linux-gnu rpi4.scm | tail -1)
echo "built $sys"
if [ "$sys" = "$(current)" ]; then echo "already running $sys"; exit 0; fi
guix copy --to="$PI" "$sys"
last=$(generations | tail -1 | cut -d' ' -f1)
n=$((last + 1))
pi "ln -sfn $sys $PROFILES/system-$n-link"
echo "registered generation $n"
switch_to "$n"
[ "${1:-deploy}" = --no-reboot ] || reboot_pi ;;
*)
sed -n '2,10p' "$0"; exit 1 ;;
esac