feat(deploy): project infrastructure capabilities
Dependency Audit / dependency-audit (push) Successful in 1m44s
Deployment Installer / deployment-installer (push) Successful in 6s
Security Audit / security-audit (push) Successful in 11m43s

This commit is contained in:
2026-08-07 01:59:45 +02:00
parent dce725636d
commit 612a44bc8e
8 changed files with 731 additions and 4 deletions
@@ -35,6 +35,11 @@ from .bundle import (
render_existing_proxy_contract,
service_names,
)
from .capabilities import (
CapabilityChangeImpact,
capability_change_impacts,
infrastructure_capability_document,
)
from .distribution import (
MAX_KEYRING_BYTES,
MAX_MANIFEST_BYTES,
@@ -83,6 +88,8 @@ class DeploymentPlan:
desired_environment_fingerprint: str
actions: tuple[PlanAction, ...]
checks: tuple[Check, ...]
infrastructure_capabilities: Mapping[str, object]
capability_impacts: tuple[CapabilityChangeImpact, ...]
@property
def blocked(self) -> bool:
@@ -97,6 +104,8 @@ class DeploymentPlan:
"blocked": self.blocked,
"actions": [action.to_dict() for action in self.actions],
"checks": [check.to_dict() for check in self.checks],
"infrastructure_capabilities": dict(self.infrastructure_capabilities),
"capability_impacts": [item.to_dict() for item in self.capability_impacts],
}
@@ -122,6 +131,14 @@ def build_plan(
spec_digest = digest_json(spec.to_dict())
compose_digest = digest_json(compose)
environment_digest = environment_fingerprint(read_env(paths.env))
infrastructure_capabilities = infrastructure_capability_document(
spec,
read_env(paths.env),
)
capability_impacts = capability_change_impacts(
previous.get("infrastructure_capabilities"),
infrastructure_capabilities,
)
actions: list[PlanAction] = []
if not previous:
@@ -166,12 +183,21 @@ def build_plan(
"Remove the service container; retained volumes are not deleted.",
)
)
for impact in capability_impacts:
actions.append(
PlanAction(
"review",
f"capability:{impact.capability_id}",
impact.detail,
)
)
if (
previous
and previous_spec_digest == spec_digest
and previous_compose_digest == compose_digest
and previous_environment_fingerprint == environment_digest
and previous_services == desired_services
and not capability_impacts
):
actions.append(
PlanAction(
@@ -180,6 +206,15 @@ def build_plan(
)
checks = list(static_checks(spec, paths))
checks.extend(
Check(
id=f"capability.change.{impact.capability_id}",
level="warning",
message=impact.detail,
action=impact.required_action,
)
for impact in capability_impacts
)
if include_host_checks:
checks.extend(host_checks(spec, paths, command_runner=command_runner))
return DeploymentPlan(
@@ -189,6 +224,8 @@ def build_plan(
desired_environment_fingerprint=environment_digest,
actions=tuple(actions),
checks=tuple(checks),
infrastructure_capabilities=infrastructure_capabilities,
capability_impacts=capability_impacts,
)