| #!/usr/bin/env bash |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| set -euo pipefail |
|
|
| HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" |
| cd "$HERE" |
|
|
| HF_ORG="${HF_ORG:-build-small-hackathon}" |
| HF_SPACE="${HF_SPACE:-phantom-grid}" |
| REPO_ID="${HF_ORG}/${HF_SPACE}" |
| IMAGE_TAG="phantom-grid:local" |
|
|
| DO_BUILD=0 |
| DO_SMOKE=0 |
| DRY_RUN=0 |
|
|
| for arg in "$@"; do |
| case "$arg" in |
| --build) DO_BUILD=1 ;; |
| --smoke) DO_SMOKE=1; DO_BUILD=1 ;; |
| --dry-run|--no-push) DRY_RUN=1 ;; |
| -h|--help) |
| sed -n '2,40p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//' |
| exit 0 ;; |
| *) echo "Unknown flag: $arg" >&2; exit 2 ;; |
| esac |
| done |
|
|
| log() { printf '\033[1;36m[ship]\033[0m %s\n' "$*"; } |
| warn() { printf '\033[1;33m[ship] WARN:\033[0m %s\n' "$*" >&2; } |
| die() { printf '\033[1;31m[ship] ERROR:\033[0m %s\n' "$*" >&2; exit 1; } |
|
|
| |
| |
| |
| log "Preflight checks..." |
| command -v python3 >/dev/null || die "python3 is required." |
|
|
| |
| if ! python3 -c "import huggingface_hub" 2>/dev/null; then |
| log "Installing huggingface_hub..." |
| python3 -m pip install --quiet --upgrade "huggingface_hub>=1.2" |
| fi |
|
|
| |
| for f in Dockerfile entrypoint.sh .dockerignore requirements-space.txt README_SPACE.md app.py; do |
| [ -f "$f" ] || die "Missing required file: $f" |
| done |
| log "All required deployment files present." |
|
|
| |
| python3 -m py_compile app.py config/settings.py grid_map/map_loader.py llm/omni_client.py \ |
| || die "Python syntax check failed." |
| bash -n entrypoint.sh || die "entrypoint.sh has a syntax error." |
| log "Syntax checks passed." |
|
|
| |
| |
| |
| if [ "$DO_BUILD" = 1 ]; then |
| command -v docker >/dev/null || die "--build requested but docker is not installed." |
| log "Building Docker image ${IMAGE_TAG} (downloads the GGUF — this is slow)..." |
| docker build -t "$IMAGE_TAG" . |
| log "Docker build succeeded." |
| fi |
|
|
| if [ "$DO_SMOKE" = 1 ]; then |
| log "Smoke-testing the container..." |
| CID="$(docker run -d -p 7860:7860 "$IMAGE_TAG")" |
| cleanup_smoke() { docker rm -f "$CID" >/dev/null 2>&1 || true; } |
| trap cleanup_smoke EXIT |
| log "Container ${CID:0:12} started; waiting for the app to answer on :7860..." |
| ok=0 |
| for i in $(seq 1 150); do |
| if curl -sf "http://127.0.0.1:7860/api/snapshot" >/dev/null 2>&1; then ok=1; break; fi |
| if ! docker ps -q --no-trunc | grep -q "$CID"; then |
| docker logs "$CID" | tail -40 >&2 |
| die "Container exited during smoke test." |
| fi |
| sleep 4 |
| done |
| [ "$ok" = 1 ] || { docker logs "$CID" | tail -40 >&2; die "App did not become healthy in time."; } |
| log "Smoke test passed: /api/snapshot responded." |
| cleanup_smoke |
| trap - EXIT |
| fi |
|
|
| |
| |
| |
| if [ "$DRY_RUN" = 1 ]; then |
| log "--dry-run: skipping create_repo + upload. Everything else passed." |
| log "Would push to: https://huggingface.co/spaces/${REPO_ID}" |
| exit 0 |
| fi |
|
|
| [ -n "${HF_TOKEN:-}" ] || die "HF_TOKEN is not set. Export a WRITE token: export HF_TOKEN=hf_..." |
|
|
| log "Creating + uploading Space ${REPO_ID} ..." |
| HF_ORG="$HF_ORG" HF_SPACE="$HF_SPACE" python3 - <<'PY' |
| import os, sys |
| from huggingface_hub import HfApi |
|
|
| token = os.environ["HF_TOKEN"] |
| org = os.environ["HF_ORG"] |
| space = os.environ["HF_SPACE"] |
| repo_id = f"{org}/{space}" |
| api = HfApi(token=token) |
|
|
| |
| who = api.whoami() |
| perm = who.get("auth", {}).get("accessToken", {}).get("role") or who.get("auth", {}).get("type") |
| print(f"[ship] Logged in as: {who.get('name')} (token role: {perm})") |
| if perm == "read": |
| sys.exit("[ship] ERROR: this is a READ token. Create a WRITE token at " |
| "https://huggingface.co/settings/tokens and re-export HF_TOKEN.") |
|
|
| api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="docker", exist_ok=True) |
| print(f"[ship] Space ready: https://huggingface.co/spaces/{repo_id}") |
|
|
| ignore = [ |
| ".git*", ".venv/*", "venv/*", "runtime/*", "tools/*", |
| "**/__pycache__/*", "*.pyc", "tmp/*", "*.log", "*.err.log", |
| "data/backups/*", "data/games/*", "data/archives/*", |
| "data/raw/archives/*", "data/*.log", "data/*.png", |
| "run_game.ps1", "run_game.cmd", |
| "README.md", "README_SPACE.md", "C:*", |
| ] |
| api.upload_folder( |
| repo_id=repo_id, repo_type="space", folder_path=".", |
| ignore_patterns=ignore, commit_message="Ship Phantom Grid Docker Space", |
| ) |
| |
| api.upload_file( |
| path_or_fileobj="README_SPACE.md", path_in_repo="README.md", |
| repo_id=repo_id, repo_type="space", commit_message="Add Space README", |
| ) |
| print(f"[ship] Upload complete. Build will start automatically.") |
| print(f"[ship] Watch the build: https://huggingface.co/spaces/{repo_id}?logs=build") |
| PY |
|
|
| log "Done. Remaining manual steps:" |
| log " - Record the demo video and paste its link into README_SPACE.md (then re-run, or edit on the Space)." |
| log " - Publish the social post and add its link too." |
| log " - Confirm the Space build goes RUNNING and a new case starts." |
|
|