#!/bin/bash # ============================================================ # HF-VPS — Clean Deployment Script # # Enforces the mandatory workflow: # local changes → GitHub (source of truth) → HF Space # # Usage: # bash scripts/deploy.sh # standard clean deploy # bash scripts/deploy.sh --factory # factory reset (full Docker rebuild, no cache) # # Requirements: # - .env.local must exist with HF_TOKEN set # - Both git remotes must be configured: # origin → https://github.com/luizcireno-crypto/HF-SaaS.git # hf → https://huggingface.co/spaces/lulavc/HF-SaaS.git # # What this does: # 1. Validates environment and git state # 2. Pushes to GitHub (origin/main) # 3. Force-pushes to HF Space (ensures no stale git refs or old files) # 4. Optionally triggers a factory restart (full clean Docker rebuild) # 5. Monitors build status until RUNNING or ERROR # ============================================================ set -e # ---- Load credentials from .env.local ---- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ROOT_DIR="$(dirname "$SCRIPT_DIR")" ENV_FILE="$ROOT_DIR/.env.local" if [ ! -f "$ENV_FILE" ]; then echo "[deploy] ERROR: .env.local not found at $ROOT_DIR/.env.local" echo "[deploy] Create it with: HF_TOKEN=hf_..." exit 1 fi source "$ENV_FILE" if [ -z "$HF_TOKEN" ]; then echo "[deploy] ERROR: HF_TOKEN is not set in .env.local" exit 1 fi HF_USERNAME="${HF_USERNAME:-lulavc}" HF_SPACE="${HF_SPACE:-HF-SaaS}" FACTORY_RESET=false # ---- Parse flags ---- for arg in "$@"; do case $arg in --factory) FACTORY_RESET=true ;; esac done echo "" echo "============================================" echo " HF-VPS Clean Deployment" echo " Space: $HF_USERNAME/$HF_SPACE" if [ "$FACTORY_RESET" = true ]; then echo " Mode: Factory Reset (full Docker rebuild)" else echo " Mode: Standard (incremental Docker rebuild)" fi echo "============================================" echo "" # ---- Step 1: Validate git state ---- cd "$ROOT_DIR" BRANCH=$(git rev-parse --abbrev-ref HEAD) if [ "$BRANCH" != "main" ]; then echo "[deploy] ERROR: Not on main branch (currently on: $BRANCH)" echo "[deploy] Switch to main before deploying." exit 1 fi if ! git diff-index --quiet HEAD --; then echo "[deploy] ERROR: Uncommitted changes detected." echo "[deploy] Commit all changes before deploying." git status --short exit 1 fi echo "[deploy] Git state OK — branch: main, working tree clean" # ---- Step 2: Push to GitHub (source of truth) ---- echo "[deploy] Pushing to GitHub (origin/main)..." git push origin main echo "[deploy] GitHub updated ✓" # ---- Step 3: Force-push to HF Space (clean git state) ---- echo "[deploy] Force-pushing to HF Space (no stale refs or old files)..." git push hf main --force echo "[deploy] HF Space git updated ✓" # ---- Step 4: Factory reset (optional — busts Docker cache) ---- if [ "$FACTORY_RESET" = true ]; then echo "[deploy] Triggering factory restart (clean Docker rebuild, no cache)..." RESTART_RESP=$(curl -s -X POST \ "https://huggingface.co/api/spaces/$HF_USERNAME/$HF_SPACE/restart?factory=true" \ -H "Authorization: Bearer $HF_TOKEN") echo "[deploy] Factory restart triggered: $RESTART_RESP" fi # ---- Step 5: Monitor build status ---- echo "" echo "[deploy] Monitoring build status..." echo "[deploy] Logs: https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE?logs=container" echo "" MAX_WAIT=300 # 5 minutes max INTERVAL=10 ELAPSED=0 while [ $ELAPSED -lt $MAX_WAIT ]; do STAGE=$(curl -s \ "https://huggingface.co/api/spaces/$HF_USERNAME/$HF_SPACE" \ -H "Authorization: Bearer $HF_TOKEN" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('runtime',{}).get('stage','unknown'))" 2>/dev/null) ERROR=$(curl -s \ "https://huggingface.co/api/spaces/$HF_USERNAME/$HF_SPACE" \ -H "Authorization: Bearer $HF_TOKEN" \ | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('runtime',{}).get('errorMessage',''))" 2>/dev/null) printf "[deploy] [%3ds] Stage: %s\n" "$ELAPSED" "$STAGE" if [ "$STAGE" = "RUNNING" ]; then echo "" echo "============================================" echo " Deployment successful!" echo " Space: https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE" echo " App: https://$HF_USERNAME-$(echo $HF_SPACE | tr '[:upper:]' '[:lower:]').hf.space" echo "============================================" exit 0 fi if [ "$STAGE" = "ERROR" ] || [ "$STAGE" = "BUILD_ERROR" ]; then echo "" echo "[deploy] ERROR: Build failed!" echo "[deploy] Error: $ERROR" echo "[deploy] Check logs: https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE?logs=container" exit 1 fi sleep $INTERVAL ELAPSED=$((ELAPSED + INTERVAL)) done echo "[deploy] Timed out after ${MAX_WAIT}s — check logs manually:" echo "[deploy] https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE?logs=container" exit 1