phantom-grid / ship_space.sh
unity4ar's picture
Ship Phantom Grid Docker Space
d2e6f94 verified
Raw
History Blame Contribute Delete
6.86 kB
#!/usr/bin/env bash
###############################################################################
# ship_space.sh — one-shot shipper for Phantom Grid -> Hugging Face Space.
#
# Copy this whole folder to a Linux machine (or run under WSL) and run:
#
# export HF_TOKEN=hf_xxx_your_WRITE_token # must be a WRITE token
# ./ship_space.sh
#
# It will:
# 1. Preflight: check tooling + that the HF token can WRITE.
# 2. (optional) Build the Docker image locally and smoke-test it.
# 3. Create the Space build-small-hackathon/phantom-grid (SDK: docker).
# 4. Upload the app (excluding .venv/runtime/etc) + the Space README.
# 5. Print the Space URL and the remaining manual steps.
#
# Flags:
# --build Build the Docker image locally before pushing.
# --smoke Build + run the container and probe health (implies --build).
# --dry-run Do everything EXCEPT create_repo / upload (no network writes).
# --no-push Alias for --dry-run.
# -h | --help Show this help.
#
# Env overrides:
# HF_TOKEN write token (required to push)
# HF_ORG default: build-small-hackathon
# HF_SPACE default: phantom-grid
###############################################################################
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; }
###############################################################################
# 1. Preflight
###############################################################################
log "Preflight checks..."
command -v python3 >/dev/null || die "python3 is required."
# huggingface_hub is needed to create + upload the Space.
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
# Validate required deliverable files exist.
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."
# Validate the app + entrypoint are syntactically sound before shipping.
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."
###############################################################################
# 2. Optional local Docker build / smoke test
###############################################################################
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
###############################################################################
# 3 + 4. Create the Space and upload
###############################################################################
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)
# Verify the token can write.
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",
)
# The Space README (with the docker frontmatter + track tag) goes in as README.md.
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."