File size: 4,102 Bytes
c519923
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""One-shot Hugging Face Space deploy for WitnessBox.

Run AFTER an HF write token is available, either as:
    HF_TOKEN=hf_xxx python3 scripts/deploy_space.py
or after `hf auth login` (the CLI stores the token; this script picks it up).

What it does, idempotently:
  1. Resolve the target namespace (personal by default; set WITNESSBOX_HF_ORG to
     push into an org you belong to, e.g. build-small-hackathon).
  2. Create the Space (gradio SDK) if it doesn't exist.
  3. Upload the app: app.py, config.py, modal_app.py, requirements.txt, README.md,
     and the witnessbox/ package (skips caches, tests, the local Modal token).
  4. Set Space secrets so the live app talks to the deployed Modal app:
        MODAL_TOKEN_ID, MODAL_TOKEN_SECRET   (read from ~/.modal.toml)
        WITNESSBOX_BACKEND=modal             (as a public variable)
  5. Print the Space URL.

Nothing here is destructive; re-running just re-uploads + re-sets.
"""
from __future__ import annotations

import os
import re
import sys

REPO_NAME = os.environ.get("WITNESSBOX_SPACE_NAME", "WitnessBox")
ORG = os.environ.get("WITNESSBOX_HF_ORG", "").strip()  # empty => personal namespace
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


def _token() -> str:
    tok = (os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") or "").strip()
    if tok:
        return tok
    # Fall back to a CLI-stored token (`hf auth login`).
    try:
        from huggingface_hub import HfFolder
        tok = HfFolder.get_token() or ""
    except Exception:
        tok = ""
    if not tok:
        sys.exit("No HF token. Set HF_TOKEN=hf_xxx (write scope) or run `hf auth login` first.")
    return tok


def _modal_tokens() -> tuple[str, str]:
    """Pull token_id/token_secret out of ~/.modal.toml (no tomllib on py3.9)."""
    path = os.path.expanduser("~/.modal.toml")
    if not os.path.exists(path):
        return "", ""
    text = open(path).read()
    tid = re.search(r'token_id\s*=\s*"([^"]+)"', text)
    tsec = re.search(r'token_secret\s*=\s*"([^"]+)"', text)
    return (tid.group(1) if tid else ""), (tsec.group(1) if tsec else "")


def main() -> int:
    from huggingface_hub import HfApi

    token = _token()
    api = HfApi(token=token)
    me = api.whoami()
    user = me["name"]
    namespace = ORG or user
    repo_id = f"{namespace}/{REPO_NAME}"
    print(f"HF user: {user}  ->  target Space: {repo_id}")

    # 1) Create the Space (gradio). exist_ok keeps this idempotent.
    api.create_repo(repo_id=repo_id, repo_type="space", space_sdk="gradio",
                    exist_ok=True, token=token)
    print(f"  space ready: https://huggingface.co/spaces/{repo_id}")

    # 2) Upload the app (whole repo minus junk; nothing here holds secrets — the
    #    Modal token lives in ~/.modal.toml, outside the repo). fnmatch '*' spans
    #    '/', so these substring globs catch nested caches too.
    ignore = ["*.pyc", "*__pycache__*", "*.pytest_cache*", "*.git*",
              "*.wav", "*.toml"]
    api.upload_folder(
        repo_id=repo_id, repo_type="space", folder_path=ROOT,
        ignore_patterns=ignore, token=token,
        commit_message="Deploy WitnessBox",
    )
    print("  files uploaded")

    # 3) Wire the live backend: Modal secrets + backend switch.
    tid, tsec = _modal_tokens()
    if tid and tsec:
        api.add_space_secret(repo_id, "MODAL_TOKEN_ID", tid, token=token)
        api.add_space_secret(repo_id, "MODAL_TOKEN_SECRET", tsec, token=token)
        api.add_space_variable(repo_id, "WITNESSBOX_BACKEND", "modal", token=token)
        print("  secrets set: MODAL_TOKEN_ID / MODAL_TOKEN_SECRET; WITNESSBOX_BACKEND=modal")
    else:
        print("  WARNING: ~/.modal.toml not found/parsed — Space will boot in MOCK mode.")
        print("           Set MODAL_TOKEN_ID / MODAL_TOKEN_SECRET in the Space settings to go live.")

    print(f"\nDONE. Space: https://huggingface.co/spaces/{repo_id}")
    print("It will build, then run app.py. First live turn warms the Modal containers.")
    return 0


if __name__ == "__main__":
    sys.exit(main())