feat: ingest repository navigation projections
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 3s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0230c-b06c-7641-808a-e191b6d1da49
This commit is contained in:
tegwick 2026-08-22 00:35:47 +02:00
parent f9a0a6dd72
commit 3db8c9cd78
9 changed files with 933 additions and 4 deletions

View file

@ -9,6 +9,11 @@ from hub_core.runtime.config import RuntimeSettings
from hub_core.runtime.compat import SQLCompatibilityStore, create_compatibility_router
from hub_core.runtime.models import HealthResponse, ReadinessResponse
from hub_core.runtime.ports import create_ports_router
from hub_core.runtime.repository_navigation import (
ProjectionRejected,
RepoProjectionClient,
RepositoryNavigationService,
)
from hub_core.runtime.store import InMemoryPortStore, PortStore
from hub_core.runtime.validation import ContractValidator
@ -17,13 +22,25 @@ def create_app(
*,
settings: RuntimeSettings | None = None,
port_store: PortStore | None = None,
repo_projection_client: RepoProjectionClient | None = None,
) -> FastAPI:
resolved_settings = settings or RuntimeSettings.from_env()
resolved_store = port_store or _create_store(resolved_settings)
owns_store = port_store is None
repository_navigation = RepositoryNavigationService(
client=repo_projection_client,
store=resolved_store,
)
@asynccontextmanager
async def lifespan(_: FastAPI):
if repo_projection_client is not None:
try:
await repository_navigation.refresh()
except ProjectionRejected:
# The readiness dependency reports the rejected or absent
# projection while the API remains available for diagnosis.
pass
yield
if owns_store and (closer := getattr(resolved_store, "aclose", None)):
await closer()
@ -42,6 +59,7 @@ def create_app(
else None
)
app.state.contract_validator = ContractValidator()
app.state.repository_navigation = repository_navigation
@app.get("/healthz", response_model=HealthResponse, tags=["system"])
async def healthz() -> HealthResponse:
@ -52,7 +70,10 @@ def create_app(
@app.get("/readyz", response_model=ReadinessResponse, tags=["system"])
async def readyz(response: Response) -> ReadinessResponse:
dependency_checks = await resolved_store.readiness_checks()
dependency_checks = {
**await resolved_store.readiness_checks(),
**await repository_navigation.readiness_checks(),
}
ready = resolved_settings.is_ready(resolved_store.backend_name) and all(
value in {"ok", "not_applicable"} for value in dependency_checks.values()
)