@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,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a publish attaches to what it publishes (#4049): an SBOM and the
|
|
3
|
+
* release's provenance statement.
|
|
4
|
+
*
|
|
5
|
+
* The provenance statement is an in-toto Statement v1 whose predicate
|
|
6
|
+
* (`https://intentius.io/chant/release/v1`) names the release, its commit,
|
|
7
|
+
* and the cut that signed it, with the cut's ssh signature in the
|
|
8
|
+
* `chant-release` namespace. Anyone holding the repository checks that
|
|
9
|
+
* signature offline with `chant release verify`, against
|
|
10
|
+
* `.chant/allowed_signers` at the release's commit. The predicate also says
|
|
11
|
+
* whether the run had an OIDC token: in CI, the provenance a registry records
|
|
12
|
+
* (npm's `--provenance`) is signed with it, and from a workstation the
|
|
13
|
+
* release's signature is the only provenance there is.
|
|
14
|
+
*
|
|
15
|
+
* The SBOM is an SPDX or CycloneDX JSON document. A target attaches the file
|
|
16
|
+
* its declaration's `sbom` names; with none named, the OCI and forge targets
|
|
17
|
+
* write one from the member's `package-lock.json` or `pom.xml` with the
|
|
18
|
+
* hermetic writer `generate-sbom` uses (../components/verbs/bom-writer.ts),
|
|
19
|
+
* dated with the cut's time, so a publish run again writes the same bytes.
|
|
20
|
+
* `sbom: false` publishes without one. The npm target carries its SBOM in
|
|
21
|
+
* each package instead, since a registry keeps no file beside a package
|
|
22
|
+
* (./publish-npm.ts).
|
|
23
|
+
*
|
|
24
|
+
* Both are written as sorted JSON, so the same release attaches the same
|
|
25
|
+
* bytes on every run, and a resumed stage finds what an earlier run sent.
|
|
26
|
+
*/
|
|
27
|
+
import { execFileSync } from "node:child_process";
|
|
28
|
+
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
29
|
+
import { dirname, join } from "node:path";
|
|
30
|
+
import { sortedJsonReplacer } from "../utils.js";
|
|
31
|
+
import { writeBom } from "../components/verbs/bom-writer.js";
|
|
32
|
+
import { parseNpmPackageLock, parsePomXml } from "../components/verbs/lockfile-sbom-generator.js";
|
|
33
|
+
import { SBOM_MEDIA_TYPES } from "../components/verbs/sbom-generator.js";
|
|
34
|
+
import { bytesDigest, checkReleaseDigest, memberFile } from "./publish-targets.js";
|
|
35
|
+
import { PublishTargetError } from "./release-publish.js";
|
|
36
|
+
import { RELEASE_SEAL_NAMESPACE } from "../workspace/identity-gate-rule.js";
|
|
37
|
+
/** The in-toto statement type the provenance statement is. */
|
|
38
|
+
export const IN_TOTO_STATEMENT_TYPE = "https://in-toto.io/Statement/v1";
|
|
39
|
+
/** The predicate type of chant's release provenance. */
|
|
40
|
+
export const RELEASE_PREDICATE_TYPE = "https://intentius.io/chant/release/v1";
|
|
41
|
+
/** The media type an in-toto statement is attached under. */
|
|
42
|
+
export const IN_TOTO_MEDIA_TYPE = "application/vnd.in-toto+json";
|
|
43
|
+
/** The file name the provenance statement is attached under. */
|
|
44
|
+
export const PROVENANCE_FILE = "release.intoto.json";
|
|
45
|
+
/** The file name of an SBOM chant writes from a lockfile. */
|
|
46
|
+
export const GENERATED_SBOM_FILE = "sbom.spdx.json";
|
|
47
|
+
/** The names an npm package's own SBOM is looked for under, at its root, when the declaration names none. */
|
|
48
|
+
export const PACKAGE_SBOM_FILES = ["sbom.spdx.json", "sbom.cdx.json", "sbom.cyclonedx.json"];
|
|
49
|
+
const json = (value) => new Uint8Array(Buffer.from(`${JSON.stringify(value, sortedJsonReplacer, 2)}\n`, "utf-8"));
|
|
50
|
+
const attachment = (kind, name, mediaType, bytes) => ({ kind, name, mediaType, bytes, digest: bytesDigest(bytes) });
|
|
51
|
+
/**
|
|
52
|
+
* The provenance statement for `subjects`, the digests of what the target
|
|
53
|
+
* publishes. Every value in it comes from the release, its cut and the run's
|
|
54
|
+
* OIDC check, never from the clock, so it is the same bytes on every run.
|
|
55
|
+
*/
|
|
56
|
+
export function releaseProvenance(ctx, subjects) {
|
|
57
|
+
const cut = ctx.cut ?? null;
|
|
58
|
+
const statement = {
|
|
59
|
+
_type: IN_TOTO_STATEMENT_TYPE,
|
|
60
|
+
subject: subjects.map((s) => ({ name: s.name, digest: { [s.digest.split(":")[0] ?? "sha256"]: s.digest.split(":")[1] ?? s.digest } })),
|
|
61
|
+
predicateType: RELEASE_PREDICATE_TYPE,
|
|
62
|
+
predicate: {
|
|
63
|
+
releaseDigest: ctx.release.releaseDigest,
|
|
64
|
+
label: ctx.label,
|
|
65
|
+
member: ctx.release.member,
|
|
66
|
+
commit: ctx.release.commit,
|
|
67
|
+
cut: cut ? { actor: cut.actor, label: cut.label, timestamp: cut.timestamp, runId: cut.runId, signature: cut.signature, namespace: RELEASE_SEAL_NAMESPACE } : null,
|
|
68
|
+
provenance: ctx.provenance.oidc ? { kind: "oidc", ci: ctx.provenance.ci } : { kind: "signature", ci: ctx.provenance.ci },
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return attachment("provenance", PROVENANCE_FILE, IN_TOTO_MEDIA_TYPE, json(statement));
|
|
72
|
+
}
|
|
73
|
+
/** The media type of an SBOM document, or null when `bytes` is neither SPDX nor CycloneDX JSON. */
|
|
74
|
+
export function sbomMediaType(bytes) {
|
|
75
|
+
let doc;
|
|
76
|
+
try {
|
|
77
|
+
doc = JSON.parse(Buffer.from(bytes).toString("utf-8"));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
if (!doc || typeof doc !== "object")
|
|
83
|
+
return null;
|
|
84
|
+
if (typeof doc.spdxVersion === "string")
|
|
85
|
+
return SBOM_MEDIA_TYPES.spdx;
|
|
86
|
+
if (doc.bomFormat === "CycloneDX")
|
|
87
|
+
return SBOM_MEDIA_TYPES.cyclonedx;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
function invalid(where) {
|
|
91
|
+
return new PublishTargetError("publish-sbom-invalid", `${where} is not an SPDX or CycloneDX JSON document`, "Point sbom at the SPDX or CycloneDX JSON the build writes.");
|
|
92
|
+
}
|
|
93
|
+
/** The lockfile nearest the member: `package-lock.json` or `pom.xml` in `cwd` or a directory above it inside its repository (only `cwd` outside one). */
|
|
94
|
+
function nearestLockfile(cwd) {
|
|
95
|
+
let top = null;
|
|
96
|
+
try {
|
|
97
|
+
top = realpathSync(execFileSync("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim());
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
top = null;
|
|
101
|
+
}
|
|
102
|
+
let dir = realpathSync(cwd);
|
|
103
|
+
for (;;) {
|
|
104
|
+
for (const [kind, file] of [["npm", "package-lock.json"], ["maven", "pom.xml"]]) {
|
|
105
|
+
if (existsSync(join(dir, file)))
|
|
106
|
+
return { kind, path: join(dir, file) };
|
|
107
|
+
}
|
|
108
|
+
const up = dirname(dir);
|
|
109
|
+
if (top === null || dir === top || up === dir || !up.startsWith(top))
|
|
110
|
+
return null;
|
|
111
|
+
dir = up;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* The SBOM a target attaches, for an OCI image or a forge release: the file
|
|
116
|
+
* `declared` names (relative to the member, checked against the release's
|
|
117
|
+
* digest when the release records it), else one written from the member's
|
|
118
|
+
* lockfile for `subject`. Null when `declared` is `false`. Throws
|
|
119
|
+
* `publish-sbom-missing` when there is neither.
|
|
120
|
+
*/
|
|
121
|
+
export function memberSbom(ctx, declared, subject, target) {
|
|
122
|
+
if (declared === false)
|
|
123
|
+
return null;
|
|
124
|
+
if (declared !== undefined) {
|
|
125
|
+
const abs = memberFile(ctx.cwd, declared);
|
|
126
|
+
const bytes = new Uint8Array(readFileSync(abs));
|
|
127
|
+
checkReleaseDigest(ctx.release, "asset", ctx.cwd, declared, [bytesDigest(bytes)]);
|
|
128
|
+
const mediaType = sbomMediaType(bytes);
|
|
129
|
+
if (!mediaType)
|
|
130
|
+
throw invalid(declared);
|
|
131
|
+
return attachment("sbom", declared.split("/").pop() ?? declared, mediaType, bytes);
|
|
132
|
+
}
|
|
133
|
+
const lock = nearestLockfile(ctx.cwd);
|
|
134
|
+
if (!lock) {
|
|
135
|
+
throw new PublishTargetError("publish-sbom-missing", `${target} attaches an SBOM to what it publishes, and its declaration names none and there is no package-lock.json or pom.xml in ${ctx.cwd} or above it to write one from`, "Set sbom in the declaration to the SPDX or CycloneDX file the build writes, or sbom: false to publish without one.");
|
|
136
|
+
}
|
|
137
|
+
const content = readFileSync(lock.path, "utf-8");
|
|
138
|
+
const packages = lock.kind === "npm" ? parseNpmPackageLock(content) : parsePomXml(content);
|
|
139
|
+
const at = new Date(ctx.cut?.timestamp ?? 0);
|
|
140
|
+
const doc = writeBom("spdx", { subjectName: subject, subjectId: ctx.release.releaseDigest, packages, generator: `chant-lockfile-sbom/${lock.kind === "npm" ? "package-lock.json" : "pom.xml"}` }, () => (Number.isNaN(at.getTime()) ? new Date(0) : at));
|
|
141
|
+
return attachment("sbom", GENERATED_SBOM_FILE, doc.mediaType, new Uint8Array(Buffer.from(doc.bytes, "utf-8")));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The SBOM an npm package carries at its root: the file `declared` names, else
|
|
145
|
+
* the first of {@link PACKAGE_SBOM_FILES} it holds, in a package directory or
|
|
146
|
+
* under `package/` in a packed `.tgz`. Null when `declared` is `false`.
|
|
147
|
+
* Throws `publish-sbom-missing` when the package has none, and
|
|
148
|
+
* `publish-sbom-invalid` when it is not SPDX or CycloneDX JSON.
|
|
149
|
+
*/
|
|
150
|
+
export function packageSbom(abs, isDirectory, path, name, declared) {
|
|
151
|
+
if (declared === false)
|
|
152
|
+
return null;
|
|
153
|
+
const candidates = declared !== undefined ? [declared] : [...PACKAGE_SBOM_FILES];
|
|
154
|
+
let listed = null;
|
|
155
|
+
if (!isDirectory) {
|
|
156
|
+
try {
|
|
157
|
+
listed = new Set(execFileSync("tar", ["-tzf", abs], { encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).split("\n").map((l) => l.trim()));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
listed = new Set();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const file of candidates) {
|
|
164
|
+
let bytes = null;
|
|
165
|
+
if (isDirectory) {
|
|
166
|
+
if (existsSync(join(abs, file)))
|
|
167
|
+
bytes = new Uint8Array(readFileSync(join(abs, file)));
|
|
168
|
+
}
|
|
169
|
+
else if (listed.has(`package/${file}`)) {
|
|
170
|
+
bytes = new Uint8Array(execFileSync("tar", ["-xzOf", abs, `package/${file}`], { stdio: ["ignore", "pipe", "ignore"] }));
|
|
171
|
+
}
|
|
172
|
+
if (bytes === null)
|
|
173
|
+
continue;
|
|
174
|
+
if (!sbomMediaType(bytes))
|
|
175
|
+
throw invalid(`${file} in ${path}`);
|
|
176
|
+
return { file, digest: bytesDigest(bytes) };
|
|
177
|
+
}
|
|
178
|
+
throw new PublishTargetError("publish-sbom-missing", `${name} (${path}) carries no SBOM: npm keeps no file beside a package, so its SBOM goes in it, and ${declared !== undefined ? declared : `none of ${PACKAGE_SBOM_FILES.join(", ")}`} is at its root`, `Write one into the package before it is packed, such as npm sbom --sbom-format spdx > ${path}/sbom.spdx.json, and keep it in the package's files; or set sbom: false to publish without one.`);
|
|
179
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials for publish targets (#4047), and the redactor every line a
|
|
3
|
+
* publish prints goes through.
|
|
4
|
+
*
|
|
5
|
+
* A target reads its credential from the environment of the run, by a name
|
|
6
|
+
* the declaration gives or the target's default, so a workstation sets it in
|
|
7
|
+
* its shell and CI in the job's secrets. chant never writes a credential to
|
|
8
|
+
* `chant/lifecycle`, to a file it keeps, or to its output: the value is held
|
|
9
|
+
* in memory for the requests that need it, and every line a target prints,
|
|
10
|
+
* and every error it throws, passes through {@link Redactor.redact} first.
|
|
11
|
+
*
|
|
12
|
+
* The redactor masks the value of each credential a target read, the values
|
|
13
|
+
* of the environment variables CI and registries use for tokens
|
|
14
|
+
* ({@link TOKEN_ENV_VARS}), and the token forms npm and HTTP headers write
|
|
15
|
+
* (`_authToken=...`, `Authorization: Bearer ...`).
|
|
16
|
+
*/
|
|
17
|
+
/** Environment variables that hold a token in CI or for a registry. Their values are masked in all publish output. */
|
|
18
|
+
export declare const TOKEN_ENV_VARS: readonly ["NODE_AUTH_TOKEN", "NPM_TOKEN", "NPM_ID_TOKEN", "SIGSTORE_ID_TOKEN", "ACTIONS_ID_TOKEN_REQUEST_TOKEN", "ACTIONS_RUNTIME_TOKEN", "GITHUB_TOKEN", "GH_TOKEN", "GITLAB_TOKEN", "CI_JOB_TOKEN", "FORGEJO_TOKEN", "GITEA_TOKEN", "OCI_PASSWORD", "DOCKER_PASSWORD"];
|
|
19
|
+
/** What a masked value is replaced with. */
|
|
20
|
+
export declare const REDACTED = "[redacted]";
|
|
21
|
+
/** Masks credentials in text a publish prints. */
|
|
22
|
+
export interface Redactor {
|
|
23
|
+
/** Add a value to mask. Values shorter than 4 characters are not masked, since they would mask ordinary text. */
|
|
24
|
+
add(value: string | undefined | null): void;
|
|
25
|
+
redact(text: string): string;
|
|
26
|
+
}
|
|
27
|
+
/** A redactor that masks the token variables of `env` and every value added to it later. */
|
|
28
|
+
export declare function createRedactor(env?: NodeJS.ProcessEnv, names?: readonly string[]): Redactor;
|
|
29
|
+
/** A credential a target read, and the variable it came from. The value is never printed. */
|
|
30
|
+
export interface Credential {
|
|
31
|
+
value: string;
|
|
32
|
+
/** The environment variable, or `docker config` for one read from the docker config file. */
|
|
33
|
+
from: string;
|
|
34
|
+
}
|
|
35
|
+
/** The first of `names` set in `env`, added to `redactor`, or null. */
|
|
36
|
+
export declare function readCredential(env: NodeJS.ProcessEnv, names: readonly string[], redactor: Redactor): Credential | null;
|
|
37
|
+
//# sourceMappingURL=publish-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-credentials.d.ts","sourceRoot":"","sources":["../../src/lifecycle/publish-credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,sHAAsH;AACtH,eAAO,MAAM,cAAc,YACzB,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,gCAAgC,EAChC,uBAAuB,EACvB,cAAc,EACd,UAAU,EACV,cAAc,EACd,cAAc,EACd,eAAe,EACf,aAAa,EACb,cAAc,EACd,iBAAiB,CACT,CAAC;AAEX,4CAA4C;AAC5C,eAAO,MAAM,QAAQ,eAAe,CAAC;AAErC,kDAAkD;AAClD,MAAM,WAAW,QAAQ;IACvB,iHAAiH;IACjH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;IAC5C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAe,EAAE,KAAK,GAAE,SAAS,MAAM,EAAO,GAAG,QAAQ,CAkBnG;AAED,6FAA6F;AAC7F,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;CACd;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,IAAI,CAStH"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials for publish targets (#4047), and the redactor every line a
|
|
3
|
+
* publish prints goes through.
|
|
4
|
+
*
|
|
5
|
+
* A target reads its credential from the environment of the run, by a name
|
|
6
|
+
* the declaration gives or the target's default, so a workstation sets it in
|
|
7
|
+
* its shell and CI in the job's secrets. chant never writes a credential to
|
|
8
|
+
* `chant/lifecycle`, to a file it keeps, or to its output: the value is held
|
|
9
|
+
* in memory for the requests that need it, and every line a target prints,
|
|
10
|
+
* and every error it throws, passes through {@link Redactor.redact} first.
|
|
11
|
+
*
|
|
12
|
+
* The redactor masks the value of each credential a target read, the values
|
|
13
|
+
* of the environment variables CI and registries use for tokens
|
|
14
|
+
* ({@link TOKEN_ENV_VARS}), and the token forms npm and HTTP headers write
|
|
15
|
+
* (`_authToken=...`, `Authorization: Bearer ...`).
|
|
16
|
+
*/
|
|
17
|
+
/** Environment variables that hold a token in CI or for a registry. Their values are masked in all publish output. */
|
|
18
|
+
export const TOKEN_ENV_VARS = [
|
|
19
|
+
"NODE_AUTH_TOKEN",
|
|
20
|
+
"NPM_TOKEN",
|
|
21
|
+
"NPM_ID_TOKEN",
|
|
22
|
+
"SIGSTORE_ID_TOKEN",
|
|
23
|
+
"ACTIONS_ID_TOKEN_REQUEST_TOKEN",
|
|
24
|
+
"ACTIONS_RUNTIME_TOKEN",
|
|
25
|
+
"GITHUB_TOKEN",
|
|
26
|
+
"GH_TOKEN",
|
|
27
|
+
"GITLAB_TOKEN",
|
|
28
|
+
"CI_JOB_TOKEN",
|
|
29
|
+
"FORGEJO_TOKEN",
|
|
30
|
+
"GITEA_TOKEN",
|
|
31
|
+
"OCI_PASSWORD",
|
|
32
|
+
"DOCKER_PASSWORD",
|
|
33
|
+
];
|
|
34
|
+
/** What a masked value is replaced with. */
|
|
35
|
+
export const REDACTED = "[redacted]";
|
|
36
|
+
/** A redactor that masks the token variables of `env` and every value added to it later. */
|
|
37
|
+
export function createRedactor(env = {}, names = []) {
|
|
38
|
+
const values = new Set();
|
|
39
|
+
const add = (v) => {
|
|
40
|
+
if (typeof v === "string" && v.trim().length >= 4)
|
|
41
|
+
values.add(v.trim());
|
|
42
|
+
};
|
|
43
|
+
for (const name of [...TOKEN_ENV_VARS, ...names])
|
|
44
|
+
add(env[name]);
|
|
45
|
+
return {
|
|
46
|
+
add,
|
|
47
|
+
redact(text) {
|
|
48
|
+
let out = text;
|
|
49
|
+
// Longest first, so a value that contains another is masked whole.
|
|
50
|
+
for (const v of [...values].sort((a, b) => b.length - a.length))
|
|
51
|
+
out = out.split(v).join(REDACTED);
|
|
52
|
+
return out
|
|
53
|
+
.replace(/(_authToken\s*=\s*)("?)[^\s"]+\2/g, `$1${REDACTED}`)
|
|
54
|
+
.replace(/(authorization:\s*(?:bearer|basic|token)\s+)[^\s"',]+/gi, `$1${REDACTED}`)
|
|
55
|
+
.replace(/(private-token:\s*|job-token:\s*)[^\s"',]+/gi, `$1${REDACTED}`);
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** The first of `names` set in `env`, added to `redactor`, or null. */
|
|
60
|
+
export function readCredential(env, names, redactor) {
|
|
61
|
+
for (const name of names) {
|
|
62
|
+
const value = env[name];
|
|
63
|
+
if (typeof value === "string" && value !== "") {
|
|
64
|
+
redactor.add(value);
|
|
65
|
+
return { value, from: name };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a member declares its publish targets (#4047): `release.publish` in
|
|
3
|
+
* its `chant.config.ts`, a list of targets that `chant release publish` runs
|
|
4
|
+
* in order.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* export default {
|
|
8
|
+
* release: {
|
|
9
|
+
* publish: [
|
|
10
|
+
* { type: "npm", packages: ["packages/core", "packages/cli"] },
|
|
11
|
+
* { type: "oci", image: "dist/api.oci.tar", repository: "ghcr.io/acme/api", tags: ["{label}"], usernameEnv: "GITHUB_ACTOR", passwordEnv: "GITHUB_TOKEN" },
|
|
12
|
+
* { type: "forge", forge: "github", repo: "acme/api", assets: ["dist/api-linux-amd64.tar.gz"] },
|
|
13
|
+
* ],
|
|
14
|
+
* },
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The config is the one at the release's commit: a publish with a target
|
|
19
|
+
* refuses a checkout whose `HEAD` is another commit. Paths are relative to
|
|
20
|
+
* the member's directory. A declaration names the environment variables its
|
|
21
|
+
* credentials are read from, never the credentials.
|
|
22
|
+
*
|
|
23
|
+
* This module imports nothing but zod, so ../config.ts can validate the block.
|
|
24
|
+
*/
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
/** npm: `npm publish` for each package, in the order given. */
|
|
27
|
+
export declare const NpmPublishSchema: z.ZodObject<{
|
|
28
|
+
type: z.ZodLiteral<"npm">;
|
|
29
|
+
packages: z.ZodArray<z.ZodString>;
|
|
30
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
31
|
+
access: z.ZodOptional<z.ZodEnum<{
|
|
32
|
+
public: "public";
|
|
33
|
+
restricted: "restricted";
|
|
34
|
+
}>>;
|
|
35
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
/** An OCI registry such as GHCR or Docker Hub: the image is pushed by digest, then tagged. */
|
|
38
|
+
export declare const OciPublishSchema: z.ZodObject<{
|
|
39
|
+
type: z.ZodLiteral<"oci">;
|
|
40
|
+
image: z.ZodString;
|
|
41
|
+
repository: z.ZodString;
|
|
42
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
43
|
+
usernameEnv: z.ZodOptional<z.ZodString>;
|
|
44
|
+
passwordEnv: z.ZodOptional<z.ZodString>;
|
|
45
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
46
|
+
}, z.core.$strict>;
|
|
47
|
+
/** Release assets on a GitHub, GitLab or Forgejo release named by the release's tag. */
|
|
48
|
+
export declare const ForgePublishSchema: z.ZodObject<{
|
|
49
|
+
type: z.ZodLiteral<"forge">;
|
|
50
|
+
forge: z.ZodEnum<{
|
|
51
|
+
forgejo: "forgejo";
|
|
52
|
+
github: "github";
|
|
53
|
+
gitlab: "gitlab";
|
|
54
|
+
}>;
|
|
55
|
+
repo: z.ZodString;
|
|
56
|
+
api: z.ZodOptional<z.ZodString>;
|
|
57
|
+
assets: z.ZodArray<z.ZodString>;
|
|
58
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
59
|
+
tokenEnv: z.ZodOptional<z.ZodString>;
|
|
60
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
61
|
+
}, z.core.$strict>;
|
|
62
|
+
export declare const PublishDeclarationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
63
|
+
type: z.ZodLiteral<"npm">;
|
|
64
|
+
packages: z.ZodArray<z.ZodString>;
|
|
65
|
+
registry: z.ZodOptional<z.ZodString>;
|
|
66
|
+
access: z.ZodOptional<z.ZodEnum<{
|
|
67
|
+
public: "public";
|
|
68
|
+
restricted: "restricted";
|
|
69
|
+
}>>;
|
|
70
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
71
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
72
|
+
type: z.ZodLiteral<"oci">;
|
|
73
|
+
image: z.ZodString;
|
|
74
|
+
repository: z.ZodString;
|
|
75
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
76
|
+
usernameEnv: z.ZodOptional<z.ZodString>;
|
|
77
|
+
passwordEnv: z.ZodOptional<z.ZodString>;
|
|
78
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
79
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
80
|
+
type: z.ZodLiteral<"forge">;
|
|
81
|
+
forge: z.ZodEnum<{
|
|
82
|
+
forgejo: "forgejo";
|
|
83
|
+
github: "github";
|
|
84
|
+
gitlab: "gitlab";
|
|
85
|
+
}>;
|
|
86
|
+
repo: z.ZodString;
|
|
87
|
+
api: z.ZodOptional<z.ZodString>;
|
|
88
|
+
assets: z.ZodArray<z.ZodString>;
|
|
89
|
+
tag: z.ZodOptional<z.ZodString>;
|
|
90
|
+
tokenEnv: z.ZodOptional<z.ZodString>;
|
|
91
|
+
sbom: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<false>]>>;
|
|
92
|
+
}, z.core.$strict>], "type">;
|
|
93
|
+
export type NpmPublishDeclaration = z.infer<typeof NpmPublishSchema>;
|
|
94
|
+
export type OciPublishDeclaration = z.infer<typeof OciPublishSchema>;
|
|
95
|
+
export type ForgePublishDeclaration = z.infer<typeof ForgePublishSchema>;
|
|
96
|
+
export type PublishDeclaration = z.infer<typeof PublishDeclarationSchema>;
|
|
97
|
+
//# sourceMappingURL=publish-declaration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-declaration.d.ts","sourceRoot":"","sources":["../../src/lifecycle/publish-declaration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB;;;;;;;;;kBAelB,CAAC;AAEZ,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB;;;;;;;;kBAgBlB,CAAC;AAEZ,wFAAwF;AACxF,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;kBAiBpB,CAAC;AAEZ,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAAyF,CAAC;AAE/H,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACrE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACrE,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AACzE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a member declares its publish targets (#4047): `release.publish` in
|
|
3
|
+
* its `chant.config.ts`, a list of targets that `chant release publish` runs
|
|
4
|
+
* in order.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* export default {
|
|
8
|
+
* release: {
|
|
9
|
+
* publish: [
|
|
10
|
+
* { type: "npm", packages: ["packages/core", "packages/cli"] },
|
|
11
|
+
* { type: "oci", image: "dist/api.oci.tar", repository: "ghcr.io/acme/api", tags: ["{label}"], usernameEnv: "GITHUB_ACTOR", passwordEnv: "GITHUB_TOKEN" },
|
|
12
|
+
* { type: "forge", forge: "github", repo: "acme/api", assets: ["dist/api-linux-amd64.tar.gz"] },
|
|
13
|
+
* ],
|
|
14
|
+
* },
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* The config is the one at the release's commit: a publish with a target
|
|
19
|
+
* refuses a checkout whose `HEAD` is another commit. Paths are relative to
|
|
20
|
+
* the member's directory. A declaration names the environment variables its
|
|
21
|
+
* credentials are read from, never the credentials.
|
|
22
|
+
*
|
|
23
|
+
* This module imports nothing but zod, so ../config.ts can validate the block.
|
|
24
|
+
*/
|
|
25
|
+
import { z } from "zod";
|
|
26
|
+
const envName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "an environment variable name");
|
|
27
|
+
/** An SBOM file, or `false` to publish without one (#4049). */
|
|
28
|
+
const sbomField = z.union([z.string().min(1), z.literal(false)]);
|
|
29
|
+
/** npm: `npm publish` for each package, in the order given. */
|
|
30
|
+
export const NpmPublishSchema = z
|
|
31
|
+
.object({
|
|
32
|
+
type: z.literal("npm"),
|
|
33
|
+
/** Package directories or packed `.tgz` files, relative to the member, published in this order. */
|
|
34
|
+
packages: z.array(z.string().min(1)).min(1),
|
|
35
|
+
/** The registry, else the one npm's own config names. */
|
|
36
|
+
registry: z.string().url().optional(),
|
|
37
|
+
access: z.enum(["public", "restricted"]).optional(),
|
|
38
|
+
/**
|
|
39
|
+
* The SBOM each package carries at its root (#4049), a file name relative
|
|
40
|
+
* to the package. Default: the first of `sbom.spdx.json`, `sbom.cdx.json`,
|
|
41
|
+
* `sbom.cyclonedx.json` it holds. `false` publishes without one.
|
|
42
|
+
*/
|
|
43
|
+
sbom: sbomField.optional(),
|
|
44
|
+
})
|
|
45
|
+
.strict();
|
|
46
|
+
/** An OCI registry such as GHCR or Docker Hub: the image is pushed by digest, then tagged. */
|
|
47
|
+
export const OciPublishSchema = z
|
|
48
|
+
.object({
|
|
49
|
+
type: z.literal("oci"),
|
|
50
|
+
/** An OCI image layout, as a directory or a tar file such as `docker save` writes, relative to the member. */
|
|
51
|
+
image: z.string().min(1),
|
|
52
|
+
/** `<registry host>/<name>`, such as `ghcr.io/acme/api` or `docker.io/acme/api`. */
|
|
53
|
+
repository: z.string().min(1),
|
|
54
|
+
/** Tags to point at the pushed digest. `{label}` is the release's label. Default: `["{label}"]`, or none for a release with no label. */
|
|
55
|
+
tags: z.array(z.string().min(1)).optional(),
|
|
56
|
+
/** The variable holding the registry user name. Default `OCI_USERNAME`. */
|
|
57
|
+
usernameEnv: envName.optional(),
|
|
58
|
+
/** The variable holding the registry password or token. Default `OCI_PASSWORD`. */
|
|
59
|
+
passwordEnv: envName.optional(),
|
|
60
|
+
/** The SBOM pushed as a referrer of the image (#4049), relative to the member. Default: one written from the member's lockfile. `false` publishes without one. */
|
|
61
|
+
sbom: sbomField.optional(),
|
|
62
|
+
})
|
|
63
|
+
.strict();
|
|
64
|
+
/** Release assets on a GitHub, GitLab or Forgejo release named by the release's tag. */
|
|
65
|
+
export const ForgePublishSchema = z
|
|
66
|
+
.object({
|
|
67
|
+
type: z.literal("forge"),
|
|
68
|
+
forge: z.enum(["github", "gitlab", "forgejo"]),
|
|
69
|
+
/** `owner/name` on GitHub and Forgejo; the project path or numeric id on GitLab. */
|
|
70
|
+
repo: z.string().min(1),
|
|
71
|
+
/** The API root: default `$GITHUB_API_URL` or `https://api.github.com` on GitHub, `$CI_API_V4_URL` or `https://gitlab.com/api/v4` on GitLab, `$GITHUB_API_URL` on Forgejo, which has no default. */
|
|
72
|
+
api: z.string().url().optional(),
|
|
73
|
+
/** Files to attach, relative to the member. */
|
|
74
|
+
assets: z.array(z.string().min(1)).min(1),
|
|
75
|
+
/** The forge release's tag. Default: the tag the cut made, `release/<label>` or `release/<member>/<label>`. */
|
|
76
|
+
tag: z.string().min(1).optional(),
|
|
77
|
+
/** The variable holding the token. Default `GITHUB_TOKEN`, `GITLAB_TOKEN` (then `CI_JOB_TOKEN`), or `FORGEJO_TOKEN`. */
|
|
78
|
+
tokenEnv: envName.optional(),
|
|
79
|
+
/** The SBOM attached to the release (#4049), relative to the member. Default: one written from the member's lockfile. `false` publishes without one. */
|
|
80
|
+
sbom: sbomField.optional(),
|
|
81
|
+
})
|
|
82
|
+
.strict();
|
|
83
|
+
export const PublishDeclarationSchema = z.discriminatedUnion("type", [NpmPublishSchema, OciPublishSchema, ForgePublishSchema]);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The forge release-asset target (#4047): files attached to the release a
|
|
3
|
+
* GitHub, GitLab or Forgejo repository has for the release's tag, through
|
|
4
|
+
* each forge's REST API and an injectable `fetch`.
|
|
5
|
+
*
|
|
6
|
+
* The tag is the declaration's `tag`, else the one the cut made
|
|
7
|
+
* (`release/<label>`, or `release/<member>/<label>`), so a release with no
|
|
8
|
+
* label and no declared tag is refused.
|
|
9
|
+
*
|
|
10
|
+
* A publish is two steps (#4048), so a consumer never sees a release with
|
|
11
|
+
* some of its assets:
|
|
12
|
+
*
|
|
13
|
+
* - GitHub and Forgejo stage on a draft release: the draft for the tag that
|
|
14
|
+
* `GET /repos/{repo}/releases` lists, else a new one,
|
|
15
|
+
* `POST /repos/{repo}/releases` with `draft: true` at the release's commit.
|
|
16
|
+
* GitHub uploads each asset to the draft's `upload_url`, Forgejo with a
|
|
17
|
+
* multipart `POST /repos/{repo}/releases/{id}/assets`. The reveal publishes
|
|
18
|
+
* the draft, `PATCH /repos/{repo}/releases/{id}` with `draft: false`, and
|
|
19
|
+
* the forge creates the tag then if it was not pushed.
|
|
20
|
+
* - GitLab has no draft release, so its stage puts each asset in the
|
|
21
|
+
* project's generic package registry (package `release`, version the
|
|
22
|
+
* label) and makes no release. The reveal creates the release at the
|
|
23
|
+
* commit with a link to each asset, in one `POST /projects/{id}/releases`.
|
|
24
|
+
*
|
|
25
|
+
* An asset already on the draft with the same name is left as it is when
|
|
26
|
+
* its `digest` matches (GitHub), so a stage run again uploads only the rest;
|
|
27
|
+
* one that differs, or one on a forge that reports no digest (Forgejo), is
|
|
28
|
+
* refused (`publish-forge-asset-exists`). A release already published for
|
|
29
|
+
* the tag is refused (`publish-forge-release-published`), since assets
|
|
30
|
+
* uploaded to it would show one at a time, unless it already holds every
|
|
31
|
+
* asset, which is what a publish revealed earlier leaves (on Forgejo and
|
|
32
|
+
* GitLab, which report no digest, by name).
|
|
33
|
+
*
|
|
34
|
+
* With the declared assets the stage attaches two more files (#4049): the
|
|
35
|
+
* SBOM (the file `sbom` names, else `sbom.spdx.json` written from the
|
|
36
|
+
* member's lockfile) and `release.intoto.json`, the release's provenance
|
|
37
|
+
* statement over the assets' digests, which carries the cut's signature
|
|
38
|
+
* (./publish-attachments.ts). They go on the draft with the assets, so
|
|
39
|
+
* nothing is visible before they are attached.
|
|
40
|
+
*
|
|
41
|
+
* The token is read from `tokenEnv`, else `GITHUB_TOKEN`, `GITLAB_TOKEN`
|
|
42
|
+
* then `CI_JOB_TOKEN`, or `FORGEJO_TOKEN`, and is masked in all output.
|
|
43
|
+
*/
|
|
44
|
+
import type { ForgePublishDeclaration } from "./publish-declaration.js";
|
|
45
|
+
import { type PublishDeps, type PublishTarget } from "./release-publish.js";
|
|
46
|
+
/** The variables a forge's token is read from, in order. */
|
|
47
|
+
export declare function forgeTokenEnv(decl: Pick<ForgePublishDeclaration, "forge" | "tokenEnv">): string[];
|
|
48
|
+
/** The API root a forge is reached at. */
|
|
49
|
+
export declare function forgeApi(decl: Pick<ForgePublishDeclaration, "forge" | "api">, env: NodeJS.ProcessEnv): string | null;
|
|
50
|
+
/** A GitLab generic package version: the label with what GitLab refuses replaced. */
|
|
51
|
+
export declare function gitlabPackageVersion(label: string): string;
|
|
52
|
+
export declare function forgeTarget(decl: ForgePublishDeclaration, deps: PublishDeps): PublishTarget;
|
|
53
|
+
//# sourceMappingURL=publish-forge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-forge.d.ts","sourceRoot":"","sources":["../../src/lifecycle/publish-forge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAMH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAErE,OAAO,EAA2C,KAAK,WAAW,EAAuC,KAAK,aAAa,EAAqB,MAAM,mBAAmB,CAAC;AAE1K,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,uBAAuB,EAAE,OAAO,GAAG,UAAU,CAAC,GAAG,MAAM,EAAE,CAKjG;AAED,0CAA0C;AAC1C,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,uBAAuB,EAAE,OAAO,GAAG,KAAK,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI,CAGpH;AAqID,qFAAqF;AACrF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,uBAAuB,EAAE,IAAI,EAAE,WAAW,GAAG,aAAa,CA0H3F"}
|