File size: 9,859 Bytes
088795a | 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 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | from __future__ import annotations
import argparse
import json
import sys
from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Any
ROOT_DIR = Path(__file__).resolve().parent.parent
if str(ROOT_DIR) not in sys.path:
sys.path.insert(0, str(ROOT_DIR))
from scripts.next_deployment_step import (
LIVE_PROOF,
LOCAL_REPORT,
SITE_REPORT,
WORKER_REPORT,
choose_next_step,
deployment_urls_look_real,
load_json,
local_ready,
)
@dataclass
class ReportStatus:
path: str
exists: bool
valid_json: bool
summary: str
@dataclass
class DeploymentStatus:
ready_for_live_urls: bool
live_urls_look_real: bool
live_complete: bool
reports_are_real_live_evidence: bool
local_report: ReportStatus
site_report: ReportStatus
worker_report: ReportStatus
live_proof: ReportStatus
missing_site_checks: list[str]
missing_worker_checks: list[str]
next_status: str
next_title: str
next_detail: str
next_command: str
def _report_status(path: Path) -> ReportStatus:
payload = load_json(path)
if payload is None:
return ReportStatus(str(path), path.exists(), False, "missing or invalid JSON")
return ReportStatus(str(path), True, True, summarize_payload(path, payload))
def _list_checks_ok(payload: Any, names: set[str]) -> bool:
if not isinstance(payload, list):
return False
checks = {str(item.get("name", "")): bool(item.get("ok")) for item in payload if isinstance(item, dict)}
return all(checks.get(name) is True for name in names)
def _missing_checks(payload: Any, names: set[str]) -> list[str]:
if not isinstance(payload, list):
return sorted(names)
checks = {str(item.get("name", "")): bool(item.get("ok")) for item in payload if isinstance(item, dict)}
return sorted(name for name in names if checks.get(name) is not True)
def _looks_like_test_artifact(payload: Any) -> bool:
if not isinstance(payload, dict):
return False
text = json.dumps(payload, ensure_ascii=False).lower()
return "pytest-" in text or "your-space.hf.space" in text or "your-vercel-app.vercel.app" in text
def _url_evidence_matches(live_payload: Any, worker_url: str | None, vercel_origin: str | None) -> bool:
if not isinstance(live_payload, dict):
return False
proof_worker = str(live_payload.get("workerUrl") or "").rstrip("/")
proof_origin = str(live_payload.get("origin") or "").rstrip("/")
expected_worker = (worker_url or proof_worker).rstrip("/")
expected_origin = (vercel_origin or proof_origin).rstrip("/")
if not deployment_urls_look_real(expected_worker, expected_origin):
return False
return proof_worker == expected_worker and proof_origin == expected_origin
def summarize_payload(path: Path, payload: Any) -> str:
if isinstance(payload, dict):
if "ready" in payload or "complete" in payload:
return f"ready={payload.get('ready')} complete={payload.get('complete')}"
if "counts" in payload:
return f"counts={payload.get('counts')}"
return f"object keys={','.join(sorted(str(key) for key in payload.keys())[:6])}"
if isinstance(payload, list):
passed = sum(1 for item in payload if isinstance(item, dict) and item.get("ok") is True)
failed = sum(1 for item in payload if isinstance(item, dict) and item.get("ok") is False)
return f"checks={len(payload)} pass={passed} fail={failed}"
return type(payload).__name__
def build_status(
worker_url: str | None = None,
vercel_origin: str | None = None,
code: str = "1234",
local_report: Path = LOCAL_REPORT,
site_report: Path = SITE_REPORT,
worker_report: Path = WORKER_REPORT,
live_proof: Path = LIVE_PROOF,
) -> DeploymentStatus:
site_required = {
"site login",
"site platform vercel",
"site worker configured",
"site large PDF ready",
"site production worker ready",
"site hosted limits documented",
"site recommended stack documented",
"site direct cloud fallback disabled",
"site worker diagnostics endpoint",
"site worker reachable from vercel",
"site worker CORS ready",
}
worker_required = {
"recommended stack documented",
"smoke upload accepted",
"smoke job complete",
"smoke usable text",
"smoke audio url",
"smoke download url",
"smoke audio bytes",
"smoke audio file signature",
"smoke download bytes",
"smoke download file signature",
"scanned smoke upload accepted",
"scanned smoke job complete",
"scanned smoke usable text",
"scanned smoke OCR extraction",
"scanned smoke audio url",
"scanned smoke download url",
"scanned smoke audio bytes",
"scanned smoke audio file signature",
"scanned smoke download bytes",
"scanned smoke download file signature",
}
live_payload = load_json(live_proof)
site_payload = load_json(site_report)
worker_payload = load_json(worker_report)
missing_site_checks = _missing_checks(site_payload, site_required)
missing_worker_checks = _missing_checks(worker_payload, worker_required)
live_complete = isinstance(live_payload, dict) and bool(live_payload.get("complete"))
live_urls_look_real = deployment_urls_look_real(worker_url, vercel_origin)
reports_are_real_live_evidence = bool(
live_complete
and not missing_site_checks
and not missing_worker_checks
and not _looks_like_test_artifact(live_payload)
and _url_evidence_matches(live_payload, worker_url, vercel_origin)
)
next_step = choose_next_step(
worker_url=worker_url,
vercel_origin=vercel_origin,
code=code,
local_report=local_report,
site_report=site_report,
worker_report=worker_report,
live_proof=live_proof,
)
if live_complete and not reports_are_real_live_evidence:
next_status = "live-proof"
next_title = "Re-run live proof against real deployed URLs"
next_detail = (
"A complete live proof file exists, but it does not look like trustworthy live evidence. "
"Re-run the proof after replacing placeholder/test URLs with the real Hugging Face worker and Vercel site."
)
next_command = (
f"python scripts\\prove_live_deployment.py {worker_url or 'https://your-space.hf.space'} "
f"--origin {vercel_origin or 'https://your-vercel-app.vercel.app'} --code {code} "
"--smoke-ocr-engine arabic "
"--check-hf-metadata --hf-metadata-report outputs\\hf-model-metadata.md "
"--proof-out outputs\\live-deployment-proof.json"
)
else:
next_status = next_step.status
next_title = next_step.title
next_detail = next_step.detail
next_command = next_step.command
return DeploymentStatus(
ready_for_live_urls=local_ready(local_report) and live_urls_look_real,
live_urls_look_real=live_urls_look_real,
live_complete=live_complete,
reports_are_real_live_evidence=reports_are_real_live_evidence,
local_report=_report_status(local_report),
site_report=_report_status(site_report),
worker_report=_report_status(worker_report),
live_proof=_report_status(live_proof),
missing_site_checks=missing_site_checks,
missing_worker_checks=missing_worker_checks,
next_status=next_status,
next_title=next_title,
next_detail=next_detail,
next_command=next_command,
)
def print_status(status: DeploymentStatus) -> None:
print(f"Ready for live URLs: {status.ready_for_live_urls}")
print(f"Live URLs look real: {status.live_urls_look_real}")
print(f"Live proof complete: {status.live_complete}")
print(f"Reports are real live evidence: {status.reports_are_real_live_evidence}")
print()
for label, report in [
("Local", status.local_report),
("Site", status.site_report),
("Worker", status.worker_report),
("Live proof", status.live_proof),
]:
print(f"{label}: {report.summary} ({report.path})")
if status.missing_site_checks:
print()
print("Missing site proof checks:")
for name in status.missing_site_checks:
print(f"- {name}")
if status.missing_worker_checks:
print()
print("Missing worker proof checks:")
for name in status.missing_worker_checks:
print(f"- {name}")
print()
print(status.next_title)
print(status.next_detail)
print()
print(status.next_command)
def main() -> None:
parser = argparse.ArgumentParser(description="Show the current deployment proof status and next command.")
parser.add_argument("--worker-url", help="Live worker URL, for example https://your-space.hf.space")
parser.add_argument("--origin", help="Live Vercel origin, for example https://your-app.vercel.app")
parser.add_argument("--code", default="1234", help="Access code for live verification.")
parser.add_argument("--json", action="store_true", help="Print JSON.")
args = parser.parse_args()
status = build_status(worker_url=args.worker_url, vercel_origin=args.origin, code=args.code)
if args.json:
print(json.dumps(asdict(status), ensure_ascii=False, indent=2))
else:
print_status(status)
if __name__ == "__main__":
main()
|