"""Push the ForgeEnv environment Space and the demo Space to the Hub. Usage (from repo root, with ``HF_TOKEN`` set or after ``huggingface-cli login``):: python scripts/deploy_spaces.py \ --user akhiilll \ --env-space-name forgeenv \ --demo-space-name forgeenv-demo The script is idempotent: if the Spaces already exist it just uploads the folders. It does NOT push the trained model weights — that happens from the Colab notebook after GRPO finishes. """ from __future__ import annotations import argparse import os import sys from pathlib import Path def _require_token() -> str: token = os.environ.get("HF_TOKEN") or os.environ.get("HUGGINGFACE_TOKEN") if not token: sys.exit( "[deploy] No HF token in env. Set HF_TOKEN or run " "`huggingface-cli login` first." ) return token def _ensure_space(api, repo_id: str, sdk: str, token: str) -> None: from huggingface_hub.utils import HfHubHTTPError try: api.repo_info(repo_id=repo_id, repo_type="space", token=token) print(f"[deploy] Space exists: {repo_id}") except HfHubHTTPError: print(f"[deploy] Creating Space: {repo_id} (sdk={sdk})") api.create_repo( repo_id=repo_id, repo_type="space", space_sdk=sdk, token=token, exist_ok=True, ) def _upload(api, repo_id: str, folder: Path, token: str) -> None: print(f"[deploy] Uploading {folder} -> {repo_id}") api.upload_folder( folder_path=str(folder), repo_id=repo_id, repo_type="space", token=token, commit_message="ForgeEnv deploy", ignore_patterns=[ "__pycache__", "*.pyc", ".pytest_cache", "*.egg-info", ".venv", "node_modules", ], ) def main() -> None: parser = argparse.ArgumentParser(description="Deploy ForgeEnv Spaces") parser.add_argument("--user", required=True, help="HF username / org") parser.add_argument("--env-space-name", default="forgeenv") parser.add_argument("--demo-space-name", default="forgeenv-demo") parser.add_argument( "--env-folder", default="forgeenv-space", help="Local path to the environment Space folder", ) parser.add_argument( "--demo-folder", default="demo-space", help="Local path to the Gradio demo Space folder", ) parser.add_argument( "--skip-demo", action="store_true", help="Only deploy the environment Space", ) args = parser.parse_args() token = _require_token() from huggingface_hub import HfApi api = HfApi() repo_root = Path(__file__).resolve().parents[1] env_folder = (repo_root / args.env_folder).resolve() demo_folder = (repo_root / args.demo_folder).resolve() if not env_folder.is_dir(): sys.exit(f"[deploy] env folder not found: {env_folder}") env_repo = f"{args.user}/{args.env_space_name}" _ensure_space(api, env_repo, sdk="docker", token=token) _upload(api, env_repo, env_folder, token) print(f"[deploy] Environment live: https://huggingface.co/spaces/{env_repo}") if args.skip_demo: return if not demo_folder.is_dir(): print(f"[deploy] demo folder missing ({demo_folder}); skipping demo") return demo_repo = f"{args.user}/{args.demo_space_name}" _ensure_space(api, demo_repo, sdk="gradio", token=token) _upload(api, demo_repo, demo_folder, token) print(f"[deploy] Demo live: https://huggingface.co/spaces/{demo_repo}") if __name__ == "__main__": main()