Download ops/deploy.py from sahel-sh/submit: direct link, hf CLI and curl.
- Browser
- Download file 5.02 kB
-
https://huggingface.co/spaces/sahel-sh/submit/resolve/main/ops/deploy.py
- Command line
-
hf download hf://spaces/sahel-sh/submit/ops/deploy.py
-
curl -L -o deploy.py https://huggingface.co/spaces/sahel-sh/submit/resolve/main/ops/deploy.py
5.02 kB
| #!/usr/bin/env python3 | |
| """Create the private submissions dataset and the Space, set secrets, push the code. | |
| Private inputs (HF_TOKEN in .env, teams.csv) are read from MAST_PRIVATE_DIR, by default the ``ops`` | |
| directory next to this checkout, so they never sit inside the repository. | |
| python ops/deploy.py --namespace mast-benchmark # full deploy | |
| python ops/deploy.py --namespace sahel-sh --dry-run # show what would happen | |
| python ops/deploy.py --sync-only # just refresh the vendored validator | |
| Never prints the token. | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import shutil | |
| import sys | |
| from pathlib import Path | |
| import os | |
| REPO_ROOT = Path(__file__).resolve().parents[1] # the submission-portal checkout | |
| PRIVATE_DIR = Path(os.environ.get("MAST_PRIVATE_DIR") or REPO_ROOT.parent / "ops") # .env, exports, roster: never inside a repository | |
| SPACE_DIR = REPO_ROOT | |
| VALIDATOR_SRC = Path(os.environ.get("MAST_VALIDATE_SRC") or REPO_ROOT.parent / "mast-validate" / "src" / "mast_validate") | |
| ENV_FILE = PRIVATE_DIR / ".env" | |
| def load_env() -> dict[str, str]: | |
| out: dict[str, str] = {} | |
| if ENV_FILE.is_file(): | |
| for line in ENV_FILE.read_text().splitlines(): | |
| line = line.strip() | |
| if line and not line.startswith("#") and "=" in line: | |
| k, v = line.split("=", 1) | |
| out[k.strip()] = v.strip().strip('"').strip("'") | |
| return out | |
| def sync_validator() -> None: | |
| dst = SPACE_DIR / "mast_validate" | |
| if dst.exists(): | |
| shutil.rmtree(dst) | |
| shutil.copytree(VALIDATOR_SRC, dst, ignore=shutil.ignore_patterns("__pycache__", "*.pyc")) | |
| print(f"vendored {VALIDATOR_SRC.name} -> {dst}") | |
| def main() -> int: | |
| ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| ap.add_argument("--namespace", default="mast-benchmark", help="owner of the private dataset") | |
| ap.add_argument("--space-namespace", default=None, | |
| help="owner of the Space (default: --namespace). Orgs need a paid plan to host Gradio " | |
| "Spaces; a personal account can host one free.") | |
| ap.add_argument("--space-name", default="submit") | |
| ap.add_argument("--dataset-name", default="mast-2026-submissions") | |
| ap.add_argument("--deadline", default=None, help="MAST_DEADLINE_UTC variable, ISO-8601") | |
| ap.add_argument("--sync-only", action="store_true") | |
| ap.add_argument("--dry-run", action="store_true") | |
| args = ap.parse_args() | |
| sync_validator() | |
| if args.sync_only: | |
| return 0 | |
| env = load_env() | |
| token = env.get("HF_TOKEN") | |
| if not token: | |
| print("ops/.env has no HF_TOKEN", file=sys.stderr) | |
| return 1 | |
| from huggingface_hub import HfApi | |
| api = HfApi(token=token) | |
| who = api.whoami() | |
| orgs = {o["name"] for o in who.get("orgs", [])} | |
| space_ns = args.space_namespace or args.namespace | |
| for ns in {args.namespace, space_ns}: | |
| if ns != who["name"] and ns not in orgs: | |
| print(f"token user {who['name']!r} is not a member of {ns!r} (orgs: {sorted(orgs)})", file=sys.stderr) | |
| return 1 | |
| dataset_id = f"{args.namespace}/{args.dataset_name}" | |
| space_id = f"{space_ns}/{args.space_name}" | |
| print(f"dataset (private): {dataset_id}\nspace (public): {space_id}") | |
| if args.dry_run: | |
| return 0 | |
| api.create_repo(dataset_id, repo_type="dataset", private=True, exist_ok=True) | |
| if not api.file_exists(dataset_id, "teams.csv", repo_type="dataset"): | |
| teams = PRIVATE_DIR / "teams.csv" | |
| content = teams.read_bytes() if teams.is_file() else b"team_name,contact_email,member_emails,tracks,aliases,registered_at\n" | |
| api.upload_file(path_or_fileobj=content, path_in_repo="teams.csv", repo_id=dataset_id, | |
| repo_type="dataset", commit_message="initial roster") | |
| print("uploaded teams.csv" + ("" if teams.is_file() else " (empty header only; run import_roster.py --push)")) | |
| api.create_repo(space_id, repo_type="space", space_sdk="gradio", private=False, exist_ok=True) | |
| api.add_space_secret(space_id, "HF_TOKEN", token) | |
| api.add_space_variable(space_id, "SUBMISSIONS_REPO", dataset_id) | |
| api.add_space_variable(space_id, "STORAGE_BACKEND", "hf") | |
| if args.deadline: | |
| api.add_space_variable(space_id, "MAST_DEADLINE_UTC", args.deadline) | |
| for k in ("SMTP_HOST", "SMTP_PORT", "SMTP_USER", "SMTP_PASSWORD", "SMTP_FROM"): | |
| if env.get(k): | |
| api.add_space_secret(space_id, k, env[k]) | |
| api.upload_folder(folder_path=str(SPACE_DIR), repo_id=space_id, repo_type="space", | |
| commit_message="deploy portal", | |
| ignore_patterns=["local-store/*", "tests/*", "__pycache__/*", "*.pyc", ".pytest_cache/*", "*.log"], | |
| delete_patterns=["*"]) | |
| print(f"deployed: https://huggingface.co/spaces/{space_id}") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |