Skip to content

Enforce deployment capability suitability (off/warn/strict)

Placeholder ppxd requested to merge feat/capability-enforcement-modes into main

Summary

  • Three-mode capability enforcement (SQUID_CAPABILITY_ENFORCEMENT, default warn — Rule 11): a target known to be unable to run an action (script syntax, action type, required feature, OS/role slot) now surfaces at plan time instead of only failing at runtime.
    • strict — fail the deployment pre-flight (before any step runs) on a capability mismatch.
    • warn (default) — skip the incompatible (action × target) with a structured warning. Identical to prior behaviour → non-breaking.
    • off — skip silently.
    • Cold-cache (target hasn't advertised capabilities yet) stays optimistic-allow in every mode — we can't prove incompatibility, so we let runtime decide. Scoped to capability blockers only; no-matching-targets / unresolved-transport are a separate concern and unaffected.
  • Preview == runtime, guaranteed: unify step→target role matching into one StepRoleMatcher shared by the planner (preview) and the executor's runtime resolution, so the machines a preview reports as matched are exactly the machines the deployment runs on. Removes the duplicate fallback matcher whose whitespace-only-roles handling could diverge.

This exceeds Octopus: Octopus has no pre-flight capability gate at all (incompatibility only fails at runtime in Calamari) and defers role→machine binding to runtime (preview and reality can differ). Here capability suitability is graded + enforced, and preview is the single source of truth for both the UI and execution.

Test plan

  • Unit: StepRoleMatcher matrix (scoped / unscoped / whitespace / case-insensitive / overlap)
  • Unit: preview/runtime consistency theory — planner MatchedTargets == executor TargetStepMatcher for the same scenario (incl. the whitespace edge)
  • Unit: enforcement matrix at plan time — strict throws on a capability blocker; warn/off/default don't; strict does not throw on a non-capability (no-targets) blocker; env-var name pinned (Rule 8); default = warn (non-breaking)
  • Full unit suite green (5681); full solution builds clean (0 errors)

Merge request reports

Loading