@intentius/chant 0.124.26 → 0.124.28
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/README.md +1 -1
- package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/dist/__golden__/wave-loop/op-held-on.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/dist/__golden__/wave-loop/op-per-run.json +7 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
- package/dist/__golden__/wave-loop/op-sealed.json +3 -0
- package/dist/__golden__/wave-loop/op-spend.json +3 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/dist/change-set-graph.d.ts +56 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +128 -0
- package/dist/change-set.d.ts +52 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +35 -4
- package/dist/chant-version.d.ts +9 -0
- package/dist/chant-version.d.ts.map +1 -0
- package/dist/chant-version.js +16 -0
- package/dist/cli/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +9 -8
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.js +30 -2
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +13 -2
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.js +17 -8
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +19 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +28 -0
- package/dist/cli/handlers/change-set.d.ts +10 -0
- package/dist/cli/handlers/change-set.d.ts.map +1 -1
- package/dist/cli/handlers/change-set.js +51 -1
- package/dist/cli/handlers/fan-out.d.ts +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +14 -3
- package/dist/cli/handlers/pr.d.ts +8 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +148 -24
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +256 -21
- package/dist/cli/handlers/release.d.ts +34 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +203 -0
- package/dist/cli/handlers/run.d.ts +28 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +170 -13
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +88 -13
- 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 +49 -3
- package/dist/cli/registry.d.ts +29 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +29 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +47 -0
- package/dist/cli/version.d.ts +3 -5
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +3 -13
- package/dist/codegen/docs-in-site.d.ts +71 -0
- package/dist/codegen/docs-in-site.d.ts.map +1 -0
- package/dist/codegen/docs-in-site.js +177 -0
- package/dist/codegen/docs-types.d.ts +14 -1
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +18 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/docs.js +40 -7
- package/dist/codegen/rehype-base-url.mjs +15 -9
- package/dist/components/auto-release.d.ts +4 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +2 -0
- package/dist/components/cli-support.d.ts +6 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +10 -9
- package/dist/components/driver.d.ts +42 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +81 -54
- package/dist/components/fan-out-run.d.ts +7 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +35 -26
- package/dist/components/pr-run.d.ts +11 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +20 -1
- package/dist/components/promote-order.d.ts +38 -0
- package/dist/components/promote-order.d.ts.map +1 -0
- package/dist/components/promote-order.js +62 -0
- package/dist/components/promote.d.ts +15 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +17 -0
- package/dist/components/release-compile.d.ts +133 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +376 -0
- package/dist/components/release-gate.d.ts +125 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +282 -0
- package/dist/components/release-promote.d.ts +96 -0
- package/dist/components/release-promote.d.ts.map +1 -0
- package/dist/components/release-promote.js +204 -0
- package/dist/components/run-progress.d.ts +7 -0
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/wave-plan.d.ts +3 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lifecycle/change-set-store.d.ts +67 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +96 -0
- package/dist/lifecycle/commit-sha.d.ts +13 -0
- package/dist/lifecycle/commit-sha.d.ts.map +1 -0
- package/dist/lifecycle/commit-sha.js +21 -0
- package/dist/lifecycle/gate-change-set.d.ts +69 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +106 -0
- package/dist/lifecycle/gate-ledger.d.ts +49 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/release-cut.d.ts +107 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +273 -0
- package/dist/lifecycle/release-ledger.d.ts +54 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +53 -20
- package/dist/lifecycle/release-store.d.ts +190 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +318 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +2 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.js +4 -2
- package/dist/op/activities/source-release.d.ts.map +1 -1
- package/dist/op/activities/source-release.js +7 -3
- package/dist/op/activities/source-rollback.d.ts.map +1 -1
- package/dist/op/activities/source-rollback.js +7 -3
- package/dist/op/activity-registry.d.ts +6 -3
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.js +7 -5
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +14 -7
- package/dist/op/gate-name.d.ts +11 -2
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +12 -3
- package/dist/op/gate-run-locator.d.ts +22 -0
- package/dist/op/gate-run-locator.d.ts.map +1 -1
- package/dist/op/gate-run-locator.js +31 -0
- package/dist/op/gate.d.ts +27 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +57 -3
- package/dist/op/local-executor.d.ts +36 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +158 -52
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +4 -0
- package/dist/op/run-live.d.ts +2 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/run-live.js +1 -1
- package/dist/op/runtime.d.ts +46 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/runtimes/local.js +12 -2
- package/dist/point-decision.d.ts +2 -0
- package/dist/point-decision.d.ts.map +1 -1
- package/dist/point-decision.js +9 -2
- package/dist/run-event-store.d.ts +77 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +204 -0
- package/dist/run-event-stream.d.ts +132 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +238 -0
- package/dist/run-event.d.ts +267 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +207 -0
- package/dist/wave-runtime.mjs +225 -242
- package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/run-event-check.js +59 -0
- package/dist/workspace/change-set-show.d.ts +56 -0
- package/dist/workspace/change-set-show.d.ts.map +1 -0
- package/dist/workspace/change-set-show.js +54 -0
- package/dist/workspace/change-set-show.schema.json +102 -0
- package/dist/workspace/change-set.schema.json +28 -1
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes-cli.js +10 -1
- package/dist/workspace/changes.d.ts +37 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/changes.js +49 -2
- package/dist/workspace/changes.schema.json +135 -0
- package/dist/workspace/ci-green-cli.d.ts +19 -4
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +75 -12
- package/dist/workspace/ci-green-forge.d.ts +20 -3
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +6 -4
- package/dist/workspace/ci-green-workflow.d.ts +11 -0
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +6 -0
- package/dist/workspace/ci-green.d.ts +273 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +449 -38
- package/dist/workspace/ci-last-green.schema.json +32 -2
- package/dist/workspace/ci-list.schema.json +525 -0
- package/dist/workspace/commit-links.d.ts +85 -0
- package/dist/workspace/commit-links.d.ts.map +1 -0
- package/dist/workspace/commit-links.js +163 -0
- package/dist/workspace/declaration.d.ts +19 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +1 -1
- package/dist/workspace/declaration.schema.json +12 -0
- package/dist/workspace/identity-gate-rule.d.ts +37 -0
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +72 -7
- package/dist/workspace/identity.d.ts +12 -0
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/identity.js +13 -1
- package/dist/workspace/intent.d.ts +6 -1
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +51 -1
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -0
- package/dist/workspace/ls.schema.json +3 -1
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +6 -0
- package/dist/workspace/promotion-order.d.ts +41 -0
- package/dist/workspace/promotion-order.d.ts.map +1 -0
- package/dist/workspace/promotion-order.js +95 -0
- package/dist/workspace/reason-codes.d.ts +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +4 -1
- package/dist/workspace/releases.d.ts +72 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +94 -0
- package/dist/workspace/releases.schema.json +391 -0
- package/dist/workspace/run-event.schema.json +288 -0
- package/dist/workspace/status-gates.d.ts +26 -1
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +31 -2
- package/dist/workspace/status-stewards.d.ts +4 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +2 -0
- package/dist/workspace/status.d.ts +39 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +48 -1
- package/dist/workspace/status.schema.json +70 -1
- package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
- package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
- package/dist/workspace/trust/ci-tag-statement.js +123 -0
- package/dist/workspace/trust/op-run-statement.d.ts +91 -0
- package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
- package/dist/workspace/trust/op-run-statement.js +143 -0
- package/dist/workspace/trust/rotation.d.ts +18 -2
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.js +65 -16
- package/dist/workspace/trust/seal.d.ts +11 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +9 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/ssh-commit.js +10 -10
- package/package.json +1 -1
- package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/src/__golden__/wave-loop/fan-out-only.json +6 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/src/__golden__/wave-loop/op-cost-member.json +3 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/src/__golden__/wave-loop/op-held-on.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/src/__golden__/wave-loop/op-per-run.json +7 -0
- package/src/__golden__/wave-loop/op-pr-review.json +7 -0
- package/src/__golden__/wave-loop/op-sealed.json +3 -0
- package/src/__golden__/wave-loop/op-spend.json +3 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/src/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +133 -0
- package/src/change-set.ts +76 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/commands/build.ts +19 -8
- package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
- package/src/cli/commands/check-lexicon-docs.ts +39 -2
- package/src/cli/commands/check-lexicon.ts +14 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
- package/src/cli/commands/init-lexicon.test.ts +25 -0
- package/src/cli/commands/init-lexicon.ts +18 -8
- package/src/cli/compiled-cli.e2e.test.ts +31 -6
- package/src/cli/handlers/approve-release.test.ts +80 -0
- package/src/cli/handlers/approve.ts +18 -2
- package/src/cli/handlers/build.test.ts +52 -0
- package/src/cli/handlers/build.ts +29 -0
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +48 -0
- package/src/cli/handlers/fan-out.ts +15 -3
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +194 -0
- package/src/cli/handlers/pr.ts +168 -34
- package/src/cli/handlers/promote.test.ts +389 -5
- package/src/cli/handlers/promote.ts +279 -20
- package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
- package/src/cli/handlers/release.e2e.test.ts +85 -0
- package/src/cli/handlers/release.ts +206 -0
- package/src/cli/handlers/run-auto-release.test.ts +71 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +176 -15
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +88 -13
- package/src/cli/mcp/never-over-mcp.test.ts +2 -0
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +37 -3
- package/src/cli/registry.ts +29 -3
- package/src/cli/run-events.ts +54 -0
- package/src/cli/version.ts +3 -13
- package/src/codegen/docs-in-site.test.ts +166 -0
- package/src/codegen/docs-in-site.ts +182 -0
- package/src/codegen/docs-site/AgentPrompt.astro +26 -1
- package/src/codegen/docs-types.ts +14 -1
- package/src/codegen/docs.ts +60 -7
- package/src/codegen/rehype-base-url.d.mts +3 -1
- package/src/codegen/rehype-base-url.mjs +15 -9
- package/src/codegen/rehype-base-url.test.ts +34 -0
- package/src/components/auto-release.test.ts +21 -0
- package/src/components/auto-release.ts +6 -1
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.ts +129 -56
- package/src/components/fan-out-run.ts +56 -35
- package/src/components/pr-run.ts +30 -1
- package/src/components/promote-order.test.ts +90 -0
- package/src/components/promote-order.ts +75 -0
- package/src/components/promote.test.ts +10 -1
- package/src/components/promote.ts +26 -1
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +445 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +341 -0
- package/src/components/release-promote.test.ts +159 -0
- package/src/components/release-promote.ts +232 -0
- package/src/components/run-progress.ts +7 -0
- package/src/components/wave-plan.ts +1 -1
- package/src/config.ts +7 -1
- package/src/index.ts +2 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +112 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +133 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +66 -1
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +411 -0
- package/src/lifecycle/release-cut.ts +367 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-store.ts +423 -0
- package/src/lifecycle/run-ledger.test.ts +10 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/op/activities/change-coverage.ts +4 -1
- package/src/op/activities/source-release.ts +8 -3
- package/src/op/activities/source-rollback.ts +8 -4
- package/src/op/activity-registry.ts +7 -5
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/discover-root.test.ts +39 -0
- package/src/op/discover.ts +15 -8
- package/src/op/gate-name.ts +13 -3
- package/src/op/gate-release.test.ts +126 -0
- package/src/op/gate-run-locator.test.ts +110 -0
- package/src/op/gate-run-locator.ts +35 -0
- package/src/op/gate.ts +88 -6
- package/src/op/local-executor.ts +207 -52
- package/src/op/op-waves-run.ts +4 -0
- package/src/op/run-events.test.ts +166 -0
- package/src/op/run-live.ts +1 -1
- package/src/op/runtime.ts +39 -0
- package/src/op/runtimes/local.test.ts +16 -0
- package/src/op/runtimes/local.ts +13 -2
- package/src/point-decision.ts +12 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +213 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +306 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +451 -0
- package/src/wave-loop-golden.test.ts +9 -0
- package/src/workspace/__fixtures__/run-event-check.ts +58 -0
- package/src/workspace/ancestry-index.test.ts +93 -0
- package/src/workspace/change-set-show.schema.json +102 -0
- package/src/workspace/change-set-show.ts +72 -0
- package/src/workspace/change-set.schema.json +28 -1
- package/src/workspace/changes-cli.ts +10 -1
- package/src/workspace/changes.schema.json +135 -0
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/changes.ts +84 -2
- package/src/workspace/ci-green-cli.ts +75 -13
- package/src/workspace/ci-green-forge.ts +24 -4
- package/src/workspace/ci-green-runs.test.ts +242 -0
- package/src/workspace/ci-green-signed.test.ts +302 -0
- package/src/workspace/ci-green-workflow.test.ts +16 -0
- package/src/workspace/ci-green-workflow.ts +12 -0
- package/src/workspace/ci-green.test.ts +131 -2
- package/src/workspace/ci-green.ts +624 -42
- package/src/workspace/ci-last-green.schema.json +32 -2
- package/src/workspace/ci-list.schema.json +525 -0
- package/src/workspace/commit-links.test.ts +279 -0
- package/src/workspace/commit-links.ts +183 -0
- package/src/workspace/declaration.schema.json +12 -0
- package/src/workspace/declaration.ts +22 -2
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent.ts +48 -1
- package/src/workspace/ls.schema.json +3 -1
- package/src/workspace/ls.ts +6 -0
- package/src/workspace/member-pipeline.test.ts +17 -0
- package/src/workspace/member-pipeline.ts +9 -0
- package/src/workspace/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +4 -1
- package/src/workspace/reason-codes.test.ts +2 -1
- package/src/workspace/reason-codes.ts +4 -1
- package/src/workspace/releases.schema.json +391 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +122 -0
- package/src/workspace/run-event.schema.json +288 -0
- package/src/workspace/status-contract.test.ts +52 -1
- package/src/workspace/status-gates.ts +56 -1
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +70 -1
- package/src/workspace/status.ts +80 -1
- package/src/workspace/trust/ci-tag-statement.ts +151 -0
- package/src/workspace/trust/op-run-statement.ts +148 -0
- package/src/workspace/trust/rotation.ts +61 -14
- package/src/workspace/trust/seal.ts +10 -0
- package/src/workspace/trust/ssh-commit.ts +10 -9
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
package/src/components/pr-run.ts
CHANGED
|
@@ -52,6 +52,7 @@ import { remainingFanOut, type ComponentChangeSignal, type FanOutPlan } from "./
|
|
|
52
52
|
import type { PrApplyRecord } from "./fan-out-record";
|
|
53
53
|
import type { CapabilityRegistry } from "./capability";
|
|
54
54
|
import type { RunProgressEvent } from "./run-progress";
|
|
55
|
+
import type { RunEventSink } from "../run-event-stream";
|
|
55
56
|
import { planWaveComponent, readComponentOutputs } from "./wave-plan";
|
|
56
57
|
|
|
57
58
|
export interface PrPlanSetOptions {
|
|
@@ -64,8 +65,17 @@ export interface PrPlanSetOptions {
|
|
|
64
65
|
vars?: Record<string, unknown>;
|
|
65
66
|
/** Outputs supplied by the caller (`--seed-outputs`). They win over outputs read here. */
|
|
66
67
|
seededOutputs?: Record<string, Record<string, unknown>>;
|
|
68
|
+
/**
|
|
69
|
+
* Where the planning's events go (#4041): a `plan` phase with no
|
|
70
|
+
* component, and in it one `plan` step per component, started as its wave
|
|
71
|
+
* is planned and settled `ok` or `failed`.
|
|
72
|
+
*/
|
|
73
|
+
onRunEvent?: RunEventSink;
|
|
67
74
|
}
|
|
68
75
|
|
|
76
|
+
/** The phase a pull request's planning is, in its run's events. */
|
|
77
|
+
export const PR_PLAN_PHASE = "plan";
|
|
78
|
+
|
|
69
79
|
/** Every member of a pull request, planned. */
|
|
70
80
|
export interface PrPlanSet {
|
|
71
81
|
/** The change-set document over every member. Its digest is what the gate binds. */
|
|
@@ -101,6 +111,8 @@ export async function planPrSet(options: PrPlanSetOptions): Promise<PrPlanSet> {
|
|
|
101
111
|
const componentOf = new Map<string, string>();
|
|
102
112
|
const plans: Record<string, Record<string, unknown>> = {};
|
|
103
113
|
let failed = false;
|
|
114
|
+
const emit = options.onRunEvent;
|
|
115
|
+
emit?.({ type: "phase-start", component: null, phase: PR_PLAN_PHASE });
|
|
104
116
|
|
|
105
117
|
for (const wave of options.plan.waves) {
|
|
106
118
|
// The dependencies' outputs first, each read once. A dependency that
|
|
@@ -115,7 +127,20 @@ export async function planPrSet(options: PrPlanSetOptions): Promise<PrPlanSet> {
|
|
|
115
127
|
}
|
|
116
128
|
}
|
|
117
129
|
const planned = await Promise.all(
|
|
118
|
-
wave.map((name) =>
|
|
130
|
+
wave.map(async (name) => {
|
|
131
|
+
const at = { component: name, phase: PR_PLAN_PHASE, step: "plan" };
|
|
132
|
+
emit?.({ type: "step-start", ...at });
|
|
133
|
+
const start = Date.now();
|
|
134
|
+
const p = await planWaveComponent(byName.get(name)!, options.registry, deployContext(options, name), outputs);
|
|
135
|
+
emit?.({
|
|
136
|
+
type: "step-settle",
|
|
137
|
+
...at,
|
|
138
|
+
status: "error" in p ? "failed" : "ok",
|
|
139
|
+
durationMs: Date.now() - start,
|
|
140
|
+
...("error" in p ? { error: p.error } : {}),
|
|
141
|
+
});
|
|
142
|
+
return p;
|
|
143
|
+
}),
|
|
119
144
|
);
|
|
120
145
|
for (const p of planned) {
|
|
121
146
|
const component = byName.get(p.component)!;
|
|
@@ -135,6 +160,7 @@ export async function planPrSet(options: PrPlanSetOptions): Promise<PrPlanSet> {
|
|
|
135
160
|
}
|
|
136
161
|
}
|
|
137
162
|
|
|
163
|
+
emit?.({ type: "phase-settle", component: null, phase: PR_PLAN_PHASE, status: failed ? "failed" : "ok" });
|
|
138
164
|
const doc = composeChangeSet(parts);
|
|
139
165
|
const members: PrMember[] = doc.members.map((m) => ({
|
|
140
166
|
member: m.member,
|
|
@@ -206,6 +232,8 @@ export interface PrApplyOptions {
|
|
|
206
232
|
/** The set {@link planPrSet} planned and the gate approved, or {@link resumePrSet}'s set. */
|
|
207
233
|
set: PrPlanSet;
|
|
208
234
|
onProgress?: (event: RunProgressEvent) => void;
|
|
235
|
+
/** Where the apply's events go (#4041): the fan-out's run events. */
|
|
236
|
+
onRunEvent?: RunEventSink;
|
|
209
237
|
/**
|
|
210
238
|
* What an earlier attempt at this approved set applied (#3464): its
|
|
211
239
|
* components, which are reported applied and not run again, and the
|
|
@@ -230,6 +258,7 @@ export async function applyPrSet(options: PrApplyOptions): Promise<{ members: Pr
|
|
|
230
258
|
plans: options.set.plans,
|
|
231
259
|
componentOutputs: { ...before },
|
|
232
260
|
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
261
|
+
...(options.onRunEvent ? { onRunEvent: options.onRunEvent } : {}),
|
|
233
262
|
...(options.onComponentSettled ? { onComponentSettled: options.onComponentSettled } : {}),
|
|
234
263
|
});
|
|
235
264
|
const completed = new Set(run.completed);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { promotionOrder, staticPromotionOrder } from "../workspace/promotion-order";
|
|
3
|
+
import { resolvePromotionSource } from "./promote-order";
|
|
4
|
+
|
|
5
|
+
describe("promotionOrder (#4030)", () => {
|
|
6
|
+
test("is the literal entries in order, with canary read from the object form", () => {
|
|
7
|
+
expect(promotionOrder(["dev", { name: "staging", canary: true }, { name: "prod", endpoint: "http://x" }, "pr-*"])).toEqual([
|
|
8
|
+
{ name: "dev", canary: false },
|
|
9
|
+
{ name: "staging", canary: true },
|
|
10
|
+
{ name: "prod", canary: false },
|
|
11
|
+
]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("is empty without environments, and a repeated name keeps its first place", () => {
|
|
15
|
+
expect(promotionOrder(undefined)).toEqual([]);
|
|
16
|
+
expect(promotionOrder(null)).toEqual([]);
|
|
17
|
+
expect(promotionOrder(["a", "b", "a"]).map((s) => s.name)).toEqual(["a", "b"]);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("resolvePromotionSource (#4030)", () => {
|
|
22
|
+
const order = promotionOrder(["dev", "staging", "prod"]);
|
|
23
|
+
|
|
24
|
+
test("defaults from to the step before to", () => {
|
|
25
|
+
expect(resolvePromotionSource({ order, to: "prod" })).toEqual({ from: "staging" });
|
|
26
|
+
expect(resolvePromotionSource({ order, to: "staging" })).toEqual({ from: "dev" });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("an adjacent from is accepted", () => {
|
|
30
|
+
expect(resolvePromotionSource({ order, to: "prod", from: "staging" })).toEqual({ from: "staging" });
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("a from that skips a step is refused, and allowSkip lifts it", () => {
|
|
34
|
+
const r = resolvePromotionSource({ order, to: "prod", from: "dev" });
|
|
35
|
+
expect(r).toMatchObject({ error: { code: "promote-skips-environment" } });
|
|
36
|
+
expect("error" in r && r.error.message).toContain("skips staging");
|
|
37
|
+
expect("error" in r && r.error.fix).toContain("--allow-skip");
|
|
38
|
+
expect(resolvePromotionSource({ order, to: "prod", from: "dev", allowSkip: true })).toEqual({ from: "dev" });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("a step outside the order is not judged", () => {
|
|
42
|
+
expect(resolvePromotionSource({ order, to: "prod", from: "hotfix" })).toEqual({ from: "hotfix" });
|
|
43
|
+
expect(resolvePromotionSource({ order, to: "pr-42", from: "dev" })).toEqual({ from: "dev" });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("going backwards is not a skip", () => {
|
|
47
|
+
expect(resolvePromotionSource({ order, to: "dev", from: "prod" })).toEqual({ from: "prod" });
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("no order leaves from as given", () => {
|
|
51
|
+
expect(resolvePromotionSource({ order: [], to: "prod" })).toEqual({ from: undefined });
|
|
52
|
+
expect(resolvePromotionSource({ order: [], to: "prod", from: "dev" })).toEqual({ from: "dev" });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe("staticPromotionOrder (#4030)", () => {
|
|
57
|
+
test("reads literal strings and objects without running the config, comments left out", () => {
|
|
58
|
+
const src = `// environments: ["old"]
|
|
59
|
+
import type { ChantConfig } from "@intentius/chant";
|
|
60
|
+
export default {
|
|
61
|
+
lexicons: ["aws"],
|
|
62
|
+
/* the order */
|
|
63
|
+
environments: [
|
|
64
|
+
"dev",
|
|
65
|
+
{ name: "staging", canary: true },
|
|
66
|
+
{ name: 'prod', endpoint: "http://localhost:4566" },
|
|
67
|
+
"pr-*",
|
|
68
|
+
],
|
|
69
|
+
} satisfies ChantConfig;
|
|
70
|
+
`;
|
|
71
|
+
expect(staticPromotionOrder(src)).toEqual([
|
|
72
|
+
{ name: "dev", canary: false },
|
|
73
|
+
{ name: "staging", canary: true },
|
|
74
|
+
{ name: "prod", canary: false },
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("a config with no environments and no spread declares none", () => {
|
|
79
|
+
expect(staticPromotionOrder('export default { lexicons: ["aws"] };\n')).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("anything computed is left to the loader", () => {
|
|
83
|
+
expect(staticPromotionOrder('export default { environments: [...base, "prod"] };')).toBeUndefined();
|
|
84
|
+
expect(staticPromotionOrder('const envs = ["a"];\nexport default { environments: envs };')).toBeUndefined();
|
|
85
|
+
expect(staticPromotionOrder('export default { environments: ["a", `b${x}`] };')).toBeUndefined();
|
|
86
|
+
expect(staticPromotionOrder('export default { environments: ["a", { name: n }] };')).toBeUndefined();
|
|
87
|
+
expect(staticPromotionOrder('export default { a: { environments: ["x"] }, environments: ["y"] };')).toBeUndefined();
|
|
88
|
+
expect(staticPromotionOrder("export default makeConfig();")).toBeUndefined();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The environment `chant components promote` reads from, given the member's
|
|
3
|
+
* promotion order (#4030, see `../workspace/promotion-order.ts`). These
|
|
4
|
+
* refusals are promote's own and are not codes of the workspace read contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { PromotionStep } from "../workspace/promotion-order";
|
|
8
|
+
|
|
9
|
+
/** The refusals `components promote` gives for the declared order. Stable: scripts may match on them. */
|
|
10
|
+
export const PROMOTE_ORDER_CODES = [
|
|
11
|
+
/** `--from` is more than one step behind `--to` in the declared order, and `--allow-skip` was not given. */
|
|
12
|
+
"promote-skips-environment",
|
|
13
|
+
/** `--to` is the first step of the declared order, so no environment comes before it to default `--from` to. */
|
|
14
|
+
"promote-no-previous-environment",
|
|
15
|
+
/** `--from` was left out and `--to` is not a step of the declared order, so there is nothing to default it from. */
|
|
16
|
+
"promote-to-not-in-order",
|
|
17
|
+
] as const;
|
|
18
|
+
export type PromoteOrderCode = (typeof PROMOTE_ORDER_CODES)[number];
|
|
19
|
+
|
|
20
|
+
export interface PromotionRefusal {
|
|
21
|
+
error: { code: PromoteOrderCode; message: string; fix: string };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The environment a promote reads from. With an order declared, `--from`
|
|
26
|
+
* defaults to the step before `to`, and a `--from` that skips a step is
|
|
27
|
+
* refused unless `allowSkip`. Without an order, or when `to` or `from` is not
|
|
28
|
+
* a step of it, the order has nothing to say and `from` is returned as given
|
|
29
|
+
* (`undefined` is the caller's to refuse, as before).
|
|
30
|
+
*/
|
|
31
|
+
export function resolvePromotionSource(opts: {
|
|
32
|
+
order: readonly PromotionStep[];
|
|
33
|
+
to: string;
|
|
34
|
+
from?: string;
|
|
35
|
+
allowSkip?: boolean;
|
|
36
|
+
}): { from: string | undefined } | PromotionRefusal {
|
|
37
|
+
const { order, to, from, allowSkip } = opts;
|
|
38
|
+
const names = order.map((s) => s.name);
|
|
39
|
+
const list = names.join(", ");
|
|
40
|
+
const toAt = names.indexOf(to);
|
|
41
|
+
if (from === undefined) {
|
|
42
|
+
if (order.length === 0) return { from: undefined };
|
|
43
|
+
if (toAt < 0) {
|
|
44
|
+
return {
|
|
45
|
+
error: {
|
|
46
|
+
code: "promote-to-not-in-order",
|
|
47
|
+
message: `--from was not given, and "${to}" is not a step of the declared promotion order (${list})`,
|
|
48
|
+
fix: `Pass --from <env>, or add "${to}" to the environments array of this member's chant.config.ts.`,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (toAt === 0) {
|
|
53
|
+
return {
|
|
54
|
+
error: {
|
|
55
|
+
code: "promote-no-previous-environment",
|
|
56
|
+
message: `"${to}" is the first step of the declared promotion order (${list}), so there is no environment to promote from`,
|
|
57
|
+
fix: `Deploy "${to}" with chant run --env ${to}, or pass --from <env> to promote from an environment outside the order.`,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return { from: names[toAt - 1] };
|
|
62
|
+
}
|
|
63
|
+
const fromAt = names.indexOf(from);
|
|
64
|
+
if (toAt >= 0 && fromAt >= 0 && fromAt < toAt - 1 && !allowSkip) {
|
|
65
|
+
const skipped = names.slice(fromAt + 1, toAt);
|
|
66
|
+
return {
|
|
67
|
+
error: {
|
|
68
|
+
code: "promote-skips-environment",
|
|
69
|
+
message: `promoting ${from} to ${to} skips ${skipped.join(", ")} in the declared promotion order (${list})`,
|
|
70
|
+
fix: `Promote to ${skipped[0]} first, or pass --allow-skip to promote ${from} to ${to} anyway.`,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return { from };
|
|
75
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Promote a release without rebuilding (#2530).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { describe, test, expect } from "vitest";
|
|
5
|
+
import { describe, test, expect, vi } from "vitest";
|
|
6
6
|
import { CapabilityRegistry, type DeployContext } from "./capability";
|
|
7
7
|
import { describeGateMismatch, memoryGateLedgerPort, type GateLedgerPort } from "../op/gate";
|
|
8
8
|
import { componentPlanDigest } from "./gate-plan";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
runPromotion,
|
|
21
21
|
promotionRecord,
|
|
22
22
|
gateApprover,
|
|
23
|
+
gateAdmission,
|
|
23
24
|
promoteArchivePaths,
|
|
24
25
|
hasPublishStep,
|
|
25
26
|
parseDigestPins,
|
|
@@ -282,10 +283,15 @@ describe("running a promotion", () => {
|
|
|
282
283
|
const { registry, ran } = fakeRegistry({ api: "sha256:api" });
|
|
283
284
|
const opts = { plan: planFor("api", "sha256:api"), components: [prepared(gated)], registry, gates };
|
|
284
285
|
|
|
286
|
+
vi.stubEnv("GITHUB_RUN_ID", "9001");
|
|
287
|
+
vi.stubEnv("GITHUB_SHA", "e".repeat(40));
|
|
285
288
|
const first = await runPromotion({ ...opts, now: "2026-01-03T00:00:00Z" });
|
|
286
289
|
expect(first.status).toBe("gated");
|
|
287
290
|
expect(first.gate?.gate).toBe("prod-release");
|
|
288
291
|
expect(ran).not.toContain("prod:api:apply");
|
|
292
|
+
// #4020: the pending fact of a component gate names the CI run, and a commit.
|
|
293
|
+
expect(first.gate?.runId).toBe("9001");
|
|
294
|
+
expect(first.gate?.gitSha).toMatch(/^[0-9a-f]{40}$/);
|
|
289
295
|
|
|
290
296
|
// `chant approve` copies the pending fact's environment and plan (#2574).
|
|
291
297
|
const bound = { planDigest: first.gate!.planDigest!, environment: first.gate!.environment! };
|
|
@@ -294,6 +300,9 @@ describe("running a promotion", () => {
|
|
|
294
300
|
const second = await runPromotion({ ...opts, now: "2026-01-03T00:02:00Z" });
|
|
295
301
|
expect(second.status).toBe("ok");
|
|
296
302
|
expect(gateApprover(second.results[0])).toBe("alice");
|
|
303
|
+
// #4020: and the gate resolution that admitted the deploy.
|
|
304
|
+
expect(gateAdmission(second.results[0])).toEqual({ op: "api", gate: "prod-release", resolvedAt: "2026-01-03T00:01:00Z" });
|
|
305
|
+
vi.unstubAllEnvs();
|
|
297
306
|
|
|
298
307
|
// The same approval does not pass a promote of another release (#2574).
|
|
299
308
|
const other = await runPromotion({
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import { parseWiringRef, type WiringRef } from "../lint/rules/comp/support";
|
|
42
|
-
import { latestPerComponent, type ReleaseRecord, type ReleaseRecordInput, type RunOrigin } from "../lifecycle/release-ledger";
|
|
42
|
+
import { latestPerComponent, type ReleaseRecord, type ReleaseRecordInput, type ReleaseAdmission, type RunOrigin } from "../lifecycle/release-ledger";
|
|
43
43
|
import { CapabilityRegistry, type Capability } from "./capability";
|
|
44
44
|
import {
|
|
45
45
|
runInterpretDriver,
|
|
@@ -47,12 +47,14 @@ import {
|
|
|
47
47
|
type DriverComponent,
|
|
48
48
|
type DriverComponentResult,
|
|
49
49
|
type DriverGate,
|
|
50
|
+
type ReleaseGateBinding,
|
|
50
51
|
type DriverPhase,
|
|
51
52
|
type DriverRunResult,
|
|
52
53
|
type DriverStep,
|
|
53
54
|
} from "./driver";
|
|
54
55
|
import type { GateLedgerPort } from "../op/gate";
|
|
55
56
|
import type { RunProgressEvent } from "./run-progress";
|
|
57
|
+
import type { RunEventSink } from "../run-event-stream";
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
60
|
* Step kinds that produce an artifact at build time: the build family, and the
|
|
@@ -535,8 +537,12 @@ export interface RunPromotionOptions {
|
|
|
535
537
|
registry: CapabilityRegistry;
|
|
536
538
|
componentOutputs?: Record<string, Record<string, unknown>>;
|
|
537
539
|
gates?: GateLedgerPort;
|
|
540
|
+
/** A promote under a release (#4045): what every gate it reaches binds. */
|
|
541
|
+
releaseGate?: ReleaseGateBinding;
|
|
538
542
|
now?: string;
|
|
539
543
|
onProgress?: (event: RunProgressEvent) => void;
|
|
544
|
+
/** Where the run's events go (#4041, `--events-json`). */
|
|
545
|
+
onRunEvent?: RunEventSink;
|
|
540
546
|
}
|
|
541
547
|
|
|
542
548
|
/**
|
|
@@ -566,7 +572,9 @@ export async function runPromotion(options: RunPromotionOptions): Promise<Driver
|
|
|
566
572
|
return item ? { version: item.digest, revision: item.source.gitSha } : undefined;
|
|
567
573
|
},
|
|
568
574
|
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
575
|
+
...(options.onRunEvent ? { onRunEvent: options.onRunEvent } : {}),
|
|
569
576
|
...(options.gates ? { gates: options.gates } : {}),
|
|
577
|
+
...(options.releaseGate ? { releaseGate: options.releaseGate } : {}),
|
|
570
578
|
...(options.now ? { now: options.now } : {}),
|
|
571
579
|
});
|
|
572
580
|
} catch (err) {
|
|
@@ -584,6 +592,20 @@ export function gateApprover(result: DriverComponentResult | undefined): string
|
|
|
584
592
|
return approver;
|
|
585
593
|
}
|
|
586
594
|
|
|
595
|
+
/**
|
|
596
|
+
* The gate resolution that admitted a component's last gate in this run (#4020):
|
|
597
|
+
* which gate, in the component's gate ledger, and when the resolution was
|
|
598
|
+
* recorded. Undefined for a component that passed no gate.
|
|
599
|
+
*/
|
|
600
|
+
export function gateAdmission(result: DriverComponentResult | undefined): ReleaseAdmission | undefined {
|
|
601
|
+
let admission: ReleaseAdmission | undefined;
|
|
602
|
+
for (const record of result?.records ?? []) {
|
|
603
|
+
// #4045: a release gate's resolution is in the release gate's ledger, not the component's.
|
|
604
|
+
if (record.approval) admission = { op: record.approval.op ?? result!.component, gate: record.approval.gate, resolvedAt: record.approval.timestamp };
|
|
605
|
+
}
|
|
606
|
+
return admission;
|
|
607
|
+
}
|
|
608
|
+
|
|
587
609
|
/** What a promote, rollback or redeploy knows about its own run, for the records it writes. */
|
|
588
610
|
export interface DeployRunInfo {
|
|
589
611
|
runId: string;
|
|
@@ -591,6 +613,8 @@ export interface DeployRunInfo {
|
|
|
591
613
|
actor: string;
|
|
592
614
|
timestamp: string;
|
|
593
615
|
approver?: string;
|
|
616
|
+
/** The gate resolution that admitted the deploy (#4020), from {@link gateAdmission}. */
|
|
617
|
+
admission?: ReleaseAdmission;
|
|
594
618
|
}
|
|
595
619
|
|
|
596
620
|
/** The fields a promote, a rollback and a redeploy record share: the artifact is the earlier release's own. */
|
|
@@ -606,6 +630,7 @@ function sharedRecord(item: PromotionItem, env: string, run: DeployRunInfo): Rel
|
|
|
606
630
|
timestamp: run.timestamp,
|
|
607
631
|
actor: run.actor,
|
|
608
632
|
...(run.approver ? { approver: run.approver } : {}),
|
|
633
|
+
...(run.admission ? { admission: run.admission } : {}),
|
|
609
634
|
...(source.manifestDigest ? { manifestDigest: source.manifestDigest } : {}),
|
|
610
635
|
...(source.inputDigest ? { inputDigest: source.inputDigest } : {}),
|
|
611
636
|
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compiling one environment of a release, and building its artifacts once
|
|
3
|
+
* (#4026, ws-104 question 5), against fixture projects in temp directories.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect } from "vitest";
|
|
7
|
+
import { withTestDir } from "@intentius/chant-test-utils";
|
|
8
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
import { CapabilityRegistry, type DeployContext } from "./capability";
|
|
11
|
+
import type { DriverComponent } from "./driver";
|
|
12
|
+
import {
|
|
13
|
+
buildReleaseArtifacts,
|
|
14
|
+
buildStepsOf,
|
|
15
|
+
compileEnvironment,
|
|
16
|
+
ReleaseCompileError,
|
|
17
|
+
templateRefs,
|
|
18
|
+
withOnlyArchiveSteps,
|
|
19
|
+
} from "./release-compile";
|
|
20
|
+
|
|
21
|
+
const api: DriverComponent = {
|
|
22
|
+
name: "api",
|
|
23
|
+
dependsOn: ["db"],
|
|
24
|
+
deploy: [
|
|
25
|
+
{ phase: "Build", steps: [{ kind: "docker-build", context: "api", into: "api.tar" }, { kind: "zip-package", source: "fn", into: "fn.zip" }] },
|
|
26
|
+
{ phase: "Publish", steps: [{ kind: "publish-image", from: "archive:api.tar", template: "not-a-template.json" }] },
|
|
27
|
+
{
|
|
28
|
+
phase: "Apply",
|
|
29
|
+
steps: [
|
|
30
|
+
{ kind: "gate", gate: "prod" },
|
|
31
|
+
{ kind: "cfn-deploy", template: "dist/api.template.json", imageRef: "@Publish.digest" },
|
|
32
|
+
{ kind: "cfn-deploy", template: "archive:dist/edge.template.json" },
|
|
33
|
+
{ kind: "helm-upgrade", chart: "charts/api", values: ["values/base.yaml", "$env.VALUES"] },
|
|
34
|
+
{ kind: "kubectl-apply", manifest: "@Render.path" },
|
|
35
|
+
{ phase: "Nested", steps: [{ kind: "kubectl-apply", manifest: "k8s/api.yaml" }] },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe("templateRefs", () => {
|
|
42
|
+
test("lists the literal paths in template, manifest and values of deploy steps, without build or publish steps", () => {
|
|
43
|
+
expect(templateRefs(api).map((r) => [r.kind, r.field, r.path])).toEqual([
|
|
44
|
+
["cfn-deploy", "template", "dist/api.template.json"],
|
|
45
|
+
["cfn-deploy", "template", "dist/edge.template.json"],
|
|
46
|
+
["helm-upgrade", "values", "values/base.yaml"],
|
|
47
|
+
["kubectl-apply", "manifest", "k8s/api.yaml"],
|
|
48
|
+
]);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("withOnlyArchiveSteps", () => {
|
|
53
|
+
test("keeps the archive steps alone, drops gates and every other step, and the dependencies", () => {
|
|
54
|
+
expect(withOnlyArchiveSteps(api)).toEqual({
|
|
55
|
+
name: "api",
|
|
56
|
+
dependsOn: [],
|
|
57
|
+
deploy: [{ phase: "Build", steps: [{ kind: "docker-build", context: "api", into: "api.tar" }, { kind: "zip-package", source: "fn", into: "fn.zip" }] }],
|
|
58
|
+
});
|
|
59
|
+
expect(withOnlyArchiveSteps({ name: "x", deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "t.json" }] }] })).toBeUndefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("buildStepsOf lists the build-time steps in order", () => {
|
|
63
|
+
expect(buildStepsOf(api).map((s) => s.kind)).toEqual(["docker-build", "zip-package"]);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function project(dir: string): void {
|
|
68
|
+
writeFileSync(
|
|
69
|
+
join(dir, "api.component.ts"),
|
|
70
|
+
`export const api = {
|
|
71
|
+
name: "api",
|
|
72
|
+
dependsOn: [],
|
|
73
|
+
deploy: [
|
|
74
|
+
{ phase: "Build", steps: [{ kind: "docker-build", context: "svc", into: "api.tar" }] },
|
|
75
|
+
{ phase: "Publish", steps: [{ kind: "publish-image", from: "archive:api.tar" }] },
|
|
76
|
+
{ phase: "Apply", steps: [{ kind: "gate", gate: "go" }, { kind: "cfn-deploy", template: "dist/api.template.json", imageRef: "@Publish.digest" }] },
|
|
77
|
+
],
|
|
78
|
+
};
|
|
79
|
+
export const off = { name: "off", dependsOn: [], enabled: false, deploy: [{ phase: "Apply", steps: [{ kind: "cfn-deploy", template: "dist/missing.json" }] }] };
|
|
80
|
+
`,
|
|
81
|
+
);
|
|
82
|
+
mkdirSync(join(dir, "svc"), { recursive: true });
|
|
83
|
+
writeFileSync(join(dir, "svc", "Dockerfile"), "FROM scratch\n");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
describe("compileEnvironment", () => {
|
|
87
|
+
test("runs the build command with CHANT_ENV, then reads the templates the enabled components name", async () => {
|
|
88
|
+
await withTestDir(async (dir) => {
|
|
89
|
+
project(dir);
|
|
90
|
+
const target = await compileEnvironment(
|
|
91
|
+
{ projectDir: dir, buildCommand: `mkdir -p dist && printf '{"env":"%s"}' "$CHANT_ENV" > dist/api.template.json` },
|
|
92
|
+
"staging",
|
|
93
|
+
);
|
|
94
|
+
expect(target.env).toBe("staging");
|
|
95
|
+
expect(target.components).toEqual(["api"]);
|
|
96
|
+
expect(target.templates).toEqual([{ component: "api", path: "dist/api.template.json", content: `{"env":"staging"}` }]);
|
|
97
|
+
expect(target.buildSteps).toEqual({ api: [{ kind: "docker-build", context: "svc", into: "api.tar" }] });
|
|
98
|
+
expect(target.buildInputs).toEqual({ api: ["svc", "svc/Dockerfile"] });
|
|
99
|
+
expect(target.componentFiles).toEqual({ api: "api.component.ts" });
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("refuses when a template a deploy names does not exist after the build", async () => {
|
|
104
|
+
await withTestDir(async (dir) => {
|
|
105
|
+
project(dir);
|
|
106
|
+
const err = await compileEnvironment({ projectDir: dir }, "dev").catch((e: unknown) => e);
|
|
107
|
+
expect(err).toBeInstanceOf(ReleaseCompileError);
|
|
108
|
+
expect((err as Error).message).toMatch(/reads template "dist\/api.template.json", and no such file exists after the build for "dev"/);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("refuses when the build command fails", async () => {
|
|
113
|
+
await withTestDir(async (dir) => {
|
|
114
|
+
project(dir);
|
|
115
|
+
await expect(compileEnvironment({ projectDir: dir, buildCommand: "exit 3" }, "dev")).rejects.toThrow(/the build command for "dev" exited 3/);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("buildReleaseArtifacts", () => {
|
|
121
|
+
test("runs only the archive steps, once, and names each image and asset with its digest", async () => {
|
|
122
|
+
await withTestDir(async (dir) => {
|
|
123
|
+
writeFileSync(
|
|
124
|
+
join(dir, "api.component.ts"),
|
|
125
|
+
`export const api = {
|
|
126
|
+
name: "api",
|
|
127
|
+
dependsOn: [],
|
|
128
|
+
deploy: [
|
|
129
|
+
{ phase: "Build", steps: [{ kind: "docker-build", context: ".", into: "api.tar" }, { kind: "zip-package", source: "fn", into: "fn.zip" }] },
|
|
130
|
+
{ phase: "Apply", steps: [{ kind: "gate", gate: "go" }, { kind: "cfn-deploy", template: "t.json" }] },
|
|
131
|
+
],
|
|
132
|
+
};`,
|
|
133
|
+
);
|
|
134
|
+
const ran: string[] = [];
|
|
135
|
+
const registry = new CapabilityRegistry();
|
|
136
|
+
registry.register({ kind: "docker-build", async run(ctx: DeployContext, input: { into: string }) { ran.push(`${ctx.env}:docker-build`); return { archivePath: input.into, digest: "sha256:img" }; } } as never);
|
|
137
|
+
registry.register({ kind: "zip-package", async run(ctx: DeployContext, input: { into: string }) { ran.push(`${ctx.env}:zip-package`); return { archivePath: input.into, digest: "sha256:zip" }; } } as never);
|
|
138
|
+
registry.register({ kind: "cfn-deploy", async run() { ran.push("cfn-deploy"); return {}; } } as never);
|
|
139
|
+
const artifacts = await buildReleaseArtifacts({ projectDir: dir }, "dev", ["api"], registry);
|
|
140
|
+
expect(ran).toEqual(["dev:docker-build", "dev:zip-package"]);
|
|
141
|
+
expect(artifacts).toEqual([
|
|
142
|
+
{ component: "api", kind: "image", path: "api.tar", digest: "sha256:img" },
|
|
143
|
+
{ component: "api", kind: "asset", path: "fn.zip", digest: "sha256:zip" },
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
});
|