#!/usr/bin/env bash # Deploy gradio-apps/compounding-test/ to a HuggingFace Space via # `git subtree push`. The monorepo stays the canonical source; the # Space repo holds only the contents of that one subdirectory. # # One-time setup (run by the user before first deploy): # git remote add hf-compounding-test https://huggingface.co/spaces// # # the remote name MUST be `hf-compounding-test` — this script looks for it # # Every deploy: # ./gradio-apps/compounding-test/deploy.sh # push current branch # ./gradio-apps/compounding-test/deploy.sh --dry-run # sanity checks only # ./gradio-apps/compounding-test/deploy.sh --force # force-push (first deploy only) set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" SPACE_DIR="$REPO_ROOT/gradio-apps/compounding-test" SPACE_PREFIX="gradio-apps/compounding-test" REMOTE_NAME="hf-compounding-test" TARGET_BRANCH="main" # HuggingFace Spaces use `main` as the deploy branch DRY_RUN=0 FORCE=0 for arg in "$@"; do case "$arg" in --dry-run) DRY_RUN=1 ;; --force) FORCE=1 ;; *) echo "Unknown flag: $arg" >&2; exit 1 ;; esac done # --------------------------------------------------------------------------- # Sanity checks (fail fast with clear messages) # --------------------------------------------------------------------------- cd "$REPO_ROOT" # 1. Space directory exists if [[ ! -d "$SPACE_DIR" ]]; then echo "✗ Space directory not found: $SPACE_DIR" >&2 exit 1 fi # 2. Required files exist at the Space root for required in app.py requirements.txt README.md; do if [[ ! -f "$SPACE_DIR/$required" ]]; then echo "✗ Missing required file at Space root: $SPACE_PREFIX/$required" >&2 exit 1 fi done # 3. README.md has the YAML header HF Spaces needs (sdk, app_file). # Without these, the Space won't build. if ! grep -q "^sdk: gradio" "$SPACE_DIR/README.md"; then echo "✗ $SPACE_PREFIX/README.md is missing 'sdk: gradio' in its YAML header." >&2 echo " HuggingFace Spaces require this to pick the Gradio runtime." >&2 exit 1 fi if ! grep -q "^app_file: app.py" "$SPACE_DIR/README.md"; then echo "✗ $SPACE_PREFIX/README.md is missing 'app_file: app.py' in its YAML header." >&2 exit 1 fi # 4. HF remote is configured if ! git remote get-url "$REMOTE_NAME" >/dev/null 2>&1; then echo "✗ Git remote '$REMOTE_NAME' not configured." >&2 echo "" >&2 echo " Add it once with:" >&2 echo " git remote add $REMOTE_NAME https://huggingface.co/spaces//" >&2 echo "" >&2 echo " (Replace / with your actual HF Space path, e.g." >&2 echo " AshwinP/compounding-test.)" >&2 exit 1 fi REMOTE_URL="$(git remote get-url "$REMOTE_NAME")" # 5. Working tree is clean within the Space directory. Uncommitted changes # inside the prefix would be SILENTLY DROPPED by git subtree, which is # a footgun — fail loud instead. if ! git diff --quiet -- "$SPACE_PREFIX" || ! git diff --cached --quiet -- "$SPACE_PREFIX"; then echo "✗ Uncommitted changes inside $SPACE_PREFIX/:" >&2 git status --short -- "$SPACE_PREFIX" >&2 echo "" >&2 echo " Commit (or stash) these before deploying — git subtree only pushes" >&2 echo " what's in the commit history, so unstaged work would silently miss" >&2 echo " the deploy." >&2 exit 1 fi # 6. Tests pass. The parser + provider tests are the gate that protects the # deployed Space's behavior — if they're red, do not deploy. echo "→ Running tests in $SPACE_PREFIX/..." if command -v python3 >/dev/null 2>&1; then if ! (cd "$SPACE_DIR" && python3 -m pytest test_diagnose.py -q 2>&1 | tail -5); then echo "✗ Tests failed. Fix before deploying." >&2 exit 1 fi else echo " (python3 not found — skipping test gate)" >&2 fi # --------------------------------------------------------------------------- # Report state # --------------------------------------------------------------------------- CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" CURRENT_SHA="$(git rev-parse --short HEAD)" echo "" echo "Ready to deploy:" echo " source prefix: $SPACE_PREFIX" echo " source commit: $CURRENT_SHA ($CURRENT_BRANCH)" echo " HF remote: $REMOTE_NAME ($REMOTE_URL)" echo " target branch: $TARGET_BRANCH" echo "" if [[ "$DRY_RUN" -eq 1 ]]; then echo "✓ Dry run — all sanity checks passed. No push performed." exit 0 fi # --------------------------------------------------------------------------- # Push to HuggingFace Space # --------------------------------------------------------------------------- # # Two push modes: # default: `git subtree push` — fast-forward only. Works for every deploy # after the first one (and for first deploys to a Space that has # never been initialized server-side). # --force: `git subtree split` to a temp branch, then `git push --force` # that branch to the remote's main. Required for the FIRST deploy # to a freshly-created HF Space, because HF auto-creates an initial # commit (README placeholder) that our subtree history can't # fast-forward over. `git subtree push --force` is NOT a valid flag # combo — only the split+push form works. if [[ "$FORCE" -eq 1 ]]; then TEMP_BRANCH="hf-deploy-$(date +%s)" echo "→ Splitting $SPACE_PREFIX into temp branch $TEMP_BRANCH..." git subtree split --prefix="$SPACE_PREFIX" -b "$TEMP_BRANCH" >/dev/null echo "→ Force-pushing $TEMP_BRANCH → $REMOTE_NAME/$TARGET_BRANCH..." if git push "$REMOTE_NAME" "$TEMP_BRANCH:$TARGET_BRANCH" --force; then git branch -D "$TEMP_BRANCH" >/dev/null echo "" echo "✓ Force-deploy pushed. HuggingFace will rebuild the Space shortly." echo " Track build status at: ${REMOTE_URL%.git}" else git branch -D "$TEMP_BRANCH" >/dev/null 2>&1 || true echo "" echo "✗ Force-push failed. Check authentication (huggingface-cli login)" >&2 echo " and Space permissions." >&2 exit 1 fi else echo "→ Pushing subtree to $REMOTE_NAME/$TARGET_BRANCH..." if git subtree push --prefix="$SPACE_PREFIX" "$REMOTE_NAME" "$TARGET_BRANCH"; then echo "" echo "✓ Deploy pushed. HuggingFace will rebuild the Space shortly." echo " Track build status at: ${REMOTE_URL%.git}" else echo "" echo "✗ Push rejected (non-fast-forward). If this is the FIRST deploy to" >&2 echo " a freshly-created HF Space, HuggingFace seeded the repo with a" >&2 echo " placeholder README that our subtree history can't fast-forward" >&2 echo " over. Re-run with --force to overwrite it:" >&2 echo "" >&2 echo " $(basename "$0") --force" >&2 echo "" >&2 echo " CAUTION: --force overwrites whatever is currently on the Space's" >&2 echo " main branch. Safe for first deploys; review carefully otherwise." >&2 exit 1 fi fi