Spaces:
Sleeping
Sleeping
File size: 6,336 Bytes
cfe0896 c5dbf9f cfe0896 c5dbf9f cfe0896 c5dbf9f cfe0896 c5dbf9f cfe0896 c5dbf9f cfe0896 c5dbf9f cfe0896 711b9b5 cfe0896 711b9b5 cfe0896 711b9b5 cfe0896 711b9b5 cfe0896 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | """
Pre-submission validator for Bug Triage OpenEnv.
Checks:
1) HF Space /reset returns HTTP 200
2) docker build succeeds
3) openenv validate succeeds
"""
from __future__ import annotations
import argparse
import os
import re
import shutil
import subprocess
import sys
import urllib.error
import urllib.request
from pathlib import Path
def log(msg: str) -> None:
print(msg)
def normalize_space_url(space_url: str) -> str:
"""Accept either hf.space or huggingface.co/spaces URLs."""
value = space_url.strip().rstrip("/")
match = re.match(r"^https?://huggingface\.co/spaces/([^/]+)/([^/]+)$", value)
if match:
owner, space = match.groups()
slug = f"{owner}-{space}".replace("_", "-")
return f"https://{slug}.hf.space"
return value
def run(cmd: list[str], cwd: Path | None = None, timeout: int = 1800) -> tuple[int, str, str]:
proc = subprocess.run(
cmd,
cwd=str(cwd) if cwd else None,
capture_output=True,
text=True,
timeout=timeout,
)
return proc.returncode, proc.stdout.strip(), proc.stderr.strip()
def check_space(space_url: str, timeout_seconds: int) -> tuple[bool, str]:
normalized_url = normalize_space_url(space_url)
reset_url = f"{normalized_url.rstrip('/')}/reset"
req = urllib.request.Request(reset_url, method="GET")
try:
with urllib.request.urlopen(req, timeout=timeout_seconds) as resp:
code = int(resp.getcode())
if code == 200:
return True, f"HF Space /reset returned HTTP {code} ({normalized_url})"
return False, f"HF Space /reset returned HTTP {code} (expected 200) ({normalized_url})"
except urllib.error.HTTPError as err:
return False, f"HF Space /reset returned HTTP {err.code} (expected 200) ({normalized_url})"
except Exception as err:
return False, f"HF Space check failed: {err} ({normalized_url})"
def check_docker_build(repo_dir: Path, docker_timeout: int) -> tuple[bool, str]:
if shutil.which("docker") is None:
return False, "docker command not found"
if (repo_dir / "Dockerfile").exists():
context = repo_dir
elif (repo_dir / "server" / "Dockerfile").exists():
context = repo_dir / "server"
else:
return False, "No Dockerfile found in repo root or server/"
code, out, err = run(["docker", "build", str(context)], timeout=docker_timeout)
if code == 0:
return True, f"Docker build succeeded ({context})"
combined = (out + "\n" + err).strip()
combined_lower = combined.lower()
if (
"failed to connect to the docker api" in combined_lower
or "dockerdesktoplinuxengine" in combined_lower
or ("access is denied" in combined_lower and ".docker\\buildx\\instances" in combined_lower)
):
return False, "Docker daemon is not running or is not accessible to the current user"
tail = "\n".join(combined.splitlines()[-20:])
return False, f"Docker build failed (timeout={docker_timeout}s)\n{tail}"
def check_openenv_validate(repo_dir: Path) -> tuple[bool, str]:
local_cli_candidates = [
repo_dir / "venv" / "Scripts" / "openenv.exe",
repo_dir / "venv" / "bin" / "openenv",
]
cmd = None
if shutil.which("openenv") is not None:
cmd = ["openenv", "validate"]
else:
for candidate in local_cli_candidates:
if candidate.exists():
cmd = [str(candidate), "validate"]
break
if cmd is None:
# Fallback for environments where console scripts are not on PATH.
cmd = [sys.executable, "-m", "openenv.cli", "validate"]
code, out, err = run(cmd, cwd=repo_dir, timeout=600)
if code == 0:
msg = out or "openenv validate passed"
return True, msg
base = "openenv validate failed"
hint = "Install with: pip install openenv-core" if "No module named" in err else ""
details = "\n".join(x for x in [base, hint, out, err] if x).strip()
return False, details
def main() -> int:
parser = argparse.ArgumentParser(description="Pre-submit validator")
parser.add_argument(
"--repo-dir",
default=".",
help="Repository root (default: current directory)",
)
parser.add_argument(
"--space-url",
default=os.getenv("HF_SPACE_URL", ""),
help="HF Space base URL (or set HF_SPACE_URL)",
)
parser.add_argument(
"--skip-space",
action="store_true",
help="Skip HF Space /reset check",
)
parser.add_argument(
"--docker-timeout",
type=int,
default=1800,
help="Docker build timeout in seconds (default: 1800)",
)
parser.add_argument(
"--http-timeout",
type=int,
default=20,
help="HTTP timeout for HF Space check in seconds",
)
args = parser.parse_args()
repo_dir = Path(args.repo_dir).resolve()
if not repo_dir.exists():
log(f"FAIL: repo dir does not exist: {repo_dir}")
return 1
log("========================================")
log("Pre-submission Validation")
log("========================================")
all_ok = True
# Step 1: HF Space /reset
log("Step 1/3: Checking HF Space /reset")
if args.skip_space:
log("SKIP: HF Space check skipped")
else:
if not args.space_url:
log("FAIL: --space-url (or HF_SPACE_URL) is required unless --skip-space is used")
all_ok = False
else:
ok, msg = check_space(args.space_url, args.http_timeout)
log(("PASS: " if ok else "FAIL: ") + msg)
all_ok = all_ok and ok
# Step 2: docker build
log("Step 2/3: Running docker build")
ok, msg = check_docker_build(repo_dir, args.docker_timeout)
log(("PASS: " if ok else "FAIL: ") + msg)
all_ok = all_ok and ok
# Step 3: openenv validate
log("Step 3/3: Running openenv validate")
ok, msg = check_openenv_validate(repo_dir)
log(("PASS: " if ok else "FAIL: ") + msg)
all_ok = all_ok and ok
log("========================================")
if all_ok:
log("All checks passed")
return 0
log("One or more checks failed")
return 1
if __name__ == "__main__":
raise SystemExit(main())
|