@intentius/chant 0.124.27 → 0.124.29
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/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +63 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +130 -0
- package/dist/change-set.d.ts +62 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +36 -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/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- 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 +15 -11
- 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 +64 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +43 -4
- 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 +10 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +54 -6
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +16 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +269 -37
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +440 -28
- package/dist/cli/handlers/release.d.ts +91 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +589 -0
- package/dist/cli/handlers/run.d.ts +38 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +234 -16
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +143 -17
- 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 +73 -5
- package/dist/cli/registry.d.ts +43 -5
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +30 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +49 -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 +6 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- 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 +50 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +116 -54
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- 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 +85 -26
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- 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 +38 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +77 -2
- package/dist/components/release-compile.d.ts +142 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +382 -0
- package/dist/components/release-gate.d.ts +135 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +288 -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 +8 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +11 -3
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -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/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +68 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +97 -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/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +78 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +138 -0
- package/dist/lifecycle/gate-ledger.d.ts +51 -4
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +154 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +366 -0
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -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-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +200 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +320 -0
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -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/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -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/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +104 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +251 -0
- package/dist/run-event-stream.d.ts +133 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +242 -0
- package/dist/run-event.d.ts +285 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +209 -0
- package/dist/wave-runtime.mjs +262 -226
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- 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 +32 -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/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- 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 +281 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +456 -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 +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +27 -1
- package/dist/workspace/declaration.schema.json +76 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- 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-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +36 -7
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +192 -22
- package/dist/workspace/intent.schema.json +702 -6
- 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/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -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 +4 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +7 -1
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +81 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +103 -0
- package/dist/workspace/releases.schema.json +513 -0
- package/dist/workspace/run-event.schema.json +295 -0
- package/dist/workspace/status-gates.d.ts +34 -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 +117 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +218 -4
- package/dist/workspace/status.schema.json +268 -2
- 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/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- 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/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +141 -0
- package/src/change-set.ts +87 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +25 -11
- 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 +84 -0
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +65 -2
- package/src/cli/handlers/build.test.ts +67 -0
- package/src/cli/handlers/build.ts +46 -4
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +228 -1
- package/src/cli/handlers/fan-out.ts +53 -6
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +341 -0
- package/src/cli/handlers/pr.ts +292 -47
- package/src/cli/handlers/promote.test.ts +669 -7
- package/src/cli/handlers/promote.ts +464 -29
- package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +199 -0
- package/src/cli/handlers/release.ts +593 -0
- package/src/cli/handlers/run-auto-release.test.ts +80 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +243 -18
- package/src/cli/main.test.ts +8 -0
- package/src/cli/main.ts +143 -17
- 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 +56 -5
- package/src/cli/registry.ts +43 -5
- package/src/cli/run-events.ts +57 -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 +30 -0
- package/src/components/auto-release.ts +9 -1
- package/src/components/capability.ts +9 -0
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +165 -56
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +104 -35
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- 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 +94 -3
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +459 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +355 -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 +16 -3
- package/src/config.ts +15 -1
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/index.ts +2 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +113 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +164 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +68 -3
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +483 -0
- package/src/lifecycle/release-cut.ts +503 -0
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +434 -0
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -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/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/point-decision.ts +12 -2
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +273 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +309 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +471 -0
- package/src/wave-loop-golden.test.ts +9 -0
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -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 +32 -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/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- 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 +635 -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 +76 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +60 -2
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +196 -26
- 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/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +5 -1
- package/src/workspace/reason-codes.test.ts +7 -1
- package/src/workspace/reason-codes.ts +7 -1
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +513 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +133 -0
- package/src/workspace/run-event.schema.json +295 -0
- package/src/workspace/status-contract.test.ts +81 -1
- package/src/workspace/status-gates.ts +64 -1
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +268 -2
- package/src/workspace/status.ts +334 -4
- 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/workspace/wip.ts +11 -2
- 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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A promote under a release (#4026, ws-104 question 5): the deploy reads the
|
|
3
|
+
* templates the release recorded for the target environment, not the ones in
|
|
4
|
+
* the promoting checkout.
|
|
5
|
+
*
|
|
6
|
+
* Each template a promoted component's deploy names ({@link templateRefs}) has
|
|
7
|
+
* to be one the release's target records for that component. Its bytes are
|
|
8
|
+
* read from `_releases/templates/` on `chant/lifecycle` and checked against
|
|
9
|
+
* the target's digest, the way a promote checks the publish digest, and a
|
|
10
|
+
* mismatch refuses the promote before anything runs. The bytes are written
|
|
11
|
+
* under `.chant/release-templates/<digest>/` in the member's directory and the
|
|
12
|
+
* step reads them from there. The path is relative and fixed by the digest,
|
|
13
|
+
* so a gate approval bound to the promote's plan still matches on a re-run.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { mkdirSync, readdirSync, rmSync, rmdirSync, writeFileSync } from "node:fs";
|
|
17
|
+
import { basename, dirname, join } from "node:path";
|
|
18
|
+
import { contentDigest } from "../content-digest";
|
|
19
|
+
import type { DriverComponent, DriverGate, DriverPhase, DriverStep } from "./driver";
|
|
20
|
+
import { TEMPLATE_FIELDS, templateRefs } from "./release-compile";
|
|
21
|
+
import type { Release, ReleaseTarget } from "../lifecycle/release-store";
|
|
22
|
+
import type { ReleaseRecord } from "../lifecycle/release-ledger";
|
|
23
|
+
import type { BuildArchiveManifest } from "./verbs/build-archive";
|
|
24
|
+
|
|
25
|
+
/** Where a promote under a release writes the recorded templates, relative to the member's directory. */
|
|
26
|
+
export const RELEASE_TEMPLATES_DIR = ".chant/release-templates";
|
|
27
|
+
|
|
28
|
+
/** A template ready to write: where, and its bytes. */
|
|
29
|
+
export interface PinnedTemplate {
|
|
30
|
+
/** Relative to the member's directory. */
|
|
31
|
+
path: string;
|
|
32
|
+
content: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The release's target for `env`, or an error naming the environments it has. */
|
|
36
|
+
export function targetFor(release: Release, env: string): { target: ReleaseTarget } | { error: string } {
|
|
37
|
+
const target = release.targets.find((t) => t.env === env);
|
|
38
|
+
if (target) return { target };
|
|
39
|
+
return {
|
|
40
|
+
error: `release ${release.releaseDigest} has no target for "${env}"; it was compiled for ${release.targets.map((t) => t.env).join(", ") || "no environment"}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Whether a release is of a project with no components (#4046): no artifact, and every template it records has `component: null`. */
|
|
45
|
+
export function isImplicitRelease(release: Release): boolean {
|
|
46
|
+
const templates = release.targets.flatMap((t) => t.templates);
|
|
47
|
+
return release.artifactDigests.length === 0 && templates.length > 0 && templates.every((t) => t.component === null);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isGate(step: DriverStep | DriverGate | DriverPhase): step is DriverGate {
|
|
51
|
+
return (step as { kind?: unknown }).kind === "gate";
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isPhase(step: DriverStep | DriverGate | DriverPhase): step is DriverPhase {
|
|
55
|
+
return typeof (step as { phase?: unknown }).phase === "string" && Array.isArray((step as DriverPhase).steps);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Point every template `components` read at the target's recorded bytes.
|
|
60
|
+
* Returns the rewritten compositions and the files to write, or the reasons
|
|
61
|
+
* the promote is refused: a template the target does not record, one whose
|
|
62
|
+
* bytes are missing, or one whose bytes do not hash to the target's digest.
|
|
63
|
+
*/
|
|
64
|
+
export async function pinReleaseTemplates(
|
|
65
|
+
components: readonly DriverComponent[],
|
|
66
|
+
release: Release,
|
|
67
|
+
target: ReleaseTarget,
|
|
68
|
+
readTemplate: (digest: string) => Promise<string | null>,
|
|
69
|
+
): Promise<{ components: DriverComponent[]; files: PinnedTemplate[] } | { errors: string[] }> {
|
|
70
|
+
const errors: string[] = [];
|
|
71
|
+
const files: PinnedTemplate[] = [];
|
|
72
|
+
const out: DriverComponent[] = [];
|
|
73
|
+
for (const component of components) {
|
|
74
|
+
const replace = new Map<string, string>();
|
|
75
|
+
for (const ref of templateRefs(component)) {
|
|
76
|
+
if (replace.has(ref.value)) continue;
|
|
77
|
+
const recorded = target.templates.find((t) => t.component === component.name && t.path === ref.path);
|
|
78
|
+
if (!recorded) {
|
|
79
|
+
errors.push(
|
|
80
|
+
`component "${component.name}" reads ${ref.field} "${ref.value}", which release ${release.releaseDigest} does not record for "${target.env}"; ` +
|
|
81
|
+
`this checkout's deploy is not the one the release was compiled from (commit ${release.commit.slice(0, 12)})`,
|
|
82
|
+
);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const content = await readTemplate(recorded.digest);
|
|
86
|
+
if (content === null) {
|
|
87
|
+
errors.push(`component "${component.name}": the bytes of template "${ref.path}" (${recorded.digest}) are not on the chant/lifecycle branch; fetch it and try again`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const got = contentDigest(content);
|
|
91
|
+
if (got !== recorded.digest) {
|
|
92
|
+
errors.push(
|
|
93
|
+
`component "${component.name}": template "${ref.path}" hashes to ${got}, but the release's target for "${target.env}" records ${recorded.digest}; refusing to deploy it`,
|
|
94
|
+
);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
const path = join(RELEASE_TEMPLATES_DIR, recorded.digest.replace(/:/g, "_"), basename(ref.path));
|
|
98
|
+
files.push({ path, content });
|
|
99
|
+
replace.set(ref.value, path);
|
|
100
|
+
}
|
|
101
|
+
const rewrite = (phase: DriverPhase): DriverPhase => ({
|
|
102
|
+
...phase,
|
|
103
|
+
steps: phase.steps.map((step) => {
|
|
104
|
+
if (isPhase(step)) return rewrite(step);
|
|
105
|
+
if (isGate(step)) return step;
|
|
106
|
+
const next: DriverStep = { ...step };
|
|
107
|
+
for (const field of TEMPLATE_FIELDS) {
|
|
108
|
+
const v = next[field];
|
|
109
|
+
if (typeof v === "string" && replace.has(v)) next[field] = replace.get(v);
|
|
110
|
+
else if (Array.isArray(v)) next[field] = v.map((x) => (typeof x === "string" && replace.has(x) ? replace.get(x) : x));
|
|
111
|
+
}
|
|
112
|
+
return next;
|
|
113
|
+
}),
|
|
114
|
+
});
|
|
115
|
+
out.push({ ...component, deploy: component.deploy.map(rewrite) });
|
|
116
|
+
}
|
|
117
|
+
return errors.length > 0 ? { errors } : { components: out, files };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The files a release's target records, read back by digest and checked, for
|
|
122
|
+
* `chant release export` (#4046). A project with no components has no deploy
|
|
123
|
+
* step for a promote to point at the recorded bytes, so its deploy (an Op such
|
|
124
|
+
* as `chant run deploy`, or a CI job) reads the files this writes instead of
|
|
125
|
+
* building them again. Each path is the one the build wrote, relative to the
|
|
126
|
+
* member's directory; a template whose bytes are missing or don't hash to the
|
|
127
|
+
* target's digest is refused, as {@link pinReleaseTemplates} refuses one.
|
|
128
|
+
*/
|
|
129
|
+
export async function targetFiles(
|
|
130
|
+
release: Release,
|
|
131
|
+
target: ReleaseTarget,
|
|
132
|
+
readTemplate: (digest: string) => Promise<string | null>,
|
|
133
|
+
): Promise<{ files: PinnedTemplate[] } | { errors: string[] }> {
|
|
134
|
+
const errors: string[] = [];
|
|
135
|
+
const files: PinnedTemplate[] = [];
|
|
136
|
+
for (const t of target.templates) {
|
|
137
|
+
const who = t.component === null ? "" : `component "${t.component}": `;
|
|
138
|
+
if (t.path.startsWith("/") || t.path.split("/").includes("..")) {
|
|
139
|
+
errors.push(`${who}template "${t.path}" is not a path inside the member's directory; refusing to write it`);
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
const content = await readTemplate(t.digest);
|
|
143
|
+
if (content === null) {
|
|
144
|
+
errors.push(`${who}the bytes of template "${t.path}" (${t.digest}) are not on the chant/lifecycle branch; fetch it and try again`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const got = contentDigest(content);
|
|
148
|
+
if (got !== t.digest) {
|
|
149
|
+
errors.push(`${who}template "${t.path}" hashes to ${got}, but release ${release.releaseDigest} records ${t.digest} for "${target.env}"; refusing to write it`);
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const same = files.find((f) => f.path === t.path);
|
|
153
|
+
if (same) {
|
|
154
|
+
if (same.content !== content) errors.push(`two components record different bytes at "${t.path}" for "${target.env}", so they can't both be written there`);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
files.push({ path: t.path, content });
|
|
158
|
+
}
|
|
159
|
+
return errors.length > 0 ? { errors } : { files };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Write the pinned templates under `dir`, the member's directory. */
|
|
163
|
+
export function writePinnedTemplates(dir: string, files: readonly PinnedTemplate[]): void {
|
|
164
|
+
for (const f of files) {
|
|
165
|
+
const full = join(dir, f.path);
|
|
166
|
+
mkdirSync(dirname(full), { recursive: true });
|
|
167
|
+
writeFileSync(full, f.content);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Remove the templates {@link writePinnedTemplates} wrote once the promote is
|
|
173
|
+
* done, and {@link RELEASE_TEMPLATES_DIR} with them when nothing else is in
|
|
174
|
+
* it, so a promote leaves the checkout as it found it. A re-run writes them
|
|
175
|
+
* again from the release.
|
|
176
|
+
*/
|
|
177
|
+
export function removePinnedTemplates(dir: string, files: readonly PinnedTemplate[]): void {
|
|
178
|
+
for (const f of files) rmSync(dirname(join(dir, f.path)), { recursive: true, force: true });
|
|
179
|
+
const root = join(dir, RELEASE_TEMPLATES_DIR);
|
|
180
|
+
try {
|
|
181
|
+
if (readdirSync(root).length === 0) rmdirSync(root);
|
|
182
|
+
} catch {
|
|
183
|
+
// Already gone, or not ours to remove.
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Whether the artifact a promote deploys under a release is one the release
|
|
189
|
+
* built (#4045, the rule #4026 left open). The release records build digests
|
|
190
|
+
* (`artifactDigests`, the digest the archive step returned), and a deploy
|
|
191
|
+
* record carries the publish digest, which is another value for an image, so
|
|
192
|
+
* the two are compared through the build manifest the source deploy record
|
|
193
|
+
* names (`manifestDigest`), whose entries carry build digests at the same
|
|
194
|
+
* archive paths:
|
|
195
|
+
*
|
|
196
|
+
* - a source record that names this release in `releaseDigest` matches;
|
|
197
|
+
* - else, each of the release's artifacts for the component must be in that
|
|
198
|
+
* manifest at its path with the release's digest, and one that differs is
|
|
199
|
+
* a mismatch;
|
|
200
|
+
* - a record with no `manifestDigest`, a manifest that isn't stored, or an
|
|
201
|
+
* artifact path the manifest doesn't hold can't be compared.
|
|
202
|
+
*
|
|
203
|
+
* A mismatch is a warning, not a refusal. The environment promoted from was
|
|
204
|
+
* deployed by its own `run --components`, which builds again, and an image
|
|
205
|
+
* build is not reproducible, so its digest differs from the cut's for the
|
|
206
|
+
* same commit. Refusing would refuse every image promote until a deploy can
|
|
207
|
+
* run the release's own artifacts (#4043).
|
|
208
|
+
*/
|
|
209
|
+
export async function releaseArtifactMismatches(
|
|
210
|
+
release: Release,
|
|
211
|
+
sources: ReadonlyArray<{ component: string; source: ReleaseRecord }>,
|
|
212
|
+
readManifest: (manifestDigest: string) => Promise<BuildArchiveManifest | null>,
|
|
213
|
+
): Promise<string[]> {
|
|
214
|
+
const out: string[] = [];
|
|
215
|
+
for (const { component, source } of sources) {
|
|
216
|
+
if (source.releaseDigest === release.releaseDigest) continue;
|
|
217
|
+
const artifacts = release.artifactDigests.filter((a) => a.component === component);
|
|
218
|
+
if (artifacts.length === 0 || source.manifestDigest === undefined) continue;
|
|
219
|
+
const manifest = await readManifest(source.manifestDigest);
|
|
220
|
+
if (!manifest) continue;
|
|
221
|
+
for (const a of artifacts) {
|
|
222
|
+
const entry = manifest.contents.find((e) => e.path === a.path && e.kind === a.kind);
|
|
223
|
+
if (entry && entry.digest !== a.digest) {
|
|
224
|
+
out.push(
|
|
225
|
+
`${component}: the ${source.env} deploy being promoted was built with ${a.path} at ${entry.digest}, ` +
|
|
226
|
+
`but release ${release.releaseDigest} built it at ${a.digest}, so the artifact deployed is not the release's own`,
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
@@ -90,6 +90,13 @@ export interface RunDoneEvent {
|
|
|
90
90
|
status: RunProgressStatus;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated Read `RunEvent` (`../run-event.ts`, exported from
|
|
95
|
+
* `@intentius/chant`) instead: one contract for Op, component and pipeline
|
|
96
|
+
* runs, with the run id, commit and runtime on every event (#4019).
|
|
97
|
+
* `runEventOfProgress` maps each of these onto it. `--progress-json` keeps
|
|
98
|
+
* printing this shape until #4041 moves it to the run-event stream.
|
|
99
|
+
*/
|
|
93
100
|
export type RunProgressEvent =
|
|
94
101
|
| RunStartEvent
|
|
95
102
|
| WaveStartEvent
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* `@<component>.publish.*`) resolves.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { resolveStepInput, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
|
|
22
|
+
import { resolveStepInput, unresolvedWiringSources, type DriverComponent, type DriverPhase, type DriverStep } from "./driver";
|
|
23
23
|
import { componentPlanDigest } from "./gate-plan";
|
|
24
24
|
import type { CapabilityRegistry, DeployContext } from "./capability";
|
|
25
25
|
import type { WaveMember } from "../gated-waves";
|
|
@@ -44,7 +44,7 @@ function emptyPart(member: WaveMember): ChangeSetPart {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/** Every capability step in a composition, nested phases included, gates left out. */
|
|
47
|
-
function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
|
|
47
|
+
export function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
|
|
48
48
|
const out: DriverStep[] = [];
|
|
49
49
|
const walk = (steps: DriverPhase["steps"]): void => {
|
|
50
50
|
for (const step of steps) {
|
|
@@ -60,6 +60,19 @@ function capabilitySteps(phases: readonly DriverPhase[]): DriverStep[] {
|
|
|
60
60
|
return out;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
/**
|
|
64
|
+
* The context a step is planned with: `ctx`, plus the components whose
|
|
65
|
+
* outputs the step reads and could not read yet (`unresolvedInputs`, #4084).
|
|
66
|
+
*/
|
|
67
|
+
export function planContext(
|
|
68
|
+
ctx: DeployContext,
|
|
69
|
+
stepInput: Record<string, unknown>,
|
|
70
|
+
componentOutputs: Record<string, Record<string, unknown>>,
|
|
71
|
+
): DeployContext {
|
|
72
|
+
const unresolved = unresolvedWiringSources(stepInput, componentOutputs);
|
|
73
|
+
return unresolved.length > 0 ? { ...ctx, unresolvedInputs: unresolved } : ctx;
|
|
74
|
+
}
|
|
75
|
+
|
|
63
76
|
/** Plan one component against the outputs earlier waves produced. Never throws: a failure comes back as `error`. */
|
|
64
77
|
export async function planWaveComponent(
|
|
65
78
|
component: DriverComponent,
|
|
@@ -76,7 +89,7 @@ export async function planWaveComponent(
|
|
|
76
89
|
if (!capability.plan) continue;
|
|
77
90
|
const { kind: _kind, ...rest } = step;
|
|
78
91
|
const input = resolveStepInput(rest, {}, componentOutputs);
|
|
79
|
-
const planned = await capability.plan(ctx, input as never);
|
|
92
|
+
const planned = await capability.plan(planContext(ctx, rest, componentOutputs), input as never);
|
|
80
93
|
const member = { member: planned.member, planDigest: planned.planDigest };
|
|
81
94
|
members.push(member);
|
|
82
95
|
parts.push(planned.changeSet ?? emptyPart(member));
|
package/src/config.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { findProjectConfig } from "./project-root";
|
|
|
12
12
|
import { evaluateProjectConfig } from "./config-sandbox";
|
|
13
13
|
import { lexiconNames, pathLexiconRoot, registerLexiconDeclarations, type LexiconDeclaration } from "./lexicon-module";
|
|
14
14
|
import { decideConfigSchema, type DecideConfig } from "./op/decide-config";
|
|
15
|
+
import { PublishDeclarationSchema, type PublishDeclaration } from "./lifecycle/publish-declaration";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* One project-declared environment (chant #1166). Historically always a bare
|
|
@@ -22,8 +23,13 @@ import { decideConfigSchema, type DecideConfig } from "./op/decide-config";
|
|
|
22
23
|
* silently querying real AWS. See {@link environmentName}, {@link
|
|
23
24
|
* environmentNames}, {@link environmentEndpoint} below, and
|
|
24
25
|
* `./live-endpoint.ts`'s `applyLiveEndpoint`, the CLI-side consumer.
|
|
26
|
+
*
|
|
27
|
+
* The array's order is the member's promotion order (#4030, see
|
|
28
|
+
* `./workspace/promotion-order.ts`), and `canary: true` marks a step as the
|
|
29
|
+
* canary step of that order. The canary is declared and surfaced; chant does
|
|
30
|
+
* not yet act on it.
|
|
25
31
|
*/
|
|
26
|
-
export type EnvironmentDeclaration = string | { name: string; endpoint?: string };
|
|
32
|
+
export type EnvironmentDeclaration = string | { name: string; endpoint?: string; canary?: boolean };
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
35
|
* `ownership.env`: a literal environment identity, or a reference to a
|
|
@@ -124,6 +130,7 @@ const EnvironmentEntrySchema = z.union([
|
|
|
124
130
|
z.object({
|
|
125
131
|
name: z.string().min(1),
|
|
126
132
|
endpoint: z.string().min(1).optional(),
|
|
133
|
+
canary: z.boolean().optional(),
|
|
127
134
|
}),
|
|
128
135
|
]);
|
|
129
136
|
|
|
@@ -163,6 +170,7 @@ export const ChantConfigSchema = z.object({
|
|
|
163
170
|
).optional(),
|
|
164
171
|
release: z.object({
|
|
165
172
|
autoRecord: z.boolean().optional(),
|
|
173
|
+
publish: z.array(PublishDeclarationSchema).optional(),
|
|
166
174
|
}).optional(),
|
|
167
175
|
// Strict: a misspelt key here would leave Ops on the local runtime with
|
|
168
176
|
// nothing said (#2523).
|
|
@@ -376,6 +384,12 @@ export interface ChantConfig {
|
|
|
376
384
|
release?: {
|
|
377
385
|
/** Set false to disable auto-emitting a release record after a successful component deploy project-wide. The `--no-release-record` CLI flag overrides this per-invocation. */
|
|
378
386
|
autoRecord?: boolean;
|
|
387
|
+
/**
|
|
388
|
+
* Where `chant release publish` publishes a cut release (#4047): npm
|
|
389
|
+
* packages, an image in an OCI registry, release assets on a forge. Run in
|
|
390
|
+
* the order given. See ./lifecycle/publish-declaration.ts.
|
|
391
|
+
*/
|
|
392
|
+
publish?: PublishDeclaration[];
|
|
379
393
|
};
|
|
380
394
|
|
|
381
395
|
/** Defaults for `chant run` and its subcommands (#2523). */
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time parameters from workspace flags (#4024): what a build for an
|
|
3
|
+
* environment resolves in a member with flags, and the precedence rule that a
|
|
4
|
+
* flag-backed parameter can't be overridden where the environment has a gate.
|
|
5
|
+
* On the fixture in `workspace/__fixtures__/flag-workspace.ts`, where member
|
|
6
|
+
* `shop`'s component declares a gate and member `blog`'s does not.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import { afterAll, beforeAll, describe, expect, test, vi } from "vitest";
|
|
12
|
+
import { buildCommand } from "./cli/commands/build";
|
|
13
|
+
import type { Serializer } from "./serializer";
|
|
14
|
+
import type { BuildParamsConfig } from "./build-params";
|
|
15
|
+
import { loadBuildParamFlags, resolveFlaggedBuildParams } from "./flag-params";
|
|
16
|
+
import { cleanScratch, scratchDir } from "./workspace/__fixtures__/contract-repo";
|
|
17
|
+
import { flagWorkspace } from "./workspace/__fixtures__/flag-workspace";
|
|
18
|
+
|
|
19
|
+
const SHOP: BuildParamsConfig = { newCheckout: { type: "boolean", default: false }, tier: { type: "string", default: "small" } };
|
|
20
|
+
const BLOG: BuildParamsConfig = { banner: { type: "boolean", default: false } };
|
|
21
|
+
|
|
22
|
+
let root: string;
|
|
23
|
+
beforeAll(() => {
|
|
24
|
+
root = flagWorkspace();
|
|
25
|
+
});
|
|
26
|
+
afterAll(cleanScratch);
|
|
27
|
+
|
|
28
|
+
const quiet = { echo: false } as const;
|
|
29
|
+
|
|
30
|
+
describe("loadBuildParamFlags", () => {
|
|
31
|
+
test("outside a workspace, and without an environment, there are no flags", async () => {
|
|
32
|
+
const plain = scratchDir("chant-flags-plain-");
|
|
33
|
+
expect(await loadBuildParamFlags(plain, "prod")).toEqual({ flags: undefined, errors: [] });
|
|
34
|
+
expect(await loadBuildParamFlags(join(root, "shop"), undefined)).toEqual({ flags: undefined, errors: [] });
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test("in a member, each flag-backed parameter with the flag's value for the environment", async () => {
|
|
38
|
+
expect(await loadBuildParamFlags(join(root, "shop"), "dev")).toEqual({
|
|
39
|
+
flags: { env: "dev", byParam: { newCheckout: { flag: "new-checkout", value: true } } },
|
|
40
|
+
errors: [],
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("a flag kind that can't be read stops the build rather than building without its flags", async () => {
|
|
45
|
+
const dir = flagWorkspace({ "flags/flag.schema.json": "{ not json" });
|
|
46
|
+
const loaded = await loadBuildParamFlags(join(dir, "shop"), "prod");
|
|
47
|
+
expect(loaded.flags).toBeUndefined();
|
|
48
|
+
expect(loaded.errors[0]).toMatch(/the workspace's flag records can't be read/);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("resolveFlaggedBuildParams", () => {
|
|
53
|
+
test("records the flag as the parameter's source, per environment", async () => {
|
|
54
|
+
const prod = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "prod");
|
|
55
|
+
expect(prod).toEqual({
|
|
56
|
+
success: true,
|
|
57
|
+
errors: [],
|
|
58
|
+
provenance: [
|
|
59
|
+
{ name: "newCheckout", value: false, source: "flag:new-checkout" },
|
|
60
|
+
{ name: "tier", value: "small", source: "default" },
|
|
61
|
+
],
|
|
62
|
+
});
|
|
63
|
+
const dev = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "dev");
|
|
64
|
+
expect(dev.provenance[0]).toEqual({ name: "newCheckout", value: true, source: "flag:new-checkout" });
|
|
65
|
+
// staging: the record gives no value, so the declared default applies.
|
|
66
|
+
expect((await resolveFlaggedBuildParams(join(root, "shop"), SHOP, quiet, "staging")).provenance[0]).toEqual({ name: "newCheckout", value: false, source: "default" });
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("--param can't override a flag in an environment with a gate: the build refuses", async () => {
|
|
70
|
+
const refused = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, cli: { newCheckout: "true" } }, "prod");
|
|
71
|
+
expect(refused.success).toBe(false);
|
|
72
|
+
expect(refused.provenance).toEqual([]);
|
|
73
|
+
expect(refused.errors).toHaveLength(1);
|
|
74
|
+
expect(refused.errors[0]).toMatch(/build parameter "newCheckout" is backed by the flag "new-checkout", and "prod" has a gate, so --param can't set it/);
|
|
75
|
+
expect(refused.errors[0]).toMatch(/gates: prod-release/);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("--params-file can't either, and a parameter no flag backs still can", async () => {
|
|
79
|
+
const file = join(scratchDir("chant-flags-file-"), "params.json");
|
|
80
|
+
writeFileSync(file, JSON.stringify({ newCheckout: true }));
|
|
81
|
+
const refused = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, paramsFile: file }, "prod");
|
|
82
|
+
expect(refused.success).toBe(false);
|
|
83
|
+
expect(refused.errors[0]).toMatch(/so --params-file can't set it/);
|
|
84
|
+
const tier = await resolveFlaggedBuildParams(join(root, "shop"), SHOP, { ...quiet, cli: { tier: "large" } }, "prod");
|
|
85
|
+
expect(tier.success).toBe(true);
|
|
86
|
+
expect(tier.provenance).toContainEqual({ name: "tier", value: "large", source: "cli" });
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("where no component declares a gate, --param still overrides the flag", async () => {
|
|
90
|
+
const r = await resolveFlaggedBuildParams(join(root, "blog"), BLOG, { ...quiet, cli: { banner: "false" } }, "prod");
|
|
91
|
+
expect(r.success).toBe(true);
|
|
92
|
+
expect(r.provenance).toEqual([{ name: "banner", value: false, source: "cli" }]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("components that can't be read refuse an override, since the gate can't be ruled out", async () => {
|
|
96
|
+
const dir = flagWorkspace({ "shop/broken.component.ts": "export const = ;\n" });
|
|
97
|
+
const r = await resolveFlaggedBuildParams(join(dir, "shop"), SHOP, { ...quiet, cli: { newCheckout: "true" } }, "prod");
|
|
98
|
+
expect(r.success).toBe(false);
|
|
99
|
+
expect(r.errors[0]).toMatch(/whether "prod" has a gate can't be told/);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("chant build --env in a member with flags", () => {
|
|
104
|
+
const runtime = join(import.meta.dirname, "runtime");
|
|
105
|
+
const paramsModule = join(import.meta.dirname, "params");
|
|
106
|
+
const serializer: Serializer = {
|
|
107
|
+
name: "aws",
|
|
108
|
+
rulePrefix: "TEST",
|
|
109
|
+
serialize: (entities) => JSON.stringify([...entities.values()].map((e) => (e as unknown as { props: unknown }).props)),
|
|
110
|
+
};
|
|
111
|
+
const shopFiles = {
|
|
112
|
+
"shop/chant.config.ts": `export default { environments: ["dev", "staging", "prod"], buildParams: { newCheckout: { type: "boolean", default: false } } };\n`,
|
|
113
|
+
"shop/main.ts":
|
|
114
|
+
`import { createResource } from ${JSON.stringify(runtime)};\n` +
|
|
115
|
+
`import { params } from ${JSON.stringify(paramsModule)};\n` +
|
|
116
|
+
`const Bucket = createResource("Test::Bucket", "aws", { arn: "Arn" });\n` +
|
|
117
|
+
`export const bucket = new Bucket({ checkout: params.newCheckout ? "new" : "old" });\n`,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
test("records the flag on BuildResult.buildParams and builds with its value", async () => {
|
|
121
|
+
const dir = flagWorkspace(shopFiles);
|
|
122
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
123
|
+
try {
|
|
124
|
+
const result = await buildCommand({ path: join(dir, "shop"), format: "json", serializers: [serializer], env: "dev" });
|
|
125
|
+
expect(result.errors).toEqual([]);
|
|
126
|
+
expect(result.buildParams).toEqual([{ name: "newCheckout", value: true, source: "flag:new-checkout" }]);
|
|
127
|
+
expect(spy.mock.calls.map((c) => String(c[0])).some((l) => l.includes("1 build parameter resolved (1 from flags)"))).toBe(true);
|
|
128
|
+
} finally {
|
|
129
|
+
spy.mockRestore();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("refuses --param for a flag-backed parameter in an environment with a gate", async () => {
|
|
134
|
+
const dir = flagWorkspace(shopFiles);
|
|
135
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
136
|
+
try {
|
|
137
|
+
const result = await buildCommand({ path: join(dir, "shop"), format: "json", serializers: [serializer], env: "prod", params: { newCheckout: "true" } });
|
|
138
|
+
expect(result.success).toBe(false);
|
|
139
|
+
expect(result.errors.join("\n")).toMatch(/"newCheckout" is backed by the flag "new-checkout", and "prod" has a gate, so --param can't set it/);
|
|
140
|
+
} finally {
|
|
141
|
+
spy.mockRestore();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
});
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time parameters from workspace flags (#4024, decided in #4016).
|
|
3
|
+
*
|
|
4
|
+
* A member of a workspace may have flag records: records of a kind with a
|
|
5
|
+
* `flag` block, each naming the member, one of its declared build
|
|
6
|
+
* parameters and a value per environment (`workspace/flags.ts`). A build
|
|
7
|
+
* for an environment (`chant build --env <env>`, `chant run --components
|
|
8
|
+
* --env <env>`, and each environment a release cut compiles) reads them, and
|
|
9
|
+
* a flag-backed parameter takes the flag's value for that environment,
|
|
10
|
+
* recorded as `source: "flag:<name>"` on `BuildResult.buildParams` and on the
|
|
11
|
+
* release target's `params`.
|
|
12
|
+
*
|
|
13
|
+
* The precedence rule: where the environment has a gate, `--param`,
|
|
14
|
+
* `--params-file` and the parameter's env var can't set a flag-backed
|
|
15
|
+
* parameter, and the build refuses. An environment has a gate when a
|
|
16
|
+
* component enabled in it declares a gate step, the same test `components
|
|
17
|
+
* promote --release` uses to bind a release gate. The test runs only when one
|
|
18
|
+
* of those sources did set a flag-backed parameter, since it imports the
|
|
19
|
+
* member's components; a project with no components has no gate.
|
|
20
|
+
*
|
|
21
|
+
* Level 0 pays one upward `existsSync` walk here and nothing more. Workspace
|
|
22
|
+
* code is imported only once a declaration is found (#2525 rule 5, pinned by
|
|
23
|
+
* #2526's goldens).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { findWorkspaceRoot } from "./project-root";
|
|
27
|
+
import { flagGateRefusal, type BuildParamFlags, type BuildParamsConfig } from "./build-params";
|
|
28
|
+
import type { BuildParamProvenance } from "./provenance";
|
|
29
|
+
import { echoBuildParams, resolveCliBuildParams, type CliBuildParamsArgs, type CliBuildParamsResolution } from "./cli/build-params-cli";
|
|
30
|
+
import { formatError } from "./cli/format";
|
|
31
|
+
|
|
32
|
+
/** The flags backing parameters of the member at `projectDir` in `env`, or why they can't be read. `flags` is undefined outside a workspace, without an environment, or when no flag names the member. */
|
|
33
|
+
export async function loadBuildParamFlags(projectDir: string, env: string | undefined): Promise<{ flags: BuildParamFlags | undefined; errors: string[] }> {
|
|
34
|
+
if (!env) return { flags: undefined, errors: [] };
|
|
35
|
+
const found = findWorkspaceRoot(projectDir);
|
|
36
|
+
if (!found) return { flags: undefined, errors: [] };
|
|
37
|
+
const { memberFlagBindings } = await import("./workspace/flags-read");
|
|
38
|
+
try {
|
|
39
|
+
const bindings = await memberFlagBindings(found.dir, projectDir, env);
|
|
40
|
+
if (!bindings) return { flags: undefined, errors: [] };
|
|
41
|
+
if (bindings.errors.length > 0) return { flags: undefined, errors: bindings.errors };
|
|
42
|
+
if (Object.keys(bindings.byParam).length === 0) return { flags: undefined, errors: [] };
|
|
43
|
+
return { flags: { env, byParam: bindings.byParam }, errors: [] };
|
|
44
|
+
} catch (err) {
|
|
45
|
+
const { WorkspaceReadError } = await import("./workspace/declaration");
|
|
46
|
+
const { RecordReadError } = await import("./workspace/records");
|
|
47
|
+
if (err instanceof WorkspaceReadError || err instanceof RecordReadError) {
|
|
48
|
+
return { flags: undefined, errors: [`the workspace's flag records can't be read, so this build can't tell which parameters they set: ${err.message}`] };
|
|
49
|
+
}
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The parameters a flag backs that `--param`, `--params-file` or an env var set instead. */
|
|
55
|
+
export function overriddenFlagParams(provenance: readonly BuildParamProvenance[], flags: BuildParamFlags | undefined): BuildParamProvenance[] {
|
|
56
|
+
if (!flags) return [];
|
|
57
|
+
return provenance.filter((p) => flags.byParam[p.name] !== undefined && (p.source === "cli" || p.source === "params-file" || p.source === "env"));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The gates the member's components declare in the environment the process
|
|
62
|
+
* builds for (`CHANT_ENV`), with the parameters as resolved, or why the
|
|
63
|
+
* components can't be read. Only components enabled under those parameters
|
|
64
|
+
* count.
|
|
65
|
+
*/
|
|
66
|
+
export async function environmentGates(projectDir: string, provenance: BuildParamProvenance[]): Promise<{ gates: string[] } | { error: string }> {
|
|
67
|
+
const { discoverComponents } = await import("./components/discover");
|
|
68
|
+
const { withoutDisabled } = await import("./components/cli-support");
|
|
69
|
+
const { gateNamesOf } = await import("./components/release-gate");
|
|
70
|
+
try {
|
|
71
|
+
const discovered = await discoverComponents(projectDir, { buildParams: provenance });
|
|
72
|
+
if (discovered.errors.length > 0) return { error: discovered.errors.map((e) => e.message).join("; ") };
|
|
73
|
+
const all = [...discovered.components.values()].map((d) => d.component);
|
|
74
|
+
const plain = all.map((c) => ({ name: c.name, dependsOn: c.dependsOn, deploy: c.deploy as never }));
|
|
75
|
+
return { gates: gateNamesOf(withoutDisabled(all, plain)) };
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolve the build parameters of the project at `projectDir` for `env`,
|
|
83
|
+
* with the workspace's flags, as `resolveCliBuildParams` does without them.
|
|
84
|
+
* Refuses when a flag record can't be read, and when `--param`,
|
|
85
|
+
* `--params-file` or an env var sets a flag-backed parameter in an
|
|
86
|
+
* environment with a gate. Prints the summary line only on success.
|
|
87
|
+
*/
|
|
88
|
+
export async function resolveFlaggedBuildParams(
|
|
89
|
+
projectDir: string,
|
|
90
|
+
buildParamsConfig: BuildParamsConfig | undefined,
|
|
91
|
+
args: CliBuildParamsArgs,
|
|
92
|
+
env: string | undefined,
|
|
93
|
+
): Promise<CliBuildParamsResolution> {
|
|
94
|
+
const loaded = await loadBuildParamFlags(projectDir, env);
|
|
95
|
+
if (loaded.errors.length > 0) {
|
|
96
|
+
return { success: false, provenance: [], errors: loaded.errors.map((message) => formatError({ message })) };
|
|
97
|
+
}
|
|
98
|
+
const { flags } = loaded;
|
|
99
|
+
const resolution = resolveCliBuildParams(buildParamsConfig, { ...args, ...(flags ? { flags } : {}), echo: false });
|
|
100
|
+
if (!resolution.success || !flags) {
|
|
101
|
+
if (resolution.success && args.echo !== false) echoBuildParams(resolution.provenance, args.verbose);
|
|
102
|
+
return resolution;
|
|
103
|
+
}
|
|
104
|
+
const overridden = overriddenFlagParams(resolution.provenance, flags);
|
|
105
|
+
if (overridden.length > 0) {
|
|
106
|
+
const found = await environmentGates(projectDir, resolution.provenance);
|
|
107
|
+
if ("error" in found) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
provenance: [],
|
|
111
|
+
errors: [
|
|
112
|
+
formatError({
|
|
113
|
+
message:
|
|
114
|
+
`${overridden.map((p) => `"${p.name}"`).join(", ")} ${overridden.length === 1 ? "is" : "are"} backed by a flag and set by ${[...new Set(overridden.map((p) => p.source))].join(", ")}, ` +
|
|
115
|
+
`and whether "${flags.env}" has a gate can't be told, because its components can't be read: ${found.error}`,
|
|
116
|
+
}),
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (found.gates.length > 0) {
|
|
121
|
+
const around = (p: BuildParamProvenance): string =>
|
|
122
|
+
p.source === "cli" ? "--param" : p.source === "params-file" ? "--params-file" : `the env var ${buildParamsConfig?.[p.name]?.env ?? "(unknown)"}`;
|
|
123
|
+
return {
|
|
124
|
+
success: false,
|
|
125
|
+
provenance: [],
|
|
126
|
+
errors: overridden.map((p) =>
|
|
127
|
+
formatError({ message: `${flagGateRefusal(p.name, flags.byParam[p.name].flag, flags.env, around(p))} (gates: ${found.gates.join(", ")})` }),
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (args.echo !== false) echoBuildParams(resolution.provenance, args.verbose);
|
|
133
|
+
return resolution;
|
|
134
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -153,6 +153,8 @@ export type { SymbolDeclaration, SymbolResolver } from "./workspace/symbols";
|
|
|
153
153
|
export * from "./lsp/lexicon-providers";
|
|
154
154
|
export * from "./mcp/types";
|
|
155
155
|
export * from "./lifecycle/index";
|
|
156
|
+
// The run-event contract (#4019): the types and the one writer of a run's stream.
|
|
157
|
+
export * from "./run-event";
|
|
156
158
|
// Op builders — use explicit exports to avoid collision with the core `build` function
|
|
157
159
|
export { Op, phase, activity, gate, kubectlApply, helmInstall, waitForStack,
|
|
158
160
|
gitlabPipeline, lifecycleSnapshot, shell, teardown, OpResource } from "./op/index";
|