@intentius/chant 0.124.27 → 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/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 +8 -0
- 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 +204 -199
- 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/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/commit-run-gate-deploy-join.test.ts +117 -0
- 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 +9 -0
- 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
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant release` (#4026, ws-104): the release is what graduates.
|
|
3
|
+
*
|
|
4
|
+
* release cut [<label>] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
|
|
5
|
+
* cut a release of this member from the checkout's HEAD, which must be green;
|
|
6
|
+
* --sign signs the cut with the actor's ssh key (#4031); `--src`, `-o` and
|
|
7
|
+
* `--lexicon` shape the build of a project with no components (#4046)
|
|
8
|
+
* release list [--json]
|
|
9
|
+
* every release of this member with its targets, its cuts and where it runs;
|
|
10
|
+
* `--json` prints the read contract's `releases` document
|
|
11
|
+
* release export <label|digest> --env <env> [-o <dir>] [--json]
|
|
12
|
+
* write the files the release recorded for <env>, checked against their digests (#4046)
|
|
13
|
+
* release compile --env <env> [--build <command>] [--artifacts --component <a,b>] --json
|
|
14
|
+
* internal: what the cut runs in a child process per environment
|
|
15
|
+
*
|
|
16
|
+
* The workstation UX for `cut` grows in #4027. A declaration whose
|
|
17
|
+
* `identity.gates` names a `release` gate requires a signed cut by an actor
|
|
18
|
+
* in its class (#4031).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { resolve } from "node:path";
|
|
22
|
+
import { formatBold, formatError, formatSuccess, formatWarning } from "../format";
|
|
23
|
+
import { parseParamFlags } from "../build-params-cli";
|
|
24
|
+
import type { CommandContext } from "../registry";
|
|
25
|
+
|
|
26
|
+
const USAGE =
|
|
27
|
+
"chant release cut [<label>] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
|
|
28
|
+
|
|
29
|
+
/** The build inputs `release cut` and `release compile` share. */
|
|
30
|
+
function compileInputs(ctx: CommandContext, params: Record<string, string> | undefined): import("../../components/release-compile").CompileInputs {
|
|
31
|
+
const { args } = ctx;
|
|
32
|
+
return {
|
|
33
|
+
projectDir: resolve("."),
|
|
34
|
+
...(args.buildCommand ? { buildCommand: args.buildCommand } : {}),
|
|
35
|
+
...(params ? { params } : {}),
|
|
36
|
+
...(args.paramsFile ? { paramsFile: args.paramsFile } : {}),
|
|
37
|
+
...(args.src ? { src: args.src } : {}),
|
|
38
|
+
...(args.output ? { output: args.output } : {}),
|
|
39
|
+
...(args.lexicon ? { lexicon: args.lexicon } : {}),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveActor(ctx: CommandContext): string | undefined {
|
|
44
|
+
return ctx.args.actor ?? process.env.GITHUB_ACTOR ?? process.env.GITLAB_USER_LOGIN ?? process.env.USER;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function runReleaseCut(ctx: CommandContext): Promise<number> {
|
|
48
|
+
const { args } = ctx;
|
|
49
|
+
const positional = args.extraPositional;
|
|
50
|
+
if (positional && args.label && positional !== args.label) {
|
|
51
|
+
console.error(formatError({ message: `the label is given twice, as "${positional}" and --label "${args.label}"`, hint: USAGE }));
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
const label = positional ?? args.label ?? null;
|
|
55
|
+
const actor = resolveActor(ctx);
|
|
56
|
+
if (!actor) {
|
|
57
|
+
console.error(formatError({ message: "Could not resolve --actor from the environment", hint: "Pass --actor explicitly, or set GITHUB_ACTOR / GITLAB_USER_LOGIN / USER." }));
|
|
58
|
+
return 1;
|
|
59
|
+
}
|
|
60
|
+
const cwd = resolve(".");
|
|
61
|
+
const { spawnTargetCompiler, ReleaseCompileError } = await import("../../components/release-compile");
|
|
62
|
+
const { cutRelease, ReleaseCutError } = await import("../../lifecycle/release-cut");
|
|
63
|
+
const { ReleaseLabelError } = await import("../../lifecycle/release-store");
|
|
64
|
+
const { pushLifecycleReport } = await import("../../lifecycle/git");
|
|
65
|
+
const params = parseParamFlags(args.param);
|
|
66
|
+
const compiler = spawnTargetCompiler(compileInputs(ctx, params));
|
|
67
|
+
try {
|
|
68
|
+
const result = await cutRelease({ cwd, compiler, label, actor, ...(args.sign !== undefined ? { sign: args.sign } : {}) });
|
|
69
|
+
const push = await pushLifecycleReport({ cwd }).catch(() => ({ pushed: false, pushWarning: undefined }));
|
|
70
|
+
if (args.json) {
|
|
71
|
+
console.log(JSON.stringify({ release: result.release, label: result.label, cut: result.cut, stored: result.stored, reused: result.reused }, null, 2));
|
|
72
|
+
} else {
|
|
73
|
+
const name = result.label ? formatBold(result.label) : "unlabelled release";
|
|
74
|
+
if (result.cut) console.error(formatSuccess(`Cut ${name}: ${result.release.releaseDigest}`));
|
|
75
|
+
else console.error(formatSuccess(`Already cut${result.label ? ` as ${formatBold(result.label)}` : ""}: ${result.release.releaseDigest}`));
|
|
76
|
+
console.error(` commit ${result.release.commit}`);
|
|
77
|
+
if (result.cut) console.error(result.cut.signature ? ` signed by ${result.cut.actor}` : " not signed");
|
|
78
|
+
console.error(` artifacts ${result.release.artifactDigests.length}, targets ${result.release.targets.map((t) => `${t.env} (${t.templates.length} template${t.templates.length === 1 ? "" : "s"})`).join(", ")}`);
|
|
79
|
+
for (const r of result.reused) console.error(` ${r.component}: artifacts unchanged, carried from ${r.from}`);
|
|
80
|
+
console.log(result.release.releaseDigest);
|
|
81
|
+
}
|
|
82
|
+
if (push.pushWarning) console.error(formatWarning({ message: push.pushWarning }));
|
|
83
|
+
return 0;
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err instanceof ReleaseCutError) {
|
|
86
|
+
console.error(formatError({ message: `${err.message} [${err.code}]`, ...(err.fix ? { hint: err.fix } : {}) }));
|
|
87
|
+
return 1;
|
|
88
|
+
}
|
|
89
|
+
if (err instanceof ReleaseLabelError) {
|
|
90
|
+
console.error(formatError({ message: `${err.message} [${err.code}]` }));
|
|
91
|
+
return 1;
|
|
92
|
+
}
|
|
93
|
+
if (err instanceof ReleaseCompileError) {
|
|
94
|
+
console.error(formatError({ message: `no release was cut: ${err.message}` }));
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export async function runReleaseList(ctx: CommandContext): Promise<number> {
|
|
102
|
+
const { releasesDocument } = await import("../../workspace/releases");
|
|
103
|
+
const doc = await releasesDocument({ cwd: resolve(".") });
|
|
104
|
+
if (ctx.args.json) {
|
|
105
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
106
|
+
return "error" in doc ? 1 : 0;
|
|
107
|
+
}
|
|
108
|
+
if ("error" in doc) {
|
|
109
|
+
console.error(formatError({ message: doc.error.message, hint: USAGE }));
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
if (doc.reason) console.error(formatWarning({ message: doc.reason.message }));
|
|
113
|
+
if (doc.releases.length === 0) {
|
|
114
|
+
console.error("No releases. Cut one from a green commit: chant release cut <label>");
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
for (const r of doc.releases) {
|
|
118
|
+
const where = r.environments.map((e) => e.env).join(", ") || "not deployed from the release";
|
|
119
|
+
// #4031: the newest cut's signature, as the policy at base judges it.
|
|
120
|
+
const newest = r.cuts.at(-1);
|
|
121
|
+
const signature = newest ? (newest.signatureCheck.status === "verified" ? `signed by ${newest.actor}` : newest.signatureCheck.status) : "-";
|
|
122
|
+
console.log(`${(r.label ?? "-").padEnd(16)} ${r.releaseDigest} ${r.commit.slice(0, 12)} ${signature} ${where}`);
|
|
123
|
+
if (newest?.signatureCheck.status === "unverified") console.error(` ${r.label ?? r.releaseDigest}: ${newest.signatureCheck.reason}`);
|
|
124
|
+
}
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* `release export <label|digest> --env <env> [-o <dir>]` (#4046): write each
|
|
130
|
+
* file the release's target for <env> records, at the path it was compiled to,
|
|
131
|
+
* under <dir> (the member's directory by default), after checking each one's
|
|
132
|
+
* bytes against its digest. This is how a project with no components deploys
|
|
133
|
+
* a release: its own deploy reads these files instead of building again.
|
|
134
|
+
*/
|
|
135
|
+
export async function runReleaseExport(ctx: CommandContext): Promise<number> {
|
|
136
|
+
const { args } = ctx;
|
|
137
|
+
const ref = args.extraPositional;
|
|
138
|
+
if (!ref || !args.env) {
|
|
139
|
+
console.error(formatError({ message: "release export needs a release and an environment", hint: USAGE }));
|
|
140
|
+
return 1;
|
|
141
|
+
}
|
|
142
|
+
const { fetchLifecycle } = await import("../../lifecycle/git");
|
|
143
|
+
const { resolveRelease, readReleaseTemplate } = await import("../../lifecycle/release-store");
|
|
144
|
+
const { targetFor, targetFiles, writePinnedTemplates } = await import("../../components/release-promote");
|
|
145
|
+
await fetchLifecycle().catch(() => false);
|
|
146
|
+
const found = await resolveRelease(ref);
|
|
147
|
+
if (!found) {
|
|
148
|
+
console.error(formatError({ message: `no release is labelled or digested "${ref}" in this member`, hint: "List them with: chant release list" }));
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
const target = targetFor(found.release, args.env);
|
|
152
|
+
if ("error" in target) {
|
|
153
|
+
console.error(formatError({ message: target.error }));
|
|
154
|
+
return 1;
|
|
155
|
+
}
|
|
156
|
+
const read = await targetFiles(found.release, target.target, (digest) => readReleaseTemplate(digest));
|
|
157
|
+
if ("errors" in read) {
|
|
158
|
+
for (const e of read.errors) console.error(formatError({ message: e }));
|
|
159
|
+
return 1;
|
|
160
|
+
}
|
|
161
|
+
const dir = resolve(args.output ?? ".");
|
|
162
|
+
writePinnedTemplates(dir, read.files);
|
|
163
|
+
if (args.json) {
|
|
164
|
+
console.log(
|
|
165
|
+
JSON.stringify(
|
|
166
|
+
{ releaseDigest: found.release.releaseDigest, label: found.label, env: args.env, dir, templates: target.target.templates, params: target.target.params },
|
|
167
|
+
null,
|
|
168
|
+
2,
|
|
169
|
+
),
|
|
170
|
+
);
|
|
171
|
+
} else {
|
|
172
|
+
console.error(formatSuccess(`Wrote ${read.files.length} file(s) of ${found.label ? formatBold(found.label) : found.release.releaseDigest} for "${args.env}"`));
|
|
173
|
+
for (const f of read.files) console.log(f.path);
|
|
174
|
+
}
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Internal: compile one environment, or build artifacts, in this process, and print the JSON the cut reads. */
|
|
179
|
+
export async function runReleaseCompile(ctx: CommandContext): Promise<number> {
|
|
180
|
+
const { args } = ctx;
|
|
181
|
+
const env = args.env;
|
|
182
|
+
if (!env) {
|
|
183
|
+
console.error(formatError({ message: "release compile needs --env <env>" }));
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
const { compileEnvironment, buildReleaseArtifacts } = await import("../../components/release-compile");
|
|
187
|
+
const params = parseParamFlags(args.param);
|
|
188
|
+
const inputs = compileInputs(ctx, params);
|
|
189
|
+
try {
|
|
190
|
+
if (args.artifacts) {
|
|
191
|
+
const names = (args.component ?? "").split(",").map((n) => n.trim()).filter(Boolean);
|
|
192
|
+
console.log(JSON.stringify({ artifacts: await buildReleaseArtifacts(inputs, env, names) }));
|
|
193
|
+
} else {
|
|
194
|
+
console.log(JSON.stringify(await compileEnvironment(inputs, env)));
|
|
195
|
+
}
|
|
196
|
+
return 0;
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error(formatError({ message: err instanceof Error ? err.message : String(err) }));
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export async function runReleaseUnknown(ctx: CommandContext): Promise<number> {
|
|
204
|
+
console.error(formatError({ message: `Unknown release subcommand: ${ctx.args.path}`, hint: USAGE }));
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `recordAutoReleasesForRun` hands the release record (#4020): the run
|
|
3
|
+
* and its origin, and for a component that passed a gate, who approved it and
|
|
4
|
+
* which resolution admitted it.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect, vi, beforeEach, afterEach } from "vitest";
|
|
7
|
+
|
|
8
|
+
const maybeRecordAutoReleaseMock = vi.fn();
|
|
9
|
+
vi.mock("../../components/auto-release", async () => {
|
|
10
|
+
const actual = await vi.importActual<typeof import("../../components/auto-release")>("../../components/auto-release");
|
|
11
|
+
return { ...actual, maybeRecordAutoRelease: (...args: unknown[]) => maybeRecordAutoReleaseMock(...args) };
|
|
12
|
+
});
|
|
13
|
+
vi.mock("../../components/manifest-persistence", async () => {
|
|
14
|
+
const actual = await vi.importActual<typeof import("../../components/manifest-persistence")>("../../components/manifest-persistence");
|
|
15
|
+
return { ...actual, maybePersistBuildManifest: async () => ({ persisted: false, reason: "no-manifest" }) };
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
import { autoReleaseRun, recordAutoReleasesForRun } from "./run";
|
|
19
|
+
import type { DriverComponentResult } from "../../components/driver";
|
|
20
|
+
|
|
21
|
+
const gatedResult = {
|
|
22
|
+
component: "api",
|
|
23
|
+
ok: true,
|
|
24
|
+
status: "ok",
|
|
25
|
+
records: [
|
|
26
|
+
{ component: "api", phase: "Deploy", kind: "gate:approve-prod", status: "ok", durationMs: 1, approval: { gate: "approve-prod", resolvedBy: "alice", timestamp: "2026-10-16T10:05:00.000Z" } },
|
|
27
|
+
{ component: "api", phase: "Deploy", kind: "apply", status: "ok", durationMs: 1 },
|
|
28
|
+
],
|
|
29
|
+
} as unknown as DriverComponentResult;
|
|
30
|
+
const plainResult = { component: "worker", ok: true, status: "ok", records: [] } as unknown as DriverComponentResult;
|
|
31
|
+
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
maybeRecordAutoReleaseMock.mockReset().mockResolvedValue({ recorded: false, reason: "no-digest" });
|
|
34
|
+
});
|
|
35
|
+
afterEach(() => vi.unstubAllEnvs());
|
|
36
|
+
|
|
37
|
+
describe("recordAutoReleasesForRun", () => {
|
|
38
|
+
test("a component that passed a gate records its approver and admission, one that did not records neither", async () => {
|
|
39
|
+
await recordAutoReleasesForRun([gatedResult, plainResult], "prod", "9001", false, { forge: "github", repo: "acme/app" });
|
|
40
|
+
const [gated, plain] = maybeRecordAutoReleaseMock.mock.calls.map((c) => c[0]);
|
|
41
|
+
expect(gated).toMatchObject({
|
|
42
|
+
component: "api", runId: "9001", runOrigin: { forge: "github", repo: "acme/app" },
|
|
43
|
+
approver: "alice", admission: { op: "api", gate: "approve-prod", resolvedAt: "2026-10-16T10:05:00.000Z" },
|
|
44
|
+
});
|
|
45
|
+
expect(plain).not.toHaveProperty("approver");
|
|
46
|
+
expect(plain).not.toHaveProperty("admission");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("the origin defaults to local", async () => {
|
|
50
|
+
await recordAutoReleasesForRun([plainResult], "prod", "local-1", false);
|
|
51
|
+
expect(maybeRecordAutoReleaseMock.mock.calls[0][0]).toMatchObject({ runId: "local-1", runOrigin: { forge: "local" } });
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("autoReleaseRun", () => {
|
|
56
|
+
test("in CI it names the CI run, so the release and the gate it passed name one run", () => {
|
|
57
|
+
vi.stubEnv("GITHUB_RUN_ID", "9001");
|
|
58
|
+
vi.stubEnv("GITHUB_REPOSITORY", "acme/app");
|
|
59
|
+
vi.stubEnv("GITHUB_SERVER_URL", "https://github.example");
|
|
60
|
+
expect(autoReleaseRun("local-1")).toEqual({
|
|
61
|
+
runId: "9001",
|
|
62
|
+
runOrigin: { forge: "github", repo: "acme/app", url: "https://github.example/acme/app/actions/runs/9001" },
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("outside CI it keeps the local id", () => {
|
|
67
|
+
vi.stubEnv("GITHUB_RUN_ID", "");
|
|
68
|
+
vi.stubEnv("CI_PIPELINE_ID", "");
|
|
69
|
+
expect(autoReleaseRun("local-1")).toEqual({ runId: "local-1", runOrigin: { forge: "local" } });
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** #4042: `chant run events <run> [--json] [--follow]`. */
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
8
|
+
import type { ParsedArgs } from "../registry";
|
|
9
|
+
import { runOpEvents } from "./run";
|
|
10
|
+
import { openRunEventStore } from "../../run-event-store";
|
|
11
|
+
import { openRunEventStream, runEventRunOf } from "../../run-event-stream";
|
|
12
|
+
|
|
13
|
+
function args(o: Partial<ParsedArgs>): ParsedArgs {
|
|
14
|
+
return { command: "run", path: ".", format: "", fix: false, watch: false, verbose: false, help: false, live: false, ...o };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let dir: string;
|
|
18
|
+
let cwd: string;
|
|
19
|
+
let out: string[];
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
cwd = process.cwd();
|
|
22
|
+
vi.stubEnv("CHANT_RUN_EVENTS_DIR", "");
|
|
23
|
+
dir = realpathSync(mkdtempSync(join(tmpdir(), "run-events-cmd-")));
|
|
24
|
+
execFileSync("git", ["init", "-q", "-b", "main"], { cwd: dir });
|
|
25
|
+
execFileSync("git", ["-c", "user.email=t@t", "-c", "user.name=t", "commit", "-q", "--allow-empty", "-m", "init"], { cwd: dir });
|
|
26
|
+
process.chdir(dir);
|
|
27
|
+
out = [];
|
|
28
|
+
vi.spyOn(process.stdout, "write").mockImplementation((s: string | Uint8Array) => (out.push(String(s)), true));
|
|
29
|
+
vi.spyOn(console, "log").mockImplementation((s: string) => void out.push(`${s}\n`));
|
|
30
|
+
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
31
|
+
});
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
vi.restoreAllMocks();
|
|
34
|
+
vi.unstubAllEnvs();
|
|
35
|
+
process.chdir(cwd);
|
|
36
|
+
rmSync(dir, { recursive: true, force: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
async function stream(runId: string, name = "deploy-api") {
|
|
40
|
+
const store = (await openRunEventStore(dir, runId))!;
|
|
41
|
+
return openRunEventStream(runEventRunOf({ kind: "op", name, runId }), store.write);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("chant run events", () => {
|
|
45
|
+
test("--json replays a settled run's lines and exits with its verdict", async () => {
|
|
46
|
+
const s = await stream("r-done");
|
|
47
|
+
s.emit({ type: "run-start", waves: null });
|
|
48
|
+
s.settle("failed", "boom");
|
|
49
|
+
const exit = await runOpEvents({ args: args({ extraPositional: "r-done", json: true }), plugins: [], serializers: [] });
|
|
50
|
+
expect(exit).toBe(1);
|
|
51
|
+
const events = out.join("").trim().split("\n").map((l) => JSON.parse(l));
|
|
52
|
+
expect(events.map((e) => e.type)).toEqual(["run-start", "run-settle"]);
|
|
53
|
+
expect(events[1]).toMatchObject({ status: "failed", error: "boom" });
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("--json replays a run in progress up to now and exits 0", async () => {
|
|
57
|
+
const s = await stream("r-live");
|
|
58
|
+
s.emit({ type: "run-start", waves: null });
|
|
59
|
+
s.emit({ type: "phase-start", component: null, phase: "Build" });
|
|
60
|
+
const exit = await runOpEvents({ args: args({ extraPositional: "deploy-api", json: true }), plugins: [], serializers: [] });
|
|
61
|
+
expect(exit).toBe(0);
|
|
62
|
+
expect(out.join("").trim().split("\n").map((l) => JSON.parse(l).type)).toEqual(["run-start", "phase-start"]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("--follow prints each event of a run in progress, then the verdict", async () => {
|
|
66
|
+
const s = await stream("r-follow");
|
|
67
|
+
s.emit({ type: "run-start", waves: null });
|
|
68
|
+
const done = runOpEvents({ args: args({ extraPositional: "r-follow", json: true, follow: true }), plugins: [], serializers: [] });
|
|
69
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
70
|
+
expect(out.join("").trim().split("\n")).toHaveLength(1);
|
|
71
|
+
s.emit({ type: "phase-start", component: null, phase: "Build" });
|
|
72
|
+
s.emit({ type: "phase-settle", component: null, phase: "Build", status: "gated" });
|
|
73
|
+
s.settle("gated");
|
|
74
|
+
expect(await done).toBe(3);
|
|
75
|
+
expect(out.join("").trim().split("\n").map((l) => JSON.parse(l).type)).toEqual(["run-start", "phase-start", "phase-settle", "run-settle"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("without --json each event is one line for a person", async () => {
|
|
79
|
+
const s = await stream("r-human");
|
|
80
|
+
s.emit({ type: "run-start", waves: null });
|
|
81
|
+
s.settle("ok");
|
|
82
|
+
expect(await runOpEvents({ args: args({ extraPositional: "r-human" }), plugins: [], serializers: [] })).toBe(0);
|
|
83
|
+
expect(out.join("")).toMatch(/run-start/);
|
|
84
|
+
expect(out.join("")).toMatch(/run-settle\s+ok/);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("a run with no kept events is refused by name; no run is refused", async () => {
|
|
88
|
+
expect(await runOpEvents({ args: args({ extraPositional: "nope" }), plugins: [], serializers: [] })).toBe(1);
|
|
89
|
+
expect(await runOpEvents({ args: args({}), plugins: [], serializers: [] })).toBe(1);
|
|
90
|
+
expect(await runOpEvents({ args: args({ extraPositional: "x", on: "fountain" }), plugins: [], serializers: [] })).toBe(1);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -98,6 +98,7 @@ vi.mock("../../lifecycle/run-ledger", async (importOriginal) => {
|
|
|
98
98
|
|
|
99
99
|
const { runOpList, runOpStatus, runOpLog, runOpSignalRenamed, runOpApprove, runOpCancel, runOp, runOpComponents } =
|
|
100
100
|
await import("./run");
|
|
101
|
+
const { checkRunEventLines } = await import("../../workspace/__fixtures__/run-event-check");
|
|
101
102
|
|
|
102
103
|
function makeArgs(overrides: Partial<ParsedArgs> = {}): ParsedArgs {
|
|
103
104
|
return {
|
|
@@ -1554,6 +1555,35 @@ describe("runOpComponents", () => {
|
|
|
1554
1555
|
vi.restoreAllMocks();
|
|
1555
1556
|
});
|
|
1556
1557
|
|
|
1558
|
+
test("--events-json streams the run-event stream as runComponents emits it, and ends one that never reached a component (#4041)", async () => {
|
|
1559
|
+
runComponentsMock.mockImplementation(
|
|
1560
|
+
async (_path: string, _selector: string, options: { onRunEvent?: (b: unknown) => void }) => {
|
|
1561
|
+
options.onRunEvent?.({ type: "run-start", waves: [["svc"]] });
|
|
1562
|
+
options.onRunEvent?.({ type: "run-settle", status: "ok", durationMs: null });
|
|
1563
|
+
return {
|
|
1564
|
+
success: true,
|
|
1565
|
+
selected: ["svc"],
|
|
1566
|
+
run: { order: ["svc"], waves: [["svc"]], results: [{ component: "svc", ok: true, status: "ok", records: [] }], ok: true, status: "ok" },
|
|
1567
|
+
};
|
|
1568
|
+
},
|
|
1569
|
+
);
|
|
1570
|
+
const stdoutWrite = vi.spyOn(process.stdout, "write").mockImplementation(() => true);
|
|
1571
|
+
vi.spyOn(process.stderr, "write").mockImplementation(() => true);
|
|
1572
|
+
|
|
1573
|
+
expect(await runOpComponents({ args: makeArgs({ path: "svc", eventsJson: true }), plugins: [], serializers: [] })).toBe(0);
|
|
1574
|
+
const events = checkRunEventLines(stdoutWrite.mock.calls.map((c) => String(c[0])));
|
|
1575
|
+
expect(events[0]).toMatchObject({ type: "run-start", kind: "component", name: "svc", waves: [["svc"]] });
|
|
1576
|
+
expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "ok" });
|
|
1577
|
+
|
|
1578
|
+
stdoutWrite.mockClear();
|
|
1579
|
+
runComponentsMock.mockReset().mockResolvedValue({ success: false, selected: [], error: "no component named svc" });
|
|
1580
|
+
expect(await runOpComponents({ args: makeArgs({ path: "svc", eventsJson: true }), plugins: [], serializers: [] })).toBe(1);
|
|
1581
|
+
const failed = checkRunEventLines(stdoutWrite.mock.calls.map((c) => String(c[0])));
|
|
1582
|
+
expect(failed.map((e) => e.type)).toEqual(["run-start", "run-settle"]);
|
|
1583
|
+
expect(failed.at(-1)).toMatchObject({ status: "failed", error: "no component named svc" });
|
|
1584
|
+
vi.restoreAllMocks();
|
|
1585
|
+
});
|
|
1586
|
+
|
|
1557
1587
|
test("without --progress-json, runComponents receives onProgress: undefined and nothing streams", async () => {
|
|
1558
1588
|
runComponentsMock.mockResolvedValue({
|
|
1559
1589
|
success: true,
|