#!/usr/bin/env bash # ───────────────────────────────────────────────────────────────────────────── # SanadR1 turnkey installer — run this ON A FRESH R1 EDU BACKPACK. # # tar xzf sanadr1-bundle-*.tar.gz && cd sanadr1-bundle && ./install.sh # # Auto-detects the robot NIC, the current user/home, and USB audio — no values # are hardcoded to any one robot. Idempotent: safe to re-run to update the image. # # The Gemini API key is taken from (first found): # 1. $SANAD_GEMINI_API_KEY in the environment # 2. an existing /.env # 3. an interactive prompt # ───────────────────────────────────────────────────────────────────────────── set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" IMG_TAR="$HERE/sanadr1-latest-arm64.tar.gz" say() { printf '\033[1;36m>> %s\033[0m\n' "$*"; } die() { printf '\033[1;31m!! %s\033[0m\n' "$*" >&2; exit 1; } # ── 1. preflight ──────────────────────────────────────────────────────────── command -v docker >/dev/null 2>&1 || die "Docker is not installed. Install Docker Engine first, then re-run." docker info >/dev/null 2>&1 || die "Docker daemon not reachable. Start it (sudo systemctl start docker) or add yourself to the 'docker' group." [ -f "$IMG_TAR" ] || die "missing image tarball: $IMG_TAR (run this from the extracted bundle dir)." arch="$(uname -m)" [ "$arch" = "aarch64" ] || say "WARNING: arch=$arch — the image is arm64/aarch64; it will only run on an ARM64 backpack." # sudo helper (for the systemd unit); prompts once if needed. SUDO=""; [ "$(id -u)" -eq 0 ] || SUDO="sudo" $SUDO true 2>/dev/null || die "need sudo to install the systemd auto-start unit." # ── 2. auto-detect the robot facts ────────────────────────────────────────── IFACE="$(ip -o -4 addr show 2>/dev/null | awk '$4 ~ /^192\.168\.123\./{print $2; exit}')" [ -n "$IFACE" ] || { IFACE=eth10; say "no NIC on 192.168.123.x yet (robot link down?) — defaulting to eth10"; } RUN_USER="$(id -un)"; RUN_HOME="$HOME" DEST="$RUN_HOME/SanadR1/docker" say "robot NIC: $IFACE" say "install as: user=$RUN_USER home=$RUN_HOME" say "install to: $DEST" # ── 3. load the image ─────────────────────────────────────────────────────── say "loading sanadr1:latest (this can take ~30-60 s)…" gunzip -c "$IMG_TAR" | docker load docker image inspect sanadr1:latest >/dev/null 2>&1 || die "image did not load." # ── 4. drop the run recipe ────────────────────────────────────────────────── mkdir -p "$DEST" cp "$HERE/run.sh" "$DEST/run.sh"; chmod +x "$DEST/run.sh" cp "$HERE/healthcheck.sh" "$DEST/healthcheck.sh"; chmod +x "$DEST/healthcheck.sh" # ── 5. .env (key + detected interface) ────────────────────────────────────── ENV_FILE="$DEST/.env" if [ -f "$ENV_FILE" ] && grep -q '^SANAD_GEMINI_API_KEY=.\+' "$ENV_FILE"; then say "keeping existing $ENV_FILE (already has a key)" else KEY="${SANAD_GEMINI_API_KEY:-}" if [ -z "$KEY" ]; then printf 'Enter SANAD_GEMINI_API_KEY (paste, then Enter): ' read -r KEY fi [ -n "$KEY" ] || die "no Gemini API key given — set SANAD_GEMINI_API_KEY or paste it when prompted." umask 077 printf 'SANAD_GEMINI_API_KEY=%s\nSANAD_DDS_INTERFACE=%s\n' "$KEY" "$IFACE" > "$ENV_FILE" say "wrote $ENV_FILE (mode 600)" fi # ensure the interface line is present even if .env was pre-supplied grep -q '^SANAD_DDS_INTERFACE=' "$ENV_FILE" || printf 'SANAD_DDS_INTERFACE=%s\n' "$IFACE" >> "$ENV_FILE" # ── 6. systemd auto-start unit (generated with the detected user/path) ────── say "installing systemd unit → /etc/systemd/system/sanadr1.service" $SUDO tee /etc/systemd/system/sanadr1.service >/dev/null </dev/null 2>&1 || $DEST/run.sh' ExecStart=/usr/bin/docker start sanadr1 ExecStop=/usr/bin/docker stop sanadr1 [Install] WantedBy=multi-user.target EOF $SUDO systemctl daemon-reload # ── 7. first run + enable on boot ─────────────────────────────────────────── say "starting the container…" "$DEST/run.sh" $SUDO systemctl enable sanadr1 >/dev/null 2>&1 || true # Activate the oneshot too (container is already up; ExecStartPre sees it and # skips run.sh, ExecStart's `docker start` is a no-op) so `systemctl is-active` # reads 'active' now, not just after the next reboot. $SUDO systemctl start sanadr1 >/dev/null 2>&1 || true # ── 8. verify (full health check) ─────────────────────────────────────────── say "waiting for the dashboard…" up=0 for i in $(seq 1 25); do curl -fsS -m3 http://127.0.0.1:8001/ >/dev/null 2>&1 && { up=1; break; }; sleep 2; done [ "$up" = 1 ] || die "dashboard did not answer in ~50 s. Check: docker logs -f sanadr1" WIFI="$(ip -o -4 addr show 2>/dev/null | awk '$4 ~ /^10\.|^192\.168\.[0-9]+\./ && $2!="lo"{print $4}' | cut -d/ -f1 | head -1)" say "dashboard up → http://${WIFI:-}:8001 — running full health check…" echo if "$DEST/healthcheck.sh"; then say "auto-start enabled. Manage with: sudo systemctl {status,restart,stop} sanadr1" exit 0 else say "install finished but the health check reported failures (see above). Re-run: $DEST/healthcheck.sh" exit 1 fi