LiveHouse-TS / scripts /deploy_cloud_space.py
ziyuzhou02's picture
Deploy GitHub main 8c9fc27f33a5
34681d6 verified
Raw
History Blame Contribute Delete
5.02 kB
#!/usr/bin/env python3
"""Deploy the tested GitHub source snapshot as the organization's Docker Space."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
ROOT = Path(__file__).resolve().parents[1]
def stage_cloud(destination: Path, revision: str, *, include_seed=True):
fixed = ["pyproject.toml", "LICENSE.txt", "space/app.py", "space/requirements.txt", "space/constraints.txt",
"space/vendor/ts_bench/collect_weekly_data.py", "space/vendor/ts_bench/export_model_inputs.py",
"bootstrap/manifest.json"]
if include_seed:
fixed.append("bootstrap/seed.tar.gz")
files = [ROOT / name for name in fixed]
for name in ("cloud", "scripts", "src", "configs", "space/src", "space/user_models"):
files.extend(p for p in (ROOT / name).rglob("*") if p.is_file()
and p.suffix in {".py", ".json", ".yaml", ".yml", ".txt", ".md"})
secrets = [v.encode() for k,v in os.environ.items() if v and len(v)>=12
and any(word in k for word in ("TOKEN", "API_KEY", "PASSWORD", "SECRET"))]
for source in sorted(set(files)):
rel = source.relative_to(ROOT)
if any(part.startswith(".env") for part in rel.parts):
raise ValueError("Environment files must never enter the Space")
content = source.read_bytes()
if any(secret in content for secret in secrets):
raise ValueError(f"Configured credential detected in deployment file {rel}")
path = destination / rel
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(content)
shutil.copy2(ROOT / "cloud/Dockerfile", destination / "Dockerfile")
shutil.copy2(ROOT / "cloud/README.md", destination / "README.md")
(destination / "deployment.json").write_text(json.dumps({
"source_repository":"https://github.com/Thinkcat-Lab/LiveHouse-TS", "source_branch":"main",
"source_revision":revision,
}, indent=2) + "\n")
def publish_bootstrap(api, state_repo):
if not api.dataset_info(state_repo).private:
raise ValueError("Bootstrap storage must use the private operator Dataset")
expected = json.loads((ROOT / "bootstrap/manifest.json").read_text())["sha256"]
archive = ROOT / "bootstrap/seed.tar.gz"
if hashlib.sha256(archive.read_bytes()).hexdigest() != expected:
raise ValueError("Local bootstrap checksum mismatch")
remote_path = f"bootstrap/{expected}.tar.gz"
if not api.file_exists(state_repo, remote_path, repo_type="dataset"):
api.upload_file(path_or_fileobj=archive, path_in_repo=remote_path,
repo_id=state_repo, repo_type="dataset",
commit_message="Store immutable bootstrap for hosted deployment")
def main():
from dotenv import load_dotenv
from huggingface_hub import HfApi
load_dotenv(ROOT / ".env")
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--repo", default=os.getenv("HF_SPACE_REPO", "ThinkcatLab/LiveHouse-TS"))
parser.add_argument("--dry-run", action="store_true")
parser.add_argument("--output-dir", type=Path, help="Keep a validated build context; requires --dry-run")
args = parser.parse_args()
if args.output_dir and not args.dry_run:
parser.error("--output-dir requires --dry-run")
revision = subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip()
with tempfile.TemporaryDirectory(prefix="livehouse-cloud-stage-") as temp:
stage = Path(temp)
stage_cloud(stage, revision, include_seed=args.dry_run)
print(f"Validated cloud deployment from {revision}: {sum(p.is_file() for p in stage.rglob('*'))} files")
if args.dry_run:
if args.output_dir:
if args.output_dir.exists():
raise SystemExit("Build context destination already exists")
shutil.copytree(stage, args.output_dir)
return
if not os.getenv("HF_TOKEN"):
raise SystemExit("HF_TOKEN required")
api = HfApi(token=os.environ["HF_TOKEN"])
publish_bootstrap(api, os.getenv("HF_STATE_REPO", "ThinkcatLab/LiveHouse-TS-state"))
# A changed README SDK converts the transferred Space to Docker while
# preserving its Git history. No hardware purchase is performed.
api.repo_info(args.repo, repo_type="space")
# A transferred Space may contain legacy top-level packages that shadow
# src/. Replace the deployment tree while retaining all Git history.
commit = api.upload_folder(folder_path=stage, repo_id=args.repo, repo_type="space",
delete_patterns=["*"],
commit_message=f"Deploy GitHub main {revision[:12]}")
print(f"Deployed {commit.oid}: https://huggingface.co/spaces/{args.repo}")
if __name__ == "__main__":
main()