Add official source edition probes
GeoIntel CI / docs-smoke (push) Canceled after 0s
GeoIntel CI / contract-smoke (push) Canceled after 0s

This commit is contained in:
Codex
2026-07-16 17:33:40 +02:00
parent fd05c46e2a
commit 595967f892
27 changed files with 1513 additions and 18 deletions
+80 -5
View File
@@ -26,6 +26,21 @@ def parse_args() -> argparse.Namespace:
default="integrity",
help="Non-zero exit policy for cron or release automation",
)
parser.add_argument(
"--probe-catalogs",
action="store_true",
help="Explicitly query the allowlisted official GRB and orthophoto metadata catalogs",
)
parser.add_argument(
"--refresh-catalogs",
action="store_true",
help="Bypass the short server-side catalog cache (implies --probe-catalogs)",
)
parser.add_argument(
"--fail-on-catalog",
action="store_true",
help="Exit non-zero when an explicitly requested official catalog is degraded or unavailable",
)
return parser.parse_args()
@@ -45,7 +60,24 @@ def fetch_report(api_url: str, project_id: str, timeout: float) -> dict:
return payload["data"]
def print_text(report: dict) -> None:
def fetch_catalog_report(api_url: str, project_id: str, timeout: float, refresh: bool) -> dict:
query = "true" if refresh else "false"
endpoint = f"{api_url.rstrip('/')}/projects/{project_id}/datasets/source-catalog-probes?refresh={query}"
request = Request(endpoint, headers={"Accept": "application/json", "User-Agent": "GeoIntel-source-audit/1.0"})
try:
with urlopen(request, timeout=timeout) as response:
payload = json.load(response)
except HTTPError as exc:
body = exc.read().decode("utf-8", errors="replace")
raise RuntimeError(f"GeoIntel catalog probe returned HTTP {exc.code}: {body}") from exc
except URLError as exc:
raise RuntimeError(f"GeoIntel API is unreachable: {exc.reason}") from exc
if not isinstance(payload, dict) or not isinstance(payload.get("data"), dict):
raise RuntimeError("Catalog probe response is not a canonical GeoIntel data envelope")
return payload["data"]
def print_text(report: dict, *, catalog_queried: bool = False) -> None:
summary = report.get("summary", {})
print(
"GeoIntel source audit: "
@@ -59,7 +91,30 @@ def print_text(report: dict) -> None:
status = item.get("status", "unknown")
if status not in {"current", "local"} or item.get("integrity", {}).get("missing_version_count", 0):
print(f"- {status:15} {item.get('display_name', item.get('source_name'))}: {item.get('reason', '')}")
print("No external catalog was queried and no dataset was modified.")
if catalog_queried:
print("The official catalog was queried read-only; no feature, raster or dataset was modified.")
else:
print("No external catalog was queried and no dataset was modified.")
def print_catalog_text(report: dict) -> None:
summary = report.get("summary", {})
print(
"Official catalog probe: "
f"{summary.get('available_count', 0)}/{summary.get('provider_count', 0)} available, "
f"{summary.get('degraded_count', 0)} degraded, "
f"{summary.get('unavailable_count', 0)} unavailable, "
f"{summary.get('different_version_count', 0)} version differences"
)
for item in report.get("items", []):
remote = item.get("remote_version") or "no official edition"
local = item.get("local_source_version") or "not loaded locally"
print(
f"- {item.get('display_name', item.get('source_name'))}: "
f"{item.get('status')} / official={remote} / local={local} / "
f"comparison={item.get('comparison_status')}"
)
print(f" {item.get('message', '')}")
def should_fail(report: dict, fail_on: str) -> bool:
@@ -76,22 +131,42 @@ def should_fail(report: dict, fail_on: str) -> bool:
return integrity > 0 or due > 0 or review > 0
def catalog_should_fail(report: dict) -> bool:
summary = report.get("summary", {})
return int(summary.get("degraded_count", 0)) > 0 or int(summary.get("unavailable_count", 0)) > 0
def main() -> int:
args = parse_args()
try:
report = fetch_report(args.api_url, args.project_id, args.timeout)
catalog_report = (
fetch_catalog_report(args.api_url, args.project_id, args.timeout, args.refresh_catalogs)
if args.probe_catalogs or args.refresh_catalogs
else None
)
except RuntimeError as exc:
print(str(exc), file=sys.stderr)
return 2
serialized = json.dumps(report, indent=2, sort_keys=True)
output_payload = (
{"source_freshness": report, "catalog_probes": catalog_report}
if catalog_report is not None
else report
)
serialized = json.dumps(output_payload, indent=2, sort_keys=True)
if args.output:
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(serialized + "\n", encoding="utf-8")
if args.json:
print(serialized)
else:
print_text(report)
return 1 if should_fail(report, args.fail_on) else 0
print_text(report, catalog_queried=catalog_report is not None)
if catalog_report is not None:
print_catalog_text(catalog_report)
failed = should_fail(report, args.fail_on)
if args.fail_on_catalog and catalog_report is not None:
failed = failed or catalog_should_fail(catalog_report)
return 1 if failed else 0
if __name__ == "__main__":