Spaces:
Running
Running
| # ============================================================================= | |
| # scripts/deploy_to_hf.sh β one-command deploy to Hugging Face Spaces. | |
| # ============================================================================= | |
| # Strategy: snapshot the current main commit into a fresh orphan branch, swap | |
| # README.md for the HF-flavored version (with the YAML frontmatter Spaces | |
| # needs), force-push that single-commit branch as HF's `main`, then clean up. | |
| # | |
| # Why an orphan branch (not a regular merge / push of main)? | |
| # 1. HF Spaces requires YAML frontmatter at the top of README.md so it | |
| # knows to launch a Docker container. GitHub's full README has none β | |
| # we only want the swap to live on the deploy branch, not on main. | |
| # 2. main's git history is ~1000+ commits. HF pushes carry the full | |
| # reachable history, and any historical commit that ever held a | |
| # binary >10 MB would fail HF's pre-receive hook. Orphan = 1 commit, | |
| # no history to litigate. | |
| # | |
| # Prerequisites: | |
| # * `hf` remote already configured (run once: | |
| # git remote add hf https://huggingface.co/spaces/messili/polyglot-alpha) | |
| # * `hf auth login` already done (token cached by huggingface_hub CLI). | |
| # * Working tree is clean on main, or untracked-only. | |
| # ----------------------------------------------------------------------------- | |
| set -euo pipefail | |
| # ---- color/log helpers ------------------------------------------------------ | |
| if [[ -t 1 ]]; then | |
| BLUE=$'\033[34m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'; RED=$'\033[31m'; RESET=$'\033[0m' | |
| else | |
| BLUE=''; GREEN=''; YELLOW=''; RED=''; RESET='' | |
| fi | |
| log() { printf '%s[deploy]%s %s\n' "$BLUE" "$RESET" "$*"; } | |
| ok() { printf '%s[deploy]%s %s\n' "$GREEN" "$RESET" "$*"; } | |
| warn() { printf '%s[deploy]%s %s\n' "$YELLOW" "$RESET" "$*" >&2; } | |
| die() { printf '%s[deploy]%s %s\n' "$RED" "$RESET" "$*" >&2; exit 1; } | |
| REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" | |
| cd "$REPO_ROOT" | |
| # ---- preflight checks ------------------------------------------------------- | |
| log "preflight checks" | |
| git remote get-url hf > /dev/null 2>&1 || die "hf remote not configured. Run: git remote add hf https://huggingface.co/spaces/messili/polyglot-alpha" | |
| [[ -f deploy/hf-readme.md ]] || die "deploy/hf-readme.md missing β HF needs the YAML frontmatter version" | |
| [[ -f Dockerfile ]] || die "Dockerfile missing β HF Spaces is a Docker SDK Space" | |
| [[ -f deploy/nginx.conf ]] || die "deploy/nginx.conf missing β required by Dockerfile" | |
| [[ -f deploy/entrypoint.sh ]] || die "deploy/entrypoint.sh missing β required by Dockerfile" | |
| # Working tree state: tracked changes block deploy, untracked are fine. | |
| if ! git diff-index --quiet HEAD --; then | |
| die "working tree has uncommitted tracked changes; commit or stash first" | |
| fi | |
| # Contract ABIs (contracts/out/*.json) are not tracked on main (excluded by | |
| # contracts/.gitignore from Foundry), but the backend NEEDS them at runtime | |
| # to know the deployed contract interfaces. Build them now so the orphan | |
| # snapshot includes a freshly compiled set. | |
| command -v forge >/dev/null 2>&1 || die "forge not installed β install Foundry: https://book.getfoundry.sh/getting-started/installation" | |
| log "running forge build to refresh contracts/out/ (compiled ABIs)" | |
| ( cd contracts && forge build --silent ) || die "forge build failed β fix contracts before deploying" | |
| [[ -d contracts/out ]] || die "forge build succeeded but contracts/out/ missing β something is very wrong" | |
| ORIGINAL_BRANCH="$(git rev-parse --abbrev-ref HEAD)" | |
| if [[ "$ORIGINAL_BRANCH" != "main" ]]; then | |
| warn "currently on '$ORIGINAL_BRANCH' (not main) β will checkout main" | |
| git checkout main | |
| fi | |
| MAIN_SHA_SHORT="$(git rev-parse --short HEAD)" | |
| TMP_BRANCH="hf-deploy-$(date +%Y%m%d-%H%M%S)" | |
| log "deploying main @ $MAIN_SHA_SHORT β hf:main (via temp orphan '$TMP_BRANCH')" | |
| # ---- cleanup trap ----------------------------------------------------------- | |
| # If anything below fails, restore the user to their starting branch, restore | |
| # contracts/.git if we moved it, and delete the temp orphan branch so they | |
| # don't end up stuck on a half-built orphan. | |
| CONTRACTS_GIT_MOVED=0 | |
| cleanup() { | |
| local exit_code=$? | |
| # Restore contracts/.git if we renamed it. | |
| if [[ "$CONTRACTS_GIT_MOVED" -eq 1 && -d contracts/.git.deploy-tmp ]]; then | |
| mv contracts/.git.deploy-tmp contracts/.git | |
| fi | |
| # Switch off temp branch and delete it. | |
| local current | |
| current="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo HEAD)" | |
| if [[ "$current" == "$TMP_BRANCH" || "$current" == "HEAD" ]]; then | |
| git checkout --force "$ORIGINAL_BRANCH" 2>/dev/null || git checkout --force main 2>/dev/null || true | |
| fi | |
| if [[ -n "${TMP_BRANCH:-}" ]] && git show-ref --verify --quiet "refs/heads/$TMP_BRANCH"; then | |
| git branch -D "$TMP_BRANCH" > /dev/null 2>&1 || true | |
| fi | |
| if [[ $exit_code -ne 0 ]]; then | |
| warn "deploy aborted (exit $exit_code)" | |
| fi | |
| exit $exit_code | |
| } | |
| trap cleanup EXIT | |
| # ---- build the orphan snapshot --------------------------------------------- | |
| log "creating orphan branch '$TMP_BRANCH'" | |
| git checkout --orphan "$TMP_BRANCH" | |
| # `git checkout --orphan` keeps the working tree but stages everything. We | |
| # want to start from a clean stage, then re-add everything filtered by | |
| # .gitignore β so a stray binary in working tree doesn't sneak through. | |
| git rm -rf --cached . > /dev/null 2>&1 || true | |
| # Foundry leaves a nested `.git` in `contracts/` (its own submodule lockfile | |
| # tracking). When the outer repo runs `git add -A`, it sees this as a | |
| # (broken) submodule and aborts with "does not have a commit checked out". | |
| # Temporarily rename it; the cleanup trap restores it on the way out. | |
| if [[ -d contracts/.git ]]; then | |
| log "temporarily renaming contracts/.git (foundry nested) so git add -A can recurse" | |
| mv contracts/.git contracts/.git.deploy-tmp | |
| CONTRACTS_GIT_MOVED=1 | |
| fi | |
| git add -A | |
| log "swapping README for HF-flavored version (deploy/hf-readme.md β README.md)" | |
| cp deploy/hf-readme.md README.md | |
| git add README.md | |
| # contracts/out (compiled ABI JSON) is in .gitignore on main, but HF Spaces | |
| # needs the ABIs at runtime β force-add the subdir into the orphan snapshot. | |
| if [[ -d contracts/out ]]; then | |
| log "force-adding contracts/out/ (ABIs needed by backend, ignored on main)" | |
| git add -f contracts/out/ | |
| fi | |
| # Drop dev-facing assets HF Spaces doesn't need and whose binaries trip | |
| # HF's pre-receive hook (e.g. submission/diagrams/*.png are GitHub hackathon | |
| # docs, never served by the Docker container). We `git rm --cached` them | |
| # from the orphan index β the working tree copies stay so main is untouched | |
| # when the cleanup trap checks us back out. | |
| HF_EXCLUDE_PATHS=( | |
| submission # hackathon write-up β GitHub-only | |
| docs # design docs β GitHub-only | |
| examples # operator example scripts β GitHub-only | |
| contracts/lib # foundry submodules β only needed for forge build | |
| contracts/cache # foundry compile cache | |
| contracts/broadcast # foundry deploy logs | |
| ) | |
| for path in "${HF_EXCLUDE_PATHS[@]}"; do | |
| if git ls-files --cached "$path" 2>/dev/null | head -1 | grep -q .; then | |
| git rm -r --cached --quiet "$path" > /dev/null 2>&1 || true | |
| fi | |
| done | |
| # Single commit β HF only ever sees this one. | |
| COMMIT_MSG="deploy: main@${MAIN_SHA_SHORT} β HF Spaces ($(date -u +%Y-%m-%dT%H:%MZ))" | |
| git commit --quiet -m "$COMMIT_MSG" | |
| ok "snapshot committed: $(git rev-parse --short HEAD)" | |
| # ---- push to HF ------------------------------------------------------------- | |
| log "force-pushing to hf:main (HF Space will auto-rebuild on receipt)" | |
| if ! git push hf "$TMP_BRANCH:main" --force 2>&1; then | |
| die "push to HF failed β see error above. Common causes: stale auth ('hf auth login'), Space settings (sdk: docker required), or large files." | |
| fi | |
| ok "pushed β https://huggingface.co/spaces/messili/polyglot-alpha" | |
| # ---- restore main, swap README back ----------------------------------------- | |
| log "restoring '$ORIGINAL_BRANCH' (your README.md never moved on main)" | |
| git checkout --force "$ORIGINAL_BRANCH" | |
| # Cleanup trap deletes the temp branch on exit. | |
| ok "deploy complete" | |
| echo | |
| echo " HF Space: https://messili-polyglot-alpha.hf.space/" | |
| echo " Build progress: https://huggingface.co/spaces/messili/polyglot-alpha" | |
| echo " Cold rebuild ~3-5 min; warm rebuild ~30-60 s." | |