| """Deploy a clone of the public demo Space (Tachyeon/afp-indian-classical-demo) |
| to your own Hugging Face account. |
| |
| What this does: |
| 1. Verifies your HF token has the required permissions |
| 2. Snapshots the upstream public demo Space (~537 MB; uses HF's CDN, fast) |
| 3. Creates a new Space under your account |
| 4. Uploads the snapshot to your Space |
| 5. Waits for the build + prints the live URL |
| |
| Usage: |
| # one-shot |
| HF_TOKEN=<your_token> python deploy.py --repo-name my-afp-demo |
| # or explicit user |
| HF_TOKEN=<your_token> python deploy.py --user <your_hf_username> --repo-name my-afp-demo |
| |
| You'll need an HF token (https://huggingface.co/settings/tokens) with `write` scope. |
| |
| Output: |
| Space URL such as https://huggingface.co/spaces/<your_user>/my-afp-demo |
| """ |
| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import sys |
| import tempfile |
| import time |
| from pathlib import Path |
|
|
| UPSTREAM = "Tachyeon/afp-indian-classical-demo" |
|
|
|
|
| def main() -> int: |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--upstream", default=UPSTREAM, |
| help="The public Space to clone (default: %(default)s)") |
| ap.add_argument("--repo-name", required=True, |
| help="Name of the new Space under your account (e.g., 'my-afp-demo')") |
| ap.add_argument("--user", default=None, |
| help="Your HF username/org (defaults to whoami)") |
| ap.add_argument("--private", action="store_true", |
| help="Create as a private Space (default: public)") |
| ap.add_argument("--dry-run", action="store_true") |
| args = ap.parse_args() |
|
|
| token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_TOKEN") |
| if not token: |
| |
| cache = Path.home() / ".cache" / "huggingface" / "token" |
| if cache.exists(): |
| token = cache.read_text().strip() |
| if not token: |
| print("ERROR: no HF token found.") |
| print(" Set HF_TOKEN env var, or run `huggingface-cli login` first.") |
| print(" Get a token at https://huggingface.co/settings/tokens (need write scope).") |
| return 1 |
|
|
| try: |
| from huggingface_hub import HfApi, snapshot_download |
| except ImportError: |
| print("ERROR: huggingface_hub not installed. Install: pip install 'huggingface_hub>=0.30'") |
| return 1 |
|
|
| api = HfApi(token=token) |
|
|
| |
| print(f"[1/5] verifying token...") |
| me = api.whoami() |
| print(f" authed as: {me['name']} (email: {me.get('email')})") |
| owner = args.user or me["name"] |
| target = f"{owner}/{args.repo_name}" |
| print(f" target Space: {target} (private={args.private})") |
|
|
| if args.dry_run: |
| print("\n--dry-run: skipping snapshot + create + upload") |
| return 0 |
|
|
| |
| print(f"\n[2/5] snapshotting upstream {args.upstream} ...") |
| t0 = time.time() |
| snap_dir = tempfile.mkdtemp(prefix="afp_demo_snap_") |
| snapshot_download( |
| repo_id=args.upstream, |
| repo_type="space", |
| local_dir=snap_dir, |
| token=token, |
| ) |
| n_files = sum(1 for _ in Path(snap_dir).rglob("*") if _.is_file()) |
| total_mb = sum(p.stat().st_size for p in Path(snap_dir).rglob("*") if p.is_file()) / 1e6 |
| print(f" pulled {n_files} files, {total_mb:.1f} MB in {time.time()-t0:.0f}s") |
|
|
| |
| print(f"\n[3/5] creating Space {target} ...") |
| repo_url = api.create_repo( |
| repo_id=target, |
| repo_type="space", |
| space_sdk="gradio", |
| private=args.private, |
| exist_ok=True, |
| ) |
| print(f" created: {repo_url}") |
|
|
| |
| print(f"\n[4/5] uploading snapshot → {target} (LFS auto-handled) ...") |
| t0 = time.time() |
| commit = api.upload_folder( |
| folder_path=snap_dir, |
| repo_id=target, |
| repo_type="space", |
| commit_message=f"Deploy clone of {args.upstream}", |
| ignore_patterns=[".git", ".git/**", "__pycache__", "*.pyc"], |
| ) |
| print(f" pushed in {time.time()-t0:.0f}s") |
| print(f" commit: {commit}") |
|
|
| |
| print(f"\n[5/5] waiting for Space to build + start (typically 5-15 min)...") |
| print(f" monitor at: https://huggingface.co/spaces/{target}") |
| print(f" app file: app.py · sdk: gradio 6.14.0 · python 3.11") |
| prev_stage = None |
| start = time.time() |
| while True: |
| try: |
| rt = api.get_space_runtime(repo_id=target) |
| stage = rt.stage |
| except Exception as e: |
| print(f" [warn] runtime fetch failed: {e}") |
| stage = "UNKNOWN" |
| if stage != prev_stage: |
| print(f" [{int(time.time()-start):4d}s] stage = {stage}") |
| prev_stage = stage |
| if stage in ("RUNNING", "RUNTIME_ERROR", "BUILD_ERROR"): |
| break |
| if time.time() - start > 1800: |
| print(" [timeout] giving up after 30 minutes — check the Space UI directly") |
| break |
| time.sleep(20) |
|
|
| if stage == "RUNNING": |
| print(f"\n✓ DEPLOYED — live at https://huggingface.co/spaces/{target}") |
| return 0 |
| else: |
| print(f"\n✗ Build stuck at stage={stage}. Check logs:") |
| print(f" https://huggingface.co/spaces/{target}/logs") |
| return 1 |
|
|
|
|
| if __name__ == "__main__": |
| sys.exit(main()) |
|
|