@intentius/chant 0.124.28 → 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/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 +8 -1
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +4 -2
- package/dist/change-set.d.ts +10 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +4 -3
- 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.map +1 -1
- package/dist/cli/commands/build.js +6 -3
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +45 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +15 -4
- package/dist/cli/handlers/fan-out.d.ts +9 -0
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +40 -3
- 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 +11 -3
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +134 -26
- 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 +210 -33
- package/dist/cli/handlers/release.d.ts +62 -5
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +414 -28
- package/dist/cli/handlers/run.d.ts +11 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +67 -6
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +61 -10
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +24 -2
- package/dist/cli/registry.d.ts +15 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +3 -2
- package/dist/cli/run-events.d.ts.map +1 -1
- package/dist/cli/run-events.js +5 -3
- package/dist/components/auto-release.d.ts +2 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +1 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts +8 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +35 -0
- 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.map +1 -1
- package/dist/components/fan-out-run.js +50 -0
- 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/promote.d.ts +23 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +60 -2
- package/dist/components/release-compile.d.ts +9 -0
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +9 -3
- package/dist/components/release-gate.d.ts +11 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +10 -4
- package/dist/components/wave-plan.d.ts +5 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +10 -2
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -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/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 +2 -1
- package/dist/lifecycle/change-set-store.d.ts.map +1 -1
- package/dist/lifecycle/change-set-store.js +2 -1
- 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 +10 -1
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
- package/dist/lifecycle/gate-change-set.js +35 -3
- package/dist/lifecycle/gate-ledger.d.ts +2 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- 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 +48 -1
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +116 -23
- 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-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 +12 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +4 -2
- 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/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -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 +29 -2
- package/dist/run-event-store.d.ts.map +1 -1
- package/dist/run-event-store.js +50 -3
- package/dist/run-event-stream.d.ts +2 -1
- package/dist/run-event-stream.d.ts.map +1 -1
- package/dist/run-event-stream.js +5 -1
- package/dist/run-event.d.ts +19 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/run-event.js +2 -0
- package/dist/wave-runtime.mjs +260 -229
- 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/change-set.schema.json +5 -1
- 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.d.ts +8 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +10 -3
- package/dist/workspace/declaration.d.ts +16 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +26 -0
- package/dist/workspace/declaration.schema.json +64 -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/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 +30 -6
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +141 -21
- package/dist/workspace/intent.schema.json +702 -6
- 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/reason-codes.d.ts +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +3 -0
- 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 +9 -0
- package/dist/workspace/releases.d.ts.map +1 -1
- package/dist/workspace/releases.js +9 -0
- package/dist/workspace/releases.schema.json +123 -1
- package/dist/workspace/run-event.schema.json +10 -3
- package/dist/workspace/status-gates.d.ts +8 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +78 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +170 -3
- package/dist/workspace/status.schema.json +198 -1
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.ts +10 -2
- package/src/change-set.ts +14 -3
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +6 -3
- package/src/cli/handlers/approve-release.test.ts +5 -1
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +47 -0
- package/src/cli/handlers/build.test.ts +15 -0
- package/src/cli/handlers/build.ts +17 -4
- package/src/cli/handlers/fan-out.test.ts +180 -1
- package/src/cli/handlers/fan-out.ts +38 -3
- 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/pr.test.ts +148 -1
- package/src/cli/handlers/pr.ts +139 -28
- package/src/cli/handlers/promote.test.ts +280 -2
- package/src/cli/handlers/promote.ts +210 -34
- package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
- 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 +146 -32
- package/src/cli/handlers/release.ts +415 -28
- package/src/cli/handlers/run-auto-release.test.ts +9 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.ts +69 -5
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +61 -10
- package/src/cli/parse-args.ts +19 -2
- package/src/cli/registry.ts +15 -3
- package/src/cli/run-events.ts +5 -2
- package/src/components/auto-release.test.ts +9 -0
- package/src/components/auto-release.ts +3 -0
- package/src/components/capability.ts +9 -0
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +36 -0
- 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 +48 -0
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/promote.ts +68 -2
- package/src/components/release-compile.ts +17 -3
- package/src/components/release-gate.ts +18 -4
- package/src/components/wave-plan.ts +15 -2
- package/src/config.ts +8 -0
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.ts +2 -1
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.ts +37 -6
- package/src/lifecycle/gate-ledger.ts +2 -2
- 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.test.ts +74 -2
- package/src/lifecycle/release-cut.ts +158 -22
- 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-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 +15 -4
- 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/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- 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.ts +63 -3
- package/src/run-event-stream.ts +4 -1
- package/src/run-event.ts +21 -1
- 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/change-set.schema.json +5 -1
- 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.ts +14 -3
- package/src/workspace/declaration.schema.json +64 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +38 -0
- 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/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 +148 -25
- 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/read-contract.e2e.test.ts +2 -1
- package/src/workspace/reason-codes.test.ts +5 -0
- package/src/workspace/reason-codes.ts +3 -0
- 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 +123 -1
- package/src/workspace/releases.ts +11 -0
- package/src/workspace/run-event.schema.json +10 -3
- package/src/workspace/status-contract.test.ts +29 -0
- package/src/workspace/status-gates.ts +8 -0
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status.schema.json +198 -1
- package/src/workspace/status.ts +254 -3
- package/src/workspace/wip.ts +11 -2
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `chant release` (#4026, ws-104): the release is what graduates.
|
|
3
3
|
*
|
|
4
|
-
* release cut [<label>] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
|
|
5
|
-
*
|
|
4
|
+
* release cut [<label>] [--yes] [--build <command>] [--param k=v]... [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json]
|
|
5
|
+
* a dry run of a cut of this member from the checkout's HEAD, which must be
|
|
6
|
+
* clean, on trunk and pushed, and green (#4027); `--yes` cuts, records,
|
|
7
|
+
* and tags the commit `release/<label>` locally;
|
|
6
8
|
* --sign signs the cut with the actor's ssh key (#4031); `--src`, `-o` and
|
|
7
9
|
* `--lexicon` shape the build of a project with no components (#4046)
|
|
10
|
+
* release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json]
|
|
11
|
+
* publish a cut release (#4027) to the targets release.publish declares
|
|
12
|
+
* (#4047): a dry run unless --yes; refuses a revoked one, one with no
|
|
13
|
+
* signature that verifies against .chant/allowed_signers unless
|
|
14
|
+
* --allow-unsigned (#4049), and with a target a checkout that is not
|
|
15
|
+
* the release's commit; warns with no OIDC provenance, and refuses
|
|
16
|
+
* under --require-provenance; attaches SBOMs and provenance (#4049)
|
|
17
|
+
* release verify <label|digest> [--json]
|
|
18
|
+
* check the release's signature offline against .chant/allowed_signers
|
|
19
|
+
* at the release's commit (#4049)
|
|
8
20
|
* release list [--json]
|
|
9
21
|
* every release of this member with its targets, its cuts and where it runs;
|
|
10
22
|
* `--json` prints the read contract's `releases` document
|
|
@@ -13,14 +25,17 @@
|
|
|
13
25
|
* release compile --env <env> [--build <command>] [--artifacts --component <a,b>] --json
|
|
14
26
|
* internal: what the cut runs in a child process per environment
|
|
15
27
|
*
|
|
16
|
-
* The workstation
|
|
17
|
-
*
|
|
18
|
-
*
|
|
28
|
+
* The workstation checks, the actor and the tag are in
|
|
29
|
+
* ../../lifecycle/release-workstation.ts, and the publish contract in
|
|
30
|
+
* ../../lifecycle/release-publish.ts. A declaration whose `identity.gates`
|
|
31
|
+
* names a `release` gate requires a signed cut by an actor in its class (#4031).
|
|
19
32
|
*/
|
|
33
|
+
import { execFileSync } from "node:child_process";
|
|
34
|
+
import { existsSync } from "node:fs";
|
|
20
35
|
import { resolve } from "node:path";
|
|
21
36
|
import { formatBold, formatError, formatSuccess, formatWarning } from "../format.js";
|
|
22
37
|
import { parseParamFlags } from "../build-params-cli.js";
|
|
23
|
-
const USAGE = "chant release cut [<label>] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
|
|
38
|
+
const USAGE = "chant release cut [<label>] [--yes] [--build <command>] [--param <k=v>] [--params-file <file>] [--src <dir>] [-o <file>] [--lexicon <name>] [--actor <name>] [--sign [<key file>]] [--json] | release publish <label|digest> [--yes] [--allow-unsigned] [--require-provenance] [--json] | release verify <label|digest> [--json] | release list [--json] | release export <label|digest> --env <env> [-o <dir>] [--json]";
|
|
24
39
|
/** The build inputs `release cut` and `release compile` share. */
|
|
25
40
|
function compileInputs(ctx, params) {
|
|
26
41
|
const { args } = ctx;
|
|
@@ -34,9 +49,45 @@ function compileInputs(ctx, params) {
|
|
|
34
49
|
...(args.lexicon ? { lexicon: args.lexicon } : {}),
|
|
35
50
|
};
|
|
36
51
|
}
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
/** What to run after a cut (#4027): publish from CI, or push the tag so CI publishes, or deploy the first environment. */
|
|
53
|
+
export function nextCommands(req) {
|
|
54
|
+
const publish = `chant release publish ${req.ref}`;
|
|
55
|
+
const deploy = req.firstEnv === null
|
|
56
|
+
? null
|
|
57
|
+
: req.noComponents
|
|
58
|
+
? `chant release export ${req.ref} --env ${req.firstEnv}`
|
|
59
|
+
: `chant components promote --release ${req.ref} --to ${req.firstEnv}`;
|
|
60
|
+
if (req.inCi)
|
|
61
|
+
return [publish, ...(deploy ? [deploy] : [])];
|
|
62
|
+
return [...(req.tag ? [`git push origin ${req.tag}`] : []), publish, ...(deploy ? [deploy] : [])];
|
|
39
63
|
}
|
|
64
|
+
/** The command line that ran, without `--json`, with `--yes` added: what a dry run says to run next. */
|
|
65
|
+
export function sameWithYes(argv) {
|
|
66
|
+
const quote = (a) => (/^[\w@%+=:,./-]+$/.test(a) ? a : `'${a.replace(/'/g, `'\\''`)}'`);
|
|
67
|
+
return ["chant", ...argv.filter((a) => a !== "--json" && a !== "--yes"), "--yes"].map(quote).join(" ");
|
|
68
|
+
}
|
|
69
|
+
function describeActorSource(source) {
|
|
70
|
+
switch (source) {
|
|
71
|
+
case "flag":
|
|
72
|
+
return "--actor";
|
|
73
|
+
case "signer":
|
|
74
|
+
return "the signers file at base, for the signing key";
|
|
75
|
+
case "ci":
|
|
76
|
+
return "the CI's actor";
|
|
77
|
+
case "git":
|
|
78
|
+
return "git user.email";
|
|
79
|
+
case "user":
|
|
80
|
+
return "$USER";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* `release cut [<label>]` (#4026, #4027). A dry run unless `--yes`: it
|
|
85
|
+
* refuses a dirty tree, a commit that is not on trunk and pushed, and one
|
|
86
|
+
* that is not green, compiles every environment, and prints what the cut
|
|
87
|
+
* would record, build and tag, and what publishes where. With `--yes` it
|
|
88
|
+
* cuts, signs with `--sign`, records the cut with its actor and run id,
|
|
89
|
+
* tags the commit locally and prints the next command.
|
|
90
|
+
*/
|
|
40
91
|
export async function runReleaseCut(ctx) {
|
|
41
92
|
const { args } = ctx;
|
|
42
93
|
const positional = args.extraPositional;
|
|
@@ -45,23 +96,169 @@ export async function runReleaseCut(ctx) {
|
|
|
45
96
|
return 1;
|
|
46
97
|
}
|
|
47
98
|
const label = positional ?? args.label ?? null;
|
|
48
|
-
const
|
|
49
|
-
if (!actor) {
|
|
50
|
-
console.error(formatError({ message: "Could not resolve --actor from the environment", hint: "Pass --actor explicitly, or set GITHUB_ACTOR / GITLAB_USER_LOGIN / USER." }));
|
|
51
|
-
return 1;
|
|
52
|
-
}
|
|
99
|
+
const dryRun = args.yes !== true;
|
|
53
100
|
const cwd = resolve(".");
|
|
54
101
|
const { spawnTargetCompiler, ReleaseCompileError } = await import("../../components/release-compile.js");
|
|
55
|
-
const { cutRelease, ReleaseCutError } = await import("../../lifecycle/release-cut.js");
|
|
102
|
+
const { cutRelease, planRelease, ReleaseCutError } = await import("../../lifecycle/release-cut.js");
|
|
56
103
|
const { ReleaseLabelError } = await import("../../lifecycle/release-store.js");
|
|
57
|
-
const { pushLifecycleReport } = await import("../../lifecycle/git.js");
|
|
58
|
-
const
|
|
59
|
-
const
|
|
104
|
+
const { pushLifecycleReport, getHeadCommit } = await import("../../lifecycle/git.js");
|
|
105
|
+
const { resolveCiRun } = await import("../../lifecycle/release-ledger.js");
|
|
106
|
+
const ws = await import("../../lifecycle/release-workstation.js");
|
|
107
|
+
const { detectProvenance, releasePublishTargets } = await import("../../lifecycle/release-publish.js");
|
|
108
|
+
const fail = (code, message, fix) => {
|
|
109
|
+
console.error(formatError({ message: `${message} [${code}]`, ...(fix ? { hint: fix } : {}) }));
|
|
110
|
+
return 1;
|
|
111
|
+
};
|
|
60
112
|
try {
|
|
61
|
-
|
|
113
|
+
// The workstation checks come first, in the order #4027 lists them; the
|
|
114
|
+
// green check and the rest are the cut's own.
|
|
115
|
+
const top = (() => {
|
|
116
|
+
try {
|
|
117
|
+
return execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
})();
|
|
123
|
+
let commit = null;
|
|
124
|
+
let trunk = null;
|
|
125
|
+
let signers = [];
|
|
126
|
+
let member = null;
|
|
127
|
+
if (top) {
|
|
128
|
+
commit = await getHeadCommit({ cwd });
|
|
129
|
+
ws.checkCleanTree(top, commit);
|
|
130
|
+
const { scopeSource } = await import("../../workspace/write-scope.js");
|
|
131
|
+
const { releaseRuleFrom } = await import("../../workspace/identity.js");
|
|
132
|
+
const source = scopeSource(cwd);
|
|
133
|
+
trunk = ws.resolveTrunk(top, source.declaration?.ci?.green?.branch ?? null);
|
|
134
|
+
ws.checkOnTrunk(top, commit, trunk);
|
|
135
|
+
signers = releaseRuleFrom(source).rule.signers.signers;
|
|
136
|
+
const { resolveMemberLedger } = await import("../../lifecycle/member-ledger.js");
|
|
137
|
+
const ledger = await resolveMemberLedger(cwd);
|
|
138
|
+
member = ledger.members.length > 0 ? ledger.members.join("/") : null;
|
|
139
|
+
}
|
|
140
|
+
// The signing key is resolved now, so a dry run shows a key that can't be read.
|
|
141
|
+
let publicKey = null;
|
|
142
|
+
if (args.sign !== undefined) {
|
|
143
|
+
const { resolveSigningKey, SealError } = await import("../../workspace/trust/seal.js");
|
|
144
|
+
try {
|
|
145
|
+
const key = resolveSigningKey(args.sign, cwd);
|
|
146
|
+
try {
|
|
147
|
+
if (!existsSync(key.file))
|
|
148
|
+
return fail("release-signer-refused", `no release was cut: the signing key ${key.file} does not exist`);
|
|
149
|
+
publicKey = ws.publicKeyOf(key.file);
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
key.cleanup();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
if (!(err instanceof SealError))
|
|
157
|
+
throw err;
|
|
158
|
+
return fail("release-signer-refused", `no release was cut: ${err.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const who = ws.resolveCutActor({ cwd, ...(args.actor ? { explicit: args.actor } : {}), publicKey, signers });
|
|
162
|
+
if (!who) {
|
|
163
|
+
console.error(formatError({ message: "Could not resolve --actor", hint: "Pass --actor explicitly, or set git's user.email, GITHUB_ACTOR, GITLAB_USER_LOGIN or USER." }));
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
const actor = who.actor;
|
|
167
|
+
const tag = label !== null && commit !== null && top ? { name: ws.releaseTagName(member, label), state: ws.checkReleaseTag(top, ws.releaseTagName(member, label), commit) } : null;
|
|
168
|
+
const ci = resolveCiRun(process.env);
|
|
169
|
+
const params = parseParamFlags(args.param);
|
|
170
|
+
const compiler = spawnTargetCompiler(compileInputs(ctx, params));
|
|
171
|
+
const request = { cwd, compiler, label, actor, ...(args.sign !== undefined ? { sign: args.sign } : {}) };
|
|
172
|
+
if (dryRun) {
|
|
173
|
+
const plan = await planRelease(request);
|
|
174
|
+
// Under the release gate, a key the signers file at base lists for someone else is refused now, not after the build.
|
|
175
|
+
if (plan.signatureRequired && publicKey !== null && !signers.some((s) => s.key === publicKey && s.principal.trim().toLowerCase() === actor.trim().toLowerCase())) {
|
|
176
|
+
return fail("release-signer-refused", `no release would be cut: the signing key is not listed for ${actor} in the signers file at base, and identity.gates.release counts only a cut its signer signs`, "Pass --actor <principal> naming the key's signer, or sign with that principal's key.");
|
|
177
|
+
}
|
|
178
|
+
const ref = plan.label ?? plan.alreadyCut ?? plan.releaseDigest;
|
|
179
|
+
const provenance = detectProvenance();
|
|
180
|
+
// The targets release publish would run, as the config here declares them; their files are read at publish.
|
|
181
|
+
const publishTargets = await releasePublishTargets(cwd).then((ts) => ts.map((t) => `${t.name} to ${t.destination}`), (err) => (err instanceof Error ? err : new Error(String(err))));
|
|
182
|
+
const deployRef = plan.label ?? "<label|digest>";
|
|
183
|
+
const doc = {
|
|
184
|
+
dryRun: true,
|
|
185
|
+
...plan,
|
|
186
|
+
actor,
|
|
187
|
+
actorSource: who.source,
|
|
188
|
+
run: ci ? { runId: ci.runId, runOrigin: ci.runOrigin } : { runId: null, runOrigin: { forge: "local" } },
|
|
189
|
+
trunk,
|
|
190
|
+
sign: args.sign !== undefined ? { key: publicKey !== null ? publicKey.split(" ")[0] : null, readable: publicKey !== null } : null,
|
|
191
|
+
tag: tag ? { name: tag.name, exists: tag.state === "present" } : null,
|
|
192
|
+
publish: { targets: Array.isArray(publishTargets) ? publishTargets : [], provenance },
|
|
193
|
+
next: [sameWithYes(process.argv.slice(2))],
|
|
194
|
+
};
|
|
195
|
+
if (args.json) {
|
|
196
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
const name = plan.label ? formatBold(plan.label) : "an unlabelled release";
|
|
200
|
+
console.log(`Dry run: cut ${name} from commit ${plan.commit.slice(0, 12)}${trunk ? ` (on ${trunk.remote}/${trunk.branch}, green)` : ""}`);
|
|
201
|
+
if (plan.alreadyCut)
|
|
202
|
+
console.log(` already cut as ${plan.alreadyCut}: the cut would record nothing new`);
|
|
203
|
+
console.log(` actor ${actor} (from ${describeActorSource(who.source)})`);
|
|
204
|
+
console.log(` run ${ci ? `${ci.runId} (${ci.runOrigin.forge})` : "minted on this machine at the cut (local)"}`);
|
|
205
|
+
console.log(` ${args.sign !== undefined ? `signed with ${publicKey ? publicKey.split(" ")[0] : "a key whose public half can't be read until it signs"}` : "not signed"}` +
|
|
206
|
+
(plan.signatureRequired ? "; identity.gates.release requires a signed cut" : ""));
|
|
207
|
+
console.log(` tag ${tag ? `${tag.name}${tag.state === "present" ? " (exists)" : ""}, made here and not pushed` : "none, since the release has no label"}`);
|
|
208
|
+
for (const t of plan.targets) {
|
|
209
|
+
console.log(` ${t.env}: ${t.templates.length} template(s)${t.params.length > 0 ? `, ${t.params.map((p) => `${p.name}=${p.value}`).join(" ")}` : ""}`);
|
|
210
|
+
}
|
|
211
|
+
for (const b of plan.build)
|
|
212
|
+
console.log(` ${b.component}: artifacts built in ${b.env}`);
|
|
213
|
+
for (const r of plan.reused)
|
|
214
|
+
console.log(` ${r.component}: artifacts unchanged, carried from ${r.from}`);
|
|
215
|
+
console.log(` digest ${plan.releaseDigest ?? "decided when the artifacts are built"}`);
|
|
216
|
+
console.log("What will be published where:");
|
|
217
|
+
if (plan.publishSteps.length === 0)
|
|
218
|
+
console.log(" no component declares a publish step");
|
|
219
|
+
for (const p of plan.publishSteps) {
|
|
220
|
+
console.log(` ${p.component}: ${p.kind}${p.to ? ` to ${p.to}` : ""} in ${p.envs.join(", ")}, by that environment's deploy (chant components promote --release ${deployRef})`);
|
|
221
|
+
}
|
|
222
|
+
const publishCmd = `chant release publish ${ref ?? "<label|digest>"}`;
|
|
223
|
+
if (!Array.isArray(publishTargets))
|
|
224
|
+
console.log(` ${publishCmd}: the publish targets can't be read: ${publishTargets.message}`);
|
|
225
|
+
else if (publishTargets.length === 0)
|
|
226
|
+
console.log(` ${publishCmd}: nothing, since this member declares no publish target (release.publish in chant.config.ts)`);
|
|
227
|
+
else
|
|
228
|
+
for (const t of publishTargets)
|
|
229
|
+
console.log(` ${publishCmd}: ${t}`);
|
|
230
|
+
if (!provenance.oidc)
|
|
231
|
+
console.log(` a publish from here would carry no OIDC provenance: ${provenance.reason}`);
|
|
232
|
+
console.error(`Dry run: nothing was cut, signed or tagged. Run again with --yes to cut: ${doc.next[0]}`);
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
const result = await cutRelease(request);
|
|
62
236
|
const push = await pushLifecycleReport({ cwd }).catch(() => ({ pushed: false, pushWarning: undefined }));
|
|
237
|
+
// The tag names the release's label, which a re-cut with no label reads from the earlier cut.
|
|
238
|
+
let tagged = null;
|
|
239
|
+
let tagWarning;
|
|
240
|
+
if (result.label !== null && top) {
|
|
241
|
+
const name = ws.releaseTagName(member, result.label);
|
|
242
|
+
try {
|
|
243
|
+
const state = ws.checkReleaseTag(top, name, result.release.commit);
|
|
244
|
+
if (state === "absent")
|
|
245
|
+
ws.createReleaseTag(top, name, result.release.commit, { releaseDigest: result.release.releaseDigest, label: result.label, member });
|
|
246
|
+
tagged = { name, created: state === "absent" };
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
tagWarning = `the release was cut, and the commit was not tagged: ${err instanceof Error ? err.message : String(err)}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
const firstEnv = result.release.targets[0]?.env ?? null;
|
|
253
|
+
const next = nextCommands({
|
|
254
|
+
ref: result.label ?? result.release.releaseDigest,
|
|
255
|
+
tag: tagged?.name ?? null,
|
|
256
|
+
inCi: ci !== undefined,
|
|
257
|
+
firstEnv,
|
|
258
|
+
noComponents: result.release.targets.every((t) => t.templates.every((tpl) => tpl.component === null)) && result.release.artifactDigests.length === 0,
|
|
259
|
+
});
|
|
63
260
|
if (args.json) {
|
|
64
|
-
console.log(JSON.stringify({ release: result.release, label: result.label, cut: result.cut, stored: result.stored, reused: result.reused }, null, 2));
|
|
261
|
+
console.log(JSON.stringify({ dryRun: false, release: result.release, label: result.label, cut: result.cut, stored: result.stored, reused: result.reused, actor, actorSource: who.source, tag: tagged, next }, null, 2));
|
|
65
262
|
}
|
|
66
263
|
else {
|
|
67
264
|
const name = result.label ? formatBold(result.label) : "unlabelled release";
|
|
@@ -71,25 +268,29 @@ export async function runReleaseCut(ctx) {
|
|
|
71
268
|
console.error(formatSuccess(`Already cut${result.label ? ` as ${formatBold(result.label)}` : ""}: ${result.release.releaseDigest}`));
|
|
72
269
|
console.error(` commit ${result.release.commit}`);
|
|
73
270
|
if (result.cut)
|
|
74
|
-
console.error(result.cut.signature ? `
|
|
271
|
+
console.error(` ${result.cut.signature ? `signed by ${result.cut.actor}` : `not signed; actor ${result.cut.actor}`}, run ${result.cut.runId}`);
|
|
75
272
|
console.error(` artifacts ${result.release.artifactDigests.length}, targets ${result.release.targets.map((t) => `${t.env} (${t.templates.length} template${t.templates.length === 1 ? "" : "s"})`).join(", ")}`);
|
|
76
273
|
for (const r of result.reused)
|
|
77
274
|
console.error(` ${r.component}: artifacts unchanged, carried from ${r.from}`);
|
|
275
|
+
if (tagged)
|
|
276
|
+
console.error(` tag ${tagged.name}${tagged.created ? "" : " (already there)"}, not pushed`);
|
|
277
|
+
console.error(`Next: ${next[0]}`);
|
|
278
|
+
for (const n of next.slice(1))
|
|
279
|
+
console.error(` or: ${n}`);
|
|
78
280
|
console.log(result.release.releaseDigest);
|
|
79
281
|
}
|
|
80
282
|
if (push.pushWarning)
|
|
81
283
|
console.error(formatWarning({ message: push.pushWarning }));
|
|
284
|
+
if (tagWarning)
|
|
285
|
+
console.error(formatWarning({ message: tagWarning }));
|
|
82
286
|
return 0;
|
|
83
287
|
}
|
|
84
288
|
catch (err) {
|
|
85
|
-
if (err instanceof ReleaseCutError) {
|
|
86
|
-
|
|
87
|
-
return 1;
|
|
88
|
-
}
|
|
89
|
-
if (err instanceof ReleaseLabelError) {
|
|
90
|
-
console.error(formatError({ message: `${err.message} [${err.code}]` }));
|
|
91
|
-
return 1;
|
|
289
|
+
if (err instanceof ws.WorkstationCheckError || err instanceof ReleaseCutError) {
|
|
290
|
+
return fail(err.code, err.message, err.fix);
|
|
92
291
|
}
|
|
292
|
+
if (err instanceof ReleaseLabelError)
|
|
293
|
+
return fail(err.code, err.message);
|
|
93
294
|
if (err instanceof ReleaseCompileError) {
|
|
94
295
|
console.error(formatError({ message: `no release was cut: ${err.message}` }));
|
|
95
296
|
return 1;
|
|
@@ -97,6 +298,186 @@ export async function runReleaseCut(ctx) {
|
|
|
97
298
|
throw err;
|
|
98
299
|
}
|
|
99
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* `release publish <label|digest> [--yes]` (#4027, #4047): publish a cut
|
|
303
|
+
* release to the targets the member declares under `release.publish` in
|
|
304
|
+
* its `chant.config.ts`. It refuses a release no cut names, one cut from a
|
|
305
|
+
* revoked commit, one with no signature that verifies against the signers
|
|
306
|
+
* file at base unless `--allow-unsigned` (#4049; never under
|
|
307
|
+
* identity.gates.release), and, when there is a target, a checkout that is
|
|
308
|
+
* not the release's commit with a clean tree. It warns before publishing
|
|
309
|
+
* when the run has no OIDC token, so what it publishes carries no
|
|
310
|
+
* provenance, and refuses under `--require-provenance`. Each target attaches
|
|
311
|
+
* an SBOM and the release's provenance statement (#4049). Every
|
|
312
|
+
* target is planned (its files read and checked, its credentials found)
|
|
313
|
+
* before any publishes. Without `--yes` it is a dry run that prints the plan
|
|
314
|
+
* and uploads nothing. With `--yes` it stages every target, then reveals them
|
|
315
|
+
* (#4048, `runPublish`), recording each step in `_releases/publishes.jsonl`,
|
|
316
|
+
* so running it again after a failure resumes. Every line is redacted.
|
|
317
|
+
*/
|
|
318
|
+
export async function runReleasePublish(ctx) {
|
|
319
|
+
const { args } = ctx;
|
|
320
|
+
const ref = args.extraPositional;
|
|
321
|
+
if (!ref) {
|
|
322
|
+
console.error(formatError({ message: "release publish needs a release", hint: USAGE }));
|
|
323
|
+
return 1;
|
|
324
|
+
}
|
|
325
|
+
const dryRun = args.yes !== true;
|
|
326
|
+
const cwd = resolve(".");
|
|
327
|
+
const fail = (code, message, fix) => {
|
|
328
|
+
console.error(formatError({ message: `${message} [${code}]`, ...(fix ? { hint: fix } : {}) }));
|
|
329
|
+
return 1;
|
|
330
|
+
};
|
|
331
|
+
const { fetchLifecycle } = await import("../../lifecycle/git.js");
|
|
332
|
+
const { preparePublish, publishContext, runPublish, PublishTargetError, PublishRunError } = await import("../../lifecycle/release-publish.js");
|
|
333
|
+
const { publishProgress, progressOf, readPublishRecords } = await import("../../lifecycle/publish-record.js");
|
|
334
|
+
await fetchLifecycle({ cwd }).catch(() => false);
|
|
335
|
+
const prepared = await preparePublish({ cwd, ref, allowUnsigned: args.allowUnsigned === true, requireProvenance: args.requireProvenance === true });
|
|
336
|
+
if ("refusal" in prepared)
|
|
337
|
+
return fail(prepared.refusal.code, prepared.refusal.message, prepared.refusal.fix);
|
|
338
|
+
const { release, label, signature, provenance, warning, signatureWarning, targets, redactor } = prepared;
|
|
339
|
+
const name = label ?? release.releaseDigest;
|
|
340
|
+
const deploySteps = release.targets.map((t) => t.env);
|
|
341
|
+
const say = (line) => console.error(redactor.redact(line));
|
|
342
|
+
const failed = (code, message, fix) => fail(code, redactor.redact(message), fix);
|
|
343
|
+
const errorOf = (err) => err instanceof PublishTargetError ? { code: err.code, message: err.message, ...(err.fix ? { fix: err.fix } : {}) } : { code: "publish-failed", message: err instanceof Error ? err.message : String(err) };
|
|
344
|
+
if (!args.json) {
|
|
345
|
+
say(`${dryRun ? "Dry run: publish" : "Publishing"} ${formatBold(name)} (${release.releaseDigest}) from commit ${release.commit.slice(0, 12)}`);
|
|
346
|
+
say(` ${signature.status === "verified" ? `signed by ${signature.actor}` : signature.status === "unsigned" ? "not signed" : `signature unverified: ${signature.reason}`}`);
|
|
347
|
+
}
|
|
348
|
+
// The warnings go to stderr with --json too, before any target runs.
|
|
349
|
+
if (signatureWarning)
|
|
350
|
+
console.error(formatWarning({ message: signatureWarning }));
|
|
351
|
+
if (warning)
|
|
352
|
+
console.error(formatWarning({ message: warning }));
|
|
353
|
+
const pctx = publishContext(prepared, { cwd, log: (line) => (args.json ? undefined : console.error(` ${line}`)) });
|
|
354
|
+
// Every target is planned before any publishes, so a missing file or credential stops the publish with nothing sent.
|
|
355
|
+
const progress = publishProgress(await readPublishRecords(release.releaseDigest, { cwd }).catch(() => []));
|
|
356
|
+
const planned = [];
|
|
357
|
+
for (const target of targets) {
|
|
358
|
+
try {
|
|
359
|
+
planned.push({ kind: target.kind, name: target.name, destination: target.destination, progress: progressOf(progress, target), items: await target.plan(pctx) });
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
const e = errorOf(err);
|
|
363
|
+
return failed(e.code, `nothing was published: ${target.name}: ${e.message}`, e.fix);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const promoteHint = () => {
|
|
367
|
+
if (deploySteps.length === 0)
|
|
368
|
+
return null;
|
|
369
|
+
const noComponents = release.artifactDigests.length === 0 && release.targets.every((t) => t.templates.every((tpl) => tpl.component === null));
|
|
370
|
+
return noComponents
|
|
371
|
+
? ` Each environment deploys the files the release recorded: chant release export ${name} --env ${deploySteps[0]}`
|
|
372
|
+
: ` A component's publish steps run in each environment's deploy of the release: chant components promote --release ${name} --to ${deploySteps[0]}`;
|
|
373
|
+
};
|
|
374
|
+
const base = { releaseDigest: release.releaseDigest, label, commit: release.commit, signature, provenance, warnings: [signatureWarning, warning].filter((w) => w !== null) };
|
|
375
|
+
if (dryRun) {
|
|
376
|
+
const next = [sameWithYes(process.argv.slice(2))];
|
|
377
|
+
if (args.json) {
|
|
378
|
+
console.log(redactor.redact(JSON.stringify({ dryRun: true, ...base, targets: planned, published: [], environments: deploySteps, next }, null, 2)));
|
|
379
|
+
return 0;
|
|
380
|
+
}
|
|
381
|
+
const out = (line) => console.log(redactor.redact(line));
|
|
382
|
+
out("What would be published where:");
|
|
383
|
+
if (planned.length === 0)
|
|
384
|
+
out(" nothing: this member declares no publish target (release.publish in chant.config.ts)");
|
|
385
|
+
for (const p of planned) {
|
|
386
|
+
const earlier = p.progress === "revealed" ? " (published by an earlier run; skipped)" : p.progress === "staged" ? " (staged by an earlier run; only revealed)" : "";
|
|
387
|
+
out(` ${p.name}${provenance.oidc && p.kind === "npm" ? ", with --provenance" : ""}${earlier}:`);
|
|
388
|
+
for (const i of p.items)
|
|
389
|
+
out(` ${describeItem(i)}`);
|
|
390
|
+
}
|
|
391
|
+
say(`Dry run: nothing was published. Run again with --yes to publish: ${next[0]}`);
|
|
392
|
+
const hint = planned.length === 0 ? promoteHint() : null;
|
|
393
|
+
if (hint)
|
|
394
|
+
say(hint);
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
if (targets.length > 0 && !args.json)
|
|
398
|
+
say(" every target stages first, and none is visible until all have staged");
|
|
399
|
+
let run;
|
|
400
|
+
try {
|
|
401
|
+
run = await runPublish({ cwd, release, label, targets, ctx: pctx, say: (line) => (args.json ? undefined : say(line)) });
|
|
402
|
+
}
|
|
403
|
+
catch (err) {
|
|
404
|
+
if (err instanceof PublishRunError) {
|
|
405
|
+
const again = sameWithYes(process.argv.slice(2));
|
|
406
|
+
const where = err.step === "stage"
|
|
407
|
+
? `nothing is visible yet${err.staged.length > 0 ? `; staged: ${err.staged.join(", ")}` : ""}`
|
|
408
|
+
: `revealed: ${err.revealed.length > 0 ? err.revealed.join(", ") : "none"}; staged and not yet revealed: ${err.staged.filter((t) => !err.revealed.includes(t)).join(", ")}`;
|
|
409
|
+
return failed(err.code, `${err.message}; ${where}`, `${err.fix ? `${err.fix} ` : ""}Run the same command again to resume; what went out is not published again: ${again}`);
|
|
410
|
+
}
|
|
411
|
+
const e = errorOf(err);
|
|
412
|
+
return failed(e.code, e.message, e.fix);
|
|
413
|
+
}
|
|
414
|
+
const { published } = run;
|
|
415
|
+
if (run.recordWarning && targets.length > 0)
|
|
416
|
+
console.error(formatWarning({ message: redactor.redact(`${run.recordWarning}. A resume asks the registries what is already there.`) }));
|
|
417
|
+
if (args.json) {
|
|
418
|
+
console.log(redactor.redact(JSON.stringify({ dryRun: false, ...base, targets: planned, published, resumed: run.resumed, environments: deploySteps }, null, 2)));
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
if (targets.length === 0) {
|
|
422
|
+
say("Nothing was published: this member declares no publish target (release.publish in chant.config.ts).");
|
|
423
|
+
const hint = promoteHint();
|
|
424
|
+
if (hint)
|
|
425
|
+
say(hint);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
for (const p of published)
|
|
429
|
+
for (const i of p.items)
|
|
430
|
+
say(` ${i.present ? "already there" : "published"}: ${describeItem(i)}`);
|
|
431
|
+
console.error(formatSuccess(`Published ${name} to ${published.length} target(s)`));
|
|
432
|
+
}
|
|
433
|
+
return 0;
|
|
434
|
+
}
|
|
435
|
+
/** One item a target publishes, for a line: what, where, and the SBOM or attachment it is (#4049). */
|
|
436
|
+
function describeItem(i) {
|
|
437
|
+
const what = i.attachment === "sbom" ? `${i.name} (SBOM)` : i.attachment === "provenance" ? `${i.name} (provenance)` : i.name;
|
|
438
|
+
return `${what} to ${i.destination}${i.sbom ? `, carrying ${i.sbom}` : ""}`;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* `release verify <label|digest> [--json]` (#4049): check, offline, that the
|
|
442
|
+
* release was signed by a key `.chant/allowed_signers` lists at the release's
|
|
443
|
+
* commit, and that its file is the digest of what it holds. Exits 0 when it
|
|
444
|
+
* verifies and 1 when it does not or can't be checked.
|
|
445
|
+
*/
|
|
446
|
+
export async function runReleaseVerify(ctx) {
|
|
447
|
+
const { args } = ctx;
|
|
448
|
+
const ref = args.extraPositional;
|
|
449
|
+
if (!ref) {
|
|
450
|
+
console.error(formatError({ message: "release verify needs a release", hint: USAGE }));
|
|
451
|
+
return 1;
|
|
452
|
+
}
|
|
453
|
+
const { verifyRelease, verificationCode } = await import("../../lifecycle/release-verify.js");
|
|
454
|
+
const result = await verifyRelease({ cwd: resolve("."), ref });
|
|
455
|
+
if ("error" in result) {
|
|
456
|
+
if (args.json)
|
|
457
|
+
console.log(JSON.stringify({ ref, error: result.error }, null, 2));
|
|
458
|
+
else
|
|
459
|
+
console.error(formatError({ message: `${result.error.message} [${result.error.code}]`, ...(result.error.fix ? { hint: result.error.fix } : {}) }));
|
|
460
|
+
return 1;
|
|
461
|
+
}
|
|
462
|
+
const code = verificationCode(result);
|
|
463
|
+
if (args.json) {
|
|
464
|
+
console.log(JSON.stringify({ ...result, ...(code ? { error: { code, message: result.reason } } : {}) }, null, 2));
|
|
465
|
+
return code ? 1 : 0;
|
|
466
|
+
}
|
|
467
|
+
const name = result.label ? `${formatBold(result.label)} (${result.releaseDigest})` : result.releaseDigest;
|
|
468
|
+
for (const c of result.cuts) {
|
|
469
|
+
console.log(` cut by ${c.actor} at ${c.timestamp}: ${c.signatureCheck.status}${c.signatureCheck.key ? ` (${c.signatureCheck.key})` : ""}`);
|
|
470
|
+
}
|
|
471
|
+
if (code) {
|
|
472
|
+
console.error(formatError({
|
|
473
|
+
message: `${name} does not verify: ${result.reason} [${code}]`,
|
|
474
|
+
hint: code === "release-digest-mismatch" ? "Do not publish or deploy it; cut the release again." : `Cut it again signed with a key ${result.signers.path} lists: chant release cut ${result.label ?? "<label>"} --sign --yes`,
|
|
475
|
+
}));
|
|
476
|
+
return 1;
|
|
477
|
+
}
|
|
478
|
+
console.error(formatSuccess(`Verified ${name}: ${result.reason}`));
|
|
479
|
+
return 0;
|
|
480
|
+
}
|
|
100
481
|
export async function runReleaseList(ctx) {
|
|
101
482
|
const { releasesDocument } = await import("../../workspace/releases.js");
|
|
102
483
|
const doc = await releasesDocument({ cwd: resolve(".") });
|
|
@@ -119,9 +500,14 @@ export async function runReleaseList(ctx) {
|
|
|
119
500
|
// #4031: the newest cut's signature, as the policy at base judges it.
|
|
120
501
|
const newest = r.cuts.at(-1);
|
|
121
502
|
const signature = newest ? (newest.signatureCheck.status === "verified" ? `signed by ${newest.actor}` : newest.signatureCheck.status) : "-";
|
|
122
|
-
|
|
503
|
+
// #4033: a release cut from a revoked commit says so at the end of its line, and why on stderr.
|
|
504
|
+
console.log(`${(r.label ?? "-").padEnd(16)} ${r.releaseDigest} ${r.commit.slice(0, 12)} ${signature} ${where}${r.revoked ? " revoked" : ""}`);
|
|
123
505
|
if (newest?.signatureCheck.status === "unverified")
|
|
124
506
|
console.error(` ${r.label ?? r.releaseDigest}: ${newest.signatureCheck.reason}`);
|
|
507
|
+
if (r.revoked) {
|
|
508
|
+
const { describeRevocation } = await import("../../lifecycle/release-revocation.js");
|
|
509
|
+
console.error(` ${r.label ?? r.releaseDigest}: revoked by ${r.revoked.tag}, ${describeRevocation(r.revoked)}`);
|
|
510
|
+
}
|
|
125
511
|
}
|
|
126
512
|
return 0;
|
|
127
513
|
}
|
|
@@ -109,7 +109,7 @@ export declare function autoReleaseRun(localId: string): {
|
|
|
109
109
|
*
|
|
110
110
|
* Returns the records it wrote, for `--digest-file` (#2602).
|
|
111
111
|
*/
|
|
112
|
-
export declare function recordAutoReleasesForRun(results: DriverComponentResult[], env: string, runId: string, disabled: boolean, runOrigin?: RunOrigin): Promise<ReleaseRecord[]>;
|
|
112
|
+
export declare function recordAutoReleasesForRun(results: DriverComponentResult[], env: string, runId: string, disabled: boolean, runOrigin?: RunOrigin, releaseDigest?: string): Promise<ReleaseRecord[]>;
|
|
113
113
|
/**
|
|
114
114
|
* `chant run --components <name|all|a,b,c> [--env <env>]` (#585) — the
|
|
115
115
|
* interpret driver's CLI entrypoint, and since #2116 the only component run
|
|
@@ -146,6 +146,16 @@ export declare function recordAutoReleasesForRun(results: DriverComponentResult[
|
|
|
146
146
|
* rather than loaded a second time.
|
|
147
147
|
*/
|
|
148
148
|
export declare function runOpComponents(ctx: CommandContext): Promise<number>;
|
|
149
|
+
/**
|
|
150
|
+
* Resolve the release `chant run --components --release` names (#4043), or the
|
|
151
|
+
* reason it can't be used. The run builds from the checked-out commit, so the
|
|
152
|
+
* release's commit must be HEAD, and the release needs a target for `env`.
|
|
153
|
+
*/
|
|
154
|
+
export declare function resolveRunRelease(ref: string, env: string, cwd: string): Promise<string | {
|
|
155
|
+
releaseDigest: string;
|
|
156
|
+
label: string | null;
|
|
157
|
+
release: import("../../lifecycle/release-store.js").Release;
|
|
158
|
+
}>;
|
|
149
159
|
/**
|
|
150
160
|
* `chant run <name>` on the resolved runtime (#2121) — the built-in `local`
|
|
151
161
|
* provider by default, a lexicon's `opRuntime` under `--on <name>`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAU1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAIlG;;;;;GAKG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA+BjC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAMjF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAChC,eAAe,EAAE,OAAO,GAAG,SAAS,EACpC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GACzC,MAAM,CAQR;AAuLD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAuED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AA8BD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAenE;AA2CD,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/cli/handlers/run.ts"],"names":[],"mappings":"AAMA,OAAO,EAAmB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAEhE,OAAO,EAAkD,KAAK,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAU1G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAWlD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAErE,OAAO,EAAgB,KAAK,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAIlG;;;;;GAKG;AACH,eAAO,MAAM,eAAe,IAAI,CAAC;AA+BjC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GAAG,OAAO,CAMjF;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAChC,eAAe,EAAE,OAAO,GAAG,SAAS,EACpC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,SAAS,GACzC,MAAM,CAQR;AAuLD,wBAAsB,SAAS,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpE;AAuED,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAetE;AA8BD,wBAAsB,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAenE;AA2CD,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,GAAG,MAAM,CAWrD;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CtE;AAOD;;;;;;;;;;;GAWG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA6CvE;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvE;AAID,iGAAiG;AACjG,wBAAsB,WAAW,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAsMD,wBAAsB,KAAK,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BhE;AAID;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAEvF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,qBAAqB,EAAE,EAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,OAAO,EACjB,SAAS,GAAE,SAA8B,EACzC,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,aAAa,EAAE,CAAC,CA+C1B;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAuCD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM;;;;GAe5E;AA8MD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnE;AAyPD,wBAAgB,YAAY,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAMjE"}
|