Spaces:
Running
Running
| # ============================================================ | |
| # 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 | |