feat(uno-q): provision-fleet flashes the default boot animation

Folds the boot animation into the fleet pass — after onboarding each
board (env + self-register + timer/cron), provision-fleet now flashes the
default sketch (sketches/matrix_rain) via flash-sketch.sh. Best-effort: a
flash hiccup logs a warning but doesn't fail the board's onboarding.

- FLASH_DEFAULT=1 (default; 0 skips) and FLASH_SKETCH=<dir> knobs.
- Upfront guards for flash-sketch.sh + the sketch dir.
- provision() refactored to a single success tail (persistence → flash).

Verified on hardware: one pass reports both "ok — onboarding (cron)" and
"ok — boot animation (matrix_rain)".

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-08 15:46:27 -07:00
co-authored by Claude Opus 4.8
parent a2ccf519d4
commit 4d9d984b04
2 changed files with 41 additions and 16 deletions
+4 -3
View File
@@ -78,12 +78,13 @@ QR encodes `…/workshop?kit=KIT-NN&code=NNNNNN`, so scanning it pre-fills both.
**All boards at once (recommended).** Map each kit to its board's adb serial in **All boards at once (recommended).** Map each kit to its board's adb serial in
`fleet.csv` (see `fleet.csv.example`), then provision the fleet in one command — `fleet.csv` (see `fleet.csv.example`), then provision the fleet in one command —
it pushes each board's env + the self-register script and enables the boot + per board it pushes the env + self-register script, enables the boot/refresh
refresh timer (root) or an equivalent cron (`MODE=cron`, the default, no root): timer (root) or an equivalent cron (`MODE=cron`, the default, no root), **and
flashes the default boot animation** (`sketches/matrix_rain`) onto the MCU:
```sh ```sh
cp fleet.csv.example fleet.csv # fill in kit,serial for each board cp fleet.csv.example fleet.csv # fill in kit,serial for each board
./provision-fleet.sh fleet.csv # (MODE=systemd for root-capable boards) ./provision-fleet.sh fleet.csv # MODE=systemd for root boards; FLASH_DEFAULT=0 to skip the animation
``` ```
**One board (manual equivalent):** **One board (manual equivalent):**
+34 -10
View File
@@ -1,8 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Fleet onboarding provisioner. For each board in an assignments file, pushes # Fleet onboarding provisioner. For each board in an assignments file, pushes
# its per-kit env (apess-node.env) + the self-register script over adb, then # its per-kit env (apess-node.env) + the self-register script over adb, enables
# enables the self-register systemd timer (root boards) or an equivalent cron # the self-register systemd timer (root boards) or an equivalent cron (no-root,
# (no-root boards, the default). Run after gen-kit-codes.sh. # the default), and flashes the default boot animation onto the MCU. Run after
# gen-kit-codes.sh.
# #
# ./provision-fleet.sh <assignments.csv> [kit-codes-dir] # ./provision-fleet.sh <assignments.csv> [kit-codes-dir]
# #
@@ -12,7 +13,10 @@
# KIT-02,88a1b2c3 # KIT-02,88a1b2c3
# kit-codes-dir default: ./kit-codes (expects env/KIT-NN.env from gen-kit-codes.sh) # kit-codes-dir default: ./kit-codes (expects env/KIT-NN.env from gen-kit-codes.sh)
# #
# Env: MODE=cron (default, no root) | systemd (needs a root-capable board). # Env:
# MODE=cron (default, no root) | systemd (needs a root-capable board)
# FLASH_DEFAULT=1 (default; 0 skips flashing the boot animation)
# FLASH_SKETCH=<dir> (default: sketches/matrix_rain)
set -uo pipefail set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)" HERE="$(cd "$(dirname "$0")" && pwd)"
@@ -20,10 +24,16 @@ ASSIGN="${1:?usage: provision-fleet.sh <assignments.csv> [kit-codes-dir]}"
KCDIR="${2:-./kit-codes}" KCDIR="${2:-./kit-codes}"
ENVDIR="$KCDIR/env" ENVDIR="$KCDIR/env"
MODE="${MODE:-cron}" MODE="${MODE:-cron}"
FLASH_DEFAULT="${FLASH_DEFAULT:-1}"
FLASH_SKETCH="${FLASH_SKETCH:-$HERE/sketches/matrix_rain}"
[ -r "$ASSIGN" ] || { echo "no assignments file: $ASSIGN" >&2; exit 1; } [ -r "$ASSIGN" ] || { echo "no assignments file: $ASSIGN" >&2; exit 1; }
[ -d "$ENVDIR" ] || { echo "no env dir: $ENVDIR (run gen-kit-codes.sh first)" >&2; exit 1; } [ -d "$ENVDIR" ] || { echo "no env dir: $ENVDIR (run gen-kit-codes.sh first)" >&2; exit 1; }
[ -r "$HERE/apess-selfregister.sh" ] || { echo "missing apess-selfregister.sh next to this script" >&2; exit 1; } [ -r "$HERE/apess-selfregister.sh" ] || { echo "missing apess-selfregister.sh next to this script" >&2; exit 1; }
if [ "$FLASH_DEFAULT" = 1 ]; then
[ -x "$HERE/flash-sketch.sh" ] || { echo "FLASH_DEFAULT=1 but flash-sketch.sh is missing/not executable (FLASH_DEFAULT=0 to skip)" >&2; exit 1; }
[ -d "$FLASH_SKETCH" ] || { echo "FLASH_DEFAULT=1 but sketch dir not found: $FLASH_SKETCH" >&2; exit 1; }
fi
provision() { # kit serial -> 0 ok / 1 fail provision() { # kit serial -> 0 ok / 1 fail
local kit="$1" serial="$2" env="$ENVDIR/$1.env" local kit="$1" serial="$2" env="$ENVDIR/$1.env"
@@ -33,22 +43,35 @@ provision() { # kit serial -> 0 ok / 1 fail
adb -s "$serial" push "$HERE/apess-selfregister.sh" /home/arduino/ >/dev/null 2>&1 || return 1 adb -s "$serial" push "$HERE/apess-selfregister.sh" /home/arduino/ >/dev/null 2>&1 || return 1
adb -s "$serial" shell 'chmod +x /home/arduino/apess-selfregister.sh' >/dev/null 2>&1 || return 1 adb -s "$serial" shell 'chmod +x /home/arduino/apess-selfregister.sh' >/dev/null 2>&1 || return 1
# persistence: systemd timer (root) or cron (no root)
local persisted=""
if [ "$MODE" = systemd ]; then if [ "$MODE" = systemd ]; then
adb -s "$serial" push "$HERE/systemd/apess-selfregister.service" /tmp/ >/dev/null 2>&1 adb -s "$serial" push "$HERE/systemd/apess-selfregister.service" /tmp/ >/dev/null 2>&1
adb -s "$serial" push "$HERE/systemd/apess-selfregister.timer" /tmp/ >/dev/null 2>&1 adb -s "$serial" push "$HERE/systemd/apess-selfregister.timer" /tmp/ >/dev/null 2>&1
if adb -s "$serial" shell 'sudo cp /tmp/apess-selfregister.service /tmp/apess-selfregister.timer /etc/systemd/system/ \ if adb -s "$serial" shell 'sudo cp /tmp/apess-selfregister.service /tmp/apess-selfregister.timer /etc/systemd/system/ \
&& sudo systemctl enable --now apess-selfregister.timer' >/dev/null 2>&1; then && sudo systemctl enable --now apess-selfregister.timer' >/dev/null 2>&1; then
echo " ok (systemd timer)"; return 0 persisted="systemd timer"
fi else
echo " ! systemd enable failed (no root?) — falling back to cron" echo " ! systemd enable failed (no root?) — falling back to cron"
fi fi
fi
# no-root: run once now (detached; it retries internally) + cron for re-announce if [ -z "$persisted" ]; then
adb -s "$serial" shell 'setsid /home/arduino/apess-selfregister.sh >/tmp/selfreg.log 2>&1 </dev/null &' >/dev/null 2>&1 || true adb -s "$serial" shell 'setsid /home/arduino/apess-selfregister.sh >/tmp/selfreg.log 2>&1 </dev/null &' >/dev/null 2>&1 || true
adb -s "$serial" shell '(crontab -l 2>/dev/null | grep -v apess-selfregister.sh; \ adb -s "$serial" shell '(crontab -l 2>/dev/null | grep -v apess-selfregister.sh; \
echo "@reboot /home/arduino/apess-selfregister.sh"; \ echo "@reboot /home/arduino/apess-selfregister.sh"; \
echo "*/5 * * * * /home/arduino/apess-selfregister.sh") | crontab -' >/dev/null 2>&1 || true echo "*/5 * * * * /home/arduino/apess-selfregister.sh") | crontab -' >/dev/null 2>&1 || true
echo " ok (cron: @reboot + every 5 min)" persisted="cron: @reboot + every 5 min"
fi
echo " ok — onboarding ($persisted)"
# default boot animation — best-effort (a flash hiccup doesn't fail onboarding)
if [ "$FLASH_DEFAULT" = 1 ]; then
if "$HERE/flash-sketch.sh" "$serial" "$FLASH_SKETCH" >/dev/null 2>&1; then
echo " ok — boot animation ($(basename "$FLASH_SKETCH"))"
else
echo " ! boot-animation flash failed (onboarding still ok)"
fi
fi
return 0 return 0
} }
@@ -64,6 +87,7 @@ while IFS=',' read -r kit serial _rest || [ -n "$kit" ]; do
if provision "$kit" "$serial"; then ok=$((ok + 1)); else echo " FAILED"; fail=$((fail + 1)); fi if provision "$kit" "$serial"; then ok=$((ok + 1)); else echo " FAILED"; fail=$((fail + 1)); fi
done < "$ASSIGN" done < "$ASSIGN"
anim="skipped"; [ "$FLASH_DEFAULT" = 1 ] && anim="$(basename "$FLASH_SKETCH")"
echo "----" echo "----"
echo "provisioned $ok board(s), $fail failed/skipped (mode: $MODE)" echo "provisioned $ok board(s), $fail failed/skipped (mode: $MODE, boot-animation: $anim)"
[ "$fail" -eq 0 ] [ "$fail" -eq 0 ]