@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
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publishing a cut release (#4027): the contract `chant release publish`
|
|
3
|
+
* keeps, and the hook its targets fill in.
|
|
4
|
+
*
|
|
5
|
+
* A publish takes a release by label or digest and, before it publishes
|
|
6
|
+
* anything, refuses:
|
|
7
|
+
*
|
|
8
|
+
* - a release no cut names (`release-not-found`);
|
|
9
|
+
* - a release cut from a revoked commit (`release-revoked`, #4033);
|
|
10
|
+
* - a release none of whose cuts carries a signature that verifies for its
|
|
11
|
+
* actor against `.chant/allowed_signers` at base (`release-unsigned` or
|
|
12
|
+
* `release-signature-unverified`, #4049), and under an `identity.gates`
|
|
13
|
+
* entry for `release` (#4031) one whose signer is not in the gate's class.
|
|
14
|
+
* `--allow-unsigned` publishes one with no verified signature anyway, with
|
|
15
|
+
* a warning, except under the release gate, which it never overrides.
|
|
16
|
+
*
|
|
17
|
+
* It then says whether the run has an OIDC token to sign provenance with
|
|
18
|
+
* ({@link detectProvenance}), and warns when it has none, before any target
|
|
19
|
+
* runs; `--require-provenance` refuses instead (`publish-provenance-missing`).
|
|
20
|
+
* The targets come from {@link releasePublishTargets}: the member declares them under `release.publish` in its `chant.config.ts` (#4047).
|
|
21
|
+
* With a target, the checkout must be the release's commit with a clean
|
|
22
|
+
* tree ({@link checkCheckoutIsRelease}), every target is planned before any
|
|
23
|
+
* publishes, and without `--yes` nothing is published. With `--yes`,
|
|
24
|
+
* {@link runPublish} stages every target, then reveals them (#4048): nothing a
|
|
25
|
+
* consumer resolves changes until every target has staged, and a publish
|
|
26
|
+
* that failed resumes from the record in `_releases/publishes.jsonl`
|
|
27
|
+
* (./publish-record.ts) without publishing again what went out. Each target
|
|
28
|
+
* attaches an SBOM and the release's provenance statement, which carries the
|
|
29
|
+
* cut's signature, when it stages (./publish-attachments.ts, #4049). The
|
|
30
|
+
* publish steps a component declares (`publish-image`, `publish-artifact`, `publish-asset`,
|
|
31
|
+
* `load-image-on-host`) are deploy steps: they run in each environment's
|
|
32
|
+
* deploy of the release (`chant components promote --release`), never here.
|
|
33
|
+
*/
|
|
34
|
+
import type { Release } from "./release-store.js";
|
|
35
|
+
import { type Redactor } from "./publish-credentials.js";
|
|
36
|
+
/** Whether this run can sign provenance with an OIDC token, and why not when it can't. */
|
|
37
|
+
export interface ProvenanceCheck {
|
|
38
|
+
/** True when the run has an OIDC token a registry accepts for provenance. */
|
|
39
|
+
oidc: boolean;
|
|
40
|
+
/** The CI the run is in, or null on a workstation. */
|
|
41
|
+
ci: "github" | "gitlab" | null;
|
|
42
|
+
/** One sentence on what was found. */
|
|
43
|
+
reason: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Whether the process environment gives this run an OIDC token for
|
|
47
|
+
* provenance: in GitHub Actions, `ACTIONS_ID_TOKEN_REQUEST_URL` and
|
|
48
|
+
* `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, which a job with `id-token: write` gets;
|
|
49
|
+
* in GitLab CI, an ID token named `SIGSTORE_ID_TOKEN` or `NPM_ID_TOKEN`
|
|
50
|
+
* declared under the job's `id_tokens`. Anywhere else there is none.
|
|
51
|
+
*/
|
|
52
|
+
export declare function detectProvenance(env?: NodeJS.ProcessEnv): ProvenanceCheck;
|
|
53
|
+
/** One thing a target publishes: a package, an image, an asset. */
|
|
54
|
+
export interface PublishItem {
|
|
55
|
+
/** What it is, such as `@acme/core@1.4.0` or `api-linux-amd64.tar.gz`. */
|
|
56
|
+
name: string;
|
|
57
|
+
/** Where it goes, such as `ghcr.io/acme/api@sha256:...` or a release's asset URL. */
|
|
58
|
+
destination: string;
|
|
59
|
+
/** Its digest, when the target knows one. */
|
|
60
|
+
digest?: string;
|
|
61
|
+
/** True when the destination already held it, so nothing was uploaded. */
|
|
62
|
+
present?: boolean;
|
|
63
|
+
/** Set on a file attached to what the target publishes (#4049): an SBOM or the release's provenance statement. */
|
|
64
|
+
attachment?: "sbom" | "provenance";
|
|
65
|
+
/** The SBOM file an npm package carries at its root (#4049). */
|
|
66
|
+
sbom?: string;
|
|
67
|
+
}
|
|
68
|
+
/** The cut a publish carries the signature of (#4049): the cut whose signature verified, else the newest. */
|
|
69
|
+
export interface PublishCut {
|
|
70
|
+
actor: string;
|
|
71
|
+
label: string | null;
|
|
72
|
+
timestamp: string;
|
|
73
|
+
runId: string | null;
|
|
74
|
+
/** The armored ssh signature in the `chant-release` namespace, or null for an unsigned cut. */
|
|
75
|
+
signature: string | null;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* What one target staged: its items, out where the target publishes but not
|
|
79
|
+
* where a consumer resolves them yet, and the values its reveal needs that it
|
|
80
|
+
* can't read again from the checkout, such as a draft release's id. The
|
|
81
|
+
* publish record keeps it, so a resume reveals without staging again. It never
|
|
82
|
+
* holds a credential.
|
|
83
|
+
*/
|
|
84
|
+
export interface StagedTarget {
|
|
85
|
+
target: string;
|
|
86
|
+
destination: string;
|
|
87
|
+
items: PublishItem[];
|
|
88
|
+
state: Record<string, string>;
|
|
89
|
+
}
|
|
90
|
+
/** What one target published. */
|
|
91
|
+
export interface PublishedTarget {
|
|
92
|
+
target: string;
|
|
93
|
+
/** Where it went, such as a registry and a package name. */
|
|
94
|
+
destination: string;
|
|
95
|
+
/** Each thing published, in order. */
|
|
96
|
+
items: PublishItem[];
|
|
97
|
+
}
|
|
98
|
+
/** Injected into the targets, so tests drive them with fakes. */
|
|
99
|
+
export interface PublishDeps {
|
|
100
|
+
/** The HTTP client the OCI and forge targets use. Default: the global `fetch`. */
|
|
101
|
+
fetch?: PublishFetch;
|
|
102
|
+
/** The npm executable. Default: `npm` on `PATH`. */
|
|
103
|
+
npm?: string;
|
|
104
|
+
}
|
|
105
|
+
/** The subset of the WHATWG `fetch` the targets use. */
|
|
106
|
+
export type PublishFetch = (url: string, init: {
|
|
107
|
+
method: string;
|
|
108
|
+
headers: Record<string, string>;
|
|
109
|
+
body?: Uint8Array | string;
|
|
110
|
+
redirect?: "follow" | "manual";
|
|
111
|
+
}) => Promise<{
|
|
112
|
+
ok: boolean;
|
|
113
|
+
status: number;
|
|
114
|
+
headers: {
|
|
115
|
+
get(name: string): string | null;
|
|
116
|
+
};
|
|
117
|
+
text(): Promise<string>;
|
|
118
|
+
}>;
|
|
119
|
+
/** What a target is given to plan and to publish. */
|
|
120
|
+
export interface PublishContext {
|
|
121
|
+
release: Release;
|
|
122
|
+
label: string | null;
|
|
123
|
+
/** The tag the cut made for the release, `release/<label>` or `release/<member>/<label>`, or null for a release with no label. */
|
|
124
|
+
releaseTag: string | null;
|
|
125
|
+
provenance: ProvenanceCheck;
|
|
126
|
+
/** The cut whose signature the provenance statement carries (#4049), or null when none is known. */
|
|
127
|
+
cut?: PublishCut | null;
|
|
128
|
+
/** The member's directory, which the declaration's paths are relative to. */
|
|
129
|
+
cwd: string;
|
|
130
|
+
/**
|
|
131
|
+
* The tag a reveal points at what was staged, in place of the target's
|
|
132
|
+
* own: npm's dist-tag (default `latest`) and the OCI tags. Null for the
|
|
133
|
+
* target's own. Staging never uses it: npm stages under `next`
|
|
134
|
+
* ({@link NPM_STAGE_TAG}) and OCI by digest.
|
|
135
|
+
*/
|
|
136
|
+
tag: string | null;
|
|
137
|
+
/** The environment credentials are read from. */
|
|
138
|
+
env: NodeJS.ProcessEnv;
|
|
139
|
+
/** Masks credentials; every line a target logs and every error it throws goes through it. */
|
|
140
|
+
redactor: Redactor;
|
|
141
|
+
/** Print one line of progress. The line is redacted. */
|
|
142
|
+
log(line: string): void;
|
|
143
|
+
}
|
|
144
|
+
/** A target refused or failed. The message never holds a credential. */
|
|
145
|
+
export declare class PublishTargetError extends Error {
|
|
146
|
+
readonly code: string;
|
|
147
|
+
readonly fix?: string | undefined;
|
|
148
|
+
constructor(code: string, message: string, fix?: string | undefined);
|
|
149
|
+
}
|
|
150
|
+
/** The npm dist-tag a set of packages is staged under before `latest` moves (#4048), as chant's own releases do (#3903). */
|
|
151
|
+
export declare const NPM_STAGE_TAG = "next";
|
|
152
|
+
/**
|
|
153
|
+
* One place a release is published to (#4047). A target publishes the
|
|
154
|
+
* release's artifacts as they were built at the cut, checked against their
|
|
155
|
+
* digests where the release records them, and never builds. Targets are
|
|
156
|
+
* declared under `release.publish` in the member's `chant.config.ts`
|
|
157
|
+
* (./publish-declaration.ts) and made by `createPublishTargets`
|
|
158
|
+
* (./publish-targets.ts). A new kind of target is a new member of the
|
|
159
|
+
* declaration's union and a factory that returns this interface.
|
|
160
|
+
*/
|
|
161
|
+
export interface PublishTarget {
|
|
162
|
+
/** The declaration's `type`. */
|
|
163
|
+
kind: "npm" | "oci" | "forge";
|
|
164
|
+
/** A name for messages, such as `npm (2 packages)`. */
|
|
165
|
+
name: string;
|
|
166
|
+
/** Where it publishes, for the line printed before it runs. */
|
|
167
|
+
destination: string;
|
|
168
|
+
/** The environment variables it reads credentials from, whose values are masked in all output. */
|
|
169
|
+
credentialEnv: string[];
|
|
170
|
+
/**
|
|
171
|
+
* Names the declaration across runs, so a resume finds the target's lines
|
|
172
|
+
* in the publish record: the kind and a digest of the declaration, which
|
|
173
|
+
* holds variable names and never a credential.
|
|
174
|
+
*/
|
|
175
|
+
key: string;
|
|
176
|
+
/**
|
|
177
|
+
* Read what it would publish and check that it can: the files are there,
|
|
178
|
+
* they match the release's digests, the credentials it needs are set.
|
|
179
|
+
* Uploads nothing and makes no request that changes anything. Throws
|
|
180
|
+
* {@link PublishTargetError}. The dry run prints what it returns, and a
|
|
181
|
+
* publish plans every target before any of them publishes.
|
|
182
|
+
*/
|
|
183
|
+
plan(ctx: PublishContext): Promise<PublishItem[]>;
|
|
184
|
+
/**
|
|
185
|
+
* Put every item where the target publishes, where no consumer resolves it
|
|
186
|
+
* yet (#4048): npm under the `next` dist-tag, OCI by digest with no tag
|
|
187
|
+
* moved, forge assets on a draft release (GitLab: in the package registry,
|
|
188
|
+
* with no release link). An item the destination already holds is not sent
|
|
189
|
+
* again and comes back `present`, so a stage run again after a failure
|
|
190
|
+
* sends only what is missing. Throws {@link PublishTargetError}.
|
|
191
|
+
*/
|
|
192
|
+
stage(ctx: PublishContext): Promise<StagedTarget>;
|
|
193
|
+
/**
|
|
194
|
+
* The one last step that makes what {@link stage} put out visible: npm
|
|
195
|
+
* moves `latest` (or `ctx.tag`) package by package in the declared order,
|
|
196
|
+
* the first package first; OCI points the tags at the digest; a forge
|
|
197
|
+
* publishes the draft (GitLab: creates the release with its links). Run
|
|
198
|
+
* again, it moves nothing that already moved. Throws {@link PublishTargetError}.
|
|
199
|
+
*/
|
|
200
|
+
reveal(ctx: PublishContext, staged: StagedTarget): Promise<PublishedTarget>;
|
|
201
|
+
}
|
|
202
|
+
/** The targets the member at `cwd` declares under `release.publish` in its `chant.config.ts`. */
|
|
203
|
+
export declare function releasePublishTargets(cwd: string, deps?: PublishDeps): Promise<PublishTarget[]>;
|
|
204
|
+
/** Why a publish was refused before anything ran. */
|
|
205
|
+
export interface PublishRefusal {
|
|
206
|
+
code: "release-not-found" | "release-revoked" | "release-unsigned" | "release-signature-unverified" | "publish-provenance-missing" | "publish-config-invalid" | "release-head-mismatch" | "release-worktree-dirty";
|
|
207
|
+
message: string;
|
|
208
|
+
fix?: string;
|
|
209
|
+
}
|
|
210
|
+
/** A release ready to publish: what it is, how its signature reads, the provenance check, and its targets. */
|
|
211
|
+
export interface PublishPlan {
|
|
212
|
+
release: Release;
|
|
213
|
+
label: string | null;
|
|
214
|
+
/** The verified cut's signature when one verifies, else the newest cut's. */
|
|
215
|
+
signature: {
|
|
216
|
+
status: "verified" | "unsigned" | "unverified";
|
|
217
|
+
actor: string | null;
|
|
218
|
+
key: string | null;
|
|
219
|
+
reason: string;
|
|
220
|
+
};
|
|
221
|
+
provenance: ProvenanceCheck;
|
|
222
|
+
/** The warning printed before publishing, when the run has no OIDC token. */
|
|
223
|
+
warning: string | null;
|
|
224
|
+
/** The warning printed before publishing a release with no verified signature under `--allow-unsigned` (#4049). */
|
|
225
|
+
signatureWarning: string | null;
|
|
226
|
+
/** The cut whose signature the provenance statement carries: the verified one, else the newest. */
|
|
227
|
+
cut: PublishCut | null;
|
|
228
|
+
/** The tag the cut made, or null for a release with no label. */
|
|
229
|
+
releaseTag: string | null;
|
|
230
|
+
targets: PublishTarget[];
|
|
231
|
+
/** Masks the run's credentials in what the publish prints. */
|
|
232
|
+
redactor: Redactor;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Resolve `ref` (a label or a digest) in the member at `cwd` and decide
|
|
236
|
+
* whether it may be published, reading the local `chant/lifecycle` branch,
|
|
237
|
+
* the local tags and the declaration at base. Fetches nothing.
|
|
238
|
+
*
|
|
239
|
+
* `allowUnsigned` publishes a release with no verified signature, which is
|
|
240
|
+
* refused otherwise (#4049); it does not override the release gate.
|
|
241
|
+
* `requireProvenance` refuses a run with no OIDC token, where it warns.
|
|
242
|
+
*/
|
|
243
|
+
export declare function preparePublish(req: {
|
|
244
|
+
cwd: string;
|
|
245
|
+
ref: string;
|
|
246
|
+
env?: NodeJS.ProcessEnv;
|
|
247
|
+
deps?: PublishDeps;
|
|
248
|
+
allowUnsigned?: boolean;
|
|
249
|
+
requireProvenance?: boolean;
|
|
250
|
+
}): Promise<PublishPlan | {
|
|
251
|
+
refusal: PublishRefusal;
|
|
252
|
+
}>;
|
|
253
|
+
/**
|
|
254
|
+
* A publish with a target reads files from the checkout it runs in: package
|
|
255
|
+
* directories, an image layout, assets. They are the release's only when the
|
|
256
|
+
* checkout is the release's commit with nothing changed, so a publish refuses
|
|
257
|
+
* a `HEAD` that is another commit and a tree with changes git sees. A publish
|
|
258
|
+
* with no target reads nothing and checks neither.
|
|
259
|
+
*/
|
|
260
|
+
export declare function checkCheckoutIsRelease(cwd: string, commit: string, name: string): PublishRefusal | null;
|
|
261
|
+
/** The context each target is planned and published with. */
|
|
262
|
+
export declare function publishContext(plan: PublishPlan, req: {
|
|
263
|
+
cwd: string;
|
|
264
|
+
env?: NodeJS.ProcessEnv;
|
|
265
|
+
tag?: string | null;
|
|
266
|
+
log: (line: string) => void;
|
|
267
|
+
}): PublishContext;
|
|
268
|
+
/** What {@link runPublish} did. */
|
|
269
|
+
export interface PublishRun {
|
|
270
|
+
/** Each target, revealed, in the order declared. */
|
|
271
|
+
published: PublishedTarget[];
|
|
272
|
+
/** The targets an earlier run had staged or revealed, so this one did not stage them again. */
|
|
273
|
+
resumed: Array<{
|
|
274
|
+
target: string;
|
|
275
|
+
step: "staged" | "revealed";
|
|
276
|
+
}>;
|
|
277
|
+
/** Why a record line was not written or not pushed, when one wasn't. A resume then asks the registries. */
|
|
278
|
+
recordWarning: string | null;
|
|
279
|
+
}
|
|
280
|
+
/** A publish that stopped, with what it left staged and revealed so a run of the same command resumes. */
|
|
281
|
+
export declare class PublishRunError extends PublishTargetError {
|
|
282
|
+
readonly step: "stage" | "reveal";
|
|
283
|
+
readonly staged: string[];
|
|
284
|
+
readonly revealed: string[];
|
|
285
|
+
constructor(code: string, message: string, fix: string | undefined, step: "stage" | "reveal", staged: string[], revealed: string[]);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Publish `targets` all or nothing (#4048), from the record of earlier runs:
|
|
289
|
+
*
|
|
290
|
+
* 1. Stage every target in the order declared, skipping one the record says
|
|
291
|
+
* staged or revealed. Each stage is recorded and pushed as it finishes.
|
|
292
|
+
* A failure here leaves nothing visible to a consumer.
|
|
293
|
+
* 2. Only when every target has staged, reveal each in the same order,
|
|
294
|
+
* skipping one the record says revealed, and record each reveal.
|
|
295
|
+
*
|
|
296
|
+
* A run of the same publish after a failure resumes: what went out is not
|
|
297
|
+
* published again, and a stage that failed part way sends only what its
|
|
298
|
+
* destination does not hold. Throws {@link PublishRunError}.
|
|
299
|
+
*/
|
|
300
|
+
export declare function runPublish(req: {
|
|
301
|
+
cwd: string;
|
|
302
|
+
release: Release;
|
|
303
|
+
label: string | null;
|
|
304
|
+
targets: readonly PublishTarget[];
|
|
305
|
+
ctx: PublishContext;
|
|
306
|
+
/** Write the record. Default true. */
|
|
307
|
+
record?: boolean;
|
|
308
|
+
/** Print one line of progress. Redacted by the caller. */
|
|
309
|
+
say?: (line: string) => void;
|
|
310
|
+
now?: () => Date;
|
|
311
|
+
}): Promise<PublishRun>;
|
|
312
|
+
//# sourceMappingURL=release-publish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-publish.d.ts","sourceRoot":"","sources":["../../src/lifecycle/release-publish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publishing a cut release (#4027): the contract `chant release publish`
|
|
3
|
+
* keeps, and the hook its targets fill in.
|
|
4
|
+
*
|
|
5
|
+
* A publish takes a release by label or digest and, before it publishes
|
|
6
|
+
* anything, refuses:
|
|
7
|
+
*
|
|
8
|
+
* - a release no cut names (`release-not-found`);
|
|
9
|
+
* - a release cut from a revoked commit (`release-revoked`, #4033);
|
|
10
|
+
* - a release none of whose cuts carries a signature that verifies for its
|
|
11
|
+
* actor against `.chant/allowed_signers` at base (`release-unsigned` or
|
|
12
|
+
* `release-signature-unverified`, #4049), and under an `identity.gates`
|
|
13
|
+
* entry for `release` (#4031) one whose signer is not in the gate's class.
|
|
14
|
+
* `--allow-unsigned` publishes one with no verified signature anyway, with
|
|
15
|
+
* a warning, except under the release gate, which it never overrides.
|
|
16
|
+
*
|
|
17
|
+
* It then says whether the run has an OIDC token to sign provenance with
|
|
18
|
+
* ({@link detectProvenance}), and warns when it has none, before any target
|
|
19
|
+
* runs; `--require-provenance` refuses instead (`publish-provenance-missing`).
|
|
20
|
+
* The targets come from {@link releasePublishTargets}: the member declares them under `release.publish` in its `chant.config.ts` (#4047).
|
|
21
|
+
* With a target, the checkout must be the release's commit with a clean
|
|
22
|
+
* tree ({@link checkCheckoutIsRelease}), every target is planned before any
|
|
23
|
+
* publishes, and without `--yes` nothing is published. With `--yes`,
|
|
24
|
+
* {@link runPublish} stages every target, then reveals them (#4048): nothing a
|
|
25
|
+
* consumer resolves changes until every target has staged, and a publish
|
|
26
|
+
* that failed resumes from the record in `_releases/publishes.jsonl`
|
|
27
|
+
* (./publish-record.ts) without publishing again what went out. Each target
|
|
28
|
+
* attaches an SBOM and the release's provenance statement, which carries the
|
|
29
|
+
* cut's signature, when it stages (./publish-attachments.ts, #4049). The
|
|
30
|
+
* publish steps a component declares (`publish-image`, `publish-artifact`, `publish-asset`,
|
|
31
|
+
* `load-image-on-host`) are deploy steps: they run in each environment's
|
|
32
|
+
* deploy of the release (`chant components promote --release`), never here.
|
|
33
|
+
*/
|
|
34
|
+
import { execFileSync } from "node:child_process";
|
|
35
|
+
import { createRedactor } from "./publish-credentials.js";
|
|
36
|
+
/**
|
|
37
|
+
* Whether the process environment gives this run an OIDC token for
|
|
38
|
+
* provenance: in GitHub Actions, `ACTIONS_ID_TOKEN_REQUEST_URL` and
|
|
39
|
+
* `ACTIONS_ID_TOKEN_REQUEST_TOKEN`, which a job with `id-token: write` gets;
|
|
40
|
+
* in GitLab CI, an ID token named `SIGSTORE_ID_TOKEN` or `NPM_ID_TOKEN`
|
|
41
|
+
* declared under the job's `id_tokens`. Anywhere else there is none.
|
|
42
|
+
*/
|
|
43
|
+
export function detectProvenance(env = process.env) {
|
|
44
|
+
if (env.GITHUB_ACTIONS === "true") {
|
|
45
|
+
return env.ACTIONS_ID_TOKEN_REQUEST_URL && env.ACTIONS_ID_TOKEN_REQUEST_TOKEN
|
|
46
|
+
? { oidc: true, ci: "github", reason: "GitHub Actions hands this job an OIDC token" }
|
|
47
|
+
: { oidc: false, ci: "github", reason: "this GitHub Actions job has no OIDC token; give the job permissions: id-token: write" };
|
|
48
|
+
}
|
|
49
|
+
if (env.GITLAB_CI === "true") {
|
|
50
|
+
return env.SIGSTORE_ID_TOKEN || env.NPM_ID_TOKEN
|
|
51
|
+
? { oidc: true, ci: "gitlab", reason: "GitLab CI hands this job an ID token" }
|
|
52
|
+
: { oidc: false, ci: "gitlab", reason: "this GitLab CI job has no ID token; declare SIGSTORE_ID_TOKEN under the job's id_tokens" };
|
|
53
|
+
}
|
|
54
|
+
return { oidc: false, ci: null, reason: "this run is not in CI, and a workstation has no OIDC token" };
|
|
55
|
+
}
|
|
56
|
+
/** A target refused or failed. The message never holds a credential. */
|
|
57
|
+
export class PublishTargetError extends Error {
|
|
58
|
+
constructor(code, message, fix) {
|
|
59
|
+
super(message);
|
|
60
|
+
this.code = code;
|
|
61
|
+
this.fix = fix;
|
|
62
|
+
this.name = "PublishTargetError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** The npm dist-tag a set of packages is staged under before `latest` moves (#4048), as chant's own releases do (#3903). */
|
|
66
|
+
export const NPM_STAGE_TAG = "next";
|
|
67
|
+
/** The targets the member at `cwd` declares under `release.publish` in its `chant.config.ts`. */
|
|
68
|
+
export async function releasePublishTargets(cwd, deps = {}) {
|
|
69
|
+
const { loadChantConfig } = await import("../config.js");
|
|
70
|
+
const { createPublishTargets } = await import("./publish-targets.js");
|
|
71
|
+
const { config } = await loadChantConfig(cwd);
|
|
72
|
+
return createPublishTargets(config.release?.publish ?? [], deps);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolve `ref` (a label or a digest) in the member at `cwd` and decide
|
|
76
|
+
* whether it may be published, reading the local `chant/lifecycle` branch,
|
|
77
|
+
* the local tags and the declaration at base. Fetches nothing.
|
|
78
|
+
*
|
|
79
|
+
* `allowUnsigned` publishes a release with no verified signature, which is
|
|
80
|
+
* refused otherwise (#4049); it does not override the release gate.
|
|
81
|
+
* `requireProvenance` refuses a run with no OIDC token, where it warns.
|
|
82
|
+
*/
|
|
83
|
+
export async function preparePublish(req) {
|
|
84
|
+
const { resolveRelease, readCuts } = await import("./release-store.js");
|
|
85
|
+
const { readRevokedCommits, revocationOf, describeRevocation } = await import("./release-revocation.js");
|
|
86
|
+
const { scopeSource } = await import("../workspace/write-scope.js");
|
|
87
|
+
const { releaseRuleFrom } = await import("../workspace/identity.js");
|
|
88
|
+
const { checkReleaseSignature } = await import("../workspace/identity-gate-rule.js");
|
|
89
|
+
const { cwd, ref } = req;
|
|
90
|
+
const found = await resolveRelease(ref, { cwd });
|
|
91
|
+
if (!found)
|
|
92
|
+
return { refusal: { code: "release-not-found", message: `no release is labelled or digested "${ref}" in this member`, fix: "List them with: chant release list" } };
|
|
93
|
+
const { release, label } = found;
|
|
94
|
+
const name = label ?? release.releaseDigest;
|
|
95
|
+
const revoked = revocationOf(release, await readRevokedCommits(cwd).catch(() => new Map()));
|
|
96
|
+
if (revoked) {
|
|
97
|
+
return {
|
|
98
|
+
refusal: {
|
|
99
|
+
code: "release-revoked",
|
|
100
|
+
message: `${name} was cut from commit ${release.commit.slice(0, 12)}, which ${revoked.tag} revokes: ${describeRevocation(revoked)}`,
|
|
101
|
+
fix: "Cut a release from a later green commit.",
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const rule = releaseRuleFrom(scopeSource(cwd));
|
|
106
|
+
const cuts = (await readCuts({ cwd })).cuts.filter((c) => c.releaseDigest === release.releaseDigest);
|
|
107
|
+
const checks = cuts.map((c) => ({
|
|
108
|
+
cut: c,
|
|
109
|
+
check: checkReleaseSignature(rule.rule, { releaseDigest: c.releaseDigest, label: c.label, actor: c.actor, timestamp: c.timestamp, signature: c.signature }),
|
|
110
|
+
}));
|
|
111
|
+
const verified = checks.find((c) => c.check.status === "verified");
|
|
112
|
+
const unsigned = checks.every((c) => c.check.status === "unsigned");
|
|
113
|
+
if (rule.requirement && !verified) {
|
|
114
|
+
return {
|
|
115
|
+
refusal: {
|
|
116
|
+
code: unsigned ? "release-unsigned" : "release-signature-unverified",
|
|
117
|
+
message: unsigned
|
|
118
|
+
? `${name} has no signed cut, and identity.gates.release in chant.workspace.json at base publishes only a signed release`
|
|
119
|
+
: `no cut of ${name} carries a signature that counts: ${checks.map((c) => c.check.reason).join("; ")}`,
|
|
120
|
+
fix: `Cut it signed by a signer${rule.requirement.class !== null ? ` in the class ${rule.requirement.class}` : ""}: chant release cut ${label ?? "<label>"} --sign --yes`,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// #4049: with or without the gate, a publish refuses a release whose signature does not verify, unless --allow-unsigned.
|
|
125
|
+
let signatureWarning = null;
|
|
126
|
+
if (!verified) {
|
|
127
|
+
const why = unsigned
|
|
128
|
+
? `${name} has no signed cut`
|
|
129
|
+
: `no cut of ${name} carries a signature that verifies against the signers file at base: ${checks.map((c) => c.check.reason).join("; ")}`;
|
|
130
|
+
if (!req.allowUnsigned) {
|
|
131
|
+
return {
|
|
132
|
+
refusal: {
|
|
133
|
+
code: unsigned ? "release-unsigned" : "release-signature-unverified",
|
|
134
|
+
message: `${why}, and chant release publish publishes only a release signed by a key in .chant/allowed_signers`,
|
|
135
|
+
fix: `Cut it signed with a key .chant/allowed_signers lists for the chant-release namespace: chant release cut ${label ?? "<label>"} --sign --yes. To publish it unsigned anyway, pass --allow-unsigned.`,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
signatureWarning = `${why}; publishing it anyway under --allow-unsigned, so chant release verify ${label ?? release.releaseDigest} will fail for it`;
|
|
140
|
+
}
|
|
141
|
+
const shown = verified ?? checks.at(-1);
|
|
142
|
+
const signature = shown
|
|
143
|
+
? { status: shown.check.status, actor: shown.cut.actor, key: shown.check.key, reason: shown.check.reason }
|
|
144
|
+
: { status: "unsigned", actor: null, key: null, reason: "no cut names the release" };
|
|
145
|
+
const cut = shown
|
|
146
|
+
? { actor: shown.cut.actor, label: shown.cut.label, timestamp: shown.cut.timestamp, runId: shown.cut.runId ?? null, signature: shown.cut.signature ?? null }
|
|
147
|
+
: null;
|
|
148
|
+
let targets;
|
|
149
|
+
try {
|
|
150
|
+
targets = await releasePublishTargets(cwd, req.deps);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
return { refusal: { code: "publish-config-invalid", message: `the publish targets can't be read: ${err instanceof Error ? err.message : String(err)}`, fix: "Fix release.publish in chant.config.ts." } };
|
|
154
|
+
}
|
|
155
|
+
// A target publishes files from this checkout, so they must be the release commit's (#4047).
|
|
156
|
+
if (targets.length > 0) {
|
|
157
|
+
const refused = checkCheckoutIsRelease(cwd, release.commit, name);
|
|
158
|
+
if (refused)
|
|
159
|
+
return { refusal: refused };
|
|
160
|
+
}
|
|
161
|
+
const env = req.env ?? process.env;
|
|
162
|
+
const provenance = detectProvenance(env);
|
|
163
|
+
if (req.requireProvenance && !provenance.oidc) {
|
|
164
|
+
return {
|
|
165
|
+
refusal: {
|
|
166
|
+
code: "publish-provenance-missing",
|
|
167
|
+
message: `${name} would be published without OIDC provenance, and --require-provenance asks for it: ${provenance.reason}`,
|
|
168
|
+
fix: "Publish from a CI job that has an OIDC token (GitHub Actions: permissions: id-token: write), or drop --require-provenance.",
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const warning = provenance.oidc
|
|
173
|
+
? null
|
|
174
|
+
: `this publish runs without OIDC, so what it publishes carries no provenance: ${provenance.reason}. Publish from CI to get provenance.`;
|
|
175
|
+
const { resolveMemberLedger } = await import("./member-ledger.js");
|
|
176
|
+
const { releaseTagName } = await import("./release-workstation.js");
|
|
177
|
+
const ledger = await resolveMemberLedger(cwd).catch(() => ({ members: [] }));
|
|
178
|
+
const member = ledger.members.length > 0 ? ledger.members.join("/") : null;
|
|
179
|
+
const releaseTag = label !== null ? releaseTagName(member, label) : null;
|
|
180
|
+
const redactor = createRedactor(env, targets.flatMap((t) => t.credentialEnv));
|
|
181
|
+
return { release, label, signature, provenance, warning, signatureWarning, cut, releaseTag, targets, redactor };
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* A publish with a target reads files from the checkout it runs in: package
|
|
185
|
+
* directories, an image layout, assets. They are the release's only when the
|
|
186
|
+
* checkout is the release's commit with nothing changed, so a publish refuses
|
|
187
|
+
* a `HEAD` that is another commit and a tree with changes git sees. A publish
|
|
188
|
+
* with no target reads nothing and checks neither.
|
|
189
|
+
*/
|
|
190
|
+
export function checkCheckoutIsRelease(cwd, commit, name) {
|
|
191
|
+
const head = tryGit(cwd, ["rev-parse", "HEAD"]);
|
|
192
|
+
if (head !== commit) {
|
|
193
|
+
return {
|
|
194
|
+
code: "release-head-mismatch",
|
|
195
|
+
message: `${name} was cut from commit ${commit.slice(0, 12)}, but this checkout is at ${head ? head.slice(0, 12) : "no commit"}, so what it would publish is not the release's`,
|
|
196
|
+
fix: `Check out the release's commit, as a CI job on its tag does: git checkout ${commit}`,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const status = tryGit(cwd, ["status", "--porcelain", "--untracked-files=normal"]);
|
|
200
|
+
if (status) {
|
|
201
|
+
const n = status.split("\n").length;
|
|
202
|
+
return {
|
|
203
|
+
code: "release-worktree-dirty",
|
|
204
|
+
message: `the working tree has ${n} changed or untracked file(s) such as ${status.split("\n")[0].slice(3)}, so what a publish reads here is not commit ${commit.slice(0, 12)}'s`,
|
|
205
|
+
fix: "Publish from a clean checkout of the release's commit. Build outputs belong in .gitignore.",
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
function tryGit(cwd, args) {
|
|
211
|
+
try {
|
|
212
|
+
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** The context each target is planned and published with. */
|
|
219
|
+
export function publishContext(plan, req) {
|
|
220
|
+
return {
|
|
221
|
+
release: plan.release,
|
|
222
|
+
label: plan.label,
|
|
223
|
+
releaseTag: plan.releaseTag,
|
|
224
|
+
provenance: plan.provenance,
|
|
225
|
+
cut: plan.cut,
|
|
226
|
+
cwd: req.cwd,
|
|
227
|
+
tag: req.tag ?? null,
|
|
228
|
+
env: req.env ?? process.env,
|
|
229
|
+
redactor: plan.redactor,
|
|
230
|
+
log: (line) => req.log(plan.redactor.redact(line)),
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/** A publish that stopped, with what it left staged and revealed so a run of the same command resumes. */
|
|
234
|
+
export class PublishRunError extends PublishTargetError {
|
|
235
|
+
constructor(code, message, fix, step, staged, revealed) {
|
|
236
|
+
super(code, message, fix);
|
|
237
|
+
this.step = step;
|
|
238
|
+
this.staged = staged;
|
|
239
|
+
this.revealed = revealed;
|
|
240
|
+
this.name = "PublishRunError";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Publish `targets` all or nothing (#4048), from the record of earlier runs:
|
|
245
|
+
*
|
|
246
|
+
* 1. Stage every target in the order declared, skipping one the record says
|
|
247
|
+
* staged or revealed. Each stage is recorded and pushed as it finishes.
|
|
248
|
+
* A failure here leaves nothing visible to a consumer.
|
|
249
|
+
* 2. Only when every target has staged, reveal each in the same order,
|
|
250
|
+
* skipping one the record says revealed, and record each reveal.
|
|
251
|
+
*
|
|
252
|
+
* A run of the same publish after a failure resumes: what went out is not
|
|
253
|
+
* published again, and a stage that failed part way sends only what its
|
|
254
|
+
* destination does not hold. Throws {@link PublishRunError}.
|
|
255
|
+
*/
|
|
256
|
+
export async function runPublish(req) {
|
|
257
|
+
const { appendPublishRecord, publishProgress, readPublishRecords } = await import("./publish-record.js");
|
|
258
|
+
const { pushLifecycleReport } = await import("./git.js");
|
|
259
|
+
const { resolveRunId } = await import("./release-ledger.js");
|
|
260
|
+
const { cwd, release, label, targets, ctx } = req;
|
|
261
|
+
const say = req.say ?? (() => undefined);
|
|
262
|
+
const now = req.now ?? (() => new Date());
|
|
263
|
+
const record = req.record ?? true;
|
|
264
|
+
const runId = resolveRunId(undefined, ctx.env).runId;
|
|
265
|
+
const progress = publishProgress(await readPublishRecords(release.releaseDigest, { cwd }).catch(() => []));
|
|
266
|
+
let recordWarning = null;
|
|
267
|
+
const write = async (target, step, items, state) => {
|
|
268
|
+
if (!record)
|
|
269
|
+
return;
|
|
270
|
+
try {
|
|
271
|
+
await appendPublishRecord({ version: 1, releaseDigest: release.releaseDigest, label, target: target.key, kind: target.kind, name: target.name, step, items, state, runId, timestamp: now().toISOString() }, ctx.redactor, { cwd });
|
|
272
|
+
const push = await pushLifecycleReport({ cwd });
|
|
273
|
+
if (push.pushWarning && recordWarning === null)
|
|
274
|
+
recordWarning = push.pushWarning;
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
recordWarning ?? (recordWarning = `the publish record was not written: ${err instanceof Error ? err.message : String(err)}`);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
const fail = (err, target, step, staged, revealed) => {
|
|
281
|
+
const e = err instanceof PublishTargetError ? err : new PublishTargetError("publish-failed", err instanceof Error ? err.message : String(err));
|
|
282
|
+
throw new PublishRunError(e.code, ctx.redactor.redact(`${target.name}: ${e.message}`), e.fix, step, staged, revealed);
|
|
283
|
+
};
|
|
284
|
+
const resumed = [];
|
|
285
|
+
const staged = new Map();
|
|
286
|
+
const stagedNames = [];
|
|
287
|
+
for (const target of targets) {
|
|
288
|
+
if (progress.revealed.has(target.key)) {
|
|
289
|
+
resumed.push({ target: target.name, step: "revealed" });
|
|
290
|
+
say(` ${target.name}: published by an earlier run; skipped`);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const earlier = progress.staged.get(target.key);
|
|
294
|
+
if (earlier) {
|
|
295
|
+
resumed.push({ target: target.name, step: "staged" });
|
|
296
|
+
say(` ${target.name}: staged by an earlier run; not staged again`);
|
|
297
|
+
staged.set(target.key, { ...earlier, target: target.name, destination: target.destination });
|
|
298
|
+
stagedNames.push(target.name);
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
say(` staging to ${target.destination}`);
|
|
302
|
+
let s;
|
|
303
|
+
try {
|
|
304
|
+
s = await target.stage(ctx);
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
return fail(err, target, "stage", stagedNames, []);
|
|
308
|
+
}
|
|
309
|
+
staged.set(target.key, s);
|
|
310
|
+
stagedNames.push(target.name);
|
|
311
|
+
await write(target, "staged", s.items, s.state);
|
|
312
|
+
}
|
|
313
|
+
const published = [];
|
|
314
|
+
const revealedNames = [];
|
|
315
|
+
for (const target of targets) {
|
|
316
|
+
const done = progress.revealed.get(target.key);
|
|
317
|
+
if (done) {
|
|
318
|
+
published.push({ ...done, target: target.name, destination: target.destination, items: done.items.map((i) => ({ ...i, present: true })) });
|
|
319
|
+
revealedNames.push(target.name);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
say(` revealing ${target.name}`);
|
|
323
|
+
let p;
|
|
324
|
+
try {
|
|
325
|
+
p = await target.reveal(ctx, staged.get(target.key));
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
return fail(err, target, "reveal", stagedNames, revealedNames);
|
|
329
|
+
}
|
|
330
|
+
published.push(p);
|
|
331
|
+
revealedNames.push(target.name);
|
|
332
|
+
await write(target, "revealed", p.items, {});
|
|
333
|
+
}
|
|
334
|
+
return { published, resumed, recordWarning };
|
|
335
|
+
}
|