From 3661871c3919b56bc87cbe141c42898ce2660234 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 23 Jun 2026 19:52:04 +0000 Subject: [PATCH] Add GitHub Action to redeploy the HuggingFace Space on merge to main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A new workflow (.github/workflows/deploy-huggingface.yml) runs the same orphan-snapshot push that deploy/huggingface/SETUP.md documents — squash the current tree to one history-free commit (HF rejects the >10 MiB .xiidm blobs in branch history) and force-push it to the Space's main — automatically on every merge to main (plus manual workflow_dispatch). It is inert until the repo sets the HF_TOKEN secret + HF_SPACE variable (and optional HF_USERNAME for org Spaces), so merging it changes nothing until opted in. LFS objects already on the Space are reused, so repeat deploys only upload what changed. SETUP.md documents the required secrets/variables and the opt-in. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Re86Rgg2KqgwRigRPFTXnY --- .github/workflows/deploy-huggingface.yml | 84 ++++++++++++++++++++++++ deploy/huggingface/SETUP.md | 22 +++++++ 2 files changed, 106 insertions(+) create mode 100644 .github/workflows/deploy-huggingface.yml diff --git a/.github/workflows/deploy-huggingface.yml b/.github/workflows/deploy-huggingface.yml new file mode 100644 index 0000000..6d5add7 --- /dev/null +++ b/.github/workflows/deploy-huggingface.yml @@ -0,0 +1,84 @@ +name: Deploy to HuggingFace Space + +# Redeploys the HuggingFace Docker Space on every merge to main (a merged +# PR pushes to main). Mirrors the manual flow in deploy/huggingface/SETUP.md: +# HF's git endpoint rejects the >10 MiB .xiidm blobs that sit in branch +# *history*, so we push ONE squashed, history-free commit of the current +# tree (large binaries ride along via Git LFS). +# +# Required repo configuration (Settings → Secrets and variables → Actions): +# - secret HF_TOKEN : a HuggingFace WRITE access token with access to the Space +# - variable HF_SPACE : the Space path, e.g. "your-user/co-study4grid-game" +# - variable HF_USERNAME : (optional) the token owner's HF username — only +# needed when the Space lives under an ORG and so +# differs from the owner part of HF_SPACE. +# +# The job no-ops (does not fail) when HF_TOKEN / HF_SPACE are absent, so the +# workflow is inert until you opt in by setting them. + +on: + push: + branches: [ main ] + workflow_dispatch: {} + +# Never run two deploys at once; the latest merge wins. +concurrency: + group: huggingface-space-deploy + cancel-in-progress: true + +jobs: + deploy: + name: Push snapshot to HF Space + runs-on: ubuntu-latest + steps: + - name: Check the deploy is configured + id: guard + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + HF_SPACE: ${{ vars.HF_SPACE }} + run: | + if [ -z "$HF_TOKEN" ] || [ -z "$HF_SPACE" ]; then + echo "::notice::HF_TOKEN secret and/or HF_SPACE variable not set — skipping deploy." + echo "configured=false" >> "$GITHUB_OUTPUT" + else + echo "configured=true" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout current tree + LFS objects + if: steps.guard.outputs.configured == 'true' + uses: actions/checkout@v4 + with: + lfs: true + fetch-depth: 1 # an orphan snapshot only needs the current tree + + - name: Configure git + LFS + if: steps.guard.outputs.configured == 'true' + run: | + git lfs install --local + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Build a history-free snapshot + if: steps.guard.outputs.configured == 'true' + run: | + cp deploy/huggingface/README.md README.md # HF needs the frontmatter at repo root + git checkout --orphan hf-deploy + git add -A + # Drop inert local dev artifacts from the deployed snapshot. + git rm -q --cached --ignore-unmatch \ + .claude/plan.md inspect_action.py profiling_patch_results.json || true + git commit -q -m "Deploy ${GITHUB_SHA::7}" + test "$(git rev-list --count HEAD)" -eq 1 # sanity: exactly one commit + + - name: Push to the HuggingFace Space + if: steps.guard.outputs.configured == 'true' + env: + HF_TOKEN: ${{ secrets.HF_TOKEN }} + HF_SPACE: ${{ vars.HF_SPACE }} + HF_USERNAME: ${{ vars.HF_USERNAME }} + run: | + user="${HF_USERNAME:-${HF_SPACE%%/*}}" + # The token is a GitHub secret → masked in logs even inside the URL. + git remote add space "https://${user}:${HF_TOKEN}@huggingface.co/spaces/${HF_SPACE}" + git -c protocol.version=0 push -f space hf-deploy:main + echo "::notice::Pushed snapshot of ${GITHUB_SHA::7} to spaces/${HF_SPACE}; HF will rebuild." diff --git a/deploy/huggingface/SETUP.md b/deploy/huggingface/SETUP.md index f50597a..a365d88 100644 --- a/deploy/huggingface/SETUP.md +++ b/deploy/huggingface/SETUP.md @@ -79,6 +79,28 @@ git add --renormalize . && git commit -m "migrate binaries to LFS" reference studies (Medium difficulty). Build without that flag for the bare workspace. +## Automated redeploy on merge to `main` (GitHub Action) + +`.github/workflows/deploy-huggingface.yml` runs the same orphan-snapshot push +automatically on every merge to `main` (and on manual `workflow_dispatch`). It +checks out the current tree with LFS, squashes it to one history-free commit, +and force-pushes it to the Space's `main`. + +Opt in by setting, in the GitHub repo **Settings → Secrets and variables → +Actions**: + +| Kind | Name | Value | +|---|---|---| +| Secret | `HF_TOKEN` | a HuggingFace **write** access token with access to the Space | +| Variable | `HF_SPACE` | the Space path, e.g. `your-user/co-study4grid-game` | +| Variable | `HF_USERNAME` | *(optional)* the token owner's HF username — only when the Space is under an **org** and so differs from the owner part of `HF_SPACE` | + +The job is inert (it logs a notice and exits cleanly) until both `HF_TOKEN` and +`HF_SPACE` are set, so merging the workflow doesn't break anything before you +opt in. The push reuses LFS objects already on the Space, so repeat deploys only +upload what changed. If you want the deploy gated on green tests, change the +trigger to a `workflow_run` on the **Tests** workflow instead of `push`. + ## Test the image locally first (recommended) ```bash