@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,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The publish targets (#4047): a member's `release.publish` declarations
|
|
3
|
+
* (./publish-declaration.ts) made into {@link PublishTarget}s, and what the
|
|
4
|
+
* targets share.
|
|
5
|
+
*
|
|
6
|
+
* - `npm` (./publish-npm.ts): `npm publish` per package, in order, with
|
|
7
|
+
* `--provenance` when the run has an OIDC token.
|
|
8
|
+
* - `oci` (./publish-oci.ts): an OCI image layout pushed to a registry such
|
|
9
|
+
* as GHCR or Docker Hub over the registry HTTP API, by digest, then tagged.
|
|
10
|
+
* - `forge` (./publish-forge.ts): files attached to the GitHub, GitLab or
|
|
11
|
+
* Forgejo release named by the release's tag.
|
|
12
|
+
*
|
|
13
|
+
* A file a target reads that the release records as an artifact at the same
|
|
14
|
+
* path is checked against the release's digest, and a file that differs is
|
|
15
|
+
* refused (`publish-artifact-digest-mismatch`). Other files are the commit's
|
|
16
|
+
* by the checkout check (`checkCheckoutIsRelease` in ./release-publish.ts).
|
|
17
|
+
*/
|
|
18
|
+
import { createHash } from "node:crypto";
|
|
19
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
20
|
+
import { isAbsolute, normalize, relative, resolve } from "node:path";
|
|
21
|
+
import { PublishTargetError } from "./release-publish.js";
|
|
22
|
+
import { sortedJsonReplacer } from "../utils.js";
|
|
23
|
+
/** Make each declaration a target, in the order declared. */
|
|
24
|
+
export async function createPublishTargets(decls, deps = {}) {
|
|
25
|
+
const out = [];
|
|
26
|
+
for (const d of decls) {
|
|
27
|
+
switch (d.type) {
|
|
28
|
+
case "npm":
|
|
29
|
+
out.push((await import("./publish-npm.js")).npmTarget(d, deps));
|
|
30
|
+
break;
|
|
31
|
+
case "oci":
|
|
32
|
+
out.push((await import("./publish-oci.js")).ociTarget(d, deps));
|
|
33
|
+
break;
|
|
34
|
+
case "forge":
|
|
35
|
+
out.push((await import("./publish-forge.js")).forgeTarget(d, deps));
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The key of a target made from `decl` (#4048): its kind and the first 16 hex
|
|
43
|
+
* digits of the SHA-256 of its sorted JSON. A declaration holds variable
|
|
44
|
+
* names, never a credential, so the key holds none either.
|
|
45
|
+
*/
|
|
46
|
+
export function publishTargetKey(decl) {
|
|
47
|
+
return `${decl.type}:${createHash("sha256").update(JSON.stringify(decl, sortedJsonReplacer)).digest("hex").slice(0, 16)}`;
|
|
48
|
+
}
|
|
49
|
+
/** `path` resolved in the member at `cwd`, refused when it is not there. */
|
|
50
|
+
export function memberFile(cwd, path) {
|
|
51
|
+
const abs = isAbsolute(path) ? path : resolve(cwd, path);
|
|
52
|
+
if (!existsSync(abs)) {
|
|
53
|
+
throw new PublishTargetError("publish-file-missing", `${path} does not exist in ${cwd}`, "Build it before publishing: a publish uploads what is there and never builds.");
|
|
54
|
+
}
|
|
55
|
+
return abs;
|
|
56
|
+
}
|
|
57
|
+
/** `sha256:<hex>` of a file's bytes. */
|
|
58
|
+
export function fileDigest(abs) {
|
|
59
|
+
return `sha256:${createHash("sha256").update(readFileSync(abs)).digest("hex")}`;
|
|
60
|
+
}
|
|
61
|
+
/** `sha256:<hex>` of bytes. */
|
|
62
|
+
export function bytesDigest(bytes) {
|
|
63
|
+
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
64
|
+
}
|
|
65
|
+
/** True when `abs` is a directory. */
|
|
66
|
+
export function isDir(abs) {
|
|
67
|
+
return statSync(abs).isDirectory();
|
|
68
|
+
}
|
|
69
|
+
const normal = (p) => normalize(p).replace(/\\/g, "/").replace(/^\.\//, "");
|
|
70
|
+
/** The release's artifact of `kind` at `path` (relative to the member at `cwd`), if it records one. */
|
|
71
|
+
export function releaseArtifactAt(release, kind, cwd, path) {
|
|
72
|
+
const rel = normal(isAbsolute(path) ? relative(cwd, path) : path);
|
|
73
|
+
return release.artifactDigests.find((a) => a.kind === kind && normal(a.path) === rel);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Refuse a file the release records at `path` whose digest is none of
|
|
77
|
+
* `digests`, the identities the target can compute for it (the file's bytes,
|
|
78
|
+
* and for an image its manifest and config digests).
|
|
79
|
+
*/
|
|
80
|
+
export function checkReleaseDigest(release, kind, cwd, path, digests) {
|
|
81
|
+
const recorded = releaseArtifactAt(release, kind, cwd, path);
|
|
82
|
+
if (!recorded || digests.includes(recorded.digest))
|
|
83
|
+
return;
|
|
84
|
+
throw new PublishTargetError("publish-artifact-digest-mismatch", `${path} is not the ${kind} release ${release.releaseDigest} built: the release records ${recorded.digest}, and the file here is ${digests[0]}`, "Publish the files the cut built. A publish never builds, so restore them from the cut's build, or cut a new release.");
|
|
85
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cutting a release from a green commit (#4026, ws-104).
|
|
3
|
+
*
|
|
4
|
+
* The cut takes the checkout's `HEAD`, refuses it unless it carries a
|
|
5
|
+
* `ci/green/<sha>` tag and no `ci/revoked/<sha>` tag (ws-103, read from local
|
|
6
|
+
* tags), and refuses a working tree with uncommitted changes to tracked
|
|
7
|
+
* files, since the compile would then not be the commit's. It compiles every
|
|
8
|
+
* environment the member declares, in its promotion order (#4030), and
|
|
9
|
+
* refuses a component whose build steps differ between environments, since
|
|
10
|
+
* "built once" can't hold for it. It then builds each component's artifacts
|
|
11
|
+
* once, stores the release and appends the cut.
|
|
12
|
+
*
|
|
13
|
+
* An artifact whose source did not change since the earlier release cut
|
|
14
|
+
* from the nearest ancestor commit is not built again: the release carries that
|
|
15
|
+
* release's entry, so a config-only release keeps the same artifact digests.
|
|
16
|
+
* "Did not change" means git sees no change between the two commits in the
|
|
17
|
+
* component's own file, the member's `chant.config.ts` and the paths its
|
|
18
|
+
* build steps read.
|
|
19
|
+
*
|
|
20
|
+
* A project with no components (#4046) needs no special case here: its
|
|
21
|
+
* targets hold the files `chant build --env <env>` writes, each with
|
|
22
|
+
* `component: null`, and `artifactDigests` is empty.
|
|
23
|
+
*
|
|
24
|
+
* Compiling goes through a {@link TargetCompiler}, so a test hands the cut
|
|
25
|
+
* fixtures and the CLI hands it child processes.
|
|
26
|
+
*
|
|
27
|
+
* Who may cut (#4031): when the declaration at base names a `release` gate
|
|
28
|
+
* in `identity.gates`, a cut must be signed (`--sign`), its signature must
|
|
29
|
+
* verify for the actor against the signers at base, and when the gate names
|
|
30
|
+
* a class the actor must be in it. A cut signed without that gate is signed
|
|
31
|
+
* all the same, and readers report whether the signature verifies. With
|
|
32
|
+
* `ci.green.signedSince` declared, a green tag counts only with a valid
|
|
33
|
+
* statement (`../workspace/ci-green.ts`), so a hand-pushed tag cuts nothing.
|
|
34
|
+
*/
|
|
35
|
+
import { type Release, type ReleaseCut, type ReleaseTarget } from "./release-store.js";
|
|
36
|
+
import type { CompiledTarget, TargetCompiler } from "../components/release-compile.js";
|
|
37
|
+
/** The stable codes a cut refuses with. */
|
|
38
|
+
export type ReleaseCutCode = "not-a-git-repository" | "release-commit-not-green" | "release-commit-revoked" | "release-worktree-dirty" | "release-no-environments" | "release-no-components" | "release-build-inputs-differ" | "release-unsigned" | "release-signer-refused" | "declaration-invalid";
|
|
39
|
+
export declare class ReleaseCutError extends Error {
|
|
40
|
+
readonly code: ReleaseCutCode;
|
|
41
|
+
readonly fix?: string | undefined;
|
|
42
|
+
constructor(code: ReleaseCutCode, message: string, fix?: string | undefined);
|
|
43
|
+
}
|
|
44
|
+
export interface CutRequest {
|
|
45
|
+
/** The member's directory. */
|
|
46
|
+
cwd: string;
|
|
47
|
+
compiler: TargetCompiler;
|
|
48
|
+
/** The label, or null to cut without one. */
|
|
49
|
+
label: string | null;
|
|
50
|
+
actor: string;
|
|
51
|
+
/** The environments in promotion order. Read from the member's `chant.config.ts` when absent. */
|
|
52
|
+
environments?: readonly string[];
|
|
53
|
+
now?: () => Date;
|
|
54
|
+
/** The process environment the CI run is read from. */
|
|
55
|
+
env?: NodeJS.ProcessEnv;
|
|
56
|
+
/**
|
|
57
|
+
* Sign the cut (#4031): an ssh key file, or true for git's
|
|
58
|
+
* `user.signingkey`, as `chant approve --sign` takes it. The signature is
|
|
59
|
+
* over the release digest, the label, the actor and the time, and `actor`
|
|
60
|
+
* must be the principal the signers file lists the key for.
|
|
61
|
+
*/
|
|
62
|
+
sign?: string | true;
|
|
63
|
+
}
|
|
64
|
+
export interface CutResult {
|
|
65
|
+
release: Release;
|
|
66
|
+
/** The cut line written, or null when this digest was already cut. */
|
|
67
|
+
cut: ReleaseCut | null;
|
|
68
|
+
/** The release's label after the cut. */
|
|
69
|
+
label: string | null;
|
|
70
|
+
/** Whether the release file was written now, rather than already stored. */
|
|
71
|
+
stored: boolean;
|
|
72
|
+
/** The components whose artifacts were taken from an earlier release, and that release's digest. */
|
|
73
|
+
reused: Array<{
|
|
74
|
+
component: string;
|
|
75
|
+
from: string;
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Whether `sha` has a green tag and no revoked tag, read from local tags,
|
|
80
|
+
* and the tags on it that were ignored for want of a valid statement
|
|
81
|
+
* (#4031). The tags are judged by `green`, the declaration's `ci.green` at
|
|
82
|
+
* base, with the runner keys read at its branch's tip; with no `ci.green`,
|
|
83
|
+
* every tag counts.
|
|
84
|
+
*/
|
|
85
|
+
export declare function greenState(top: string, sha: string, green?: import("../workspace/declaration.js").CiGreen | null): Promise<{
|
|
86
|
+
state: "green" | "revoked" | "none";
|
|
87
|
+
ignored: Array<{
|
|
88
|
+
tag: string;
|
|
89
|
+
status: string;
|
|
90
|
+
reason: string;
|
|
91
|
+
}>;
|
|
92
|
+
}>;
|
|
93
|
+
/** Which components' build steps differ between environments, with the environments they appear in. */
|
|
94
|
+
export declare function differingBuildSteps(targets: readonly CompiledTarget[]): Array<{
|
|
95
|
+
component: string;
|
|
96
|
+
envs: string[];
|
|
97
|
+
}>;
|
|
98
|
+
/** What a dry run of the cut found (#4027): what the cut would do, with nothing built, signed or written. */
|
|
99
|
+
export interface CutPlan {
|
|
100
|
+
commit: string;
|
|
101
|
+
member: string | null;
|
|
102
|
+
label: string | null;
|
|
103
|
+
/** The release's digest when no artifact needs building, so it is known now; null when a build decides it. */
|
|
104
|
+
releaseDigest: string | null;
|
|
105
|
+
/** The digest already cut under `label`, or this digest when it is already cut: the cut would then write no new release. */
|
|
106
|
+
alreadyCut: string | null;
|
|
107
|
+
/** Each environment's target: its params, and its templates by component and path. */
|
|
108
|
+
targets: Array<{
|
|
109
|
+
env: string;
|
|
110
|
+
params: ReleaseTarget["params"];
|
|
111
|
+
templates: Array<{
|
|
112
|
+
component: string | null;
|
|
113
|
+
path: string;
|
|
114
|
+
}>;
|
|
115
|
+
}>;
|
|
116
|
+
/** The components whose artifacts would be built, and the environment each is built in. */
|
|
117
|
+
build: Array<{
|
|
118
|
+
component: string;
|
|
119
|
+
env: string;
|
|
120
|
+
}>;
|
|
121
|
+
/** The components whose artifacts would be carried from an earlier release. */
|
|
122
|
+
reused: CutResult["reused"];
|
|
123
|
+
/** Each component's publish steps, with the environments whose deploy runs them. */
|
|
124
|
+
publishSteps: Array<{
|
|
125
|
+
component: string;
|
|
126
|
+
kind: string;
|
|
127
|
+
to: string | null;
|
|
128
|
+
envs: string[];
|
|
129
|
+
}>;
|
|
130
|
+
/** Whether the release gate in identity.gates requires a signed cut. */
|
|
131
|
+
signatureRequired: boolean;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The dry run of {@link cutRelease} (#4027): the same checks and compile,
|
|
135
|
+
* then what the cut would build, carry and record, without building an
|
|
136
|
+
* artifact, signing, or writing anything. Throws what the cut throws before
|
|
137
|
+
* it builds, and `ReleaseLabelError` when the label is invalid or names a
|
|
138
|
+
* release of another commit.
|
|
139
|
+
*/
|
|
140
|
+
export declare function planRelease(req: CutRequest): Promise<CutPlan>;
|
|
141
|
+
/**
|
|
142
|
+
* Cut a release of the member at `req.cwd` from its checkout's `HEAD`. Writes
|
|
143
|
+
* the release and the cut to the local `chant/lifecycle` branch and does not
|
|
144
|
+
* push. Throws {@link ReleaseCutError} before anything is written when the
|
|
145
|
+
* commit isn't green, the tree is dirty, or an environment can't be compiled
|
|
146
|
+
* to the same artifacts, and `ReleaseLabelError` (./release-store.ts) when the
|
|
147
|
+
* label is taken or the release already carries another one.
|
|
148
|
+
*
|
|
149
|
+
* The cut line names its run (#4027): the CI run's id and origin in CI, and
|
|
150
|
+
* outside CI an id minted from the cut's time, `local-<epoch ms>`, with the
|
|
151
|
+
* origin `{ forge: "local" }`.
|
|
152
|
+
*/
|
|
153
|
+
export declare function cutRelease(req: CutRequest): Promise<CutResult>;
|
|
154
|
+
//# sourceMappingURL=release-cut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-cut.d.ts","sourceRoot":"","sources":["../../src/lifecycle/release-cut.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAUH,OAAO,EAUL,KAAK,OAAO,EAEZ,KAAK,UAAU,EACf,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAEpF,2CAA2C;AAC3C,MAAM,MAAM,cAAc,GACtB,sBAAsB,GACtB,0BAA0B,GAC1B,wBAAwB,GACxB,wBAAwB,GACxB,yBAAyB,GACzB,uBAAuB,GACvB,6BAA6B,GAC7B,kBAAkB,GAClB,wBAAwB,GACxB,qBAAqB,CAAC;AAE1B,qBAAa,eAAgB,SAAQ,KAAK;IAEtC,QAAQ,CAAC,IAAI,EAAE,cAAc;IAE7B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM;IAHvB,YACW,IAAI,EAAE,cAAc,EAC7B,OAAO,EAAE,MAAM,EACN,GAAG,CAAC,EAAE,MAAM,YAAA,EAItB;CACF;AAED,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,cAAc,CAAC;IACzB,6CAA6C;IAC7C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACvB,yCAAyC;IACzC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,4EAA4E;IAC5E,MAAM,EAAE,OAAO,CAAC;IAChB,oGAAoG;IACpG,MAAM,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAcD;;;;;;GAMG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,OAAO,0BAA0B,EAAE,OAAO,GAAG,IAAW,GAC9D,OAAO,CAAC;IAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;IAAC,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAanH;AAED,uGAAuG;AACvG,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,GAAG,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAapH;AAyKD,6GAA6G;AAC7G,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,8GAA8G;IAC9G,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4HAA4H;IAC5H,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sFAAsF;IACtF,OAAO,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;QAAC,SAAS,EAAE,KAAK,CAAC;YAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;IAC/H,2FAA2F;IAC3F,KAAK,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,+EAA+E;IAC/E,MAAM,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC5B,oFAAoF;IACpF,YAAY,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC5F,wEAAwE;IACxE,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA4DnE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA6DpE"}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cutting a release from a green commit (#4026, ws-104).
|
|
3
|
+
*
|
|
4
|
+
* The cut takes the checkout's `HEAD`, refuses it unless it carries a
|
|
5
|
+
* `ci/green/<sha>` tag and no `ci/revoked/<sha>` tag (ws-103, read from local
|
|
6
|
+
* tags), and refuses a working tree with uncommitted changes to tracked
|
|
7
|
+
* files, since the compile would then not be the commit's. It compiles every
|
|
8
|
+
* environment the member declares, in its promotion order (#4030), and
|
|
9
|
+
* refuses a component whose build steps differ between environments, since
|
|
10
|
+
* "built once" can't hold for it. It then builds each component's artifacts
|
|
11
|
+
* once, stores the release and appends the cut.
|
|
12
|
+
*
|
|
13
|
+
* An artifact whose source did not change since the earlier release cut
|
|
14
|
+
* from the nearest ancestor commit is not built again: the release carries that
|
|
15
|
+
* release's entry, so a config-only release keeps the same artifact digests.
|
|
16
|
+
* "Did not change" means git sees no change between the two commits in the
|
|
17
|
+
* component's own file, the member's `chant.config.ts` and the paths its
|
|
18
|
+
* build steps read.
|
|
19
|
+
*
|
|
20
|
+
* A project with no components (#4046) needs no special case here: its
|
|
21
|
+
* targets hold the files `chant build --env <env>` writes, each with
|
|
22
|
+
* `component: null`, and `artifactDigests` is empty.
|
|
23
|
+
*
|
|
24
|
+
* Compiling goes through a {@link TargetCompiler}, so a test hands the cut
|
|
25
|
+
* fixtures and the CLI hands it child processes.
|
|
26
|
+
*
|
|
27
|
+
* Who may cut (#4031): when the declaration at base names a `release` gate
|
|
28
|
+
* in `identity.gates`, a cut must be signed (`--sign`), its signature must
|
|
29
|
+
* verify for the actor against the signers at base, and when the gate names
|
|
30
|
+
* a class the actor must be in it. A cut signed without that gate is signed
|
|
31
|
+
* all the same, and readers report whether the signature verifies. With
|
|
32
|
+
* `ci.green.signedSince` declared, a green tag counts only with a valid
|
|
33
|
+
* statement (`../workspace/ci-green.ts`), so a hand-pushed tag cuts nothing.
|
|
34
|
+
*/
|
|
35
|
+
import { execFileSync } from "node:child_process";
|
|
36
|
+
import { realpathSync } from "node:fs";
|
|
37
|
+
import { relative, resolve } from "node:path";
|
|
38
|
+
import { canonicalJson } from "../effect-receipt.js";
|
|
39
|
+
import { contentDigest } from "../content-digest.js";
|
|
40
|
+
import { getHeadCommit } from "./git.js";
|
|
41
|
+
import { resolveMemberLedger } from "./member-ledger.js";
|
|
42
|
+
import { resolveCiRun } from "./release-ledger.js";
|
|
43
|
+
import { appendCut, invalidLabel, labelsByDigest, makeRelease, persistRelease, readAllReleases, readCuts, readRelease, ReleaseLabelError, } from "./release-store.js";
|
|
44
|
+
export class ReleaseCutError extends Error {
|
|
45
|
+
constructor(code, message, fix) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.fix = fix;
|
|
49
|
+
this.name = "ReleaseCutError";
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function git(top, args) {
|
|
53
|
+
return execFileSync("git", args, { cwd: top, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
54
|
+
}
|
|
55
|
+
function tryGit(top, args) {
|
|
56
|
+
try {
|
|
57
|
+
return git(top, args);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether `sha` has a green tag and no revoked tag, read from local tags,
|
|
65
|
+
* and the tags on it that were ignored for want of a valid statement
|
|
66
|
+
* (#4031). The tags are judged by `green`, the declaration's `ci.green` at
|
|
67
|
+
* base, with the runner keys read at its branch's tip; with no `ci.green`,
|
|
68
|
+
* every tag counts.
|
|
69
|
+
*/
|
|
70
|
+
export async function greenState(top, sha, green = null) {
|
|
71
|
+
const { judgedCiTags } = await import("./release-revocation.js");
|
|
72
|
+
let tags;
|
|
73
|
+
try {
|
|
74
|
+
tags = await judgedCiTags(top, green);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const { WorkspaceReadError } = await import("../workspace/declaration.js");
|
|
78
|
+
if (err instanceof WorkspaceReadError)
|
|
79
|
+
throw new ReleaseCutError("declaration-invalid", err.message);
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
const ignored = tags.ignored.get(sha) ?? [];
|
|
83
|
+
if (tags.revoked.has(sha))
|
|
84
|
+
return { state: "revoked", ignored };
|
|
85
|
+
return { state: tags.green.has(sha) ? "green" : "none", ignored };
|
|
86
|
+
}
|
|
87
|
+
/** Which components' build steps differ between environments, with the environments they appear in. */
|
|
88
|
+
export function differingBuildSteps(targets) {
|
|
89
|
+
const byComponent = new Map();
|
|
90
|
+
for (const t of targets) {
|
|
91
|
+
for (const [component, steps] of Object.entries(t.buildSteps)) {
|
|
92
|
+
if (!byComponent.has(component))
|
|
93
|
+
byComponent.set(component, new Map());
|
|
94
|
+
byComponent.get(component).set(t.env, canonicalJson(steps));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const out = [];
|
|
98
|
+
for (const [component, perEnv] of byComponent) {
|
|
99
|
+
if (new Set(perEnv.values()).size > 1)
|
|
100
|
+
out.push({ component, envs: [...perEnv.keys()] });
|
|
101
|
+
}
|
|
102
|
+
return out.sort((a, b) => (a.component < b.component ? -1 : 1));
|
|
103
|
+
}
|
|
104
|
+
/** The earlier release of this member cut from the nearest ancestor of `commit`. */
|
|
105
|
+
async function previousRelease(top, commit, cwd) {
|
|
106
|
+
const { releases } = await readAllReleases({ cwd });
|
|
107
|
+
const { cuts } = await readCuts({ cwd });
|
|
108
|
+
const cutAt = new Map();
|
|
109
|
+
for (const c of cuts)
|
|
110
|
+
if (!cutAt.has(c.releaseDigest) || c.timestamp > cutAt.get(c.releaseDigest))
|
|
111
|
+
cutAt.set(c.releaseDigest, c.timestamp);
|
|
112
|
+
const candidates = releases
|
|
113
|
+
.filter((r) => cutAt.has(r.releaseDigest) && r.commit !== commit)
|
|
114
|
+
.filter((r) => tryGit(top, ["merge-base", "--is-ancestor", r.commit, commit]) !== null)
|
|
115
|
+
.map((r) => ({ r, depth: Number(tryGit(top, ["rev-list", "--count", r.commit]) ?? 0), at: cutAt.get(r.releaseDigest) }))
|
|
116
|
+
// The nearest ancestor first, then the newest cut of it.
|
|
117
|
+
.sort((a, b) => b.depth - a.depth || (b.at < a.at ? -1 : b.at > a.at ? 1 : 0));
|
|
118
|
+
return candidates[0]?.r;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The checks and the compile a cut makes before it builds or writes: the
|
|
122
|
+
* commit is green and not revoked, the cut is signed when the release gate
|
|
123
|
+
* asks, the tree is clean, every environment compiles with the same build
|
|
124
|
+
* steps, and which artifacts are carried from an earlier release.
|
|
125
|
+
*/
|
|
126
|
+
async function prepare(req) {
|
|
127
|
+
const cwd = resolve(req.cwd);
|
|
128
|
+
const top = tryGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
129
|
+
if (!top)
|
|
130
|
+
throw new ReleaseCutError("not-a-git-repository", "a release is cut from a commit, and this directory is not in a git repository");
|
|
131
|
+
const commit = await getHeadCommit({ cwd });
|
|
132
|
+
// #4031: the declaration at base says whether tags need a statement and who may cut.
|
|
133
|
+
const { scopeSource } = await import("../workspace/write-scope.js");
|
|
134
|
+
const { releaseRuleFrom } = await import("../workspace/identity.js");
|
|
135
|
+
const source = scopeSource(cwd);
|
|
136
|
+
const { state, ignored } = await greenState(top, commit, source.declaration?.ci?.green ?? null);
|
|
137
|
+
if (state !== "green") {
|
|
138
|
+
const why = ignored.length > 0 ? `; ignored: ${ignored.map((t) => `${t.tag} (${t.status}): ${t.reason}`).join("; ")}` : "";
|
|
139
|
+
throw new ReleaseCutError(state === "revoked" ? "release-commit-revoked" : "release-commit-not-green", state === "revoked"
|
|
140
|
+
? `commit ${commit.slice(0, 12)} has a ci/revoked tag: it went red after it was green, so no release is cut from it`
|
|
141
|
+
: ignored.some((t) => t.tag.startsWith("ci/green/"))
|
|
142
|
+
? `commit ${commit.slice(0, 12)} has no ci/green tag that counts, so no release is cut from it${why}`
|
|
143
|
+
: `commit ${commit.slice(0, 12)} has no ci/green tag, so no release is cut from it`, "Check out a green commit (`chant ci last-green` names the newest) after fetching the tags: git fetch origin --tags");
|
|
144
|
+
}
|
|
145
|
+
// Who may cut is the declaration's identity.gates.release, read at base.
|
|
146
|
+
const releaseRule = releaseRuleFrom(source);
|
|
147
|
+
if (releaseRule.requirement && req.sign === undefined) {
|
|
148
|
+
throw new ReleaseCutError("release-unsigned", `chant.workspace.json at base names a release gate in identity.gates${releaseRule.requirement.class !== null ? `, from the class ${releaseRule.requirement.class}` : ""}, and this cut is not signed`, `chant release cut <label> --actor <principal> --sign [<key file>], with a key ${releaseRule.rule.signers.signersPath} at base lists for that principal.`);
|
|
149
|
+
}
|
|
150
|
+
const dirty = tryGit(top, ["status", "--porcelain", "--untracked-files=no"]);
|
|
151
|
+
if (dirty) {
|
|
152
|
+
throw new ReleaseCutError("release-worktree-dirty", `the working tree has uncommitted changes to tracked files, so a compile here would not be commit ${commit.slice(0, 12)}'s`, "Commit or stash them, or cut from a clean checkout of the commit.");
|
|
153
|
+
}
|
|
154
|
+
let envs = req.environments;
|
|
155
|
+
if (!envs) {
|
|
156
|
+
const { readMemberPromotionOrder } = await import("../workspace/promotion-order.js");
|
|
157
|
+
envs = ((await readMemberPromotionOrder(cwd)) ?? []).map((s) => s.name);
|
|
158
|
+
}
|
|
159
|
+
if (envs.length === 0) {
|
|
160
|
+
throw new ReleaseCutError("release-no-environments", "this member's chant.config.ts declares no environments, so there is nothing to compile a release for", "Declare them in promotion order: environments: [\"dev\", \"staging\", \"prod\"]");
|
|
161
|
+
}
|
|
162
|
+
const compiled = [];
|
|
163
|
+
for (const env of envs)
|
|
164
|
+
compiled.push(await req.compiler.compile(env));
|
|
165
|
+
// A project with no components compiles to its build output (#4046), so only a target with neither is empty.
|
|
166
|
+
if (compiled.every((t) => t.components.length === 0 && t.templates.length === 0)) {
|
|
167
|
+
throw new ReleaseCutError("release-no-components", "no component is enabled in any environment and nothing was compiled, so there is nothing to release", "Enable a component under one environment's parameters, or remove the component files so the project's build output is released.");
|
|
168
|
+
}
|
|
169
|
+
const differing = differingBuildSteps(compiled);
|
|
170
|
+
if (differing.length > 0) {
|
|
171
|
+
throw new ReleaseCutError("release-build-inputs-differ", `the build steps of ${differing.map((d) => `"${d.component}"`).join(", ")} differ between environments (${[...new Set(differing.flatMap((d) => d.envs))].join(", ")}), ` +
|
|
172
|
+
"so their artifacts can't be built once for every environment", "Make the build steps read no environment-specific value; pass the value to a deploy step instead.");
|
|
173
|
+
}
|
|
174
|
+
// Which environment each component's artifacts are built in: the first one it is enabled in.
|
|
175
|
+
const firstEnv = new Map();
|
|
176
|
+
for (const t of compiled)
|
|
177
|
+
for (const c of t.components)
|
|
178
|
+
if (!firstEnv.has(c) && (t.buildSteps[c]?.length ?? 0) > 0)
|
|
179
|
+
firstEnv.set(c, t);
|
|
180
|
+
// git names paths from its top with symlinks resolved (/private/var on macOS).
|
|
181
|
+
const realCwd = realpathSync(cwd);
|
|
182
|
+
const previous = firstEnv.size > 0 ? await previousRelease(top, commit, cwd) : undefined;
|
|
183
|
+
const reused = [];
|
|
184
|
+
const artifacts = [];
|
|
185
|
+
const toBuild = new Map();
|
|
186
|
+
for (const [component, target] of firstEnv) {
|
|
187
|
+
const prior = previous?.artifactDigests.filter((a) => a.component === component) ?? [];
|
|
188
|
+
if (previous && prior.length > 0) {
|
|
189
|
+
const paths = [target.componentFiles[component], "chant.config.ts", ...(target.buildInputs[component] ?? [])]
|
|
190
|
+
.filter((p) => typeof p === "string")
|
|
191
|
+
.map((p) => relative(top, resolve(realCwd, p)))
|
|
192
|
+
.filter((p) => p !== "" && !p.startsWith(".."));
|
|
193
|
+
// With no path to compare, the source can't be shown unchanged, so it is built.
|
|
194
|
+
const changed = paths.length === 0 ? null : tryGit(top, ["diff", "--name-only", previous.commit, commit, "--", ...paths]);
|
|
195
|
+
if (changed === "") {
|
|
196
|
+
artifacts.push(...prior);
|
|
197
|
+
reused.push({ component, from: previous.releaseDigest });
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
toBuild.set(target.env, [...(toBuild.get(target.env) ?? []), component]);
|
|
202
|
+
}
|
|
203
|
+
const ledger = await resolveMemberLedger(cwd);
|
|
204
|
+
return {
|
|
205
|
+
cwd,
|
|
206
|
+
commit,
|
|
207
|
+
envs,
|
|
208
|
+
compiled,
|
|
209
|
+
releaseRule,
|
|
210
|
+
toBuild,
|
|
211
|
+
carried: artifacts,
|
|
212
|
+
reused,
|
|
213
|
+
member: ledger.members.length > 0 ? ledger.members.join("/") : null,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
/** The targets and template bytes of a compile, each template by its content digest. */
|
|
217
|
+
function targetsOf(compiled) {
|
|
218
|
+
const templates = [];
|
|
219
|
+
const targets = compiled.map((t) => ({
|
|
220
|
+
env: t.env,
|
|
221
|
+
params: t.params,
|
|
222
|
+
templates: t.templates.map((tpl) => {
|
|
223
|
+
const digest = contentDigest(tpl.content);
|
|
224
|
+
templates.push({ digest, content: tpl.content });
|
|
225
|
+
return { component: tpl.component, path: tpl.path, digest };
|
|
226
|
+
}),
|
|
227
|
+
}));
|
|
228
|
+
return { targets, templates };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* The dry run of {@link cutRelease} (#4027): the same checks and compile,
|
|
232
|
+
* then what the cut would build, carry and record, without building an
|
|
233
|
+
* artifact, signing, or writing anything. Throws what the cut throws before
|
|
234
|
+
* it builds, and `ReleaseLabelError` when the label is invalid or names a
|
|
235
|
+
* release of another commit.
|
|
236
|
+
*/
|
|
237
|
+
export async function planRelease(req) {
|
|
238
|
+
const p = await prepare(req);
|
|
239
|
+
const { targets } = targetsOf(p.compiled);
|
|
240
|
+
const build = [];
|
|
241
|
+
for (const env of p.envs)
|
|
242
|
+
for (const component of p.toBuild.get(env) ?? [])
|
|
243
|
+
build.push({ component, env });
|
|
244
|
+
const releaseDigest = build.length === 0 ? makeRelease({ member: p.member, commit: p.commit, artifactDigests: p.carried, targets }).releaseDigest : null;
|
|
245
|
+
const { cuts } = await readCuts({ cwd: p.cwd });
|
|
246
|
+
const labels = labelsByDigest(cuts);
|
|
247
|
+
let alreadyCut = null;
|
|
248
|
+
if (req.label !== null) {
|
|
249
|
+
const why = invalidLabel(req.label);
|
|
250
|
+
if (why)
|
|
251
|
+
throw new ReleaseLabelError("release-label-invalid", why);
|
|
252
|
+
const named = [...labels].find(([, l]) => l === req.label)?.[0];
|
|
253
|
+
if (named !== undefined) {
|
|
254
|
+
const other = await readRelease(named, { cwd: p.cwd });
|
|
255
|
+
const sameCommit = other?.commit === p.commit;
|
|
256
|
+
if (!sameCommit || (releaseDigest !== null && named !== releaseDigest)) {
|
|
257
|
+
throw new ReleaseLabelError("release-label-taken", `the label "${req.label}" already names release ${named}${other && !sameCommit ? `, cut from commit ${other.commit.slice(0, 12)}` : ""}`);
|
|
258
|
+
}
|
|
259
|
+
alreadyCut = named;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (releaseDigest !== null && alreadyCut === null && cuts.some((c) => c.releaseDigest === releaseDigest)) {
|
|
263
|
+
const own = labels.get(releaseDigest);
|
|
264
|
+
if (own !== undefined && req.label !== null && own !== req.label) {
|
|
265
|
+
throw new ReleaseLabelError("release-already-labelled", `release ${releaseDigest} is already labelled "${own}", and a release takes one label`);
|
|
266
|
+
}
|
|
267
|
+
alreadyCut = releaseDigest;
|
|
268
|
+
}
|
|
269
|
+
const steps = new Map();
|
|
270
|
+
for (const t of p.compiled) {
|
|
271
|
+
for (const [component, list] of Object.entries(t.publishSteps ?? {})) {
|
|
272
|
+
for (const step of list) {
|
|
273
|
+
const kind = typeof step.kind === "string" ? step.kind : "publish";
|
|
274
|
+
const to = typeof step.to === "string" ? step.to : null;
|
|
275
|
+
const key = JSON.stringify([component, kind, to]);
|
|
276
|
+
const entry = steps.get(key) ?? { component, kind, to, envs: [] };
|
|
277
|
+
if (!entry.envs.includes(t.env))
|
|
278
|
+
entry.envs.push(t.env);
|
|
279
|
+
steps.set(key, entry);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
commit: p.commit,
|
|
285
|
+
member: p.member,
|
|
286
|
+
label: req.label,
|
|
287
|
+
releaseDigest,
|
|
288
|
+
alreadyCut,
|
|
289
|
+
targets: targets.map((t) => ({ env: t.env, params: t.params, templates: t.templates.map(({ component, path }) => ({ component, path })) })),
|
|
290
|
+
build,
|
|
291
|
+
reused: p.reused,
|
|
292
|
+
publishSteps: [...steps.values()],
|
|
293
|
+
signatureRequired: p.releaseRule.requirement !== null,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Cut a release of the member at `req.cwd` from its checkout's `HEAD`. Writes
|
|
298
|
+
* the release and the cut to the local `chant/lifecycle` branch and does not
|
|
299
|
+
* push. Throws {@link ReleaseCutError} before anything is written when the
|
|
300
|
+
* commit isn't green, the tree is dirty, or an environment can't be compiled
|
|
301
|
+
* to the same artifacts, and `ReleaseLabelError` (./release-store.ts) when the
|
|
302
|
+
* label is taken or the release already carries another one.
|
|
303
|
+
*
|
|
304
|
+
* The cut line names its run (#4027): the CI run's id and origin in CI, and
|
|
305
|
+
* outside CI an id minted from the cut's time, `local-<epoch ms>`, with the
|
|
306
|
+
* origin `{ forge: "local" }`.
|
|
307
|
+
*/
|
|
308
|
+
export async function cutRelease(req) {
|
|
309
|
+
const p = await prepare(req);
|
|
310
|
+
const { cwd, commit, compiled, releaseRule, reused } = p;
|
|
311
|
+
const artifacts = [...p.carried];
|
|
312
|
+
for (const env of p.envs) {
|
|
313
|
+
const names = p.toBuild.get(env);
|
|
314
|
+
if (names)
|
|
315
|
+
artifacts.push(...(await req.compiler.buildArtifacts(env, names)));
|
|
316
|
+
}
|
|
317
|
+
const { targets, templates } = targetsOf(compiled);
|
|
318
|
+
const release = makeRelease({ member: p.member, commit, artifactDigests: artifacts, targets });
|
|
319
|
+
const timestamp = (req.now ?? (() => new Date()))().toISOString();
|
|
320
|
+
const signed = { releaseDigest: release.releaseDigest, label: req.label, actor: req.actor, timestamp };
|
|
321
|
+
let signature = null;
|
|
322
|
+
if (req.sign !== undefined) {
|
|
323
|
+
const { resolveSigningKey, sealReleaseCut, SealError } = await import("../workspace/trust/seal.js");
|
|
324
|
+
try {
|
|
325
|
+
const key = resolveSigningKey(req.sign, cwd);
|
|
326
|
+
try {
|
|
327
|
+
signature = sealReleaseCut(key.file, signed).signature;
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
key.cleanup();
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
if (!(err instanceof SealError))
|
|
335
|
+
throw err;
|
|
336
|
+
throw new ReleaseCutError("release-signer-refused", `no release was cut: ${err.message}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (releaseRule.requirement) {
|
|
340
|
+
const { admitReleaseCut } = await import("../workspace/identity-gate-rule.js");
|
|
341
|
+
const why = admitReleaseCut(releaseRule.rule, { ...signed, signature });
|
|
342
|
+
if (why !== null) {
|
|
343
|
+
throw new ReleaseCutError("release-signer-refused", `no release was cut: ${why}`, `identity.gates.release in chant.workspace.json at base counts only a cut signed by a key ${releaseRule.rule.signers.signersPath} lists for the actor` +
|
|
344
|
+
(releaseRule.requirement.class !== null ? `, who holds the role of the class ${releaseRule.requirement.class}` : "") +
|
|
345
|
+
". Pass --actor <principal> naming that signer.");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const stored = await persistRelease(release, templates, { cwd });
|
|
349
|
+
const ci = resolveCiRun(req.env ?? process.env);
|
|
350
|
+
const outcome = await appendCut({
|
|
351
|
+
releaseDigest: release.releaseDigest,
|
|
352
|
+
label: req.label,
|
|
353
|
+
actor: req.actor,
|
|
354
|
+
timestamp,
|
|
355
|
+
runId: ci?.runId ?? `local-${Date.parse(timestamp)}`,
|
|
356
|
+
runOrigin: ci?.runOrigin ?? { forge: "local" },
|
|
357
|
+
signature,
|
|
358
|
+
}, { cwd });
|
|
359
|
+
return {
|
|
360
|
+
release,
|
|
361
|
+
cut: outcome.appended ? outcome.cut : null,
|
|
362
|
+
label: outcome.appended ? outcome.cut.label : outcome.label,
|
|
363
|
+
stored: stored.written,
|
|
364
|
+
reused,
|
|
365
|
+
};
|
|
366
|
+
}
|