| from __future__ import annotations |
|
|
| import argparse |
| import os |
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
|
|
| ROOT_DIR = Path(__file__).resolve().parent.parent |
| DEFAULT_BUNDLE_DIR = ROOT_DIR / "outputs" / "huggingface-space" |
|
|
|
|
| def load_env_file(path: Path) -> None: |
| if not path.exists(): |
| return |
| for raw_line in path.read_text(encoding="utf-8").splitlines(): |
| line = raw_line.strip() |
| if not line or line.startswith("#") or "=" not in line: |
| continue |
| key, value = line.split("=", 1) |
| os.environ.setdefault(key.strip(), value.strip().strip('"').strip("'")) |
|
|
|
|
| def require_huggingface_hub(): |
| try: |
| from huggingface_hub import HfApi, create_repo, upload_folder |
| except ImportError as exc: |
| raise SystemExit( |
| "huggingface_hub is not installed. Run: .\\.venv\\Scripts\\python.exe -m pip install huggingface_hub" |
| ) from exc |
| return HfApi, create_repo, upload_folder |
|
|
|
|
| def run_export(bundle_dir: Path, force_export: bool) -> None: |
| if bundle_dir.exists() and not force_export: |
| return |
| command = [ |
| sys.executable, |
| str(ROOT_DIR / "scripts" / "export_hf_space.py"), |
| "--out", |
| str(bundle_dir), |
| ] |
| if force_export: |
| command.append("--force") |
| subprocess.run(command, cwd=ROOT_DIR, check=True) |
|
|
|
|
| def get_token(explicit_token: str | None) -> str: |
| load_env_file(ROOT_DIR / ".env") |
| token = explicit_token or os.getenv("HF_TOKEN") or os.getenv("HF_API_TOKEN") or os.getenv("HUGGINGFACE_API_TOKEN") |
| if not token: |
| raise SystemExit( |
| "Missing Hugging Face token. Set HF_TOKEN, HF_API_TOKEN, or HUGGINGFACE_API_TOKEN, " |
| "or pass --token. Create a write token at https://huggingface.co/settings/tokens." |
| ) |
| return token |
|
|
|
|
| def token_available(explicit_token: str | None = None) -> bool: |
| load_env_file(ROOT_DIR / ".env") |
| return bool(explicit_token or os.getenv("HF_TOKEN") or os.getenv("HF_API_TOKEN") or os.getenv("HUGGINGFACE_API_TOKEN")) |
|
|
|
|
| def split_repo_id(repo_id: str) -> tuple[str, str]: |
| if "/" not in repo_id: |
| raise ValueError("Hugging Face Space repo id must look like username/space-name") |
| owner, space_name = repo_id.split("/", 1) |
| if not owner or not space_name: |
| raise ValueError("Hugging Face Space repo id must include both owner and space name") |
| return owner, space_name |
|
|
|
|
| def worker_url_for_repo(repo_id: str) -> str: |
| owner, space_name = split_repo_id(repo_id) |
| return f"https://{owner}-{space_name}.hf.space".lower() |
|
|
|
|
| def deploy_hf_space( |
| repo_id: str, |
| bundle_dir: Path = DEFAULT_BUNDLE_DIR, |
| token: str | None = None, |
| private: bool = False, |
| force_export: bool = False, |
| commit_message: str = "Deploy Arabic Audio Reader worker", |
| ) -> dict[str, object]: |
| bundle_dir = bundle_dir.resolve() |
| run_export(bundle_dir, force_export) |
| hf_token = get_token(token) |
| _hf_api, create_repo, upload_folder = require_huggingface_hub() |
|
|
| create_repo( |
| repo_id=repo_id, |
| repo_type="space", |
| space_sdk="docker", |
| private=private, |
| exist_ok=True, |
| token=hf_token, |
| ) |
| commit_info = upload_folder( |
| repo_id=repo_id, |
| repo_type="space", |
| folder_path=str(bundle_dir), |
| path_in_repo=".", |
| commit_message=commit_message, |
| token=hf_token, |
| ) |
| worker_url = worker_url_for_repo(repo_id) |
| return { |
| "repoId": repo_id, |
| "workerUrl": worker_url, |
| "bundleDir": str(bundle_dir), |
| "commitUrl": getattr(commit_info, "commit_url", None), |
| "nextCommand": ( |
| f"python scripts\\deployment_handoff.py {worker_url} " |
| "--origin https://arabic-translator-mu.vercel.app --code 1234" |
| ), |
| } |
|
|
|
|
| def main_with_args(argv: list[str] | None = None) -> int: |
| parser = argparse.ArgumentParser(description="Create or update the Hugging Face Docker Space worker.") |
| parser.add_argument( |
| "repo_id", |
| help="Hugging Face Space repo id, for example username/arabic-audio-reader-worker.", |
| ) |
| parser.add_argument("--bundle-dir", type=Path, default=DEFAULT_BUNDLE_DIR) |
| parser.add_argument("--token", help="Hugging Face write token. Prefer setting HF_TOKEN instead.") |
| parser.add_argument("--private", action="store_true", help="Create the Space as private.") |
| parser.add_argument("--force-export", action="store_true", help="Rebuild the worker bundle before upload.") |
| parser.add_argument("--commit-message", default="Deploy Arabic Audio Reader worker") |
| parser.add_argument("--json", action="store_true") |
| args = parser.parse_args(argv) |
|
|
| result = deploy_hf_space( |
| repo_id=args.repo_id, |
| bundle_dir=args.bundle_dir, |
| token=args.token, |
| private=args.private, |
| force_export=args.force_export, |
| commit_message=args.commit_message, |
| ) |
| if args.json: |
| import json |
|
|
| print(json.dumps(result, indent=2)) |
| else: |
| print(f"Uploaded worker bundle to https://huggingface.co/spaces/{args.repo_id}") |
| print(f"Worker URL: {result['workerUrl']}") |
| print("Next:") |
| print(result["nextCommand"]) |
| return 0 |
|
|
|
|
| def main() -> int: |
| return main_with_args() |
|
|
|
|
| if __name__ == "__main__": |
| raise SystemExit(main()) |
|
|