#!/usr/bin/env bash # Back up a student's Uno Q before reflashing it for the APESS workshop. # # ./backup-uno-q.sh [team-label] [outdir] # # Students arrive having spent a week doing their own sensor work on these # boards. THAT WORK IS IRREPLACEABLE — this runs before provision-uno-q.sh and # must succeed before anything is overwritten. # # Strategy: DENYLIST, not allowlist. We do not know where a given team put # their data (App Lab project dir, a loose CSV, a sketch folder), so we capture # all of /home/arduino and exclude only what we can reproduce ourselves: # the GGUF models, the Arduino core cache, our llama/zeroclaw binaries, the # embedded SPA, and build output. On a reference board that leaves ~1-2 MB; # a board with a week of logged data will be larger but still quick. # # The archive is VERIFIED READABLE before the script reports success — an # unverified backup is not a backup. set -euo pipefail SERIAL="${1:?usage: backup-uno-q.sh [team-label] [outdir]}" LABEL="${2:-$SERIAL}" OUTDIR="${3:-./backups}" a() { adb -s "$SERIAL" "$@"; } STAMP="$(date -u +%Y%m%dT%H%M%SZ)" NAME="unoq-${LABEL}-${STAMP}" REMOTE_TAR="/tmp/${NAME}.tar.gz" LOCAL_TAR="${OUTDIR}/${NAME}.tar.gz" LOCAL_MANIFEST="${OUTDIR}/${NAME}.manifest.txt" # Reproducible — ours, or regenerable. Everything else is theirs and is kept. # # DELIBERATELY NOT EXCLUDED: ~/.local/share/arduino-app-cli/examples (~37M). # Those are Arduino's stock App Lab examples, so in principle reproducible — # but several of them (real-time-accelerometer, anomaly-detection, # air-quality-monitoring) are exactly what a student doing sensor work would # open and then edit IN PLACE. Excluding them would silently discard a week of # work to save 25MB. We keep them. A backup is insurance, not a size contest. EXCLUDES=( '--exclude=./models' # 1.5G of GGUF weights — we push these '--exclude=./.arduino15' # ~570M Arduino cores/index — arduino-cli refetches '--exclude=./llama' # llama.cpp binaries — we push these '--exclude=./web-dist' # embedded SPA build — we push this '--exclude=./.cache' # regenerable '--exclude=./lost+found' '--exclude=./zeroclaw' # the daemon binary... '--exclude=./zeroclaw.*' # ...and its .bak copies '--exclude=./llama8083.log' '--exclude=./zc-daemon.log' '--exclude=./zc-supervisor.log' '--exclude=*/build' # sketch build output — recompiled on demand ) mkdir -p "$OUTDIR" echo "==> [1/5] board reachable?" a shell 'echo ok' >/dev/null 2>&1 || { echo " FAIL: board $SERIAL not reachable over adb"; exit 1; } a shell 'test -d /home/arduino' >/dev/null 2>&1 || { echo " FAIL: /home/arduino missing"; exit 1; } echo " $SERIAL ok" echo "==> [2/5] inventory what will be captured" a shell ' cd /home/arduino || exit 1 echo " user-work directories:" for d in sketches Arduino ArduinoApps .arduino-bricks; do if [ -e "$d" ]; then printf " %-18s %s\n" "$d" "$(du -sh "$d" 2>/dev/null | cut -f1)"; fi done echo " data-shaped files (top 10 by size):" find . -maxdepth 4 \( -name "*.csv" -o -name "*.tsv" -o -name "*.dat" -o -name "*.json" -o -name "*.txt" -o -name "*.py" \) \ -not -path "./.arduino15/*" -not -path "./.cache/*" -not -path "*/build/*" -not -path "./.zeroclaw/*" \ -printf " %s\t%p\n" 2>/dev/null | sort -rn | head -10 echo " (none found)" ' 2>/dev/null || true echo "==> [3/5] archive on board" a shell "cd /home/arduino && tar czf '$REMOTE_TAR' ${EXCLUDES[*]} . 2>/dev/null; echo done" >/dev/null SIZE="$(a shell "du -h '$REMOTE_TAR' 2>/dev/null | cut -f1" | tr -d '\r\n ')" echo " archive: $SIZE" echo "==> [4/5] pull + verify" a pull "$REMOTE_TAR" "$LOCAL_TAR" >/dev/null 2>&1 || { echo " FAIL: could not pull archive"; exit 1; } # An unverified backup is not a backup: list the archive and require real content. if ! tar tzf "$LOCAL_TAR" > "$LOCAL_MANIFEST" 2>/dev/null; then echo " FAIL: archive is not readable — DO NOT REFLASH THIS BOARD" exit 1 fi ENTRIES="$(wc -l < "$LOCAL_MANIFEST" | tr -d ' ')" if [ "$ENTRIES" -lt 5 ]; then echo " FAIL: archive has only $ENTRIES entries — suspiciously empty, DO NOT REFLASH" exit 1 fi echo " verified: $ENTRIES entries, manifest at $LOCAL_MANIFEST" echo "==> [5/5] clean up board temp" a shell "rm -f '$REMOTE_TAR'" >/dev/null 2>&1 || true cat <