As deploy-rpi.sh, with GL U-Boot's single FIT in place of extlinux.conf: switching to a generation writes its FIT (scripts/mt6000-fit.sh, built on the workstation) to the `kernel' partition and checks it. Deployed generation 2 (drops the bring-up self-reboot), rolled back to 1 and forward again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
3.9 KiB
Bash
Executable File
100 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Update the running GL-MT6000 over SSH without reflashing.
|
|
#
|
|
# ./deploy-mt6000.sh build mt6000.scm, copy, switch, reboot
|
|
# ./deploy-mt6000.sh --no-reboot ... activate only (the new kernel and
|
|
# services start on the next boot)
|
|
# ./deploy-mt6000.sh switch N make existing generation N the default
|
|
# (roll back), activate, reboot
|
|
# ./deploy-mt6000.sh list show the router's system generations
|
|
#
|
|
# As deploy-rpi.sh: the system is cross-built here and sent with `guix copy'.
|
|
# GL's U-Boot boots one FIT from the `kernel' partition (p6), with no menu,
|
|
# so switching to a generation means writing its FIT (scripts/mt6000-fit.sh,
|
|
# built here) to p6. If a new generation doesn't come up, roll back from
|
|
# ssh if it gets that far, else through U-Boot's TFTP recovery
|
|
# (docs/PITFALLS.md #51) -- an interrupted p6 write needs that too.
|
|
#
|
|
# Needs: ~/.ssh/config entry for $ROUTER, /etc/guix/signing-key.* on this
|
|
# host, and that key in the router's guix-service-type authorized-keys
|
|
# (mt6000.scm).
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
export PATH=$HOME/.local/bin:$PATH
|
|
export GUIX_PACKAGE_PATH="$PWD/packages:$HOME/channels/nonguix"
|
|
ROUTER=${ROUTER:-mt6000}
|
|
PROFILES=/var/guix/profiles
|
|
KERNEL_PART=/dev/mmcblk0p6
|
|
|
|
# -n: never read stdin (it would swallow `while read` loop input).
|
|
router() { ssh -n -o BatchMode=yes "$ROUTER" "$@"; }
|
|
|
|
# "N /gnu/store/…-system" for each generation, ascending.
|
|
generations() {
|
|
router "cd $PROFILES && for l in system-*-link; do n=\${l#system-}; echo \"\${n%-link} \$(readlink \$l)\"; done" |
|
|
sort -n
|
|
}
|
|
|
|
current() { router "readlink -f $PROFILES/system"; }
|
|
|
|
# Write the FIT for system $1 to p6 and check it. The system's kernel and
|
|
# initrd must be in this host's store (they are: it built them).
|
|
install_fit() {
|
|
local sys=$1 tmp sum
|
|
[ -d "$sys" ] || guix build "$sys" >/dev/null
|
|
tmp=$(mktemp -d)
|
|
scripts/mt6000-fit.sh "$sys" "$tmp/boot.itb"
|
|
ssh -o BatchMode=yes "$ROUTER" "dd of=$KERNEL_PART bs=64k conv=fsync 2>/dev/null" < "$tmp/boot.itb"
|
|
sum=$(router "head -c $(stat -c %s "$tmp/boot.itb") $KERNEL_PART | sha256sum" | cut -c1-64)
|
|
if [ "$sum" != "$(sha256sum < "$tmp/boot.itb" | cut -c1-64)" ]; then
|
|
rm -rf "$tmp"
|
|
echo "FIT on $KERNEL_PART does not match: rewrite it before rebooting" >&2
|
|
exit 1
|
|
fi
|
|
rm -rf "$tmp"
|
|
echo "FIT written to $KERNEL_PART"
|
|
}
|
|
|
|
# Point the system profile at generation $1, write its FIT, activate it.
|
|
switch_to() {
|
|
local n=$1 path
|
|
path=$(router "readlink $PROFILES/system-$n-link")
|
|
install_fit "$path"
|
|
router "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.
|
|
router "GUIX_NEW_SYSTEM=$path $path/activate" ||
|
|
echo "warning: activation reported errors" >&2
|
|
}
|
|
|
|
reboot_router() {
|
|
echo "rebooting $ROUTER"
|
|
router "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-mt6000.sh switch N}
|
|
switch_to "$n"
|
|
[ "${3:-}" = --no-reboot ] || reboot_router ;;
|
|
deploy|--no-reboot)
|
|
sys=$(guix system build --target=aarch64-linux-gnu mt6000.scm | tail -1)
|
|
echo "built $sys"
|
|
if [ "$sys" = "$(current)" ]; then echo "already running $sys"; exit 0; fi
|
|
guix copy --to="$ROUTER" "$sys"
|
|
last=$(generations | tail -1 | cut -d' ' -f1)
|
|
n=$((last + 1))
|
|
router "ln -sfn $sys $PROFILES/system-$n-link"
|
|
echo "registered generation $n"
|
|
switch_to "$n"
|
|
[ "${1:-deploy}" = --no-reboot ] || reboot_router ;;
|
|
*)
|
|
sed -n '2,10p' "$0"; exit 1 ;;
|
|
esac
|