| 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()
|
|
|