#!/usr/bin/env bash # ============================================================================== # sync.sh — 把工作成果推回 Hugging Face (agent 崩潰後的唯一保險) # # ./sync.sh "訊息" # 提交並推到兩個 repo # ./sync.sh --status # 只看狀態 # ./sync.sh --pull # 從遠端拉回最新 (崩潰後續作第一步) # # 兩個目標: # 1. HelloSun/testzerogpu (Space, /app 的 origin) — 部署用 # 2. HelloSun/qwen38-27b-ssd-streaming (儲存 repo) — 工作成果備份 # # Token: 環境變數 HF_TOKEN, 或檔案 .hf_token # ============================================================================== set -uo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SPACE_REPO="HelloSun/testzerogpu" STORAGE_REPO="HelloSun/qwen38-27b-ssd-streaming" msg="" MODE=push for a in "$@"; do case "$a" in --status) MODE=status ;; --pull) MODE=pull ;; *) msg="$a" ;; esac done [[ -z "$msg" ]] && msg="sync: $(date -Iseconds)" cd "$REPO_DIR" || { echo "no repo dir"; exit 1; } # ── token ── if [[ -z "${HF_TOKEN:-}" && -f "$REPO_DIR/.hf_token" ]]; then HF_TOKEN="$(tr -d ' \n\r' < "$REPO_DIR/.hf_token")" fi if [[ -z "${HF_TOKEN:-}" ]]; then echo "[sync] 找不到 HF_TOKEN"; exit 1 fi export HF_TOKEN git config user.email "${HF_GIT_EMAIL:-agent@huggingface.co}" git config user.name "${HF_GIT_NAME:-Auto Upload Agent}" # ── remotes ── ensure_remote() { local name="$1" repo="$2" local url="https://oauth2:${HF_TOKEN}@huggingface.co/${repo}" if git remote get-url "$name" >/dev/null 2>&1; then git remote set-url "$name" "$url" else git remote add "$name" "$url" fi } ensure_remote origin "spaces/$SPACE_REPO" ensure_remote storage "$STORAGE_REPO" case "$MODE" in status) echo "── git status ──"; git status --porcelain | head -50 echo "── last commits ──"; git log --oneline -8 echo "── remotes ──"; git remote -v | sed 's/oauth2:[^@]*/oauth2:***/' exit 0 ;; pull) echo "[sync] 拉取最新進度 ..." git fetch --all --prune 2>&1 | tail -5 for b in origin storage; do git checkout -B main "$b/main" 2>/dev/null && echo "[sync] 已對齊 $b/main" done exit 0 ;; esac # ── 排除大檔 (模型/日誌雜訊) ── cat > "$REPO_DIR/.gitignore.sync" <<'EOF' models/ validate/*.log validate/memwatch.log *.gguf *.swap node_modules/ EOF [[ -f "$REPO_DIR/.gitignore" ]] || cp "$REPO_DIR/.gitignore.sync" "$REPO_DIR/.gitignore" git add -A 2>&1 | tail -3 if git diff --cached --quiet; then echo "[sync] 沒有變更" exit 0 fi git commit -q -m "$msg" || { echo "[sync] commit 失敗"; exit 1; } echo "[sync] committed: $(git rev-parse --short HEAD)" push_one() { local name="$1" git push -q "$name" HEAD:main 2>&1 | tail -3 local rc=$? if [[ $rc -eq 0 ]]; then echo "[sync] ✔ pushed -> $name" else echo "[sync] ✘ push 失敗 -> $name" fi return $rc } rc=0 push_one origin || rc=1 push_one storage || rc=1 exit $rc