page / scripts /rebuild-space.sh
GGSheng's picture
feat: deploy to hf space | model=claude-22327b
31b91d7 verified
Raw
History Blame Contribute Delete
8.89 kB
#!/usr/bin/env bash
# ============================================================
# OpenClaw HF Space 使用最新代码重建脚本
#
# 用法:
# ./rebuild-space.sh <SPACE_REPO_ID> [HF_TOKEN]
#
# 示例:
# ./rebuild-space.sh <user>/<space> hf_xxxxx
#
# 方式1:直接提供参数
# ./scripts/rebuild-space.sh <user>/<space> hf_xxxxx
#
# 方式2:设置环境变量
# export SPACE_REPO_ID="<user>/<space>"
# export HF_TOKEN="hf_xxxxx"
# ./scripts/rebuild-space.sh
#
# 方式3:使用缓存的 token(默认从 ~/.cache/huggingface/token 读取)
# ./scripts/rebuild-space.sh <user>/<space>
#
# 环境变量 (可选):
# SPACE_REPO_ID - Space 仓库 ID (例如 <user>/<space>)
# HF_TOKEN - Hugging Face API Token
# HF_TOKEN_FILE - Token 文件路径 (默认 ~/.cache/huggingface/token)
#
# 说明:
# 此脚本用于将本地最新代码强制推送到 Hugging Face Space 并触发重建
# - 自动创建 Space(如果不存在)
# - 覆盖 Space 中的所有文件
# - 排除 .git, logs, scripts, docs, tests 等非必要文件
# - 推送完成后自动请求 Space 重启以使更改生效
#
# 注意事项:
# 1. 确保 HF_TOKEN 有 write 权限
# 2. Space 必须使用 Docker SDK
# 3. 推送后 Space 会自动重启(需等待构建完成)
# 4. 此操作会覆盖 Space 中的现有文件
# ============================================================
# ---- Fix terminal for interactive input ----
if [[ -t 0 ]]; then
stty sane 2>/dev/null || true
fi
update_readme_title() {
local new_title="$1"
local readme_file="$REPO_ROOT/README.md"
local temp_file
if [[ ! -f "$readme_file" ]]; then
return 0
fi
temp_file="$(mktemp)"
if sed "s/^title:.*/title: $new_title/" "$readme_file" > "$temp_file"; then
mv "$temp_file" "$readme_file"
else
rm -f "$temp_file"
fi
}
restore_readme_title() {
local original_title="$1"
local readme_file="$REPO_ROOT/README.md"
local temp_file
if [[ ! -f "$readme_file" ]]; then
return 0
fi
temp_file="$(mktemp)"
if sed "s/^title:.*/title: $original_title/" "$readme_file" > "$temp_file"; then
mv "$temp_file" "$readme_file"
else
rm -f "$temp_file"
fi
}
prompt_yes_no() {
local prompt="$1"
local default="${2:-}"
local choice=""
local answer=""
while [[ -z "$answer" ]]; do
if [[ -t 0 ]]; then
if [[ -n "$default" ]]; then
read -r -p "$prompt (Y/n) [$default]: " choice || true
else
read -r -p "$prompt (y/N) [$default]: " choice || true
fi
else
choice="$default"
fi
choice="$(printf '%s' "$choice" | tr -d '\r\n' | tr '[:upper:]' '[:lower:]')"
if [[ -z "$choice" ]]; then
choice="$default"
fi
case "$choice" in
y|yes) answer="yes" ;;
n|no) answer="no" ;;
*) printf 'Please answer y or n.\n' >&2 ;;
esac
done
printf '%s' "$answer"
}
prompt_secret() {
local prompt="$1"
local value=""
local char=""
if [[ -t 0 && -t 1 ]]; then
printf '%s: ' "$prompt" >&2
while IFS= read -r -s -n 1 char; do
if [[ -z "$char" ]]; then
break
fi
case "$char" in
$'\e') break ;;
'') break ;;
*) value+="$char"; printf '*' >&2 ;;
esac
done
printf '\n' >&2
fi
printf '%s' "$value"
}
prompt_hf_account_switch() {
local current_username=""
local hf_whoami_output
hf_whoami_output="$(hf auth whoami 2>&1 || true)"
current_username="$(printf '%s\n' "$hf_whoami_output" | sed -nE 's/^[[:space:]]*user:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n 1)"
if [[ -z "$current_username" ]]; then
current_username="$(printf '%s\n' "$hf_whoami_output" | sed -nE 's/.*[Ll]ogged in as[[:space:]]+([^[:space:]]+).*/\1/p' | head -n 1)"
fi
if [[ -n "$current_username" ]] && [[ -t 0 ]]; then
local use_current
use_current="$(prompt_yes_no "HF CLI is logged in as '$current_username'. Use this user?" "y")"
if [[ "$use_current" == "yes" ]]; then
return 0
fi
fi
while true; do
if [[ -t 0 ]]; then
local token
token="$(prompt_secret "Enter HF_TOKEN to switch account")"
if [[ -z "$token" ]]; then
printf '[WARN] Empty token, please try again or press Ctrl+C to cancel\n' >&2
continue
fi
local login_output
login_output="$(hf auth login --token "$token" 2>&1)"
if [[ $? -eq 0 ]]; then
local verify_output
verify_output="$(hf auth whoami 2>&1 || true)"
local new_username
new_username="$(printf '%s\n' "$verify_output" | sed -nE 's/^[[:space:]]*user:[[:space:]]*([^[:space:]]+).*/\1/p' | head -n 1)"
if [[ -z "$new_username" ]]; then
new_username="$(printf '%s\n' "$verify_output" | sed -nE 's/.*[Ll]ogged in as[[:space:]]+([^[:space:]]+).*/\1/p' | head -n 1)"
fi
if [[ -n "$current_username" ]] && [[ -n "$new_username" ]] && [[ "$current_username" != "$new_username" ]]; then
printf '[INFO] HF account switched: %s -> %s\n' "$current_username" "$new_username"
elif [[ -n "$new_username" ]]; then
printf '[INFO] HF login successful as: %s\n' "$new_username"
else
printf '[INFO] HF login successful\n'
fi
return 0
else
printf '[ERROR] HF login failed: %s\n' "$login_output" >&2
continue
fi
else
return 1
fi
done
}
SPACE_REPO_ID="${1:-${SPACE_REPO_ID:-}}"
HF_TOKEN="${2:-${HF_TOKEN:-}}"
if [[ -t 0 ]]; then
prompt_hf_account_switch || true
fi
if [[ -z "$SPACE_REPO_ID" ]]; then
echo "Usage: $0 <SPACE_REPO_ID> [HF_TOKEN]"
echo ""
echo "Examples:"
echo " $0 <user>/<space> hf_xxxxx"
echo " SPACE_REPO_ID=<user>/<space> HF_TOKEN=hf_xxxxx $0"
echo " $0 <user>/<space>"
echo ""
echo "Environment variables:"
echo " SPACE_REPO_ID - Space repo ID (例如 <user>/<space>)"
echo " HF_TOKEN - Hugging Face API Token"
echo " HF_TOKEN_FILE - Token file path (default: ~/.cache/huggingface/token)"
exit 1
fi
if [[ -z "$HF_TOKEN" ]]; then
HF_TOKEN_FILE="${HF_TOKEN_FILE:-$HOME/.cache/huggingface/token}"
if [[ -f "$HF_TOKEN_FILE" ]]; then
HF_TOKEN="$(cat "$HF_TOKEN_FILE")"
fi
fi
if [[ -z "$HF_TOKEN" ]]; then
echo "Error: HF_TOKEN is required. Provide as 2nd arg, set HF_TOKEN env var, or ensure ~/.cache/huggingface/token exists."
exit 1
fi
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
SPACE_NAME="${SPACE_REPO_ID##*/}"
echo "============================================"
echo "OpenClaw HF Space Rebuild Script"
echo "============================================"
echo "Space: $SPACE_REPO_ID"
echo "Repo: $REPO_ROOT"
echo ""
cd "$REPO_ROOT"
echo "[1/5] Logging into Hugging Face..."
echo "$HF_TOKEN" | hf auth login --token "$HF_TOKEN"
echo ""
echo "[2/5] Saving original README title..."
original_readme_title="$(sed -n 's/^title: *//p' "$REPO_ROOT/README.md" | head -n 1)"
update_readme_title "$SPACE_NAME"
restore_readme_on_exit() {
restore_readme_title "${original_readme_title:-HF Space}"
}
trap restore_readme_on_exit EXIT
echo ""
echo "[3/5] Ensuring Space exists..."
hf repos create "$SPACE_REPO_ID" --repo-type space --space-sdk docker --exist-ok
echo ""
echo "[4/5] Uploading files to Space..."
upload_tmp=$(mktemp)
hf upload "$SPACE_REPO_ID" . --repo-type space \
--exclude '.git/**' \
--exclude '.git' \
--exclude '.github/**' \
--exclude 'logs/**' \
--exclude '*.log' \
--exclude 'docs/**' \
--exclude 'tests/**' \
--exclude 'LICENSE' \
--exclude '.dockerignore' \
--exclude '.python-version' \
--commit-message "fix: improve SSH service stability and backup.py error handling" 2>&1 | tee "$upload_tmp"
upload_exit_code=${PIPESTATUS[0]}
if [ $upload_exit_code -ne 0 ]; then
echo "Error: Upload failed:"
cat "$upload_tmp"
rm -f "$upload_tmp"
exit 1
fi
rm -f "$upload_tmp"
echo ""
echo "[5/5] Requesting Space restart..."
restart_tmp=$(mktemp)
hf spaces restart "$SPACE_REPO_ID" 2>&1 | tee "$restart_tmp"
restart_exit_code=${PIPESTATUS[0]}
if [ $restart_exit_code -ne 0 ]; then
echo "Warning: Restart request failed:"
cat "$restart_tmp"
fi
rm -f "$restart_tmp"
echo ""
echo "============================================"
echo "Done! Space updated: https://huggingface.co/spaces/$SPACE_REPO_ID"
echo "============================================"