File size: 5,147 Bytes
07aa9a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#!/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