@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
|
@@ -23,8 +23,9 @@
|
|
|
23
23
|
* component in the project and not the selection.
|
|
24
24
|
* - the upstream is in the change and its plan cannot say, or says the value
|
|
25
25
|
* is known only after apply: no value. The dependent's members list the
|
|
26
|
-
* reference under `unknownInputs
|
|
27
|
-
*
|
|
26
|
+
* reference under `unknownInputs`. The apply plans it again once the
|
|
27
|
+
* upstream applied, and the approval covers that round when it matches the
|
|
28
|
+
* preview (see `coveredRound`).
|
|
28
29
|
*
|
|
29
30
|
* `--seed-outputs` wins over both. The apply stage's plan reads the same
|
|
30
31
|
* values, so on the same estate it gets the digest the plan stage showed.
|
|
@@ -66,14 +67,24 @@
|
|
|
66
67
|
* says whether the approved change set covers what it planned, by the same
|
|
67
68
|
* test a resume uses ({@link planCoveredBy}). Covered, the wave applies under
|
|
68
69
|
* the pull request's approval; not covered, it needs its own.
|
|
70
|
+
*
|
|
71
|
+
* ## Groups
|
|
72
|
+
*
|
|
73
|
+
* A fan-out with `groups` (ws-107, `./groups.ts`) plans each group with one
|
|
74
|
+
* `planGroup` call at its wave and applies it with one `runGroup` call
|
|
75
|
+
* there. A member's read of another member of its group is computed by that
|
|
76
|
+
* apply: it is no `unknownInputs` entry and holds nothing back. Each member's
|
|
77
|
+
* digest binds the group's, so the one approval binds the composition.
|
|
69
78
|
*/
|
|
70
79
|
import { composeChangeSet, verifyChangeSetDigest } from "../change-set.js";
|
|
71
80
|
import { evaluateGate } from "../op/gate.js";
|
|
72
81
|
import { memberCounts, PR_APPLY_GATE, prRoundGate, PR_REPORT_CONTRACT, PR_REPORT_SCHEMA_ID, } from "../pr-loop.js";
|
|
73
|
-
import { deployContext, movedWiringReferences, unresolvedWiringReferences, } from "./driver.js";
|
|
82
|
+
import { deployContext, movedWiringReferences, unresolvedWiringReferences, wiringReads, } from "./driver.js";
|
|
74
83
|
import { deployUnits } from "./deploy-units.js";
|
|
75
84
|
import { runFanOut } from "./fan-out-run.js";
|
|
76
85
|
import { remainingFanOut } from "./fan-out.js";
|
|
86
|
+
import { coveredRound } from "./covered-round.js";
|
|
87
|
+
import { groupsByComponent, narrowGroups, planComponentGroup, regroupRemaining } from "./groups.js";
|
|
77
88
|
import { capabilitySteps, planWaveComponent, readDeployedOutputs } from "./wave-plan.js";
|
|
78
89
|
/** The phase a pull request's planning is, in its run's events. */
|
|
79
90
|
export const PR_PLAN_PHASE = "plan";
|
|
@@ -105,17 +116,24 @@ export async function planPrSet(options) {
|
|
|
105
116
|
const parts = [];
|
|
106
117
|
const componentOf = new Map();
|
|
107
118
|
const unknownOf = new Map();
|
|
119
|
+
const readsBy = new Map();
|
|
108
120
|
const plans = {};
|
|
121
|
+
const shapes = {};
|
|
109
122
|
let failed = false;
|
|
110
123
|
const emit = options.onRunEvent;
|
|
111
124
|
emit?.({ type: "phase-start", component: null, phase: PR_PLAN_PHASE });
|
|
125
|
+
// ws-107: each group plans once, in its wave, with one call.
|
|
126
|
+
const groupOf = groupsByComponent(options.plan);
|
|
127
|
+
const groups = [];
|
|
112
128
|
for (const wave of options.plan.waves) {
|
|
113
129
|
// The dependencies' outputs first, each once. One the change plans is
|
|
114
130
|
// read from its plan, which an earlier wave made (#3999). Any other is
|
|
115
131
|
// read from the deployed estate (#4104). A dependency that cannot say
|
|
116
132
|
// leaves its references unresolved, which its dependent's plan reports
|
|
117
|
-
// if it needs them.
|
|
118
|
-
const deps = [
|
|
133
|
+
// if it needs them. A member of the dependent's own group plans with it.
|
|
134
|
+
const deps = [
|
|
135
|
+
...new Set(wave.flatMap((name) => (byName.get(name)?.dependsOn ?? []).filter((dep) => !groupOf.get(name)?.components.includes(dep)))),
|
|
136
|
+
].filter((dep) => !read.has(dep));
|
|
119
137
|
const fromEstate = [];
|
|
120
138
|
for (const dep of deps) {
|
|
121
139
|
read.add(dep);
|
|
@@ -130,25 +148,62 @@ export async function planPrSet(options) {
|
|
|
130
148
|
planned[dep] = Object.keys(known).sort();
|
|
131
149
|
}
|
|
132
150
|
Object.assign(deployed, await readDeployedOutputs(fromEstate, byName, options.registry, (dep) => deployContext(options, dep), outputs));
|
|
133
|
-
const
|
|
134
|
-
const at = { component: name, phase: PR_PLAN_PHASE, step: "plan" };
|
|
135
|
-
emit?.({ type: "step-start", ...at });
|
|
136
|
-
const start = Date.now();
|
|
137
|
-
const p = await planWaveComponent(byName.get(name), options.registry, deployContext(options, name), outputs);
|
|
151
|
+
const settle = (name, start, p) => {
|
|
138
152
|
emit?.({
|
|
139
153
|
type: "step-settle",
|
|
140
|
-
|
|
154
|
+
component: name,
|
|
155
|
+
phase: PR_PLAN_PHASE,
|
|
156
|
+
step: "plan",
|
|
141
157
|
status: "error" in p ? "failed" : "ok",
|
|
142
158
|
durationMs: Date.now() - start,
|
|
143
159
|
...("error" in p ? { error: p.error } : {}),
|
|
144
160
|
});
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
};
|
|
162
|
+
const groupsHere = [...new Set(wave.map((name) => groupOf.get(name)).filter((g) => g !== undefined))];
|
|
163
|
+
const results = (await Promise.all([
|
|
164
|
+
...wave
|
|
165
|
+
.filter((name) => !groupOf.has(name))
|
|
166
|
+
.map(async (name) => {
|
|
167
|
+
emit?.({ type: "step-start", component: name, phase: PR_PLAN_PHASE, step: "plan" });
|
|
168
|
+
const start = Date.now();
|
|
169
|
+
const p = await planWaveComponent(byName.get(name), options.registry, deployContext(options, name), outputs);
|
|
170
|
+
settle(name, start, p);
|
|
171
|
+
return [p];
|
|
172
|
+
}),
|
|
173
|
+
...groupsHere.map(async (group) => {
|
|
174
|
+
for (const name of group.components)
|
|
175
|
+
emit?.({ type: "step-start", component: name, phase: PR_PLAN_PHASE, step: "plan" });
|
|
176
|
+
const start = Date.now();
|
|
177
|
+
const g = await planComponentGroup({
|
|
178
|
+
group,
|
|
179
|
+
components: byName,
|
|
180
|
+
registry: options.registry,
|
|
181
|
+
env: options.env,
|
|
182
|
+
...(options.vars ? { vars: options.vars } : {}),
|
|
183
|
+
ctxOf: (name) => deployContext(options, name),
|
|
184
|
+
outputs,
|
|
185
|
+
});
|
|
186
|
+
for (const p of g.plans)
|
|
187
|
+
settle(p.component, start, p);
|
|
188
|
+
const error = g.plans.find((p) => "error" in p)?.error;
|
|
189
|
+
groups.push({ ...group, digest: g.digest ?? null, ...(error !== undefined ? { error } : {}), ...(g.run ? { run: g.run } : {}) });
|
|
190
|
+
return g.plans;
|
|
191
|
+
}),
|
|
192
|
+
])).flat();
|
|
193
|
+
/** What each member of a group planned to expose, for its group-mates' reads. */
|
|
194
|
+
const plannedHere = new Map(results.map((p) => [p.component, "error" in p ? undefined : p.outputs?.known]));
|
|
147
195
|
for (const p of results) {
|
|
148
196
|
const component = byName.get(p.component);
|
|
149
|
-
const
|
|
197
|
+
const group = groupOf.get(p.component);
|
|
198
|
+
const mates = group ? new Set(group.components) : undefined;
|
|
199
|
+
const unknown = unknownInputsOf(component, outputs, inChange, mates);
|
|
150
200
|
if (unknown.unknown.length > 0 || unknown.build.length > 0)
|
|
151
201
|
unknownOf.set(p.component, unknown);
|
|
202
|
+
// A read of a group-mate is known when that mate's plan knows it.
|
|
203
|
+
const readsView = group ? { ...outputs, ...Object.fromEntries(group.components.filter((c) => c !== p.component).map((c) => [c, { ...(plannedHere.get(c) ?? {}) }])) } : outputs;
|
|
204
|
+
const reads = readsOf(component, readsView);
|
|
205
|
+
if (reads.length > 0)
|
|
206
|
+
readsBy.set(p.component, reads);
|
|
152
207
|
if ("error" in p) {
|
|
153
208
|
failed = true;
|
|
154
209
|
plannedBy.set(p.component, undefined);
|
|
@@ -160,6 +215,7 @@ export async function planPrSet(options) {
|
|
|
160
215
|
}
|
|
161
216
|
plannedBy.set(p.component, p.outputs);
|
|
162
217
|
plans[p.component] = p.plans;
|
|
218
|
+
Object.assign(shapes, p.shapes);
|
|
163
219
|
for (const part of p.parts) {
|
|
164
220
|
parts.push(part);
|
|
165
221
|
componentOf.set(part.member.member, p.component);
|
|
@@ -171,6 +227,7 @@ export async function planPrSet(options) {
|
|
|
171
227
|
const members = doc.members.map((m) => {
|
|
172
228
|
const component = componentOf.get(m.member) ?? m.member;
|
|
173
229
|
const unknown = unknownOf.get(component);
|
|
230
|
+
const reads = readsBy.get(component);
|
|
174
231
|
return {
|
|
175
232
|
member: m.member,
|
|
176
233
|
component,
|
|
@@ -180,17 +237,23 @@ export async function planPrSet(options) {
|
|
|
180
237
|
...(m.error ? { error: m.error } : {}),
|
|
181
238
|
...(unknown?.unknown.length ? { unknownInputs: unknown.unknown } : {}),
|
|
182
239
|
...(unknown?.build.length ? { buildInputs: unknown.build } : {}),
|
|
240
|
+
...(reads ? { reads } : {}),
|
|
183
241
|
};
|
|
184
242
|
});
|
|
185
243
|
const sorted = (r) => Object.fromEntries(Object.entries(r).sort(([a], [b]) => a.localeCompare(b)));
|
|
186
244
|
const deployedOutputs = sorted(deployed);
|
|
187
245
|
const plannedOutputs = sorted(planned);
|
|
246
|
+
const at = new Map(options.plan.order.map((name, i) => [name, i]));
|
|
247
|
+
groups.sort((a, b) => (at.get(a.components[0]) ?? 0) - (at.get(b.components[0]) ?? 0));
|
|
188
248
|
return {
|
|
189
249
|
doc,
|
|
190
250
|
members,
|
|
191
251
|
plans,
|
|
192
252
|
outputs,
|
|
193
253
|
failed,
|
|
254
|
+
...(groups.length > 0 ? { groups } : {}),
|
|
255
|
+
...(options.plan.fallbacks?.length ? { fallbacks: options.plan.fallbacks } : {}),
|
|
256
|
+
...(Object.keys(shapes).length > 0 ? { shapes } : {}),
|
|
194
257
|
...(Object.keys(deployedOutputs).length > 0 ? { deployedOutputs } : {}),
|
|
195
258
|
...(Object.keys(plannedOutputs).length > 0 ? { plannedOutputs } : {}),
|
|
196
259
|
};
|
|
@@ -201,33 +264,64 @@ export async function planPrSet(options) {
|
|
|
201
264
|
* that upstream applies, each sorted. `build` are the published artifact
|
|
202
265
|
* identities a build in the change produces (`WiringReference.publish`):
|
|
203
266
|
* the build's provenance binds those, so they are never approved again.
|
|
204
|
-
* `unknown` are the rest.
|
|
267
|
+
* `unknown` are the rest. A reference to a member of `group`, the
|
|
268
|
+
* component's own group (ws-107), is neither: the group's apply computes it.
|
|
205
269
|
*/
|
|
206
|
-
function unknownInputsOf(component, outputs, inChange) {
|
|
270
|
+
function unknownInputsOf(component, outputs, inChange, group) {
|
|
207
271
|
const unknown = new Set();
|
|
208
272
|
const build = new Set();
|
|
209
273
|
for (const step of capabilitySteps(component.deploy)) {
|
|
210
274
|
const { kind: _kind, ...rest } = step;
|
|
211
275
|
for (const r of unresolvedWiringReferences(rest, outputs)) {
|
|
276
|
+
if (group?.has(r.source))
|
|
277
|
+
continue;
|
|
212
278
|
if (inChange.has(r.source))
|
|
213
279
|
(r.publish ? build : unknown).add(r.ref);
|
|
214
280
|
}
|
|
215
281
|
}
|
|
216
282
|
return { unknown: [...unknown].sort(), build: [...build].sort() };
|
|
217
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Every other component's output `component`'s steps read, one entry per
|
|
286
|
+
* upstream, sorted: what the report's `reads` says. A read whose value
|
|
287
|
+
* `outputs` holds is listed as well as one it lacks (`unknown`), so the
|
|
288
|
+
* report names each read between components whether or not the plan knew
|
|
289
|
+
* its value. A component's reads of its own outputs are left out.
|
|
290
|
+
*/
|
|
291
|
+
function readsOf(component, outputs) {
|
|
292
|
+
const by = new Map();
|
|
293
|
+
for (const step of capabilitySteps(component.deploy)) {
|
|
294
|
+
const { kind: _kind, ...rest } = step;
|
|
295
|
+
for (const r of wiringReads(rest, outputs)) {
|
|
296
|
+
if (r.source === component.name)
|
|
297
|
+
continue;
|
|
298
|
+
const name = r.ref.slice(r.source.length + 1);
|
|
299
|
+
const at = by.get(r.source) ?? { outputs: new Set(), unknown: new Set() };
|
|
300
|
+
by.set(r.source, at);
|
|
301
|
+
at.outputs.add(name);
|
|
302
|
+
if (!r.known)
|
|
303
|
+
at.unknown.add(name);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return [...by.entries()]
|
|
307
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
308
|
+
.map(([upstream, r]) => ({ upstream, outputs: [...r.outputs].sort(), ...(r.unknown.size > 0 ? { unknown: [...r.unknown].sort() } : {}) }));
|
|
309
|
+
}
|
|
218
310
|
/**
|
|
219
311
|
* Why `component` cannot apply the plan that read `read` now that `now`
|
|
220
312
|
* stands (#3999): each reference its steps make that resolves to another
|
|
221
313
|
* value, said once. `undefined` when every one resolves as it did. A
|
|
222
314
|
* published artifact's identity from a build in `inChange` never holds a
|
|
223
|
-
* member back: the build's provenance binds it, not this approval.
|
|
315
|
+
* member back: the build's provenance binds it, not this approval. Nor
|
|
316
|
+
* does a reference to a member of `group`, the component's own group
|
|
317
|
+
* (ws-107), whose apply computes it.
|
|
224
318
|
*/
|
|
225
|
-
export function heldReason(component, read, now, inChange = new Set()) {
|
|
319
|
+
export function heldReason(component, read, now, inChange = new Set(), group = new Set()) {
|
|
226
320
|
const refs = new Set();
|
|
227
321
|
for (const step of capabilitySteps(component.deploy)) {
|
|
228
322
|
const { kind: _kind, ...rest } = step;
|
|
229
323
|
for (const r of movedWiringReferences(rest, read, now))
|
|
230
|
-
if (!(r.publish && inChange.has(r.source)))
|
|
324
|
+
if (!(r.publish && inChange.has(r.source)) && !group.has(r.source))
|
|
231
325
|
refs.add(r.ref);
|
|
232
326
|
}
|
|
233
327
|
if (refs.size === 0)
|
|
@@ -289,6 +383,97 @@ export function approversWhoAuthored(approvedBy, authors) {
|
|
|
289
383
|
const wrote = new Set(authors.map(norm));
|
|
290
384
|
return approvedBy.filter((by) => wrote.has(norm(by)));
|
|
291
385
|
}
|
|
386
|
+
/**
|
|
387
|
+
* Whether the first approval covers a round's fresh plan (#4419). The
|
|
388
|
+
* approval binds `options.set.doc.digest`; the round's components and
|
|
389
|
+
* members must be ones that plan previewed, and what differs is only what
|
|
390
|
+
* it marked unknown.
|
|
391
|
+
*/
|
|
392
|
+
function roundCover(options, fresh, plan, now, inChange, byName) {
|
|
393
|
+
if (options.approval?.status !== "approved")
|
|
394
|
+
return { covered: false, reason: "no approval of the first plan stands" };
|
|
395
|
+
// ws-107: a round's group is covered only when it has the approved group's members.
|
|
396
|
+
const moved = groupsMoved(options.set.groups ?? [], fresh.groups ?? [], plan.order);
|
|
397
|
+
if (moved)
|
|
398
|
+
return { covered: false, reason: moved };
|
|
399
|
+
// The values the round reads of a component it plans again are the ones
|
|
400
|
+
// that plan says it will expose: it has not applied yet, so the outputs
|
|
401
|
+
// standing say nothing of it. An output the first plan knew and the round's
|
|
402
|
+
// plan knows as the same value is not a changed input, whichever of the
|
|
403
|
+
// two applies first.
|
|
404
|
+
const reads = { ...now };
|
|
405
|
+
for (const name of plan.order)
|
|
406
|
+
if (fresh.outputs[name])
|
|
407
|
+
reads[name] = fresh.outputs[name];
|
|
408
|
+
const changedKnown = {};
|
|
409
|
+
for (const name of plan.order) {
|
|
410
|
+
const component = byName.get(name);
|
|
411
|
+
if (!component)
|
|
412
|
+
continue;
|
|
413
|
+
const refs = new Set();
|
|
414
|
+
for (const step of capabilitySteps(component.deploy)) {
|
|
415
|
+
const { kind: _kind, ...rest } = step;
|
|
416
|
+
for (const r of movedWiringReferences(rest, options.set.outputs, reads))
|
|
417
|
+
if (!(r.publish && inChange.has(r.source)))
|
|
418
|
+
refs.add(r.ref);
|
|
419
|
+
}
|
|
420
|
+
changedKnown[name] = [...refs].sort();
|
|
421
|
+
}
|
|
422
|
+
// A member whose round plan still reads only outputs of components this
|
|
423
|
+
// round plans and has not applied (a chain of imports, #4466d) cannot apply
|
|
424
|
+
// in this round: it is held again once those apply, and its own round
|
|
425
|
+
// checks its coverage. Only the members this round can apply are checked.
|
|
426
|
+
const inRound = plan.order;
|
|
427
|
+
const fromRound = (ref) => inRound.some((c) => ref === c || ref.startsWith(`${c}.`));
|
|
428
|
+
const waits = (m) => (m.unknownInputs?.length ?? 0) > 0 && m.unknownInputs.every(fromRound);
|
|
429
|
+
const approved = { doc: options.set.doc, members: options.set.members, ...(options.set.shapes ? { shapes: options.set.shapes } : {}) };
|
|
430
|
+
return coveredRound({
|
|
431
|
+
approved,
|
|
432
|
+
approvedDigest: options.set.doc.digest,
|
|
433
|
+
fresh: { doc: fresh.doc, members: fresh.members.filter((m) => !waits(m)), ...(fresh.shapes ? { shapes: fresh.shapes } : {}) },
|
|
434
|
+
changedKnown,
|
|
435
|
+
attributes: (member) => memberAttributes(options.set.doc, fresh.doc, member, fresh.shapes?.[member] !== undefined),
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Why the groups `fresh` planned are not the `approved` ones over the
|
|
440
|
+
* components in `order` (ws-107), or `undefined` when each group the round
|
|
441
|
+
* plans has the members the approved group had.
|
|
442
|
+
*/
|
|
443
|
+
function groupsMoved(approved, fresh, order) {
|
|
444
|
+
const same = (a, b) => a.length === b.length && a.every((c) => b.includes(c));
|
|
445
|
+
const inRound = new Set(order);
|
|
446
|
+
for (const was of approved) {
|
|
447
|
+
const here = was.components.filter((c) => inRound.has(c));
|
|
448
|
+
if (here.length === 0)
|
|
449
|
+
continue;
|
|
450
|
+
const now = fresh.find((g) => g.components.includes(here[0]));
|
|
451
|
+
if (!now || !same(now.components, was.components)) {
|
|
452
|
+
return `the approved plan grouped ${was.components.join(", ")}, and the round ${now ? `groups ${now.components.join(", ")}` : `plans ${here.join(", ")} without that group`}`;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
for (const now of fresh) {
|
|
456
|
+
if (!approved.some((was) => same(was.components, now.components)))
|
|
457
|
+
return `the round groups ${now.components.join(", ")}, which the approved plan did not`;
|
|
458
|
+
}
|
|
459
|
+
return undefined;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* The part of coverage that reads the change set's own attributes, shared by
|
|
463
|
+
* a round, a resume and a wave: a member whose planner describes no changes
|
|
464
|
+
* (and gave no shape) is covered only by the digest it was approved with, and
|
|
465
|
+
* anything else is {@link planCoveredBy}'s.
|
|
466
|
+
*/
|
|
467
|
+
function memberAttributes(approved, fresh, member, hasShape) {
|
|
468
|
+
if (!hasShape) {
|
|
469
|
+
const now = fresh.members.find((m) => m.member === member);
|
|
470
|
+
const was = approved.members.find((m) => m.member === member);
|
|
471
|
+
if (now && was && now.planner === "chant" && now.lexicon === "chant" && now.status === "planned" && now.planDigest !== was.planDigest) {
|
|
472
|
+
return `${member} now plans to ${now.planDigest}, not the approved ${was.planDigest ?? "(none)"}, and its planner describes no changes to compare`;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return planCoveredBy(approved, fresh, member);
|
|
476
|
+
}
|
|
292
477
|
/** Two runs of one apply as one: `b` ran after `a`, against the outputs `a` left. */
|
|
293
478
|
function joinRuns(a, b) {
|
|
294
479
|
const status = a.status === "fail" || b.status === "fail" ? "fail" : a.status === "gated" || b.status === "gated" ? "gated" : "ok";
|
|
@@ -303,6 +488,8 @@ function joinRuns(a, b) {
|
|
|
303
488
|
componentOutputs: b.componentOutputs,
|
|
304
489
|
...(gate ? { gate } : {}),
|
|
305
490
|
...(b.held ? { held: b.held } : {}),
|
|
491
|
+
...(a.notStarted || b.notStarted ? { notStarted: [...new Set([...(a.notStarted ?? []), ...(b.notStarted ?? [])])].sort() } : {}),
|
|
492
|
+
...(a.groupOutcomes || b.groupOutcomes ? { groupOutcomes: { ...(a.groupOutcomes ?? {}), ...(b.groupOutcomes ?? {}) } } : {}),
|
|
306
493
|
};
|
|
307
494
|
}
|
|
308
495
|
/**
|
|
@@ -325,18 +512,24 @@ export async function applyPrSet(options) {
|
|
|
325
512
|
...Object.fromEntries(Object.entries(options.set.outputs).filter(([name]) => !toApply.has(name))),
|
|
326
513
|
...(options.resumed?.outputs ?? {}),
|
|
327
514
|
};
|
|
328
|
-
const runRound = (plan,
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
515
|
+
const runRound = (plan, set, read, outputs) => {
|
|
516
|
+
// ws-107: each group runs the plan its group planned, and a read of a group-mate holds nothing back.
|
|
517
|
+
const groupOf = groupsByComponent(plan);
|
|
518
|
+
const groupRuns = Object.fromEntries((set.groups ?? []).filter((g) => g.run).map((g) => [g.components[0], g.run]));
|
|
519
|
+
return runFanOut(plan, options.components, options.registry, {
|
|
520
|
+
env: options.env,
|
|
521
|
+
...(options.vars ? { vars: options.vars } : {}),
|
|
522
|
+
plans: set.plans,
|
|
523
|
+
...(Object.keys(groupRuns).length > 0 ? { groupRuns } : {}),
|
|
524
|
+
...(options.targets ? { targets: options.targets } : {}),
|
|
525
|
+
componentOutputs: outputs,
|
|
526
|
+
hold: (name, now) => heldReason(byName.get(name), read, now, inChange, new Set(groupOf.get(name)?.components ?? [])),
|
|
527
|
+
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
528
|
+
...(options.onRunEvent ? { onRunEvent: options.onRunEvent } : {}),
|
|
529
|
+
...(options.onComponentSettled ? { onComponentSettled: options.onComponentSettled } : {}),
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
let run = await runRound(options.plan, options.set, options.set.outputs, { ...standing });
|
|
340
533
|
const rounds = [];
|
|
341
534
|
/** Per component, the round that applied it or holds it, and that round's set, after the first. */
|
|
342
535
|
const roundOf = new Map();
|
|
@@ -345,7 +538,8 @@ export async function applyPrSet(options) {
|
|
|
345
538
|
const round = rounds.length + 2;
|
|
346
539
|
const gate = prRoundGate(options.gate ?? PR_APPLY_GATE, round);
|
|
347
540
|
const heldNames = new Set(run.held.map((h) => h.component));
|
|
348
|
-
|
|
541
|
+
// A held group is planned again as the same group (ws-107).
|
|
542
|
+
const plan = narrowGroups(remainingFanOut(options.plan, options.components, { completed: options.plan.order.filter((name) => !heldNames.has(name)) }), options.components, options.plan.groups);
|
|
349
543
|
const set = await planPrSet({
|
|
350
544
|
components: options.components,
|
|
351
545
|
plan,
|
|
@@ -357,41 +551,63 @@ export async function applyPrSet(options) {
|
|
|
357
551
|
});
|
|
358
552
|
for (const name of plan.order)
|
|
359
553
|
roundOf.set(name, { round, set });
|
|
360
|
-
const
|
|
361
|
-
|
|
554
|
+
const cover = roundCover(options, set, plan, run.componentOutputs, inChange, byName);
|
|
555
|
+
const record = (status, approval, message, covered) => {
|
|
556
|
+
rounds.push({
|
|
557
|
+
round,
|
|
558
|
+
gate,
|
|
559
|
+
digest: set.doc.digest,
|
|
560
|
+
components: plan.order,
|
|
561
|
+
approval,
|
|
562
|
+
status,
|
|
563
|
+
...(message ? { message } : {}),
|
|
564
|
+
...(covered ? { covered } : {}),
|
|
565
|
+
...(!covered && options.approval?.status === "approved" && cover.covered === false ? { notCovered: cover.reason } : {}),
|
|
566
|
+
changeSet: set.doc,
|
|
567
|
+
});
|
|
362
568
|
};
|
|
363
569
|
if (set.failed) {
|
|
364
570
|
record("plan-failed", { status: "pending" });
|
|
365
571
|
stopped = "plan-failed";
|
|
366
572
|
break;
|
|
367
573
|
}
|
|
368
|
-
const decision =
|
|
574
|
+
const decision = cover.covered && options.approval
|
|
575
|
+
? { satisfied: true, approval: options.approval, covered: { digest: cover.digest, filledInputs: cover.filledInputs } }
|
|
576
|
+
: await options.decideRound({ round, gate, set, plan, ...(options.approval?.status === "approved" && !cover.covered ? { notCovered: cover.reason } : {}) });
|
|
369
577
|
if (!decision.satisfied) {
|
|
370
578
|
record("waiting", decision.approval, decision.message);
|
|
371
579
|
stopped = "waiting";
|
|
372
580
|
break;
|
|
373
581
|
}
|
|
374
|
-
const next = await runRound(plan, set
|
|
375
|
-
record(next.status === "fail" ? "failed" : "applied", decision.approval);
|
|
582
|
+
const next = await runRound(plan, set, set.outputs, run.componentOutputs);
|
|
583
|
+
record(next.status === "fail" ? "failed" : "applied", decision.approval, undefined, decision.covered);
|
|
376
584
|
run = joinRuns(run, next);
|
|
377
585
|
}
|
|
378
586
|
const completed = new Set(run.completed);
|
|
587
|
+
const notStarted = new Set(run.notStarted ?? []);
|
|
379
588
|
const held = new Map((run.held ?? []).map((h) => [h.component, h]));
|
|
380
589
|
const blocked = new Map(run.blocked.map((b) => [b.component, b.blockedBy]));
|
|
381
590
|
const errorOf = (component) => {
|
|
382
591
|
const result = [...run.results].reverse().find((r) => r.component === component);
|
|
383
592
|
if (result?.status === "gated")
|
|
384
593
|
return `stopped at its own gate ${result.gate?.gate ?? ""}`.trim();
|
|
385
|
-
return result?.records.find((r) => r.status === "fail")?.error;
|
|
594
|
+
return result?.records.find((r) => r.status === "fail" || (notStarted.has(component) && r.status === "skipped"))?.error;
|
|
386
595
|
};
|
|
387
596
|
const members = options.set.members.map((m) => {
|
|
388
597
|
if (priorCompleted.has(m.component))
|
|
389
598
|
return { ...m, status: "applied" };
|
|
390
599
|
const later = roundOf.get(m.component);
|
|
391
600
|
const fresh = later?.set.members.find((x) => x.member === m.member);
|
|
392
|
-
const
|
|
601
|
+
const planned = later && fresh ? { ...m, planDigest: fresh.planDigest, counts: fresh.counts, round: later.round } : m;
|
|
602
|
+
// #4446: how its part of a group's apply ended, as the capability said.
|
|
603
|
+
const groupOutcome = run.groupOutcomes?.[m.component];
|
|
604
|
+
const asPlanned = groupOutcome ? { ...planned, groupOutcome } : planned;
|
|
393
605
|
if (completed.has(m.component))
|
|
394
606
|
return { ...asPlanned, status: "applied" };
|
|
607
|
+
if (notStarted.has(m.component)) {
|
|
608
|
+
const error = errorOf(m.component);
|
|
609
|
+
return { ...asPlanned, status: "not-started", ...(error ? { error } : {}) };
|
|
610
|
+
}
|
|
395
611
|
const h = held.get(m.component);
|
|
396
612
|
if (h)
|
|
397
613
|
return { ...asPlanned, status: "waiting", held: h.heldBy ? `${h.heldBy} is held: ${h.reason}` : h.reason };
|
|
@@ -506,25 +722,46 @@ export function planCoveredBy(approved, fresh, member) {
|
|
|
506
722
|
* is `record.digest`, and the document gives that digest. The digest binds
|
|
507
723
|
* the entries, holes and side effects {@link planCoveredBy} reads (#3555),
|
|
508
724
|
* so a record cannot widen what a re-planned member may do.
|
|
725
|
+
*
|
|
726
|
+
* A group that failed part way (ws-107, "Resume"; #4446) leaves its
|
|
727
|
+
* `partial`, `failed` and `not-started` members; `applied` and `no-changes`
|
|
728
|
+
* ones are in `completed`. What is left of each approved group is grouped
|
|
729
|
+
* again ({@link regroupRemaining}), so it plans as a smaller group, or one
|
|
730
|
+
* by one when the rule or the capability says so. The smaller group's
|
|
731
|
+
* digest differs from the approved one, so coverage rests on entries and
|
|
732
|
+
* shapes, as for any resumed member.
|
|
509
733
|
*/
|
|
510
734
|
export async function resumePrSet(options) {
|
|
511
735
|
const { record } = options;
|
|
512
736
|
if (record.changeSet.digest !== record.digest || !verifyChangeSetDigest(record.changeSet)) {
|
|
513
737
|
return { ok: false, reasons: [`the record's change set is not the one its digest ${record.digest} names`] };
|
|
514
738
|
}
|
|
515
|
-
const plan =
|
|
739
|
+
const plan = await regroupRemaining({
|
|
740
|
+
components: options.components,
|
|
741
|
+
plan: remainingFanOut(options.plan, options.components, { completed: options.completed }),
|
|
742
|
+
registry: options.registry,
|
|
743
|
+
env: options.env,
|
|
744
|
+
...(options.vars ? { vars: options.vars } : {}),
|
|
745
|
+
groups: options.plan.groups,
|
|
746
|
+
...(options.plan.fallbacks ? { fallbacks: options.plan.fallbacks } : {}),
|
|
747
|
+
});
|
|
748
|
+
// The outputs of the applied members of a group with members left: the values the group's apply computed.
|
|
749
|
+
const left = new Set(plan.order);
|
|
750
|
+
const fromGroup = Object.fromEntries((options.plan.groups ?? [])
|
|
751
|
+
.filter((g) => g.components.some((c) => left.has(c)))
|
|
752
|
+
.flatMap((g) => g.components.filter((c) => !left.has(c) && options.applied?.[c] !== undefined))
|
|
753
|
+
.map((c) => [c, options.applied[c]]));
|
|
516
754
|
const fresh = await planPrSet({
|
|
517
755
|
components: options.components,
|
|
518
756
|
plan,
|
|
519
757
|
registry: options.registry,
|
|
520
758
|
env: options.env,
|
|
521
759
|
...(options.vars ? { vars: options.vars } : {}),
|
|
522
|
-
seededOutputs: { ...(options.seededOutputs ?? {}), ...options.record.planOutputs },
|
|
760
|
+
seededOutputs: { ...(options.seededOutputs ?? {}), ...options.record.planOutputs, ...fromGroup },
|
|
523
761
|
...(options.targets ? { targets: options.targets } : {}),
|
|
524
762
|
});
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
.filter((r) => r !== undefined);
|
|
763
|
+
// #4422: decided as a round decides (`coveredRound`), by the same function a wave uses.
|
|
764
|
+
const reasons = waveNotCovered(options.record.changeSet, fresh.doc, options.record.members);
|
|
528
765
|
if (reasons.length > 0)
|
|
529
766
|
return { ok: false, reasons, fresh };
|
|
530
767
|
return {
|
|
@@ -535,7 +772,9 @@ export async function resumePrSet(options) {
|
|
|
535
772
|
doc: options.record.changeSet,
|
|
536
773
|
members: options.record.members.map((m) => ({ ...m, status: "planned" })),
|
|
537
774
|
plans: fresh.plans,
|
|
538
|
-
outputs: options.record.planOutputs,
|
|
775
|
+
outputs: { ...options.record.planOutputs, ...fromGroup },
|
|
776
|
+
...(fresh.groups ? { groups: fresh.groups } : {}),
|
|
777
|
+
...(fresh.fallbacks ? { fallbacks: fresh.fallbacks } : {}),
|
|
539
778
|
failed: false,
|
|
540
779
|
},
|
|
541
780
|
};
|
|
@@ -554,7 +793,17 @@ export function prWavePlan(plan, components, wave, completed = []) {
|
|
|
554
793
|
const seeds = [
|
|
555
794
|
...new Set([...plan.seeds, ...order.flatMap((name) => (byName.get(name)?.dependsOn ?? []).filter((dep) => !inWave.has(dep)))]),
|
|
556
795
|
].sort();
|
|
557
|
-
|
|
796
|
+
// ws-107: a group sits in one wave, so what is left of it plans and applies as one there.
|
|
797
|
+
const groups = (plan.groups ?? []).map((g) => ({ ...g, components: g.components.filter((c) => inWave.has(c)) })).filter((g) => g.components.length > 1);
|
|
798
|
+
return {
|
|
799
|
+
order,
|
|
800
|
+
waves: order.length > 0 ? [order] : [],
|
|
801
|
+
skipped: [],
|
|
802
|
+
seeds,
|
|
803
|
+
indeterminate: plan.indeterminate,
|
|
804
|
+
digest: plan.digest,
|
|
805
|
+
...(groups.length > 0 ? { groups } : {}),
|
|
806
|
+
};
|
|
558
807
|
}
|
|
559
808
|
/**
|
|
560
809
|
* Why the approved change set does not cover a wave's new plan, one line per
|
|
@@ -564,27 +813,77 @@ export function prWavePlan(plan, components, wave, completed = []) {
|
|
|
564
813
|
* returns no change set) is covered only by the digest it was approved
|
|
565
814
|
* with, since with no entries there is nothing to compare a moved plan by.
|
|
566
815
|
*/
|
|
567
|
-
export function waveNotCovered(approved, fresh, approvedMembers = []) {
|
|
816
|
+
export function waveNotCovered(approved, fresh, approvedMembers = [], shapes = {}) {
|
|
568
817
|
// #3999: a member whose approved plan waited only on an artifact a build in
|
|
569
818
|
// the change publishes is covered by that build's provenance, as it is
|
|
570
819
|
// without --wave-gate.
|
|
571
820
|
const byBuild = new Set(approvedMembers.filter((m) => m.buildInputs?.length && !m.unknownInputs?.length).map((m) => m.member));
|
|
821
|
+
const prMember = new Map(approvedMembers.map((m) => [m.member, m]));
|
|
822
|
+
// #4422: the same decision a round makes (`coveredRound`), so a resume, a
|
|
823
|
+
// wave and a round agree for equivalent inputs. A member the document names
|
|
824
|
+
// and the caller does not stands for a component of its own name.
|
|
825
|
+
const approvedSide = {
|
|
826
|
+
doc: approved,
|
|
827
|
+
members: approved.members.map((m) => ({
|
|
828
|
+
member: m.member,
|
|
829
|
+
component: prMember.get(m.member)?.component ?? m.member,
|
|
830
|
+
...(prMember.get(m.member)?.unknownInputs?.length ? { unknownInputs: prMember.get(m.member).unknownInputs } : {}),
|
|
831
|
+
})),
|
|
832
|
+
...(shapes.approved && shapes.fresh ? { shapes: shapes.approved } : {}),
|
|
833
|
+
};
|
|
572
834
|
return fresh.members
|
|
573
835
|
.map((m) => {
|
|
574
836
|
if (byBuild.has(m.member))
|
|
575
837
|
return undefined;
|
|
576
838
|
const was = approved.members.find((a) => a.member === m.member);
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
839
|
+
if (!was)
|
|
840
|
+
return planCoveredBy(approved, fresh, m.member);
|
|
841
|
+
if (was.planDigest !== null && m.planDigest === was.planDigest)
|
|
842
|
+
return undefined;
|
|
843
|
+
const hasShape = shapes.approved !== undefined && shapes.fresh?.[m.member] !== undefined;
|
|
844
|
+
const cover = coveredRound({
|
|
845
|
+
approved: approvedSide,
|
|
846
|
+
approvedDigest: approved.digest,
|
|
847
|
+
fresh: {
|
|
848
|
+
doc: fresh,
|
|
849
|
+
members: [{ member: m.member, component: prMember.get(m.member)?.component ?? m.member }],
|
|
850
|
+
...(hasShape ? { shapes: shapes.fresh } : {}),
|
|
851
|
+
},
|
|
852
|
+
attributes: (member) => memberAttributes(approved, fresh, member, hasShape),
|
|
853
|
+
});
|
|
854
|
+
return cover.covered ? undefined : cover.reason;
|
|
582
855
|
})
|
|
583
856
|
.filter((r) => r !== undefined);
|
|
584
857
|
}
|
|
858
|
+
/**
|
|
859
|
+
* The report's `groups` (#4446): each group the set planned, with the wave
|
|
860
|
+
* of `waves` its members sit in and the reads of group-mates its members'
|
|
861
|
+
* change-set parts name, which the group's apply computes.
|
|
862
|
+
*/
|
|
863
|
+
function reportGroups(set, waves) {
|
|
864
|
+
const componentOf = new Map(set.members.map((m) => [m.member, m.component]));
|
|
865
|
+
return (set.groups ?? []).map((g) => {
|
|
866
|
+
const wave = waves.findIndex((w) => g.components.some((c) => w.includes(c))) + 1;
|
|
867
|
+
const reads = set.doc.members
|
|
868
|
+
.filter((m) => g.components.includes(componentOf.get(m.member) ?? m.member))
|
|
869
|
+
.flatMap((m) => (m.group?.reads ?? []).map((ref) => ({ component: componentOf.get(m.member) ?? m.member, ref })))
|
|
870
|
+
.sort((a, b) => a.component.localeCompare(b.component) || a.ref.localeCompare(b.ref));
|
|
871
|
+
return {
|
|
872
|
+
capability: g.capability,
|
|
873
|
+
components: [...g.components],
|
|
874
|
+
wave: wave > 0 ? wave : 1,
|
|
875
|
+
digest: g.digest,
|
|
876
|
+
...(g.error !== undefined ? { error: g.error } : {}),
|
|
877
|
+
...(reads.length > 0 ? { reads } : {}),
|
|
878
|
+
};
|
|
879
|
+
});
|
|
880
|
+
}
|
|
585
881
|
/** The report a stage writes. */
|
|
586
882
|
export function prReport(input) {
|
|
587
883
|
const changed = new Set(input.signal.changed);
|
|
884
|
+
const groups = reportGroups(input.set, input.plan.waves);
|
|
885
|
+
const groupOf = new Map(groups.flatMap((g, i) => g.components.map((c) => [c, i])));
|
|
886
|
+
const members = (input.members ?? input.set.members).map((m) => (groupOf.has(m.component) ? { ...m, group: groupOf.get(m.component) } : m));
|
|
588
887
|
return {
|
|
589
888
|
$schema: PR_REPORT_SCHEMA_ID,
|
|
590
889
|
contract: PR_REPORT_CONTRACT,
|
|
@@ -615,7 +914,9 @@ export function prReport(input) {
|
|
|
615
914
|
indeterminate: input.plan.indeterminate,
|
|
616
915
|
waves: input.plan.waves,
|
|
617
916
|
},
|
|
618
|
-
|
|
917
|
+
...(groups.length > 0 ? { groups } : {}),
|
|
918
|
+
...(input.set.fallbacks?.length ? { fallbacks: input.set.fallbacks.map((f) => ({ ...f })) } : {}),
|
|
919
|
+
members,
|
|
619
920
|
changeSet: input.set.doc,
|
|
620
921
|
};
|
|
621
922
|
}
|