Project rail composition and readiness
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 4s

This commit is contained in:
codex 2026-07-26 13:34:56 +02:00
parent 9af9ee3e72
commit 4fcfee0479
7 changed files with 238 additions and 4 deletions

View file

@ -114,6 +114,7 @@ EDGE_TYPE_CANON_MAP: dict[str, CanonEdgeMapping] = {
"available_via": CanonEdgeMapping("exposes", "model/network", "partial"),
"attributed_to_cost_center": CanonEdgeMapping("governed_by", "model/governance", "gap"),
"attributed_to_profit_center": CanonEdgeMapping("governed_by", "model/governance", "gap"),
"base_rail": CanonEdgeMapping("depends_on", "model/landscape", "partial"),
"binds_rapp": CanonEdgeMapping("deploys", "model/devsecops", "partial"),
"binds": CanonEdgeMapping("depends_on", "model/landscape", "partial"),
"builds_container": CanonEdgeMapping("built_from", "model/devsecops", "partial"),

View file

@ -2045,6 +2045,14 @@ def _project_repository_family_graph(repositories: list[dict[str, Any]]) -> tupl
if edge is not None:
edges[_edge_key(edge)] = edge
base_rail = projection.get("base_rail")
if isinstance(base_rail, str) and base_rail:
target_id = _repository_graph_node_id(base_rail)
nodes.setdefault(target_id, _placeholder_repository_node(base_rail))
edge = _repository_projection_edge(slug, base_rail, "base_rail", projection)
if edge is not None:
edges[_edge_key(edge)] = edge
for rail_slug in _string_list(projection.get("hosted_rails")):
target_id = _repository_graph_node_id(rail_slug)
nodes.setdefault(target_id, _placeholder_repository_node(rail_slug))
@ -2075,6 +2083,11 @@ def _repository_family_projection(repository: dict[str, Any]) -> dict[str, Any]:
return {}
return {
"ownership_repo": _text_value(declaration, "ownership_repo"),
"contract_version": _text_value(declaration, "contract_version"),
"composition_kind": _text_value(declaration, "composition_kind"),
"base_rail": _text_value(declaration, "base_rail"),
"base_rail_contract": _text_value(declaration, "base_rail_contract"),
"readiness_state": _text_value(declaration, "readiness_state"),
"source_links": [{"label": "Rail declaration", "path": str(declaration_path)}],
}
@ -2091,6 +2104,7 @@ def _repository_family_projection(repository: dict[str, Any]) -> dict[str, Any]:
return {
"ownership_repo": _text_value(declaration, "ownership_repo"),
"supported_rails": supported_rails,
"readiness_state": _text_value(declaration, "readiness_state"),
"source_links": [{"label": "Rapp declaration", "path": str(declaration_path)}],
}
@ -2131,6 +2145,16 @@ def _repository_family_projection(repository: dict[str, Any]) -> dict[str, Any]:
"ownership_repo": _text_value(declaration, "ownership_repo"),
"hosted_rails": hosted_rails,
"bound_rapps": bound_rapps,
"relation_metadata": {
"hosts_rail": _relation_metadata(
rails_binding.get("hosted_rails") if isinstance(rails_binding, dict) else None,
key="rail_id",
),
"binds_rapp": _relation_metadata(
rapps_binding.get("bound_rapps") if isinstance(rapps_binding, dict) else None,
key="rapp_id",
),
},
"source_links": source_links,
}
@ -2161,6 +2185,11 @@ def _repository_projection_node(repository: dict[str, Any], projection: dict[str
"substrate_kind": str(repository.get("substrate_kind") or ""),
"path": str(repository.get("path") or ""),
"declaration_paths": _string_list(repository.get("declaration_paths")),
"contract_version": projection.get("contract_version"),
"composition_kind": projection.get("composition_kind"),
"base_rail": projection.get("base_rail"),
"base_rail_contract": projection.get("base_rail_contract"),
"readiness_state": projection.get("readiness_state"),
}
if source_links:
attributes["source_path"] = str(source_links[0].get("path") or "")
@ -2211,6 +2240,13 @@ def _repository_projection_edge(
if source_links:
attributes["source_path"] = str(source_links[0].get("path") or "")
attributes["source_links"] = source_links
relation_metadata = projection.get("relation_metadata")
if isinstance(relation_metadata, dict):
by_type = relation_metadata.get(edge_type)
if isinstance(by_type, dict):
metadata = by_type.get(target_slug)
if isinstance(metadata, dict):
attributes.update(metadata)
return {
"from": _repository_graph_node_id(source_slug),
"to": _repository_graph_node_id(target_slug),
@ -2251,6 +2287,30 @@ def _string_list(value: object) -> list[str]:
return result
def _relation_metadata(value: object, *, key: str) -> dict[str, dict[str, Any]]:
if not isinstance(value, list):
return {}
result: dict[str, dict[str, Any]] = {}
for item in value:
if not isinstance(item, dict):
continue
relation_id = _text_value(item, key)
if not relation_id:
continue
metadata: dict[str, Any] = {}
readiness_state = _text_value(item, "readiness_state")
if readiness_state:
metadata["readiness_state"] = readiness_state
contract_version = _text_value(item, "contract_version")
if contract_version:
metadata["contract_version"] = contract_version
evidence = _string_list(item.get("evidence"))
if evidence:
metadata["readiness_evidence"] = evidence
result[relation_id] = metadata
return result
def _relation_id_list(value: object, *, key: str) -> list[str]:
if isinstance(value, list):
result: list[str] = []