Spaces:
Running on Zero
Running on Zero
| # Upload the FormScout source tree to both the model repo and the Space. | |
| # | |
| # Usage: | |
| # ./scripts/hf_upload.sh # message from last git commit | |
| # ./scripts/hf_upload.sh "feat: my change" # custom message | |
| # | |
| # Pushes to: | |
| # silas-therapy/small-functional-movement-screening (model repo) | |
| # spaces/silas-therapy/small-functional-movement-screening (Gradio Space) | |
| # | |
| # `hf upload` does NOT read .hfignore β it only honors .gitignore, and only at | |
| # commit time (after hashing and pre-uploading everything). So we parse | |
| # .hfignore ourselves into --exclude globs and pass them explicitly. | |
| # | |
| # If the filtered file count still exceeds LARGE_THRESHOLD, we fall back to | |
| # `hf upload-large-folder` (resumable, multi-threaded). Caveats of that mode: | |
| # no --create-pr and no custom commit message β it commits directly to main | |
| # in multiple commits. | |
| set -euo pipefail | |
| cd "$(dirname "$0")/.." | |
| REPO_NAME="small-functional-movement-screening" | |
| BLADE_OWNER="${FORMSCOUT_HF_BLADE_OWNER:-build-small-hackathon}" | |
| MODEL_REPO="silas-therapy/$REPO_NAME" | |
| SPACE_REPO="spaces/silas-therapy/$REPO_NAME" | |
| SPACE_BLADESZASZA_REPO="spaces/$BLADE_OWNER/$REPO_NAME" | |
| MSG="${1:-$(git log -1 --pretty=%s)}" | |
| LARGE_THRESHOLD="${FORMSCOUT_HF_LARGE_THRESHOLD:-500}" | |
| # Belt-and-suspenders extras on top of .hfignore. `.cache/` is the resume | |
| # state upload-large-folder writes into the folder being uploaded. | |
| PATTERNS=( | |
| "*.pdf" | |
| "**/node_modules/**" | |
| ".cache/**" | |
| ) | |
| # Parse .hfignore into fnmatch-style globs. fnmatch's `*` crosses `/`, but a | |
| # bare name like `.DS_Store` or `dir/` only matches at the root, so emit both | |
| # the rooted and `**/`-prefixed forms. | |
| while IFS= read -r line; do | |
| line="${line%%#*}" | |
| line="${line#"${line%%[![:space:]]*}"}" | |
| line="${line%"${line##*[![:space:]]}"}" | |
| [[ -z "$line" ]] && continue | |
| if [[ "$line" == */ ]]; then | |
| PATTERNS+=("${line}**" "**/${line}**") | |
| else | |
| PATTERNS+=("$line" "**/$line") | |
| fi | |
| done < .hfignore | |
| EXCLUDES=() | |
| for p in "${PATTERNS[@]}"; do | |
| EXCLUDES+=(--exclude="$p") | |
| done | |
| # Count what would actually be uploaded, using the same filter the hub client | |
| # applies, so the mode decision matches reality. | |
| N_FILES=$(python3 - "${PATTERNS[@]}" <<'EOF' | |
| import sys | |
| from pathlib import Path | |
| from huggingface_hub.utils import filter_repo_objects | |
| patterns = sys.argv[1:] | |
| files = ( | |
| str(p) for p in Path(".").rglob("*") | |
| if p.is_file() and p.parts[0] != ".git" | |
| ) | |
| print(len(list(filter_repo_objects(files, ignore_patterns=patterns)))) | |
| EOF | |
| ) | |
| echo "ββ $N_FILES files to upload after .hfignore filtering" | |
| if (( N_FILES == 0 )); then | |
| echo "β nothing to upload β check .hfignore" >&2 | |
| exit 1 | |
| fi | |
| # upload_repo <repo> [pr|direct] | |
| # pr β open a PR (shared org repos; review before merge) | |
| # direct β commit straight to main (repos you own; deploys immediately) | |
| upload_repo() { | |
| local repo="$1" | |
| local mode="${2:-pr}" | |
| if (( N_FILES > LARGE_THRESHOLD )); then | |
| echo "ββ $repo: $N_FILES files > $LARGE_THRESHOLD, using upload-large-folder" | |
| echo " (resumable; commits directly to main β no PR, no custom message)" | |
| hf upload-large-folder "$repo" . "${EXCLUDES[@]}" | |
| elif [[ "$mode" == "direct" ]]; then | |
| echo "ββ uploading (direct β main) to: $repo" | |
| hf upload "$repo" . . "${EXCLUDES[@]}" --commit-message="$MSG" | |
| else | |
| echo "ββ uploading (PR) to: $repo" | |
| hf upload "$repo" . . "${EXCLUDES[@]}" --create-pr --commit-message="$MSG" | |
| fi | |
| } | |
| # Ensure the personal ZeroGPU Space exists. Tries zero-a10g (needs Pro/ZeroGPU); | |
| # falls back to cpu-basic so the upload still has a target (set ZeroGPU in | |
| # Settings afterward). Idempotent via --exist-ok. | |
| ensure_blade_space() { | |
| local id="$BLADE_OWNER/$REPO_NAME" | |
| if hf repos create "$id" --type space --space-sdk gradio --flavor zero-a10g --exist-ok 2>/dev/null; then | |
| echo "ββ Space ready (ZeroGPU / zero-a10g): $id"; return 0 | |
| fi | |
| if hf repos create "$id" --type space --space-sdk gradio --exist-ok 2>/dev/null; then | |
| echo "ββ Space created cpu-basic (set ZeroGPU in Settings β Hardware): $id"; return 0 | |
| fi | |
| return 1 | |
| } | |
| blade_help() { | |
| cat >&2 <<EOF | |
| ββ β Could not create/deploy to $SPACE_BLADESZASZA_REPO | |
| Your active HF token can push to silas-therapy but not create repos under | |
| "$BLADE_OWNER". To deploy your own ZeroGPU Space: | |
| 1) In the HF UI create a Space: $BLADE_OWNER/$REPO_NAME | |
| SDK = Gradio, Hardware = ZeroGPU (Nvidia A10G). | |
| 2) Re-auth with a token that can write there: | |
| hf auth login (token with 'Write' role, or fine-grained with | |
| write access to $BLADE_OWNER) | |
| 3) Re-run ./scripts/hf_upload.sh | |
| EOF | |
| } | |
| # Shared org repos β PRs; personal ZeroGPU Space β created + direct deploy. | |
| upload_repo "$MODEL_REPO" pr | |
| upload_repo "$SPACE_REPO" pr | |
| set +e | |
| if ensure_blade_space; then | |
| upload_repo "$SPACE_BLADESZASZA_REPO" direct || blade_help | |
| else | |
| blade_help | |
| fi | |
| set -e | |
| echo "β done (silas-therapy PRs created; see any notes above for the personal Space)" | |