name: Deploy # Continuous deployment to Hugging Face Spaces: # push to dev -> staging Space (TempuraML/lawn-estimator-dev) # push to main -> production Space (TempuraML/lawn-estimator) # # A deploy = force-push the branch content to the Space's git repo (the Space is # a mirror; secrets live in HF settings, not git, so a force-push never touches # them). Self-gating: while the HF_TOKEN secret is absent this skips cleanly # (Stage B not activated), so it can live on dev/main before the token exists. on: push: branches: [dev, main] # Docs-only changes shouldn't restart the Space. NOTE: README.md carries the # Space frontmatter (sdk/app_port) — if you change *that*, deploy manually # (git push to the space remote) since this skips it. paths-ignore: - "**.md" - "docs/**" - "LICENSE" concurrency: group: deploy-${{ github.ref }} cancel-in-progress: false # never interrupt a push mid-deploy jobs: deploy: runs-on: ubuntu-latest permissions: contents: read # checkout checks: read # the wait-for-CI gate below env: HF_TOKEN: ${{ secrets.HF_TOKEN }} HF_USERNAME: ${{ vars.HF_USERNAME }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: fetch-depth: 0 # full history for the force-push to the Space # A push to dev/main triggers CI and this workflow in PARALLEL — without # this gate a commit whose CI fails still ships to the Space. Fail closed: # no green "lint-and-test" check within 20 min = no deploy. Skipped while # HF_TOKEN is absent (Stage B not activated) so the self-gating still holds. - name: Wait for CI to pass on this commit if: env.HF_TOKEN != '' env: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail echo "Waiting for CI (lint-and-test) on ${GITHUB_SHA}..." deadline=$((SECONDS + 1200)) while true; do status=$(gh api "repos/${GITHUB_REPOSITORY}/commits/${GITHUB_SHA}/check-runs" \ --jq '[.check_runs[] | select(.name == "lint-and-test")] | if length == 0 then "pending:" else "\(.[0].status):\(.[0].conclusion // "")" end') case "$status" in completed:success) echo "CI passed — deploying." break ;; completed:*) echo "::error::CI concluded '${status#completed:}' for ${GITHUB_SHA} — refusing to deploy." exit 1 ;; *) if [ "$SECONDS" -ge "$deadline" ]; then echo "::error::Timed out waiting for CI on ${GITHUB_SHA} — refusing to deploy." exit 1 fi echo "CI is '${status%:}' — waiting..." sleep 20 ;; esac done - name: Deploy to Hugging Face Space run: | set -euo pipefail if [ -z "${HF_TOKEN:-}" ]; then echo "::notice::HF_TOKEN not set — Stage B not activated yet; skipping deploy." exit 0 fi if [ -z "${HF_USERNAME:-}" ]; then echo "::error::Set the HF_USERNAME repo variable (your Hugging Face username) to deploy." exit 1 fi if [ "${GITHUB_REF_NAME}" = "main" ]; then SPACE="TempuraML/lawn-estimator" echo "Deploying ${GITHUB_SHA} to PRODUCTION Space: ${SPACE}" else SPACE="TempuraML/lawn-estimator-dev" echo "Deploying ${GITHUB_SHA} to STAGING Space: ${SPACE}" fi git push --force "https://${HF_USERNAME}:${HF_TOKEN}@huggingface.co/spaces/${SPACE}" "HEAD:main" echo "Deployed to ${SPACE}."