Spaces:
Running
Running
File size: 4,998 Bytes
5673a53 bfdf2aa 5673a53 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | #!/usr/bin/env python
"""
Create/update the Daggr Studio Space and wait for it to come up.
python scripts/deploy_space.py # deploy jkorstad/daggr-studio
python scripts/deploy_space.py --repo jkorstad/x --hardware cpu-basic
python scripts/deploy_space.py --poll-only # just watch the runtime
The Space runs the `docker` SDK (we control Python + gradio versions), on CPU only - it
orchestrates other Spaces, it never runs GPU models itself.
"""
from __future__ import annotations
import argparse
import os
import sys
import time
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
#: Everything the Space needs at runtime. Deliberately excludes the local venv, output
#: artifacts, docs and the registry/verification logs (the JSON registry IS included).
INCLUDE_FILES = ["app.py", "requirements.txt", "Dockerfile", "README.md", "pytest.ini",
"LICENSE", "MASTER_PLAN.md"]
INCLUDE_DIRS = ["daggrstudio", "scripts", "tests"]
def token() -> str | None:
for var in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN"):
if os.environ.get(var):
return os.environ[var]
cached = Path.home() / ".cache" / "huggingface" / "token"
return cached.read_text().strip() if cached.exists() else None
def iter_files():
for name in INCLUDE_FILES:
path = ROOT / name
if path.exists():
yield path, name
for dirname in INCLUDE_DIRS:
base = ROOT / dirname
for path in sorted(base.rglob("*")):
if not path.is_file():
continue
rel = path.relative_to(ROOT).as_posix()
if "__pycache__" in rel or rel.endswith(".pyc"):
continue
if dirname == "scripts" and path.name.startswith(("smoke", "acceptance")):
continue # dev-only scripts; not needed in the Space image
yield path, rel
def poll(api, repo: str, timeout: int = 900) -> str:
started = time.time()
last = ""
while time.time() - started < timeout:
try:
info = api.space_info(repo, expand=["runtime"])
stage = str(getattr(info.runtime, "stage", "") if info.runtime else "")
except Exception as exc:
stage = f"unknown ({type(exc).__name__})"
if stage != last:
print(f" [{time.time() - started:5.0f}s] {stage}", flush=True)
last = stage
if stage in ("RUNNING", "RUNNING_BUILDING"):
return stage
if stage in ("BUILD_ERROR", "CONFIG_ERROR", "RUNTIME_ERROR", "DELETED"):
raw = getattr(info.runtime, "raw", {}) or {}
print(f"\nSpace failed to start: {stage}")
print(f" error: {str(raw.get('errorMessage'))[:1500]}")
return stage
time.sleep(15)
return "TIMEOUT"
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--repo", default=os.environ.get("DAGGRSTUDIO_SPACE",
"jkorstad/daggr-studio"))
parser.add_argument("--hardware", default="cpu-basic")
parser.add_argument("--poll-only", action="store_true")
parser.add_argument("--timeout", type=int, default=900)
args = parser.parse_args()
tok = token()
if not tok:
print("No HF token available; set HF_TOKEN.")
return 2
from huggingface_hub import HfApi
api = HfApi(token=tok)
user = api.whoami().get("name")
repo = args.repo if "/" in args.repo else f"{user}/{args.repo}"
if not args.poll_only:
print(f"creating/updating space {repo} (docker, {args.hardware})")
api.create_repo(repo_id=repo, repo_type="space", space_sdk="docker",
space_hardware=args.hardware, exist_ok=True)
uploaded = 0
for path, rel in iter_files():
api.upload_file(path_or_fileobj=str(path), path_in_repo=rel, repo_id=repo,
repo_type="space",
commit_message=f"deploy: {rel}")
uploaded += 1
print(f"uploaded {uploaded} files")
try:
api.add_space_secret(repo_id=repo, key="HF_TOKEN", value=tok)
print("set HF_TOKEN secret (used only for the capped community pool)")
except Exception as exc:
print(f"could not set the secret: {type(exc).__name__}: {exc}")
print("waiting for the Space to come up…")
stage = poll(api, repo, timeout=args.timeout)
url = f"https://huggingface.co/spaces/{repo}"
print(f"\n{stage} {url}")
if stage in ("RUNNING", "RUNNING_BUILDING"):
host = repo.replace("/", "-").replace("_", "-").lower() + ".hf.space"
print(f" app: https://{host}")
print(f" builder: https://{host}/builder")
print(f" health: https://{host}/healthz")
return 0 if stage in ("RUNNING", "RUNNING_BUILDING") else 1
if __name__ == "__main__":
raise SystemExit(main()) |