Implement bounded SBOM Nexus catch-up
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 23s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a028f0-a42f-7582-89a8-ebaad7343834
This commit is contained in:
tegwick 2026-08-22 20:45:02 +02:00
parent 192f74f678
commit 8e8c74bd4c
3 changed files with 221 additions and 20 deletions

View file

@ -36,9 +36,10 @@ Until CUST-WP-0062-T03 lands there is no live endpoint — the query is exercise
against a test double (``tests/test_sbom_nexus_context_resolver.py``) and the
daily definition stays ``enabled: false``.
This adapter is read-only. Ingest of the selected repos is a declared bounded
side-effect owned by ACTIVITY-WP-0030-T02 and is deliberately not implemented
here.
With ``params.apply: true`` the adapter performs the declared T02 side-effect:
each selected repository receives exactly one terminal ingest or skip outcome.
The ranked response is truncated before any write, so the number of processed
repositories can never exceed ``limit``. The default remains read-only.
Config: SBOM_NEXUS_URL env var (default: http://127.0.0.1:8010).
"""
@ -47,6 +48,7 @@ from __future__ import annotations
import os
from typing import Any
from urllib.parse import quote
import httpx
@ -84,6 +86,14 @@ def _fetch_json(path: str, params: dict[str, Any] | None = None) -> Any:
return response.json()
def _post_json(path: str, payload: dict[str, Any] | None = None) -> Any:
url = f"{_base_url()}{path}"
with httpx.Client(timeout=_TIMEOUT_SECONDS) as client:
response = client.post(url, json=payload)
response.raise_for_status()
return response.json()
def _int_or(value: Any, default: int) -> int:
try:
return int(value)
@ -147,7 +157,7 @@ def _catch_up(params: dict[str, Any]) -> dict[str, Any]:
never_count = _int_or(payload.get("never_count"), 0)
stale_count = _int_or(payload.get("stale_count"), len(repos))
return {
result = {
"repos": repos,
"selected_count": len(repos),
"stale_count": stale_count,
@ -155,6 +165,68 @@ def _catch_up(params: dict[str, Any]) -> dict[str, Any]:
"total_count": total_count,
"limit": limit,
}
if params.get("apply") is True:
result.update(_apply_bounded_ingest(repos))
return result
def _skip(repo_slug: str, reason: str, detail: str | None = None) -> dict[str, Any]:
payload: dict[str, Any] = {"reason": reason}
if detail:
payload["detail"] = detail[:300]
raw = _post_json(f"/sbom/{quote(repo_slug, safe='')}/skip", payload)
if not isinstance(raw, dict) or raw.get("status") != "skipped":
raise RuntimeError(f"sbom-nexus skip returned an invalid outcome for {repo_slug}")
return raw
def _ingest(repo_slug: str) -> dict[str, Any]:
raw = _post_json(f"/sbom/{quote(repo_slug, safe='')}/ingest")
if not isinstance(raw, dict) or raw.get("status") not in {"ingested", "skipped"}:
return _skip(repo_slug, "ingest-error", "invalid ingest outcome")
return raw
def _apply_bounded_ingest(repos: list[dict[str, Any]]) -> dict[str, Any]:
updated: list[dict[str, Any]] = []
skipped: list[dict[str, Any]] = []
for repo in repos:
repo_slug = str(repo["repo_slug"])
try:
if repo.get("checkout_available") is False:
outcome = _skip(repo_slug, "no-checkout")
else:
outcome = _ingest(repo_slug)
except Exception as exc:
# A transport or contract failure still needs a terminal Nexus
# outcome so the same impossible repository cannot pin the queue.
outcome = _skip(repo_slug, "ingest-error", type(exc).__name__)
compact = {
key: outcome.get(key)
for key in (
"repo_slug",
"status",
"reason",
"snapshot_id",
"entry_count",
"snapshot_at",
"source_revision",
)
if outcome.get(key) is not None
}
compact.setdefault("repo_slug", repo_slug)
if outcome.get("status") == "ingested":
updated.append(compact)
else:
skipped.append(compact)
return {
"attempted_count": len(repos),
"updated": updated,
"skipped": skipped,
}
class SbomNexusContextResolver(ContextResolver):