pai_pet / scripts /deploy.sh
IDS75912's picture
Claude Opus 5.5
Add deploy script: push, update the robot and restart Pai Pet
362788c
Raw History Blame Contribute Delete
4.36 kB
#!/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=<ip>)"
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"