Spaces:
Running
Running
| # Sync main to Hugging Face Spaces as a history-free snapshot. | |
| # Usage: | |
| # bash scripts/sync-space.sh | |
| # bash scripts/sync-space.sh space | |
| # bash scripts/sync-space.sh space space-show | |
| set -euo pipefail | |
| SOURCE_BRANCH="${SOURCE_BRANCH:-main}" | |
| TEMP_BRANCH="${TEMP_BRANCH:-__space-sync-tmp}" | |
| AUTO_STASH="${AUTO_STASH:-1}" | |
| INCLUDE_UNTRACKED="${INCLUDE_UNTRACKED:-1}" | |
| if [ "$#" -gt 0 ]; then | |
| REMOTES=("$@") | |
| else | |
| REMOTES=("space" "space-show") | |
| fi | |
| # Binary files blocked by HF Spaces git server. | |
| EXCLUDE_PATTERNS=( | |
| "public/readme-images/*.png" | |
| "src/audio/tracks/*.mp3" | |
| ) | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| HF_README_FRONTMATTER_PATH="$SCRIPT_DIR/hf-readme-frontmatter.txt" | |
| update_space_readme() { | |
| local readme_path="README.md" | |
| [[ -f "$readme_path" && -f "$HF_README_FRONTMATTER_PATH" ]] || return 0 | |
| local temp_body | |
| temp_body="$(mktemp)" | |
| awk ' | |
| BEGIN { in_frontmatter = 0; frontmatter_done = 0 } | |
| NR == 1 && $0 == "---" { in_frontmatter = 1; next } | |
| in_frontmatter && $0 == "---" && !frontmatter_done { frontmatter_done = 1; next } | |
| in_frontmatter && !frontmatter_done { next } | |
| { print } | |
| ' "$readme_path" > "$temp_body" | |
| { | |
| cat "$HF_README_FRONTMATTER_PATH" | |
| printf "\n\n" | |
| cat "$temp_body" | |
| } > "$readme_path" | |
| rm -f "$temp_body" | |
| } | |
| current="$(git branch --show-current)" | |
| if [ "$current" != "$SOURCE_BRANCH" ]; then | |
| echo "Error: please checkout $SOURCE_BRANCH first" | |
| exit 1 | |
| fi | |
| for remote in "${REMOTES[@]}"; do | |
| if ! git remote get-url "$remote" >/dev/null 2>&1; then | |
| echo "Error: remote not found: $remote" | |
| exit 1 | |
| fi | |
| done | |
| stash_created=0 | |
| temp_branch_created=0 | |
| cleanup() { | |
| set +e | |
| if [ "$temp_branch_created" -eq 1 ]; then | |
| git checkout -f "$SOURCE_BRANCH" >/dev/null 2>&1 || echo "Warning: failed to switch back to $SOURCE_BRANCH" | |
| git branch -D "$TEMP_BRANCH" >/dev/null 2>&1 || echo "Warning: failed to delete temp branch $TEMP_BRANCH" | |
| fi | |
| if [ "$stash_created" -eq 1 ]; then | |
| echo "Restoring stashed changes..." | |
| git stash pop --index "stash@{0}" >/dev/null 2>&1 || echo "Warning: failed to auto-restore stash. Recover it manually with: git stash list" | |
| fi | |
| } | |
| trap cleanup EXIT | |
| if [ -n "$(git status --porcelain)" ]; then | |
| if [ "$AUTO_STASH" != "1" ]; then | |
| echo "Error: working tree not clean, please commit or stash first" | |
| exit 1 | |
| fi | |
| echo "Working tree is not clean. Auto-stashing changes..." | |
| if [ "$INCLUDE_UNTRACKED" = "1" ]; then | |
| git stash push --include-untracked -m "__space_sync_auto__" >/dev/null | |
| else | |
| git stash push -m "__space_sync_auto__" >/dev/null | |
| fi | |
| stash_created=1 | |
| fi | |
| git checkout --orphan "$TEMP_BRANCH" >/dev/null | |
| temp_branch_created=1 | |
| update_space_readme | |
| git add -A | |
| for pattern in "${EXCLUDE_PATTERNS[@]}"; do | |
| git rm -rf --cached --ignore-unmatch -- $pattern 2>/dev/null || true | |
| done | |
| git commit -m "Sync from $SOURCE_BRANCH: $(git log "$SOURCE_BRANCH" -1 --format='%h %s')" >/dev/null | |
| push_failed=0 | |
| for remote in "${REMOTES[@]}"; do | |
| echo "Pushing to $remote..." | |
| if git push "$remote" "$TEMP_BRANCH:main" --force; then | |
| echo " ✓ $remote pushed" | |
| else | |
| echo " ✗ $remote push failed" | |
| push_failed=1 | |
| fi | |
| done | |
| if [ "$push_failed" -ne 0 ]; then | |
| echo "Error: one or more remotes failed" | |
| exit 1 | |
| fi | |
| echo "Done!" | |