@intentius/chant 0.124.51 → 0.124.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/discover.js +3 -1
- package/dist/behaviour.d.ts +1 -1
- package/dist/behaviour.d.ts.map +1 -1
- package/dist/capabilities/templates.d.ts +65 -0
- package/dist/capabilities/templates.d.ts.map +1 -0
- package/dist/capabilities/templates.js +193 -0
- package/dist/capabilities/templates.json +145 -0
- package/dist/change-set-graph.d.ts +35 -3
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +60 -9
- package/dist/change-set.d.ts +65 -7
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +23 -12
- package/dist/ci-identity.d.ts +7 -2
- package/dist/ci-identity.d.ts.map +1 -1
- package/dist/ci-identity.js +4 -1
- package/dist/cli/commands/import-index.d.ts +52 -0
- package/dist/cli/commands/import-index.d.ts.map +1 -0
- package/dist/cli/commands/import-index.js +226 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +4 -2
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/components.js +11 -2
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +16 -3
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/graph.js +22 -4
- package/dist/cli/handlers/index-cloud.d.ts +14 -0
- package/dist/cli/handlers/index-cloud.d.ts.map +1 -0
- package/dist/cli/handlers/index-cloud.js +22 -0
- package/dist/cli/handlers/index-diff.d.ts +18 -0
- package/dist/cli/handlers/index-diff.d.ts.map +1 -0
- package/dist/cli/handlers/index-diff.js +142 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts +25 -0
- package/dist/cli/handlers/lifecycle-drift.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-drift.js +64 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +188 -28
- package/dist/cli/handlers/misc.d.ts +9 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/misc.js +77 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +17 -3
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +89 -15
- package/dist/cli/handlers/query.d.ts.map +1 -1
- package/dist/cli/handlers/query.js +28 -4
- package/dist/cli/handlers/scenario.d.ts.map +1 -1
- package/dist/cli/handlers/scenario.js +8 -7
- package/dist/cli/handlers/search.d.ts +34 -4
- package/dist/cli/handlers/search.d.ts.map +1 -1
- package/dist/cli/handlers/search.js +216 -36
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +55 -1
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -1
- package/dist/cli/mcp/lifecycle-tools.js +3 -2
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.js +4 -3
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +55 -2
- package/dist/cli/registry.d.ts +19 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cloud-lister/aws.d.ts +35 -0
- package/dist/cloud-lister/aws.d.ts.map +1 -0
- package/dist/cloud-lister/aws.js +203 -0
- package/dist/cloud-lister/azure.d.ts +34 -0
- package/dist/cloud-lister/azure.d.ts.map +1 -0
- package/dist/cloud-lister/azure.js +129 -0
- package/dist/cloud-lister/common.d.ts +40 -0
- package/dist/cloud-lister/common.d.ts.map +1 -0
- package/dist/cloud-lister/common.js +83 -0
- package/dist/cloud-lister/credentials.d.ts +50 -0
- package/dist/cloud-lister/credentials.d.ts.map +1 -0
- package/dist/cloud-lister/credentials.js +86 -0
- package/dist/cloud-lister/fakes.d.ts +27 -0
- package/dist/cloud-lister/fakes.d.ts.map +1 -0
- package/dist/cloud-lister/fakes.js +50 -0
- package/dist/cloud-lister/gcp.d.ts +42 -0
- package/dist/cloud-lister/gcp.d.ts.map +1 -0
- package/dist/cloud-lister/gcp.js +143 -0
- package/dist/cloud-lister/guard.d.ts +49 -0
- package/dist/cloud-lister/guard.d.ts.map +1 -0
- package/dist/cloud-lister/guard.js +103 -0
- package/dist/cloud-lister/ids.d.ts +23 -0
- package/dist/cloud-lister/ids.d.ts.map +1 -0
- package/dist/cloud-lister/ids.js +42 -0
- package/dist/cloud-lister/index.d.ts +67 -0
- package/dist/cloud-lister/index.d.ts.map +1 -0
- package/dist/cloud-lister/index.js +102 -0
- package/dist/cloud-lister/kubernetes.d.ts +64 -0
- package/dist/cloud-lister/kubernetes.d.ts.map +1 -0
- package/dist/cloud-lister/kubernetes.js +276 -0
- package/dist/cloud-lister/markers.d.ts +31 -0
- package/dist/cloud-lister/markers.d.ts.map +1 -0
- package/dist/cloud-lister/markers.js +73 -0
- package/dist/cloud-lister/mask.d.ts +31 -0
- package/dist/cloud-lister/mask.d.ts.map +1 -0
- package/dist/cloud-lister/mask.js +74 -0
- package/dist/cloud-lister/sigv4.d.ts +29 -0
- package/dist/cloud-lister/sigv4.d.ts.map +1 -0
- package/dist/cloud-lister/sigv4.js +54 -0
- package/dist/cloud-lister/transport.d.ts +55 -0
- package/dist/cloud-lister/transport.d.ts.map +1 -0
- package/dist/cloud-lister/transport.js +86 -0
- package/dist/cloud-lister/types.d.ts +121 -0
- package/dist/cloud-lister/types.d.ts.map +1 -0
- package/dist/cloud-lister/types.js +24 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.js +6 -0
- package/dist/components/capability-plugin.d.ts +9 -0
- package/dist/components/capability-plugin.d.ts.map +1 -1
- package/dist/components/capability.d.ts +224 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +21 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +58 -14
- package/dist/components/covered-round.d.ts +173 -0
- package/dist/components/covered-round.d.ts.map +1 -0
- package/dist/components/covered-round.js +200 -0
- package/dist/components/declared-reads.d.ts +38 -0
- package/dist/components/declared-reads.d.ts.map +1 -0
- package/dist/components/declared-reads.js +131 -0
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/deploy-units.js +4 -0
- package/dist/components/driver.d.ts +42 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +74 -3
- package/dist/components/fan-out-run.d.ts +23 -1
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +82 -14
- package/dist/components/fan-out-support.d.ts +6 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out-support.js +4 -1
- package/dist/components/fan-out.d.ts +28 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/groups.d.ts +229 -0
- package/dist/components/groups.d.ts.map +1 -0
- package/dist/components/groups.js +760 -0
- package/dist/components/index.d.ts +7 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +5 -0
- package/dist/components/plan-shape-conformance.d.ts +48 -0
- package/dist/components/plan-shape-conformance.d.ts.map +1 -0
- package/dist/components/plan-shape-conformance.js +122 -0
- package/dist/components/pr-run.d.ts +62 -7
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +354 -53
- package/dist/components/promote-rounds.d.ts +9 -0
- package/dist/components/promote-rounds.d.ts.map +1 -1
- package/dist/components/promote-rounds.js +23 -2
- package/dist/components/promote.d.ts +17 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +36 -6
- package/dist/components/release-gate.d.ts +23 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +97 -33
- package/dist/components/run-groups.d.ts +58 -0
- package/dist/components/run-groups.d.ts.map +1 -0
- package/dist/components/run-groups.js +78 -0
- package/dist/components/starter-plugin.d.ts +1 -0
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/starter-plugin.js +6 -1
- package/dist/components/verbs/index.d.ts +1 -0
- package/dist/components/verbs/index.d.ts.map +1 -1
- package/dist/components/verbs/index.js +1 -0
- package/dist/components/verbs/member-outputs.d.ts +187 -0
- package/dist/components/verbs/member-outputs.d.ts.map +1 -0
- package/dist/components/verbs/member-outputs.js +218 -0
- package/dist/components/wave-plan.d.ts +2 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +4 -1
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +27 -0
- package/dist/deep-observation.d.ts +1 -13
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/deep-observation.js +5 -22
- package/dist/drift-record.d.ts +542 -0
- package/dist/drift-record.d.ts.map +1 -0
- package/dist/drift-record.js +886 -0
- package/dist/engine-facts.d.ts +144 -0
- package/dist/engine-facts.d.ts.map +1 -0
- package/dist/engine-facts.js +14 -0
- package/dist/graph-ir.d.ts +153 -2
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-ir.js +224 -6
- package/dist/graph-refs.d.ts +145 -0
- package/dist/graph-refs.d.ts.map +1 -1
- package/dist/graph-refs.js +305 -0
- package/dist/graph-schema/fixtures/adopt.json +15 -0
- package/dist/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/dist/graph-schema/fixtures/index.json +68 -0
- package/dist/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/dist/graph-schema/fixtures.d.ts +23 -0
- package/dist/graph-schema/fixtures.d.ts.map +1 -0
- package/dist/graph-schema/fixtures.js +246 -0
- package/dist/graph-schema/generate.d.ts +15 -0
- package/dist/graph-schema/generate.d.ts.map +1 -0
- package/dist/graph-schema/generate.js +267 -0
- package/dist/graph-schema/graph.schema.json +1387 -0
- package/dist/health.schema.json +2 -1
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/instance-nodes.d.ts +159 -0
- package/dist/instance-nodes.d.ts.map +1 -0
- package/dist/instance-nodes.js +231 -0
- package/dist/lexicon.d.ts +72 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/adoption-file.d.ts +56 -0
- package/dist/lifecycle/adoption-file.d.ts.map +1 -0
- package/dist/lifecycle/adoption-file.js +123 -0
- package/dist/lifecycle/cloud-index.d.ts +186 -0
- package/dist/lifecycle/cloud-index.d.ts.map +1 -0
- package/dist/lifecycle/cloud-index.js +618 -0
- package/dist/lifecycle/derive-edges.d.ts +59 -0
- package/dist/lifecycle/derive-edges.d.ts.map +1 -0
- package/dist/lifecycle/derive-edges.js +126 -0
- package/dist/lifecycle/git.d.ts +10 -9
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +7 -1
- package/dist/lifecycle/identity-match.d.ts +133 -0
- package/dist/lifecycle/identity-match.d.ts.map +1 -0
- package/dist/lifecycle/identity-match.js +147 -0
- package/dist/lifecycle/index-diff.d.ts +224 -0
- package/dist/lifecycle/index-diff.d.ts.map +1 -0
- package/dist/lifecycle/index-diff.js +327 -0
- package/dist/lifecycle/index-document.d.ts +130 -0
- package/dist/lifecycle/index-document.d.ts.map +1 -0
- package/dist/lifecycle/index-document.js +189 -0
- package/dist/lifecycle/index-drift.d.ts +121 -0
- package/dist/lifecycle/index-drift.d.ts.map +1 -0
- package/dist/lifecycle/index-drift.js +178 -0
- package/dist/lifecycle/index-import.d.ts +168 -0
- package/dist/lifecycle/index-import.d.ts.map +1 -0
- package/dist/lifecycle/index-import.js +515 -0
- package/dist/lifecycle/index-join.d.ts +129 -0
- package/dist/lifecycle/index-join.d.ts.map +1 -0
- package/dist/lifecycle/index-join.js +248 -0
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/observation-baseline.d.ts +61 -21
- package/dist/lifecycle/observation-baseline.d.ts.map +1 -1
- package/dist/lifecycle/observation-baseline.js +144 -19
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/observe.js +6 -1
- package/dist/lifecycle/query.d.ts +45 -4
- package/dist/lifecycle/query.d.ts.map +1 -1
- package/dist/lifecycle/query.js +89 -7
- package/dist/lifecycle/replay.d.ts +10 -3
- package/dist/lifecycle/replay.d.ts.map +1 -1
- package/dist/lifecycle/replay.js +10 -6
- package/dist/lifecycle/scenario.d.ts +4 -3
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/snapshot-store-fake.d.ts +9 -0
- package/dist/lifecycle/snapshot-store-fake.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-fake.js +21 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts +102 -0
- package/dist/lifecycle/snapshot-store-s3.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store-s3.js +254 -0
- package/dist/lifecycle/snapshot-store.d.ts +170 -0
- package/dist/lifecycle/snapshot-store.d.ts.map +1 -0
- package/dist/lifecycle/snapshot-store.js +832 -0
- package/dist/lifecycle/snapshot.d.ts +11 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -1
- package/dist/lifecycle/snapshot.js +18 -5
- package/dist/lifecycle/type-listing.d.ts +148 -0
- package/dist/lifecycle/type-listing.d.ts.map +1 -0
- package/dist/lifecycle/type-listing.js +166 -0
- package/dist/lint/component-checks.d.ts +15 -1
- package/dist/lint/component-checks.d.ts.map +1 -1
- package/dist/lint/component-checks.js +1 -0
- package/dist/lint/presets/relaxed.json +2 -1
- package/dist/lint/presets/strict.json +2 -1
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/fail/dns.component.js +13 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp008/pass/dns.component.js +8 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts +33 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp008-applies-signal-only-root.js +123 -0
- package/dist/lint/rules/comp/index.d.ts +2 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/comp/index.js +4 -1
- package/dist/observation.d.ts +19 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +18 -1
- package/dist/op/activities/activity-contracts.d.ts +1 -1
- package/dist/op/activities/activity-contracts.js +1 -1
- package/dist/op/activities/change-set.d.ts +6 -4
- package/dist/op/activities/change-set.d.ts.map +1 -1
- package/dist/op/activities/change-set.js +4 -2
- package/dist/op/activity-failure.d.ts +27 -0
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-failure.js +31 -0
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/index.js +1 -1
- package/dist/op/local-executor.d.ts +5 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +22 -8
- package/dist/pr-loop.d.ts +92 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +62 -3
- package/dist/pulumi-urn.d.ts +101 -0
- package/dist/pulumi-urn.d.ts.map +1 -0
- package/dist/pulumi-urn.js +131 -0
- package/dist/release-floors.d.ts +23 -0
- package/dist/release-floors.d.ts.map +1 -0
- package/dist/release-floors.js +22 -0
- package/dist/sensitive-key.d.ts +23 -0
- package/dist/sensitive-key.d.ts.map +1 -0
- package/dist/sensitive-key.js +31 -0
- package/dist/wave-runtime.mjs +234 -234
- package/dist/workspace/adoption-report.d.ts +53 -0
- package/dist/workspace/adoption-report.d.ts.map +1 -0
- package/dist/workspace/adoption-report.js +76 -0
- package/dist/workspace/capabilities-contract.d.ts +7 -0
- package/dist/workspace/capabilities-contract.d.ts.map +1 -1
- package/dist/workspace/capabilities-contract.js +2 -1
- package/dist/workspace/capabilities.schema.json +98 -0
- package/dist/workspace/change-set.schema.json +42 -3
- package/dist/workspace/ci-cloud-identity.d.ts +13 -1
- package/dist/workspace/ci-cloud-identity.d.ts.map +1 -1
- package/dist/workspace/ci-cloud-identity.js +30 -1
- package/dist/workspace/compose-graph.d.ts +10 -1
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/compose-graph.js +6 -1
- package/dist/workspace/conformance/index.d.ts +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/index.js +6 -1
- package/dist/workspace/drift-cli.d.ts +81 -0
- package/dist/workspace/drift-cli.d.ts.map +1 -0
- package/dist/workspace/drift-cli.js +317 -0
- package/dist/workspace/drift.schema.json +672 -0
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/graph-cli.js +3 -0
- package/dist/workspace/graph.schema.json +72 -0
- package/dist/workspace/init.d.ts +5 -1
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/init.js +55 -9
- package/dist/workspace/ledger.schema.json +1 -1
- package/dist/workspace/nested-graph.d.ts.map +1 -1
- package/dist/workspace/nested-graph.js +2 -0
- package/dist/workspace/pr-report.schema.json +76 -3
- package/dist/workspace/promote-waves.d.ts +112 -0
- package/dist/workspace/promote-waves.d.ts.map +1 -0
- package/dist/workspace/promote-waves.js +290 -0
- package/dist/workspace/promote.d.ts +84 -2
- package/dist/workspace/promote.d.ts.map +1 -1
- package/dist/workspace/promote.js +347 -61
- package/dist/workspace/reason-codes.d.ts +12 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +14 -0
- package/dist/workspace/search-contract.d.ts +3 -3
- package/dist/workspace/search-contract.d.ts.map +1 -1
- package/dist/workspace/search-contract.js +2 -0
- package/dist/workspace/search.schema.json +103 -5
- package/package.json +14 -1
- package/src/__fixtures__/drift-record/README.md +58 -0
- package/src/__fixtures__/drift-record/choudoufu-cone.ndjson +4 -0
- package/src/__fixtures__/drift-record/choudoufu-resource.ndjson +3 -0
- package/src/__fixtures__/drift-record/component-status-ledger.json +65 -0
- package/src/__fixtures__/drift-record/component-status.json +91 -0
- package/src/__fixtures__/drift-record/deep-diff.json +259 -0
- package/src/__fixtures__/drift-record/lifecycle-diff-live.json +825 -0
- package/src/__fixtures__/drift-record/live-diff.json +125 -0
- package/src/audit/discover.e2e.test.ts +14 -0
- package/src/audit/discover.ts +3 -1
- package/src/behaviour.ts +1 -1
- package/src/capabilities/templates.json +145 -0
- package/src/capabilities/templates.test.ts +212 -0
- package/src/capabilities/templates.ts +204 -0
- package/src/change-set-bundle.test.ts +83 -0
- package/src/change-set-graph.ts +80 -9
- package/src/change-set.test.ts +24 -0
- package/src/change-set.ts +77 -14
- package/src/ci-identity-targets.test.ts +23 -0
- package/src/ci-identity.ts +10 -4
- package/src/cli/commands/import-index.test.ts +117 -0
- package/src/cli/commands/import-index.ts +265 -0
- package/src/cli/commands/lint.ts +4 -2
- package/src/cli/handlers/components.test.ts +9 -0
- package/src/cli/handlers/components.ts +12 -2
- package/src/cli/handlers/fan-out.ts +17 -3
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +26 -4
- package/src/cli/handlers/index-cloud.ts +32 -0
- package/src/cli/handlers/index-diff.test.ts +68 -0
- package/src/cli/handlers/index-diff.ts +146 -0
- package/src/cli/handlers/ledger-root-member.test.ts +161 -0
- package/src/cli/handlers/lifecycle-drift.ts +68 -0
- package/src/cli/handlers/lifecycle.test.ts +33 -11
- package/src/cli/handlers/lifecycle.ts +205 -30
- package/src/cli/handlers/misc.ts +73 -0
- package/src/cli/handlers/pr-waves-blocked.test.ts +37 -2
- package/src/cli/handlers/pr.ts +16 -3
- package/src/cli/handlers/promote.test.ts +64 -0
- package/src/cli/handlers/promote.ts +81 -6
- package/src/cli/handlers/query.ts +27 -4
- package/src/cli/handlers/run-declared-reads-cli.test.ts +187 -0
- package/src/cli/handlers/run-groups-cli.test.ts +229 -0
- package/src/cli/handlers/scenario.test.ts +13 -0
- package/src/cli/handlers/scenario.ts +8 -7
- package/src/cli/handlers/search-index.test.ts +255 -0
- package/src/cli/handlers/search-json.test.ts +1 -1
- package/src/cli/handlers/search-ownership.test.ts +194 -0
- package/src/cli/handlers/search.ts +271 -38
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +56 -1
- package/src/cli/mcp/lifecycle-tools.ts +3 -2
- package/src/cli/mcp/resource-handlers.ts +4 -3
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +40 -2
- package/src/cli/registry.ts +19 -1
- package/src/cloud-lister/aws.test.ts +202 -0
- package/src/cloud-lister/aws.ts +226 -0
- package/src/cloud-lister/azure.test.ts +132 -0
- package/src/cloud-lister/azure.ts +142 -0
- package/src/cloud-lister/common.ts +115 -0
- package/src/cloud-lister/credentials.ts +110 -0
- package/src/cloud-lister/fakes.ts +66 -0
- package/src/cloud-lister/gcp.test.ts +121 -0
- package/src/cloud-lister/gcp.ts +159 -0
- package/src/cloud-lister/guard.test.ts +198 -0
- package/src/cloud-lister/guard.ts +111 -0
- package/src/cloud-lister/ids.ts +54 -0
- package/src/cloud-lister/index.ts +134 -0
- package/src/cloud-lister/kubernetes.test.ts +160 -0
- package/src/cloud-lister/kubernetes.ts +284 -0
- package/src/cloud-lister/markers.ts +74 -0
- package/src/cloud-lister/mask.ts +77 -0
- package/src/cloud-lister/shape.test.ts +81 -0
- package/src/cloud-lister/sigv4.ts +70 -0
- package/src/cloud-lister/transport.ts +118 -0
- package/src/cloud-lister/types.ts +135 -0
- package/src/components/capability-plugin-loader.ts +4 -0
- package/src/components/capability-plugin.ts +10 -0
- package/src/components/capability.ts +238 -0
- package/src/components/cli-support.ts +70 -14
- package/src/components/covered-round.test.ts +166 -0
- package/src/components/covered-round.ts +293 -0
- package/src/components/declared-reads.test.ts +103 -0
- package/src/components/declared-reads.ts +134 -0
- package/src/components/deploy-units.ts +4 -0
- package/src/components/driver.ts +101 -4
- package/src/components/fan-out-run.ts +95 -15
- package/src/components/fan-out-support.ts +10 -1
- package/src/components/fan-out.ts +30 -0
- package/src/components/groups.test.ts +320 -0
- package/src/components/groups.ts +880 -0
- package/src/components/index.ts +59 -0
- package/src/components/plan-shape-conformance.ts +130 -0
- package/src/components/pr-env-run-bundle.test.ts +2 -0
- package/src/components/pr-run.test.ts +728 -4
- package/src/components/pr-run.ts +395 -55
- package/src/components/promote-rounds.ts +25 -2
- package/src/components/promote.ts +55 -5
- package/src/components/registry.test.ts +1 -1
- package/src/components/release-gate.ts +115 -30
- package/src/components/release-groups.test.ts +362 -0
- package/src/components/run-groups.ts +113 -0
- package/src/components/starter-plugin.ts +6 -0
- package/src/components/verbs/index.ts +1 -0
- package/src/components/verbs/member-outputs.test.ts +205 -0
- package/src/components/verbs/member-outputs.ts +308 -0
- package/src/components/wave-plan.ts +5 -2
- package/src/config.ts +44 -0
- package/src/deep-observation.ts +5 -23
- package/src/drift-record.test.ts +596 -0
- package/src/drift-record.ts +1206 -0
- package/src/engine-facts.guard.test.ts +111 -0
- package/src/engine-facts.test.ts +178 -0
- package/src/engine-facts.ts +154 -0
- package/src/gated-waves-bundle.test.ts +2 -0
- package/src/graph-ir-live.test.ts +92 -0
- package/src/graph-ir.ts +317 -7
- package/src/graph-refs.ts +416 -0
- package/src/graph-schema/fixtures/adopt.json +15 -0
- package/src/graph-schema/fixtures/choudoufu.engine-graph.json +169 -0
- package/src/graph-schema/fixtures/index.json +68 -0
- package/src/graph-schema/fixtures/plunge.engine-graph.json +158 -0
- package/src/graph-schema/fixtures.ts +267 -0
- package/src/graph-schema/generate.ts +263 -0
- package/src/graph-schema/graph-schema.test.ts +153 -0
- package/src/graph-schema/graph.schema.json +1387 -0
- package/src/health.schema.json +2 -1
- package/src/identity.ts +2 -0
- package/src/index.ts +2 -0
- package/src/instance-nodes.ts +288 -0
- package/src/lexicon.ts +76 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T120000Z-aaaaaaaaaaaa.json +149 -0
- package/src/lifecycle/__fixtures__/index-diff/20261026T130000Z-bbbbbbbbbbbb.json +127 -0
- package/src/lifecycle/adoption-file.test.ts +100 -0
- package/src/lifecycle/adoption-file.ts +154 -0
- package/src/lifecycle/cloud-index.test.ts +517 -0
- package/src/lifecycle/cloud-index.ts +736 -0
- package/src/lifecycle/derive-edges.test.ts +193 -0
- package/src/lifecycle/derive-edges.ts +182 -0
- package/src/lifecycle/git.ts +10 -4
- package/src/lifecycle/identity-match.test.ts +308 -0
- package/src/lifecycle/identity-match.ts +257 -0
- package/src/lifecycle/index-diff.test.ts +198 -0
- package/src/lifecycle/index-diff.ts +476 -0
- package/src/lifecycle/index-document.test.ts +64 -0
- package/src/lifecycle/index-document.ts +299 -0
- package/src/lifecycle/index-drift.test.ts +191 -0
- package/src/lifecycle/index-drift.ts +304 -0
- package/src/lifecycle/index-import.test.ts +255 -0
- package/src/lifecycle/index-import.ts +634 -0
- package/src/lifecycle/index-join.test.ts +250 -0
- package/src/lifecycle/index-join.ts +304 -0
- package/src/lifecycle/index-store-load-bearing.test.ts +187 -0
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/member-ledger.test.ts +25 -7
- package/src/lifecycle/observation-baseline.test.ts +72 -1
- package/src/lifecycle/observation-baseline.ts +190 -27
- package/src/lifecycle/observe.ts +6 -1
- package/src/lifecycle/query.test.ts +69 -1
- package/src/lifecycle/query.ts +103 -10
- package/src/lifecycle/replay.test.ts +9 -5
- package/src/lifecycle/replay.ts +9 -5
- package/src/lifecycle/scenario.ts +4 -3
- package/src/lifecycle/snapshot-store-fake.ts +29 -0
- package/src/lifecycle/snapshot-store-s3.ts +316 -0
- package/src/lifecycle/snapshot-store.test.ts +385 -0
- package/src/lifecycle/snapshot-store.ts +981 -0
- package/src/lifecycle/snapshot.test.ts +13 -0
- package/src/lifecycle/snapshot.ts +23 -6
- package/src/lifecycle/type-listing.test.ts +166 -0
- package/src/lifecycle/type-listing.ts +309 -0
- package/src/lint/component-checks.ts +17 -1
- package/src/lint/config.test.ts +3 -0
- package/src/lint/presets/relaxed.json +2 -1
- package/src/lint/presets/strict.json +2 -1
- package/src/lint/rules/__fixtures__/comp/comp008/fail/dns.component.ts +15 -0
- package/src/lint/rules/__fixtures__/comp/comp008/pass/dns.component.ts +10 -0
- package/src/lint/rules/comp/comp.test.ts +2 -1
- package/src/lint/rules/comp/comp008-applies-signal-only-root.ts +121 -0
- package/src/lint/rules/comp/comp008.test.ts +77 -0
- package/src/lint/rules/comp/index.ts +4 -1
- package/src/observation-edges.test.ts +33 -0
- package/src/observation.ts +36 -1
- package/src/op/activities/activity-contracts.ts +1 -1
- package/src/op/activities/change-set.ts +8 -5
- package/src/op/activity-failure.ts +36 -0
- package/src/op/index.ts +2 -1
- package/src/op/local-executor.test.ts +80 -0
- package/src/op/local-executor.ts +27 -9
- package/src/pr-loop-bundle.test.ts +5 -2
- package/src/pr-loop.test.ts +57 -1
- package/src/pr-loop.ts +154 -6
- package/src/pulumi-urn.ts +151 -0
- package/src/release-floors.ts +22 -0
- package/src/sensitive-key.ts +34 -0
- package/src/workspace/adoption-report.test.ts +61 -0
- package/src/workspace/adoption-report.ts +103 -0
- package/src/workspace/capabilities-contract.test.ts +4 -0
- package/src/workspace/capabilities-contract.ts +9 -1
- package/src/workspace/capabilities.schema.json +98 -0
- package/src/workspace/change-set.schema.json +42 -3
- package/src/workspace/ci-cloud-identity.ts +31 -1
- package/src/workspace/compose-graph.test.ts +12 -0
- package/src/workspace/compose-graph.ts +12 -1
- package/src/workspace/conformance/conformance.e2e.test.ts +2 -1
- package/src/workspace/conformance/index.ts +6 -1
- package/src/workspace/drift-cli.test.ts +65 -0
- package/src/workspace/drift-cli.ts +365 -0
- package/src/workspace/drift.schema.json +672 -0
- package/src/workspace/graph-cli.ts +5 -0
- package/src/workspace/graph.schema.json +72 -0
- package/src/workspace/init.test.ts +38 -1
- package/src/workspace/init.ts +50 -8
- package/src/workspace/intent-gaps.test.ts +18 -2
- package/src/workspace/ledger.schema.json +1 -1
- package/src/workspace/nested-graph.ts +1 -0
- package/src/workspace/pr-report.schema.json +76 -3
- package/src/workspace/promote-waves.test.ts +296 -0
- package/src/workspace/promote-waves.ts +340 -0
- package/src/workspace/promote.test.ts +158 -4
- package/src/workspace/promote.ts +398 -57
- package/src/workspace/reason-codes.test.ts +4 -1
- package/src/workspace/reason-codes.ts +14 -0
- package/src/workspace/search-contract.ts +2 -0
- package/src/workspace/search.schema.json +103 -5
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composed runs at promote, fan-out and `run --components` (ws-107, #4465),
|
|
3
|
+
* against a fake capability: `fake-stack` creates one resource and exposes an
|
|
4
|
+
* `id` known only after it applies. With `group` it declares `planGroup` and
|
|
5
|
+
* `runGroup`; without, it plans and applies component by component, as
|
|
6
|
+
* before. The pull-request run's groups are in `pr-run.test.ts`.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, test } from "vitest";
|
|
10
|
+
import {
|
|
11
|
+
CapabilityRegistry,
|
|
12
|
+
type CapabilityGroupOutcome,
|
|
13
|
+
type DeployContext,
|
|
14
|
+
type GroupContext,
|
|
15
|
+
type GroupMember,
|
|
16
|
+
type GroupRun,
|
|
17
|
+
type PlannedOutputs,
|
|
18
|
+
} from "./capability";
|
|
19
|
+
import { computePlanDigest } from "../lifecycle/plan-digest";
|
|
20
|
+
import { isEmptyChangeSet } from "../change-set";
|
|
21
|
+
import { makeRelease, type Release } from "../lifecycle/release-store";
|
|
22
|
+
import type { ReleaseRecord } from "../lifecycle/release-ledger";
|
|
23
|
+
import { memoryGateLedgerPort } from "../op/gate";
|
|
24
|
+
import { runInterpretDriver, type DriverComponent, type DriverRunResult, type ReleaseGateBinding } from "./driver";
|
|
25
|
+
import { remainingFanOut } from "./fan-out";
|
|
26
|
+
import { runFanOut } from "./fan-out-run";
|
|
27
|
+
import { componentSetFanOut, formRunGroups, planAndRunComponentGroup, regroupRemaining, WAVE_GATE_FALLBACK } from "./groups";
|
|
28
|
+
import { runPromotion, type PromotionPlan } from "./promote";
|
|
29
|
+
import { runPromotionInRounds } from "./promote-rounds";
|
|
30
|
+
import { planReleaseSet } from "./release-gate";
|
|
31
|
+
import { renderRunGroups, runGroupsReport } from "./run-groups";
|
|
32
|
+
|
|
33
|
+
interface StackInput {
|
|
34
|
+
root: string;
|
|
35
|
+
vars?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const so = (stack: string, name: string) => ({ stackOutput: { stack, name } });
|
|
39
|
+
const sha = (c: string) => `sha256:${c.repeat(64)}`;
|
|
40
|
+
|
|
41
|
+
const stack = (name: string, dependsOn: string[] = [], vars?: Record<string, unknown>): DriverComponent => ({
|
|
42
|
+
name,
|
|
43
|
+
dependsOn,
|
|
44
|
+
deploy: [{ phase: "Apply", steps: [{ kind: "fake-stack", root: name, ...(vars ? { vars } : {}) }] }],
|
|
45
|
+
});
|
|
46
|
+
/** a; b reads a's new id; c reads b's. */
|
|
47
|
+
const ABC = [stack("a"), stack("b", ["a"], { upstream: so("a", "id") }), stack("c", ["b"], { upstream: so("b", "id") })];
|
|
48
|
+
|
|
49
|
+
function world(opts: { group?: boolean } = {}) {
|
|
50
|
+
const w = {
|
|
51
|
+
registry: new CapabilityRegistry(),
|
|
52
|
+
planned: [] as string[],
|
|
53
|
+
applied: [] as string[],
|
|
54
|
+
planGroupCalls: [] as string[][],
|
|
55
|
+
runGroupCalls: [] as Array<{ components: string[]; run: GroupRun }>,
|
|
56
|
+
outcome: undefined as ((members: readonly GroupMember[]) => CapabilityGroupOutcome) | undefined,
|
|
57
|
+
};
|
|
58
|
+
const planOf = (input: StackInput) => {
|
|
59
|
+
const planDigest = computePlanDigest("fake-stack", { root: input.root, vars: input.vars ?? null });
|
|
60
|
+
const outputs: PlannedOutputs = { known: {}, unknown: ["id"] };
|
|
61
|
+
return {
|
|
62
|
+
member: input.root,
|
|
63
|
+
planDigest,
|
|
64
|
+
artifact: { planDigest },
|
|
65
|
+
outputs,
|
|
66
|
+
changeSet: {
|
|
67
|
+
member: { member: input.root, lexicon: "test", planner: "tofu" as const, status: "planned" as const, planDigest, holes: [] },
|
|
68
|
+
entries: [{ member: input.root, lexicon: "test", planner: "tofu" as const, address: "res.x", type: "res", action: "create" as const, attributes: [] }],
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
w.registry.register({
|
|
73
|
+
kind: "fake-stack",
|
|
74
|
+
async plan(_ctx: DeployContext, input: StackInput) {
|
|
75
|
+
w.planned.push(input.root);
|
|
76
|
+
return planOf(input);
|
|
77
|
+
},
|
|
78
|
+
async run(_ctx: DeployContext, input: StackInput) {
|
|
79
|
+
w.applied.push(input.root);
|
|
80
|
+
return { outputs: { id: `${input.root}-id` } };
|
|
81
|
+
},
|
|
82
|
+
...(opts.group
|
|
83
|
+
? {
|
|
84
|
+
async planGroup(_ctx: GroupContext, members: readonly GroupMember<StackInput>[]) {
|
|
85
|
+
w.planGroupCalls.push(members.map((m) => m.component));
|
|
86
|
+
return {
|
|
87
|
+
members: Object.fromEntries(members.map((m) => [m.component, planOf(m.input!)])),
|
|
88
|
+
digest: computePlanDigest("fake-composition", members.map((m) => m.component)),
|
|
89
|
+
artifact: { composed: members.map((m) => m.component) },
|
|
90
|
+
};
|
|
91
|
+
},
|
|
92
|
+
async runGroup(_ctx: GroupContext, run: GroupRun, members: readonly GroupMember<StackInput>[]) {
|
|
93
|
+
w.runGroupCalls.push({ components: members.map((m) => m.component), run });
|
|
94
|
+
if (w.outcome) return w.outcome(members);
|
|
95
|
+
return { members: Object.fromEntries(members.map((m) => [m.component, { status: "applied" as const, outputs: { id: `${m.component}-id` } }])) };
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
: {}),
|
|
99
|
+
} as never);
|
|
100
|
+
return w;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const release = (): Release =>
|
|
104
|
+
makeRelease({
|
|
105
|
+
member: null,
|
|
106
|
+
commit: "a".repeat(40),
|
|
107
|
+
artifactDigests: [],
|
|
108
|
+
targets: [{ env: "prod", templates: [], params: [] }],
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const source = (component: string): ReleaseRecord => ({
|
|
112
|
+
version: 1,
|
|
113
|
+
component,
|
|
114
|
+
env: "staging",
|
|
115
|
+
digest: sha("d"),
|
|
116
|
+
gitSha: "a".repeat(40),
|
|
117
|
+
runId: "run-1",
|
|
118
|
+
timestamp: "2026-10-01T00:00:00.000Z",
|
|
119
|
+
actor: "ci",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const promotion = (components: readonly DriverComponent[]): PromotionPlan => ({
|
|
123
|
+
from: "staging",
|
|
124
|
+
to: "prod",
|
|
125
|
+
items: components.map((c) => ({ component: c.name, digest: sha("d"), source: source(c.name) })),
|
|
126
|
+
notPromoted: [],
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
async function grouped(components: DriverComponent[], registry: CapabilityRegistry, waveGate = false) {
|
|
130
|
+
return formRunGroups({ components, plan: componentSetFanOut(components), registry, env: "prod", ...(waveGate ? { waveGate: true } : {}) });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function planned(components: DriverComponent[], registry: CapabilityRegistry, waveGate = false) {
|
|
134
|
+
const plan = await grouped(components, registry, waveGate);
|
|
135
|
+
const set = await planReleaseSet({ release: release(), env: "prod", components, digests: new Map(), registry, current: new Map(), grouped: plan });
|
|
136
|
+
return { plan, set };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const binding = (digest: string): ReleaseGateBinding => ({ op: "release", planDigest: digest, releaseDigest: sha("r") });
|
|
140
|
+
|
|
141
|
+
describe("promote --release plans a group as one (#4465)", () => {
|
|
142
|
+
test("A, B reading A and C reading B: one planGroup call, the group digest in every member's part, no planned outputs between members", async () => {
|
|
143
|
+
const w = world({ group: true });
|
|
144
|
+
const { plan, set } = await planned(ABC, w.registry);
|
|
145
|
+
expect(plan.groups).toEqual([{ capability: "fake-stack", components: ["a", "b", "c"] }]);
|
|
146
|
+
expect(w.planGroupCalls).toEqual([["a", "b", "c"]]);
|
|
147
|
+
expect(w.planned).toEqual([]);
|
|
148
|
+
const digest = set.groups?.[0]?.digest;
|
|
149
|
+
expect(digest).toEqual(expect.stringMatching(/^jcs1-sha256:[0-9a-f]{64}$/));
|
|
150
|
+
expect(set.groups?.[0]?.run?.digest).toBe(digest);
|
|
151
|
+
// The release gate's change set names the group (its digest) on each of its members, and what each reads inside it.
|
|
152
|
+
expect(set.doc.members.map((m) => [m.member, m.status, m.group])).toEqual([
|
|
153
|
+
["a", "planned", { digest, reads: [] }],
|
|
154
|
+
["b", "planned", { digest, reads: ["a.id"] }],
|
|
155
|
+
["c", "planned", { digest, reads: ["b.id"] }],
|
|
156
|
+
]);
|
|
157
|
+
expect(isEmptyChangeSet(set.doc)).toBe(false);
|
|
158
|
+
// A read of a group-mate is no planned output and no unknown input, so the promote runs no round for it.
|
|
159
|
+
expect(set.plannedOutputs).toBeUndefined();
|
|
160
|
+
expect(set.unknownInputs).toBeUndefined();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("without the group pair it plans component by component, as before", async () => {
|
|
164
|
+
const w = world();
|
|
165
|
+
const { plan, set } = await planned(ABC, w.registry);
|
|
166
|
+
expect(plan.groups).toBeUndefined();
|
|
167
|
+
expect(w.planGroupCalls).toEqual([]);
|
|
168
|
+
expect(w.planned).toEqual(["a", "b", "c"]);
|
|
169
|
+
expect(set.groups).toBeUndefined();
|
|
170
|
+
expect(set.doc.members.every((m) => m.group === undefined)).toBe(true);
|
|
171
|
+
expect(set.unknownInputs).toEqual({ b: ["a.id"], c: ["b.id"] });
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("with --wave-gate no group forms: each member falls back as wave-gate and plans by itself", async () => {
|
|
175
|
+
const w = world({ group: true });
|
|
176
|
+
const { plan, set } = await planned(ABC, w.registry, true);
|
|
177
|
+
expect(plan.groups).toBeUndefined();
|
|
178
|
+
expect(plan.waves).toEqual([["a"], ["b"], ["c"]]);
|
|
179
|
+
expect(plan.fallbacks?.map((f) => [f.component, f.kind])).toEqual([
|
|
180
|
+
["a", WAVE_GATE_FALLBACK],
|
|
181
|
+
["b", WAVE_GATE_FALLBACK],
|
|
182
|
+
["c", WAVE_GATE_FALLBACK],
|
|
183
|
+
]);
|
|
184
|
+
expect(w.planGroupCalls).toEqual([]);
|
|
185
|
+
expect(w.planned).toEqual(["a", "b", "c"]);
|
|
186
|
+
expect(set.fallbacks).toEqual(plan.fallbacks);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
describe("promote --release applies a group as one (#4465)", () => {
|
|
191
|
+
test("one runGroup call with the plan the release gate bound, and no round", async () => {
|
|
192
|
+
const w = world({ group: true });
|
|
193
|
+
const { plan, set } = await planned(ABC, w.registry);
|
|
194
|
+
const groupRuns = (set.groups ?? []).map((g) => g.run!);
|
|
195
|
+
const runs: string[][] = [];
|
|
196
|
+
const result = await runPromotionInRounds({
|
|
197
|
+
components: ABC,
|
|
198
|
+
outputs: {},
|
|
199
|
+
binding: binding(set.doc.digest),
|
|
200
|
+
approved: set.doc,
|
|
201
|
+
reads: set.reads,
|
|
202
|
+
grouped: plan,
|
|
203
|
+
replan: async () => {
|
|
204
|
+
throw new Error("no round: nothing is planned again");
|
|
205
|
+
},
|
|
206
|
+
bind: async () => {
|
|
207
|
+
throw new Error("no round: nothing is bound again");
|
|
208
|
+
},
|
|
209
|
+
run: (components, outputs, b) => {
|
|
210
|
+
runs.push(components.map((c) => c.name));
|
|
211
|
+
return runPromotion({
|
|
212
|
+
plan: promotion(components),
|
|
213
|
+
components,
|
|
214
|
+
registry: w.registry,
|
|
215
|
+
componentOutputs: outputs,
|
|
216
|
+
gates: memoryGateLedgerPort(),
|
|
217
|
+
releaseGate: b,
|
|
218
|
+
grouped: plan,
|
|
219
|
+
groupRuns,
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
expect(result.stages).toEqual([]);
|
|
224
|
+
expect(runs).toEqual([["a", "b", "c"]]);
|
|
225
|
+
expect(w.runGroupCalls.map((c) => c.components)).toEqual([["a", "b", "c"]]);
|
|
226
|
+
expect(w.runGroupCalls[0]!.run.digest).toBe(set.groups![0]!.digest);
|
|
227
|
+
expect(w.applied).toEqual([]);
|
|
228
|
+
expect(result.run.status).toBe("ok");
|
|
229
|
+
expect(result.completed).toEqual(["a", "b", "c"]);
|
|
230
|
+
expect(result.outputs).toMatchObject({ a: { id: "a-id" }, b: { id: "b-id" }, c: { id: "c-id" } });
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
test("a group with no approved plan fails every member unless the promote plans it in its wave", async () => {
|
|
234
|
+
const w = world({ group: true });
|
|
235
|
+
const plan = await grouped(ABC, w.registry);
|
|
236
|
+
const refused = await runPromotion({ plan: promotion(ABC), components: ABC, registry: w.registry, gates: memoryGateLedgerPort(), grouped: plan, groupRuns: [] });
|
|
237
|
+
expect(refused.status).toBe("fail");
|
|
238
|
+
expect(w.runGroupCalls).toEqual([]);
|
|
239
|
+
const ran = await runPromotion({ plan: promotion(ABC), components: ABC, registry: w.registry, gates: memoryGateLedgerPort(), grouped: plan, planGroupsAtWave: true });
|
|
240
|
+
expect(ran.status).toBe("ok");
|
|
241
|
+
expect(w.planGroupCalls).toEqual([["a", "b", "c"]]);
|
|
242
|
+
expect(w.runGroupCalls.map((c) => c.components)).toEqual([["a", "b", "c"]]);
|
|
243
|
+
expect(ran.groupOutcomes).toEqual({ a: "applied", b: "applied", c: "applied" });
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
test("without the group pair each component runs its own deploy, as before", async () => {
|
|
247
|
+
const w = world();
|
|
248
|
+
const plan = await grouped(ABC, w.registry);
|
|
249
|
+
const ran = await runPromotion({ plan: promotion(ABC), components: ABC, registry: w.registry, gates: memoryGateLedgerPort(), grouped: plan });
|
|
250
|
+
expect(ran.status).toBe("ok");
|
|
251
|
+
expect(w.applied).toEqual(["a", "b", "c"]);
|
|
252
|
+
expect(ran.groupOutcomes).toBeUndefined();
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("a resume after the group failed part way plans only the members left, as a smaller group", async () => {
|
|
256
|
+
const w = world({ group: true });
|
|
257
|
+
const plan = await grouped(ABC, w.registry);
|
|
258
|
+
// The first attempt: a applied, b failed, c did not start.
|
|
259
|
+
w.outcome = () => ({
|
|
260
|
+
members: { a: { status: "applied", outputs: { id: "a-id" } }, b: { status: "failed", error: "boom" }, c: { status: "not-started" } },
|
|
261
|
+
});
|
|
262
|
+
const first = await runPromotion({ plan: promotion(ABC), components: ABC, registry: w.registry, gates: memoryGateLedgerPort(), grouped: plan, planGroupsAtWave: true });
|
|
263
|
+
expect(first.status).toBe("fail");
|
|
264
|
+
expect(first.groupOutcomes).toEqual({ a: "applied", b: "failed", c: "not-started" });
|
|
265
|
+
const completed = first.results.filter((r) => r.status === "ok").map((r) => r.component);
|
|
266
|
+
expect(completed).toEqual(["a"]);
|
|
267
|
+
|
|
268
|
+
// The resume groups what is left, as the promote handler does, and plans only that.
|
|
269
|
+
w.planGroupCalls.length = 0;
|
|
270
|
+
w.outcome = undefined;
|
|
271
|
+
const left = await regroupRemaining({
|
|
272
|
+
components: ABC,
|
|
273
|
+
plan: remainingFanOut(plan, ABC, { completed }),
|
|
274
|
+
registry: w.registry,
|
|
275
|
+
env: "prod",
|
|
276
|
+
groups: plan.groups,
|
|
277
|
+
});
|
|
278
|
+
expect(left.groups).toEqual([{ capability: "fake-stack", components: ["b", "c"] }]);
|
|
279
|
+
const set = await planReleaseSet({
|
|
280
|
+
release: release(),
|
|
281
|
+
env: "prod",
|
|
282
|
+
components: ABC,
|
|
283
|
+
digests: new Map(),
|
|
284
|
+
registry: w.registry,
|
|
285
|
+
current: new Map(),
|
|
286
|
+
seededOutputs: { a: { id: "a-id" } },
|
|
287
|
+
only: new Set(["b", "c"]),
|
|
288
|
+
grouped: left,
|
|
289
|
+
});
|
|
290
|
+
expect(w.planGroupCalls).toEqual([["b", "c"]]);
|
|
291
|
+
expect(set.doc.members.map((m) => m.member)).toEqual(["b", "c"]);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe("components fan-out and run --components apply a group as one (#4465)", () => {
|
|
296
|
+
test("runFanOut plans a group in its wave and applies it with one runGroup call", async () => {
|
|
297
|
+
const w = world({ group: true });
|
|
298
|
+
const plan = await grouped(ABC, w.registry);
|
|
299
|
+
const result = await runFanOut(plan, ABC, w.registry, { env: "prod", gates: memoryGateLedgerPort(), planGroupsAtWave: true });
|
|
300
|
+
expect(result.status).toBe("ok");
|
|
301
|
+
expect(w.planGroupCalls).toEqual([["a", "b", "c"]]);
|
|
302
|
+
expect(w.runGroupCalls.map((c) => c.components)).toEqual([["a", "b", "c"]]);
|
|
303
|
+
expect(result.groupOutcomes).toEqual({ a: "applied", b: "applied", c: "applied" });
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("runInterpretDriver runs a group once, in place of its members' own deploys", async () => {
|
|
307
|
+
const w = world({ group: true });
|
|
308
|
+
const plan = await grouped(ABC, w.registry);
|
|
309
|
+
const byName = new Map(ABC.map((c) => [c.name, c]));
|
|
310
|
+
const run: DriverRunResult = await runInterpretDriver(ABC, w.registry, {
|
|
311
|
+
env: "prod",
|
|
312
|
+
gates: memoryGateLedgerPort(),
|
|
313
|
+
groups: {
|
|
314
|
+
waves: plan.waves,
|
|
315
|
+
groups: plan.groups!,
|
|
316
|
+
run: (group, componentOutputs) =>
|
|
317
|
+
planAndRunComponentGroup({ group, components: byName, registry: w.registry, env: "prod", ctxOf: (name) => ({ env: "prod", component: name }), componentOutputs }),
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
expect(run.status).toBe("ok");
|
|
321
|
+
expect(run.waves).toEqual([["a", "b", "c"]]);
|
|
322
|
+
expect(w.applied).toEqual([]);
|
|
323
|
+
expect(w.runGroupCalls.map((c) => c.components)).toEqual([["a", "b", "c"]]);
|
|
324
|
+
expect(run.componentOutputs).toMatchObject({ c: { id: "c-id" } });
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
describe("the promote report names the group and each member's outcome (#4465)", () => {
|
|
329
|
+
test("a group that stopped part way: its members, wave, digest and reads, each member's outcome, and what the same promote plans next", async () => {
|
|
330
|
+
const w = world({ group: true });
|
|
331
|
+
const { plan, set } = await planned(ABC, w.registry);
|
|
332
|
+
const digest = set.groups![0]!.digest!;
|
|
333
|
+
const report = runGroupsReport({ grouped: plan, planned: set.groups!, doc: set.doc, outcomes: { a: "applied", b: "partial", c: "not-started" } });
|
|
334
|
+
expect(report.groups).toEqual([
|
|
335
|
+
{
|
|
336
|
+
capability: "fake-stack",
|
|
337
|
+
components: ["a", "b", "c"],
|
|
338
|
+
wave: 1,
|
|
339
|
+
digest,
|
|
340
|
+
reads: [
|
|
341
|
+
{ member: "b", ref: "a.id" },
|
|
342
|
+
{ member: "c", ref: "b.id" },
|
|
343
|
+
],
|
|
344
|
+
outcomes: { a: "applied", b: "partial", c: "not-started" },
|
|
345
|
+
},
|
|
346
|
+
]);
|
|
347
|
+
expect(renderRunGroups(report, "the same promote")).toEqual([
|
|
348
|
+
`a, b and c plan and apply as one (fake-stack, wave 1, group ${digest}); b reads a.id and c reads b.id, known when the group applies`,
|
|
349
|
+
" a applied; b applied in part, and what applied stays; c did not start",
|
|
350
|
+
" the group's apply stopped part way and nothing that applied is undone; the same promote plans b and c again, as one group where they still join",
|
|
351
|
+
]);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test("under --wave-gate the report says why each member planned by itself", async () => {
|
|
355
|
+
const w = world({ group: true });
|
|
356
|
+
const plan = await grouped(ABC, w.registry, true);
|
|
357
|
+
const lines = renderRunGroups(runGroupsReport({ grouped: plan }), "the same promote");
|
|
358
|
+
expect(lines).toEqual([
|
|
359
|
+
"planned one by one, not as a group: a, b, c (wave-gate): a, b and c would plan and apply as one, but under --wave-gate each wave plans component by component, so no group forms",
|
|
360
|
+
]);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a promote under a release, `components fan-out` and `run --components`
|
|
3
|
+
* say about the groups they applied as one (ws-107, #4465), as `pr-report`
|
|
4
|
+
* says it for a pull request since #4446: each group with its members, the
|
|
5
|
+
* wave it ran in, its digest and the reads its apply computes, how each
|
|
6
|
+
* member ended, and why each component that could have grouped did not.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ChangeSetDocument } from "../change-set";
|
|
10
|
+
import type { GroupMemberStatus } from "./capability";
|
|
11
|
+
import type { FanOutGroup, FanOutGroupFallback, FanOutPlan } from "./fan-out";
|
|
12
|
+
|
|
13
|
+
/** One group a run applied as one. */
|
|
14
|
+
export interface RunGroupReport {
|
|
15
|
+
/** The kind of the capability every member's one step names. */
|
|
16
|
+
capability: string;
|
|
17
|
+
/** The members, in apply order: upstreams first. */
|
|
18
|
+
components: string[];
|
|
19
|
+
/** The 1-based wave of the grouped fan-out it ran in. */
|
|
20
|
+
wave: number;
|
|
21
|
+
/** The group digest folded into each member's `planDigest`; `null` when it failed to plan. Absent when the run planned it in its wave. */
|
|
22
|
+
digest?: string | null;
|
|
23
|
+
/** Why the group failed to plan. */
|
|
24
|
+
error?: string;
|
|
25
|
+
/** Each read of another member its plan did not know, by change-set member, which the group's apply computes. Absent when none. */
|
|
26
|
+
reads?: Array<{ member: string; ref: string }>;
|
|
27
|
+
/** How each member's part ended, as the capability said. Absent when the group did not run. */
|
|
28
|
+
outcomes?: Record<string, GroupMemberStatus>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RunGroupsReport {
|
|
32
|
+
groups?: RunGroupReport[];
|
|
33
|
+
fallbacks?: FanOutGroupFallback[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The report of `grouped`'s groups. `planned` are the groups a release gate's
|
|
38
|
+
* plan planned (`ReleasePlan.groups`), with their digests; `doc` is the
|
|
39
|
+
* change set they are in, which names each member's reads; `outcomes` is the
|
|
40
|
+
* run's `groupOutcomes`.
|
|
41
|
+
*/
|
|
42
|
+
export function runGroupsReport(input: {
|
|
43
|
+
grouped: FanOutPlan | undefined;
|
|
44
|
+
planned?: ReadonlyArray<FanOutGroup & { digest: string | null; error?: string }>;
|
|
45
|
+
doc?: ChangeSetDocument;
|
|
46
|
+
outcomes?: Record<string, GroupMemberStatus>;
|
|
47
|
+
}): RunGroupsReport {
|
|
48
|
+
const waves = input.grouped?.waves ?? [];
|
|
49
|
+
const waveOf = new Map(waves.flatMap((w, i) => w.map((name) => [name, i + 1] as const)));
|
|
50
|
+
const groups: Array<FanOutGroup & { digest?: string | null; error?: string }> = input.planned?.length ? [...input.planned] : [...(input.grouped?.groups ?? [])];
|
|
51
|
+
const out: RunGroupReport[] = groups.map((g) => {
|
|
52
|
+
const reads = g.digest
|
|
53
|
+
? (input.doc?.members ?? []).filter((m) => m.group?.digest === g.digest).flatMap((m) => m.group!.reads.map((ref) => ({ member: m.member, ref })))
|
|
54
|
+
: [];
|
|
55
|
+
const outcomes = Object.fromEntries(g.components.filter((c) => input.outcomes?.[c] !== undefined).map((c) => [c, input.outcomes![c]!]));
|
|
56
|
+
return {
|
|
57
|
+
capability: g.capability,
|
|
58
|
+
components: [...g.components],
|
|
59
|
+
wave: waveOf.get(g.components[0]!) ?? 0,
|
|
60
|
+
...(g.digest !== undefined ? { digest: g.digest } : {}),
|
|
61
|
+
...(g.error !== undefined ? { error: g.error } : {}),
|
|
62
|
+
...(reads.length > 0 ? { reads } : {}),
|
|
63
|
+
...(Object.keys(outcomes).length > 0 ? { outcomes } : {}),
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
const fallbacks = input.grouped?.fallbacks ?? [];
|
|
67
|
+
return { ...(out.length > 0 ? { groups: out } : {}), ...(fallbacks.length > 0 ? { fallbacks: [...fallbacks] } : {}) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** `a`, `b` and `c`. */
|
|
71
|
+
const listed = (items: string[]): string => (items.length > 1 ? `${items.slice(0, -1).join(", ")} and ${items.at(-1)}` : (items[0] ?? ""));
|
|
72
|
+
|
|
73
|
+
const OUTCOME_WORDS: Record<GroupMemberStatus, string> = {
|
|
74
|
+
applied: "applied",
|
|
75
|
+
"no-changes": "had no changes",
|
|
76
|
+
partial: "applied in part, and what applied stays",
|
|
77
|
+
failed: "failed",
|
|
78
|
+
"not-started": "did not start",
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The report as lines for a person: one per group naming its members, wave,
|
|
83
|
+
* digest and the reads its apply computes, then how each member ended, and,
|
|
84
|
+
* when the apply stopped part way, what `again` (the command to run again)
|
|
85
|
+
* plans next. Then why the components that could have grouped did not; a
|
|
86
|
+
* fallback of kind `off` is not printed.
|
|
87
|
+
*/
|
|
88
|
+
export function renderRunGroups(report: RunGroupsReport, again: string): string[] {
|
|
89
|
+
const out: string[] = [];
|
|
90
|
+
for (const g of report.groups ?? []) {
|
|
91
|
+
const reads = (g.reads ?? []).map((r) => `${r.member} reads ${r.ref}`);
|
|
92
|
+
out.push(
|
|
93
|
+
`${listed(g.components)} plan and apply as one (${g.capability}, wave ${g.wave}${g.digest ? `, group ${g.digest}` : ""})` +
|
|
94
|
+
(g.error ? `; the group failed to plan, so nothing of it applied: ${g.error}` : reads.length > 0 ? `; ${listed(reads)}, known when the group applies` : ""),
|
|
95
|
+
);
|
|
96
|
+
const outcomes = g.outcomes ?? {};
|
|
97
|
+
const ran = g.components.filter((c) => outcomes[c] !== undefined);
|
|
98
|
+
if (ran.length === 0) continue;
|
|
99
|
+
out.push(` ${ran.map((c) => `${c} ${OUTCOME_WORDS[outcomes[c]!]}`).join("; ")}`);
|
|
100
|
+
const left = ran.filter((c) => outcomes[c] !== "applied" && outcomes[c] !== "no-changes");
|
|
101
|
+
if (left.length > 0) {
|
|
102
|
+
out.push(` the group's apply stopped part way and nothing that applied is undone; ${again} plans ${listed(left)} again, as one group where they still join`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const byWhy = new Map<string, string[]>();
|
|
106
|
+
for (const f of report.fallbacks ?? []) {
|
|
107
|
+
if (f.kind === "off") continue;
|
|
108
|
+
const why = `(${f.kind}): ${f.reason}`;
|
|
109
|
+
byWhy.set(why, [...(byWhy.get(why) ?? []), f.component]);
|
|
110
|
+
}
|
|
111
|
+
for (const [why, names] of byWhy) out.push(`planned one by one, not as a group: ${names.join(", ")} ${why}`);
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
wranglerDeployCapability,
|
|
48
48
|
wranglerVersionsPromoteCapability,
|
|
49
49
|
r2SyncCapability,
|
|
50
|
+
memberOutputsStarterCapability,
|
|
50
51
|
} from "./verbs/index";
|
|
51
52
|
import { ownPackageVersion, type CapabilityPlugin } from "./capability-plugin";
|
|
52
53
|
|
|
@@ -82,6 +83,10 @@ export const STARTER_VERB_FAMILIES = {
|
|
|
82
83
|
// there is no cloudflare lexicon for these three verbs to live in. Revisit
|
|
83
84
|
// if/when the zone-plane lexicon (#1294) lands.
|
|
84
85
|
cloudflare: ["wrangler-deploy", "wrangler-versions-promote", "r2-sync"],
|
|
86
|
+
// Another workspace member's outputs, read through the engine it runs on
|
|
87
|
+
// (#4455). One agnostic verb; each engine's lexicon contributes a source
|
|
88
|
+
// (`CapabilityPlugin.engineOutputSources`).
|
|
89
|
+
engineOutputs: ["member-outputs"],
|
|
85
90
|
} as const;
|
|
86
91
|
|
|
87
92
|
/**
|
|
@@ -112,6 +117,7 @@ function starterCapabilities(): Array<Capability<never, unknown>> {
|
|
|
112
117
|
wranglerDeployCapability,
|
|
113
118
|
wranglerVersionsPromoteCapability,
|
|
114
119
|
r2SyncCapability,
|
|
120
|
+
memberOutputsStarterCapability,
|
|
115
121
|
] as unknown as Array<Capability<never, unknown>>;
|
|
116
122
|
}
|
|
117
123
|
|