#!/usr/bin/env bash # Deploy Pai Pet in one go: push to the Hugging Face Space, update the app on # the robot, and (re)start it. # # Usage (from anywhere in the repo): # ./scripts/deploy.sh # # The robot is found automatically. To point it at a specific address: # ROBOT=192.168.1.209 ./scripts/deploy.sh set -euo pipefail APP=pai_pet SPACE=IsabellePai/pai_pet FALLBACK_IP=192.168.1.209 # last known address of the robot cd "$(git rev-parse --show-toplevel)" say() { printf '\n\033[1m==> %s\033[0m\n' "$*"; } fail() { printf '\n\033[31mERROR: %s\033[0m\n' "$*" >&2; exit 1; } json() { python3 -c "import sys,json; d=json.load(sys.stdin); print($1)"; } # --- 1. Uncommitted changes are not deployed: warn --------------------------- if [ -n "$(git status --porcelain)" ]; then say "You have changes that are not committed (they will NOT be deployed):" git status --short read -r -p "Continue anyway? [y/N] " answer [[ "$answer" =~ ^[yY]$ ]] || exit 1 fi # --- 2. Push to the Space ---------------------------------------------------- say "Pushing to Hugging Face ($SPACE)" git push origin main HEAD_SHA=$(git rev-parse HEAD) SPACE_SHA=$(curl -s -m 15 "https://huggingface.co/api/spaces/$SPACE" | json "d.get('sha','')" || true) [ "$SPACE_SHA" = "$HEAD_SHA" ] || fail "The Space is on ${SPACE_SHA:0:7}, but your code is on ${HEAD_SHA:0:7}. Did the push work?" echo "Space is on ${HEAD_SHA:0:7}" # --- 3. Find the robot --------------------------------------------------------- say "Looking for the robot" robot_ok() { curl -s -m 4 "http://$1:8000/api/daemon/status" >/dev/null; } HOST="${ROBOT:-}" if [ -z "$HOST" ]; then if robot_ok reachy-mini.local; then HOST=reachy-mini.local elif command -v powershell.exe >/dev/null; then # WSL can't resolve .local names, but Windows can. HOST=$(timeout 20 powershell.exe -NoProfile -Command \ "(Resolve-DnsName reachy-mini.local -Type A -ErrorAction SilentlyContinue | Select-Object -First 1).IPAddress" \ 2>/dev/null | tr -d '\r' || true) fi if [ -z "$HOST" ] || ! robot_ok "$HOST"; then HOST=$FALLBACK_IP; fi fi robot_ok "$HOST" || fail "Can't reach the robot at $HOST. Is it on, and on the same Wi-Fi? (Or set ROBOT=)" API="http://$HOST:8000/api/apps" echo "Robot found at $HOST" # --- 4. Stop the app if it is running ---------------------------------------- RUNNING=$(curl -s -m 8 "$API/current-app-status" | json "(d or {}).get('info',{}).get('name','')") if [ -n "$RUNNING" ]; then say "Stopping $RUNNING" curl -s -m 20 -X POST "$API/stop-current-app" >/dev/null fi # --- 5. Update (or install, the first time) ---------------------------------- INSTALLED=$(curl -s -m 8 "$API/list-available/installed" | json "'$APP' in [a['name'] for a in d]") if [ "$INSTALLED" = "True" ]; then say "Updating $APP on the robot" JOB=$(curl -s -m 30 -X POST "$API/update/$APP" | json "d['job_id']") else say "Installing $APP on the robot" JOB=$(curl -s -m 30 -X POST "$API/install" -H "Content-Type: application/json" \ -d "{\"name\": \"$APP\", \"source_kind\": \"hf_space\", \"url\": \"https://huggingface.co/spaces/$SPACE\"}" | json "d['job_id']") fi STATUS="" for _ in $(seq 1 60); do # up to ~3 minutes STATUS=$(curl -s -m 8 "$API/job-status/$JOB" | json "d['status']" || true) [ "$STATUS" = done ] || [ "$STATUS" = failed ] && break printf '.'; sleep 3 done echo if [ "$STATUS" != done ]; then curl -s -m 8 "$API/job-status/$JOB" | json "'\n'.join(d.get('logs',[])[-15:])" || true fail "Update did not finish (status: ${STATUS:-unknown})" fi ROBOT_SHA=$(curl -s -m 30 "$API/check-updates" | json \ "next((a['installed_sha'] for a in d['apps_with_updates'] if a['app_name']=='$APP'), '$HEAD_SHA')") [ "$ROBOT_SHA" = "$HEAD_SHA" ] || fail "The robot installed ${ROBOT_SHA:0:7}, expected ${HEAD_SHA:0:7}" echo "Robot has ${HEAD_SHA:0:7}" # --- 6. Start it ------------------------------------------------------------- say "Starting $APP (the robot will move)" curl -s -m 20 -X POST "$API/start-app/$APP" >/dev/null STATE="" for _ in $(seq 1 15); do STATE=$(curl -s -m 8 "$API/current-app-status" | json "(d or {}).get('state','')" || true) [ "$STATE" = running ] && break sleep 1 done [ "$STATE" = running ] || fail "$APP did not start (state: ${STATE:-not running})" say "Done: $APP ${HEAD_SHA:0:7} is running on the robot"