openoperator / scripts /deploy_hf_space.py
OpenOperator Deployer
Deploy source revision ef73a4b421f2d1ececca20f99c9118f4621c5e28
6918275
Raw History Blame Contribute Delete
7.81 kB
#!/usr/bin/env python3
"""Deterministic deployment script to synchronize a green commit to Hugging Face Space."""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
import tempfile
import urllib.request
from pathlib import Path
def deployment_identity(root, requested, space):
head = subprocess.check_output(
["git", "rev-parse", "HEAD"], cwd=root, text=True
).strip()
dirty = bool(
subprocess.check_output(
["git", "status", "--porcelain", "--untracked-files=all"],
cwd=root,
text=True,
)
)
sha = subprocess.check_output(
[
"git",
"rev-parse",
"--verify",
"--end-of-options",
(requested or head) + "^{commit}",
],
cwd=root,
text=True,
).strip()
print(
f"Local HEAD: {head}\nDeployment SHA: {sha}\nWorking tree clean: {not dirty}\nTarget HF Space: {space}"
)
if dirty:
raise RuntimeError(
"Refusing deployment: working tree contains uncommitted changes. "
"The HF deployment is built from a committed Git SHA. "
"Commit/stash your changes before deploying."
)
if sha != head:
print(
"Explicit historical snapshot selected; local HEAD is not being deployed."
)
return sha
def archive_snapshot(root, sha, snapshot):
with tempfile.TemporaryFile() as archive:
subprocess.run(
["git", "archive", "--format=tar", sha],
cwd=root,
stdout=archive,
check=True,
)
archive.seek(0)
subprocess.run(
["tar", "-xf", "-", "-C", str(snapshot)], stdin=archive, check=True
)
(snapshot / "openoperator-build.json").write_text(
json.dumps({"source_sha": sha}) + "\n", encoding="utf-8"
)
def verify_health(url, sha):
try:
with urllib.request.urlopen(url, timeout=15) as response:
data = json.loads(response.read(16384))
return response.status == 200 and data.get("sha") == sha
except (OSError, ValueError, AttributeError):
return False
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--space", default="Leon4gr45/openoperator", help="Target HF Space"
)
parser.add_argument("--sha", default="", help="Expected GitHub commit SHA")
parser.add_argument(
"--verify",
action="store_true",
help="Require a live /health SHA match after pushing",
)
parser.add_argument(
"--health-url", default="https://leon4gr45-openoperator.hf.space/health"
)
args = parser.parse_args()
root = Path(__file__).resolve().parents[1]
try:
sha = deployment_identity(root, args.sha.strip(), args.space)
except (RuntimeError, subprocess.CalledProcessError) as error:
print(
str(error)
if isinstance(error, RuntimeError)
else "Invalid committed deployment SHA",
file=sys.stderr,
)
return 1
hf_token = os.environ.get("HF_TOKEN", "").strip()
if not hf_token:
print(
"Error: HF_TOKEN environment variable is required to push to Hugging Face Space.",
file=sys.stderr,
)
return 1
with tempfile.TemporaryDirectory() as temp_dir:
snapshot = Path(temp_dir) / "hf_snapshot"
snapshot.mkdir(parents=True, exist_ok=True)
archive_snapshot(root, sha, snapshot)
readme_path = snapshot / "README.md"
content = (
readme_path.read_text(encoding="utf-8") if readme_path.exists() else ""
)
metadata = """---
title: OpenOperator
emoji: 🤖
colorFrom: indigo
colorTo: blue
sdk: docker
app_port: 7860
pinned: false
---
"""
if not content.startswith("---"):
readme_path.write_text(metadata + content, encoding="utf-8")
build_json = snapshot / "openoperator-build.json"
build_json.write_text(json.dumps({"source_sha": sha}) + "\n", encoding="utf-8")
# Create .gitattributes to track large binary extensions via LFS / ignore if needed
gitattributes = snapshot / ".gitattributes"
gitattributes.write_text(
"*.png filter=lfs diff=lfs merge=lfs -text\n*.jpg filter=lfs diff=lfs merge=lfs -text\n*.jpeg filter=lfs diff=lfs merge=lfs -text\n*.gif filter=lfs diff=lfs merge=lfs -text\n*.webm filter=lfs diff=lfs merge=lfs -text\n*.ttf filter=lfs diff=lfs merge=lfs -text\n*.woff filter=lfs diff=lfs merge=lfs -text\n*.woff2 filter=lfs diff=lfs merge=lfs -text\n",
encoding="utf-8",
)
subprocess.check_call(["git", "init", "--initial-branch=main"], cwd=snapshot)
subprocess.check_call(
["git", "config", "user.name", "OpenOperator Deployer"], cwd=snapshot
)
subprocess.check_call(
["git", "config", "user.email", "deploy@openoperator.local"], cwd=snapshot
)
# Remove offending documentation and raw binary files (like .png, .jpg, .gif, .webm, .ttf, .woff2) from snapshot
for pattern in ["docs/res", "plugins/_whats_new/webui/assets"]:
path = snapshot / pattern
if path.exists():
import shutil
shutil.rmtree(path, ignore_errors=True)
for binary_file in snapshot.glob("**/*"):
if binary_file.is_file() and binary_file.suffix in [
".png",
".jpg",
".jpeg",
".gif",
".webm",
".ttf",
".woff",
".woff2",
]:
try:
binary_file.unlink()
except OSError:
print("Snapshot cleanup failed; refusing upload.", file=sys.stderr)
return 1
subprocess.check_call(["git", "add", "--all"], cwd=snapshot)
subprocess.check_call(
["git", "commit", "-m", f"Deploy source revision {sha}"], cwd=snapshot
)
remote_url = f"https://huggingface.co/spaces/{args.space}"
subprocess.check_call(
["git", "remote", "add", "huggingface", remote_url], cwd=snapshot
)
print(f"Pushing snapshot to Hugging Face Space {args.space}...")
askpass = Path(temp_dir) / "askpass.py"
askpass.write_text(
"#!/usr/bin/env python3\nimport os,sys\nprint('oauth2' if 'username' in sys.argv[1].lower() else os.environ['HF_TOKEN'])\n"
)
askpass.chmod(0o700)
push_env = dict(os.environ, GIT_ASKPASS=str(askpass), GIT_TERMINAL_PROMPT="0")
result = subprocess.run(
["git", "push", "--force", "huggingface", "main:main"],
cwd=snapshot,
env=push_env,
capture_output=True,
check=False,
)
if result.returncode:
print(
"HF push failed; credential-bearing transport output withheld.",
file=sys.stderr,
)
return 1
print(
f"Snapshot push completed for {args.space} at SHA {sha}; push alone is not deployment qualification."
)
print(f"Verify {args.health_url}: health SHA must equal {sha}.")
if args.verify:
if not verify_health(args.health_url, sha):
print(
"Deployment unqualified: /health SHA does not match the requested SHA.",
file=sys.stderr,
)
return 2
print(
"Deployment qualified: live /health SHA matches requested deployment SHA."
)
return 0
if __name__ == "__main__":
raise SystemExit(main())