@intentius/chant 0.124.28 → 0.124.30
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 +18 -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 +67 -8
- 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 +91 -10
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +73 -2
- package/dist/cli/registry.d.ts +32 -4
- 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 +49 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +38 -4
- package/dist/components/pr-run.d.ts +20 -2
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +26 -13
- 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 +16 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +36 -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-ledger.d.ts +8 -3
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +2 -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/lifecycle/run-ledger.d.ts +1 -1
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +9 -0
- package/dist/op/activities/activity-contracts.d.ts +1 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +1 -0
- package/dist/op/activities/shell.d.ts +6 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +1 -1
- package/dist/op/local-executor.d.ts +23 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +9 -3
- package/dist/op/runtime.d.ts +27 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtime.js +20 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/pr-forge.d.ts +23 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +44 -0
- package/dist/pr-loop.d.ts +17 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +30 -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 +4 -3
- package/dist/run-event-stream.d.ts.map +1 -1
- package/dist/run-event-stream.js +7 -3
- package/dist/run-event.d.ts +34 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/run-event.js +4 -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-forge.d.ts +2 -0
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green.d.ts +10 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +14 -5
- package/dist/workspace/ci-run.d.ts +222 -0
- package/dist/workspace/ci-run.d.ts.map +1 -0
- package/dist/workspace/ci-run.js +464 -0
- package/dist/workspace/ci-serve.d.ts +214 -0
- package/dist/workspace/ci-serve.d.ts.map +1 -0
- package/dist/workspace/ci-serve.js +412 -0
- 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/pr-report.schema.json +5 -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 +24 -6
- package/dist/workspace/status-gates.d.ts +8 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-stewards.d.ts +2 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +1 -0
- 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 +210 -2
- 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 +30 -0
- package/src/cli/handlers/build.ts +20 -4
- package/src/cli/handlers/fan-out.test.ts +262 -1
- package/src/cli/handlers/fan-out.ts +72 -8
- 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 +144 -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 +91 -10
- package/src/cli/parse-args.ts +54 -2
- package/src/cli/registry.ts +32 -4
- 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 +67 -4
- package/src/components/pr-run.test.ts +127 -0
- package/src/components/pr-run.ts +32 -12
- 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 +45 -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-ledger.ts +9 -3
- 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/lifecycle/run-ledger.ts +7 -1
- package/src/op/activities/activity-contracts.ts +1 -0
- package/src/op/activities/shell.ts +7 -1
- package/src/op/local-executor.ts +27 -4
- package/src/op/runtime.ts +42 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/pr-forge.test.ts +25 -0
- package/src/pr-forge.ts +62 -0
- package/src/pr-loop.test.ts +13 -1
- package/src/pr-loop.ts +41 -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 +8 -4
- package/src/run-event.ts +38 -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-forge.ts +2 -0
- package/src/workspace/ci-green.ts +20 -5
- package/src/workspace/ci-run.test.ts +416 -0
- package/src/workspace/ci-run.ts +571 -0
- package/src/workspace/ci-serve.test.ts +268 -0
- package/src/workspace/ci-serve.ts +521 -0
- 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/pr-report.schema.json +5 -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 +24 -6
- 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-stewards.ts +3 -0
- package/src/workspace/status.schema.json +210 -2
- package/src/workspace/status.ts +254 -3
- package/src/workspace/wip.ts +11 -2
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
|
|
28
|
+
const envName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/, "an environment variable name");
|
|
29
|
+
|
|
30
|
+
/** An SBOM file, or `false` to publish without one (#4049). */
|
|
31
|
+
const sbomField = z.union([z.string().min(1), z.literal(false)]);
|
|
32
|
+
|
|
33
|
+
/** npm: `npm publish` for each package, in the order given. */
|
|
34
|
+
export const NpmPublishSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
type: z.literal("npm"),
|
|
37
|
+
/** Package directories or packed `.tgz` files, relative to the member, published in this order. */
|
|
38
|
+
packages: z.array(z.string().min(1)).min(1),
|
|
39
|
+
/** The registry, else the one npm's own config names. */
|
|
40
|
+
registry: z.string().url().optional(),
|
|
41
|
+
access: z.enum(["public", "restricted"]).optional(),
|
|
42
|
+
/**
|
|
43
|
+
* The SBOM each package carries at its root (#4049), a file name relative
|
|
44
|
+
* to the package. Default: the first of `sbom.spdx.json`, `sbom.cdx.json`,
|
|
45
|
+
* `sbom.cyclonedx.json` it holds. `false` publishes without one.
|
|
46
|
+
*/
|
|
47
|
+
sbom: sbomField.optional(),
|
|
48
|
+
})
|
|
49
|
+
.strict();
|
|
50
|
+
|
|
51
|
+
/** An OCI registry such as GHCR or Docker Hub: the image is pushed by digest, then tagged. */
|
|
52
|
+
export const OciPublishSchema = z
|
|
53
|
+
.object({
|
|
54
|
+
type: z.literal("oci"),
|
|
55
|
+
/** An OCI image layout, as a directory or a tar file such as `docker save` writes, relative to the member. */
|
|
56
|
+
image: z.string().min(1),
|
|
57
|
+
/** `<registry host>/<name>`, such as `ghcr.io/acme/api` or `docker.io/acme/api`. */
|
|
58
|
+
repository: z.string().min(1),
|
|
59
|
+
/** Tags to point at the pushed digest. `{label}` is the release's label. Default: `["{label}"]`, or none for a release with no label. */
|
|
60
|
+
tags: z.array(z.string().min(1)).optional(),
|
|
61
|
+
/** The variable holding the registry user name. Default `OCI_USERNAME`. */
|
|
62
|
+
usernameEnv: envName.optional(),
|
|
63
|
+
/** The variable holding the registry password or token. Default `OCI_PASSWORD`. */
|
|
64
|
+
passwordEnv: envName.optional(),
|
|
65
|
+
/** 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. */
|
|
66
|
+
sbom: sbomField.optional(),
|
|
67
|
+
})
|
|
68
|
+
.strict();
|
|
69
|
+
|
|
70
|
+
/** Release assets on a GitHub, GitLab or Forgejo release named by the release's tag. */
|
|
71
|
+
export const ForgePublishSchema = z
|
|
72
|
+
.object({
|
|
73
|
+
type: z.literal("forge"),
|
|
74
|
+
forge: z.enum(["github", "gitlab", "forgejo"]),
|
|
75
|
+
/** `owner/name` on GitHub and Forgejo; the project path or numeric id on GitLab. */
|
|
76
|
+
repo: z.string().min(1),
|
|
77
|
+
/** 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. */
|
|
78
|
+
api: z.string().url().optional(),
|
|
79
|
+
/** Files to attach, relative to the member. */
|
|
80
|
+
assets: z.array(z.string().min(1)).min(1),
|
|
81
|
+
/** The forge release's tag. Default: the tag the cut made, `release/<label>` or `release/<member>/<label>`. */
|
|
82
|
+
tag: z.string().min(1).optional(),
|
|
83
|
+
/** The variable holding the token. Default `GITHUB_TOKEN`, `GITLAB_TOKEN` (then `CI_JOB_TOKEN`), or `FORGEJO_TOKEN`. */
|
|
84
|
+
tokenEnv: envName.optional(),
|
|
85
|
+
/** The SBOM attached to the release (#4049), relative to the member. Default: one written from the member's lockfile. `false` publishes without one. */
|
|
86
|
+
sbom: sbomField.optional(),
|
|
87
|
+
})
|
|
88
|
+
.strict();
|
|
89
|
+
|
|
90
|
+
export const PublishDeclarationSchema = z.discriminatedUnion("type", [NpmPublishSchema, OciPublishSchema, ForgePublishSchema]);
|
|
91
|
+
|
|
92
|
+
export type NpmPublishDeclaration = z.infer<typeof NpmPublishSchema>;
|
|
93
|
+
export type OciPublishDeclaration = z.infer<typeof OciPublishSchema>;
|
|
94
|
+
export type ForgePublishDeclaration = z.infer<typeof ForgePublishSchema>;
|
|
95
|
+
export type PublishDeclaration = z.infer<typeof PublishDeclarationSchema>;
|
|
@@ -0,0 +1,325 @@
|
|
|
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
|
+
|
|
45
|
+
import { readFileSync } from "node:fs";
|
|
46
|
+
import { basename } from "node:path";
|
|
47
|
+
import { memberSbom, releaseProvenance } from "./publish-attachments";
|
|
48
|
+
import { readCredential, type Credential } from "./publish-credentials";
|
|
49
|
+
import type { ForgePublishDeclaration } from "./publish-declaration";
|
|
50
|
+
import { bytesDigest, checkReleaseDigest, memberFile, publishTargetKey } from "./publish-targets";
|
|
51
|
+
import { PublishTargetError, type PublishContext, type PublishDeps, type PublishFetch, type PublishItem, type PublishTarget, type StagedTarget } from "./release-publish";
|
|
52
|
+
|
|
53
|
+
/** The variables a forge's token is read from, in order. */
|
|
54
|
+
export function forgeTokenEnv(decl: Pick<ForgePublishDeclaration, "forge" | "tokenEnv">): string[] {
|
|
55
|
+
if (decl.tokenEnv) return [decl.tokenEnv];
|
|
56
|
+
if (decl.forge === "github") return ["GITHUB_TOKEN"];
|
|
57
|
+
if (decl.forge === "gitlab") return ["GITLAB_TOKEN", "CI_JOB_TOKEN"];
|
|
58
|
+
return ["FORGEJO_TOKEN"];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The API root a forge is reached at. */
|
|
62
|
+
export function forgeApi(decl: Pick<ForgePublishDeclaration, "forge" | "api">, env: NodeJS.ProcessEnv): string | null {
|
|
63
|
+
const root = decl.api ?? (decl.forge === "github" ? (env.GITHUB_API_URL ?? "https://api.github.com") : decl.forge === "gitlab" ? (env.CI_API_V4_URL ?? "https://gitlab.com/api/v4") : (env.GITHUB_API_URL ?? null));
|
|
64
|
+
return root ? root.replace(/\/+$/, "") : null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface Asset {
|
|
68
|
+
path: string;
|
|
69
|
+
name: string;
|
|
70
|
+
bytes: Uint8Array;
|
|
71
|
+
digest: string;
|
|
72
|
+
/** Set on the SBOM and the provenance statement chant attaches (#4049). */
|
|
73
|
+
attachment?: "sbom" | "provenance";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface ForgeRelease {
|
|
77
|
+
id: string;
|
|
78
|
+
/** True for a GitHub or Forgejo draft, which no consumer sees. */
|
|
79
|
+
draft: boolean;
|
|
80
|
+
/** Asset names on the release, with the digest the forge reports when it reports one. */
|
|
81
|
+
assets: Map<string, string | null>;
|
|
82
|
+
uploadUrl?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
class ForgeClient {
|
|
86
|
+
constructor(
|
|
87
|
+
private readonly decl: ForgePublishDeclaration,
|
|
88
|
+
private readonly api: string,
|
|
89
|
+
private readonly token: Credential,
|
|
90
|
+
private readonly fetch: PublishFetch,
|
|
91
|
+
private readonly ctx: PublishContext,
|
|
92
|
+
) {}
|
|
93
|
+
|
|
94
|
+
private headers(): Record<string, string> {
|
|
95
|
+
const t = this.token.value;
|
|
96
|
+
if (this.decl.forge === "github") return { authorization: `Bearer ${t}`, accept: "application/vnd.github+json", "user-agent": "chant" };
|
|
97
|
+
if (this.decl.forge === "forgejo") return { authorization: `token ${t}`, accept: "application/json" };
|
|
98
|
+
return this.token.from === "CI_JOB_TOKEN" ? { "job-token": t } : { "private-token": t };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async call(method: string, url: string, body?: Uint8Array | string, extra: Record<string, string> = {}): Promise<{ status: number; json: unknown; text: string }> {
|
|
102
|
+
const headers = { ...this.headers(), ...(typeof body === "string" ? { "content-type": "application/json" } : {}), ...extra };
|
|
103
|
+
const res = await this.fetch(url, { method, headers, ...(body !== undefined ? { body } : {}) });
|
|
104
|
+
const text = await res.text();
|
|
105
|
+
let json: unknown = null;
|
|
106
|
+
try {
|
|
107
|
+
json = text ? JSON.parse(text) : null;
|
|
108
|
+
} catch {
|
|
109
|
+
json = null;
|
|
110
|
+
}
|
|
111
|
+
return { status: res.status, json, text };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
fail(what: string, status: number, text: string): never {
|
|
115
|
+
throw new PublishTargetError("publish-forge-failed", this.ctx.redactor.redact(`${what} on ${this.decl.forge} ${this.decl.repo} answered ${status}${text ? `: ${text.slice(0, 300)}` : ""}`));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private get repoPath(): string {
|
|
119
|
+
return this.decl.forge === "gitlab" ? `${this.api}/projects/${encodeURIComponent(this.decl.repo)}` : `${this.api}/repos/${this.decl.repo}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private toRelease(body: Record<string, unknown>, tag: string): ForgeRelease {
|
|
123
|
+
const assets = new Map<string, string | null>();
|
|
124
|
+
if (this.decl.forge === "gitlab") {
|
|
125
|
+
const links = (body.assets as { links?: Array<{ name: string }> } | undefined)?.links ?? [];
|
|
126
|
+
for (const l of links) assets.set(l.name, null);
|
|
127
|
+
} else {
|
|
128
|
+
for (const a of (body.assets as Array<{ name: string; digest?: string | null }> | undefined) ?? []) assets.set(a.name, typeof a.digest === "string" ? a.digest : null);
|
|
129
|
+
}
|
|
130
|
+
return { id: String(body.id ?? body.tag_name ?? tag), draft: body.draft === true, assets, ...(typeof body.upload_url === "string" ? { uploadUrl: body.upload_url } : {}) };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** The published release for `tag`, or null when the forge has none. A draft is not found here. */
|
|
134
|
+
async published(tag: string): Promise<ForgeRelease | null> {
|
|
135
|
+
const enc = encodeURIComponent(tag);
|
|
136
|
+
const found = await this.call("GET", this.decl.forge === "gitlab" ? `${this.repoPath}/releases/${enc}` : `${this.repoPath}/releases/tags/${enc}`);
|
|
137
|
+
if (found.status === 404) return null;
|
|
138
|
+
if (found.status !== 200 || !found.json) this.fail(`reading release ${tag}`, found.status, found.text);
|
|
139
|
+
return this.toRelease(found.json as Record<string, unknown>, tag);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** GitHub and Forgejo: the draft release for `tag`, made at `commit` when there is none. */
|
|
143
|
+
async draft(tag: string, commit: string, name: string): Promise<ForgeRelease> {
|
|
144
|
+
const list = await this.call("GET", `${this.repoPath}/releases?per_page=100`);
|
|
145
|
+
if (list.status !== 200) this.fail("listing releases", list.status, list.text);
|
|
146
|
+
const drafts = Array.isArray(list.json) ? (list.json as Array<Record<string, unknown>>) : [];
|
|
147
|
+
const mine = drafts.find((r) => r.draft === true && r.tag_name === tag);
|
|
148
|
+
if (mine) {
|
|
149
|
+
this.ctx.log(`using the draft ${this.decl.forge} release for ${tag}`);
|
|
150
|
+
return this.toRelease(mine, tag);
|
|
151
|
+
}
|
|
152
|
+
this.ctx.log(`creating a draft ${this.decl.forge} release for ${tag} at ${commit.slice(0, 12)}`);
|
|
153
|
+
const create = await this.call("POST", `${this.repoPath}/releases`, JSON.stringify({ tag_name: tag, target_commitish: commit, name, draft: true }));
|
|
154
|
+
if (create.status !== 201 || !create.json) this.fail(`creating a draft release ${tag}`, create.status, create.text);
|
|
155
|
+
return this.toRelease(create.json as Record<string, unknown>, tag);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** GitHub and Forgejo: publish the draft `id`. */
|
|
159
|
+
async publishDraft(id: string, tag: string): Promise<void> {
|
|
160
|
+
const res = await this.call("PATCH", `${this.repoPath}/releases/${id}`, JSON.stringify({ draft: false }));
|
|
161
|
+
if (res.status !== 200) this.fail(`publishing the draft release ${tag}`, res.status, res.text);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** GitLab: create the release for `tag` at `commit` with a link to each asset. */
|
|
165
|
+
async createWithLinks(tag: string, commit: string, name: string, links: Array<{ name: string; url: string }>): Promise<void> {
|
|
166
|
+
const res = await this.call(
|
|
167
|
+
"POST",
|
|
168
|
+
`${this.repoPath}/releases`,
|
|
169
|
+
JSON.stringify({ tag_name: tag, ref: commit, name, assets: { links: links.map((l) => ({ ...l, link_type: "package" })) } }),
|
|
170
|
+
);
|
|
171
|
+
if (res.status !== 201) this.fail(`creating release ${tag}`, res.status, res.text);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Upload one asset to `rel`, returning where it can be downloaded. */
|
|
175
|
+
async upload(rel: ForgeRelease | null, version: string, asset: Asset): Promise<string> {
|
|
176
|
+
if (this.decl.forge === "github") {
|
|
177
|
+
const base = (rel?.uploadUrl ?? `${this.repoPath}/releases/${rel?.id}/assets`).replace(/\{[^}]*\}$/, "");
|
|
178
|
+
const res = await this.call("POST", `${base}?name=${encodeURIComponent(asset.name)}`, asset.bytes, { "content-type": "application/octet-stream" });
|
|
179
|
+
if (res.status !== 201) this.fail(`uploading ${asset.name}`, res.status, res.text);
|
|
180
|
+
return String((res.json as { browser_download_url?: string } | null)?.browser_download_url ?? asset.name);
|
|
181
|
+
}
|
|
182
|
+
if (this.decl.forge === "forgejo") {
|
|
183
|
+
const boundary = `chant-${bytesDigest(asset.bytes).slice(7, 31)}`;
|
|
184
|
+
const head = Buffer.from(`--${boundary}\r\nContent-Disposition: form-data; name="attachment"; filename="${asset.name.replace(/"/g, "")}"\r\nContent-Type: application/octet-stream\r\n\r\n`);
|
|
185
|
+
const tail = Buffer.from(`\r\n--${boundary}--\r\n`);
|
|
186
|
+
const body = new Uint8Array(Buffer.concat([head, Buffer.from(asset.bytes), tail]));
|
|
187
|
+
const res = await this.call("POST", `${this.repoPath}/releases/${rel?.id}/assets?name=${encodeURIComponent(asset.name)}`, body, { "content-type": `multipart/form-data; boundary=${boundary}` });
|
|
188
|
+
if (res.status !== 201) this.fail(`uploading ${asset.name}`, res.status, res.text);
|
|
189
|
+
return String((res.json as { browser_download_url?: string } | null)?.browser_download_url ?? asset.name);
|
|
190
|
+
}
|
|
191
|
+
const url = `${this.repoPath}/packages/generic/release/${encodeURIComponent(version)}/${encodeURIComponent(asset.name)}`;
|
|
192
|
+
const put = await this.call("PUT", url, asset.bytes, { "content-type": "application/octet-stream" });
|
|
193
|
+
if (put.status !== 201 && put.status !== 200) this.fail(`uploading ${asset.name} to the generic package registry`, put.status, put.text);
|
|
194
|
+
return url;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** A GitLab generic package version: the label with what GitLab refuses replaced. */
|
|
199
|
+
export function gitlabPackageVersion(label: string): string {
|
|
200
|
+
return label.replace(/[^0-9A-Za-z.\-_+]/g, "-");
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function forgeTarget(decl: ForgePublishDeclaration, deps: PublishDeps): PublishTarget {
|
|
204
|
+
const fetchImpl: PublishFetch = deps.fetch ?? ((url, init) => fetch(url, init as RequestInit));
|
|
205
|
+
const tokenNames = forgeTokenEnv(decl);
|
|
206
|
+
const setup = (ctx: PublishContext) => {
|
|
207
|
+
const tag = decl.tag ?? ctx.releaseTag;
|
|
208
|
+
if (!tag) {
|
|
209
|
+
throw new PublishTargetError("publish-forge-no-tag", `a ${decl.forge} release is named by a tag, and release ${ctx.release.releaseDigest} has no label`, "Cut the release with a label, or set tag in the declaration.");
|
|
210
|
+
}
|
|
211
|
+
const api = forgeApi(decl, ctx.env);
|
|
212
|
+
if (!api) throw new PublishTargetError("publish-forge-no-api", "a Forgejo target needs its API root, and neither api nor GITHUB_API_URL is set", "Set api, such as https://forgejo.example/api/v1.");
|
|
213
|
+
const token = readCredential(ctx.env, tokenNames, ctx.redactor);
|
|
214
|
+
if (!token) {
|
|
215
|
+
throw new PublishTargetError("publish-credentials-missing", `the ${decl.forge} target reads its token from ${tokenNames.join(" or ")}, and none is set`, `Set ${tokenNames[0]} in the job's or the shell's environment.`);
|
|
216
|
+
}
|
|
217
|
+
const assets: Asset[] = decl.assets.map((p) => {
|
|
218
|
+
const abs = memberFile(ctx.cwd, p);
|
|
219
|
+
const bytes = new Uint8Array(readFileSync(abs));
|
|
220
|
+
const digest = bytesDigest(bytes);
|
|
221
|
+
checkReleaseDigest(ctx.release, "asset", ctx.cwd, p, [digest]);
|
|
222
|
+
return { path: p, name: basename(p), bytes, digest };
|
|
223
|
+
});
|
|
224
|
+
// #4049: the SBOM and the provenance statement over the assets go on the release with them.
|
|
225
|
+
const sbom = memberSbom(ctx, decl.sbom, `${decl.repo}@${tag}`, `the ${decl.forge} target`);
|
|
226
|
+
const provenance = releaseProvenance(
|
|
227
|
+
ctx,
|
|
228
|
+
assets.map((a) => ({ name: a.name, digest: a.digest })),
|
|
229
|
+
);
|
|
230
|
+
for (const a of [sbom, provenance]) if (a) assets.push({ path: a.name, name: a.name, bytes: a.bytes, digest: a.digest, attachment: a.kind });
|
|
231
|
+
const names = new Set<string>();
|
|
232
|
+
for (const a of assets) {
|
|
233
|
+
if (names.has(a.name)) throw new PublishTargetError("publish-forge-asset-name-taken", `two assets are named ${a.name}, and a release holds one asset per name`);
|
|
234
|
+
names.add(a.name);
|
|
235
|
+
}
|
|
236
|
+
return { tag, api, token, assets };
|
|
237
|
+
};
|
|
238
|
+
const att = (a: Asset): Pick<PublishItem, "attachment"> => (a.attachment ? { attachment: a.attachment } : {});
|
|
239
|
+
const where = (tag: string, name: string) => `${decl.forge} ${decl.repo} release ${tag}, asset ${name}`;
|
|
240
|
+
const exists = (tag: string, a: Asset, theirs: string | null | undefined, before: PublishItem[]) =>
|
|
241
|
+
new PublishTargetError(
|
|
242
|
+
"publish-forge-asset-exists",
|
|
243
|
+
`release ${tag} on ${decl.forge} ${decl.repo} already has an asset named ${a.name}${theirs ? ` with digest ${theirs}, not ${a.digest}` : ", and the forge reports no digest to compare"}${before.length > 0 ? `; ${before.map((i) => i.name).join(", ")} were staged before it` : ""}`,
|
|
244
|
+
"Delete the asset on the forge, or cut a new release.",
|
|
245
|
+
);
|
|
246
|
+
return {
|
|
247
|
+
kind: "forge",
|
|
248
|
+
name: `${decl.forge} release assets ${decl.repo}`,
|
|
249
|
+
destination: `${decl.forge} ${decl.repo}`,
|
|
250
|
+
credentialEnv: tokenNames,
|
|
251
|
+
key: publishTargetKey(decl),
|
|
252
|
+
async plan(ctx) {
|
|
253
|
+
const { tag, assets } = setup(ctx);
|
|
254
|
+
return assets.map((a) => ({ name: a.path, destination: where(tag, a.name), digest: a.digest, ...att(a) }));
|
|
255
|
+
},
|
|
256
|
+
async stage(ctx): Promise<StagedTarget> {
|
|
257
|
+
const { tag, api, token, assets } = setup(ctx);
|
|
258
|
+
ctx.log(`using the token from ${token.from}`);
|
|
259
|
+
const client = new ForgeClient(decl, api, token, fetchImpl, ctx);
|
|
260
|
+
const done = (items: PublishItem[], state: Record<string, string>): StagedTarget => ({ target: this.name, destination: this.destination, items, state });
|
|
261
|
+
|
|
262
|
+
// A release already published: done when it holds every asset, else refused, since uploads to it would show one at a time.
|
|
263
|
+
const published = await client.published(tag);
|
|
264
|
+
if (published) {
|
|
265
|
+
const items: PublishItem[] = [];
|
|
266
|
+
for (const a of assets) {
|
|
267
|
+
if (!published.assets.has(a.name)) {
|
|
268
|
+
throw new PublishTargetError(
|
|
269
|
+
"publish-forge-release-published",
|
|
270
|
+
`release ${tag} on ${decl.forge} ${decl.repo} is already published without ${a.name}, so the assets uploaded to it would show one at a time`,
|
|
271
|
+
"Delete the forge release so the publish makes it whole, or set tag in the declaration to a tag with no release.",
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
const theirs = published.assets.get(a.name);
|
|
275
|
+
if (theirs !== a.digest && !(decl.forge !== "github" && theirs === null)) throw exists(tag, a, theirs, items);
|
|
276
|
+
items.push({ name: a.path, destination: where(tag, a.name), digest: a.digest, ...att(a), present: true });
|
|
277
|
+
}
|
|
278
|
+
ctx.log(`release ${tag} is already published with every asset`);
|
|
279
|
+
return done(items, { published: "true" });
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const version = gitlabPackageVersion(ctx.label ?? tag);
|
|
283
|
+
if (decl.forge === "gitlab") {
|
|
284
|
+
// No draft on GitLab: the assets go to the package registry, and the release is made at the reveal.
|
|
285
|
+
const items: PublishItem[] = [];
|
|
286
|
+
for (const a of assets) {
|
|
287
|
+
ctx.log(`uploading ${a.name} (${a.bytes.byteLength} bytes) to the generic package registry`);
|
|
288
|
+
items.push({ name: a.path, destination: await client.upload(null, version, a), digest: a.digest, ...att(a) });
|
|
289
|
+
}
|
|
290
|
+
return done(items, {});
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const rel = await client.draft(tag, ctx.release.commit, ctx.label ?? tag);
|
|
294
|
+
const items: PublishItem[] = [];
|
|
295
|
+
for (const a of assets) {
|
|
296
|
+
if (rel.assets.has(a.name)) {
|
|
297
|
+
const theirs = rel.assets.get(a.name);
|
|
298
|
+
if (theirs !== a.digest) throw exists(tag, a, theirs, items);
|
|
299
|
+
items.push({ name: a.path, destination: where(tag, a.name), digest: a.digest, ...att(a), present: true });
|
|
300
|
+
ctx.log(`${a.name} is already on the draft release ${tag}`);
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
ctx.log(`uploading ${a.name} (${a.bytes.byteLength} bytes) to the draft`);
|
|
304
|
+
items.push({ name: a.path, destination: await client.upload(rel, version, a), digest: a.digest, ...att(a) });
|
|
305
|
+
}
|
|
306
|
+
return done(items, { releaseId: rel.id });
|
|
307
|
+
},
|
|
308
|
+
async reveal(ctx, staged) {
|
|
309
|
+
const { tag, api, token } = setup(ctx);
|
|
310
|
+
const client = new ForgeClient(decl, api, token, fetchImpl, ctx);
|
|
311
|
+
const items = staged.items.map((i) => ({ name: i.name, destination: i.destination, ...(i.digest ? { digest: i.digest } : {}), ...(i.attachment ? { attachment: i.attachment } : {}) }));
|
|
312
|
+
if (staged.state.published === "true") return { target: this.name, destination: this.destination, items: items.map((i) => ({ ...i, present: true })) };
|
|
313
|
+
if (decl.forge === "gitlab") {
|
|
314
|
+
ctx.log(`creating the gitlab release ${tag} at ${ctx.release.commit.slice(0, 12)} with ${items.length} asset link(s)`);
|
|
315
|
+
await client.createWithLinks(tag, ctx.release.commit, ctx.label ?? tag, items.map((i) => ({ name: basename(i.name), url: i.destination })));
|
|
316
|
+
} else {
|
|
317
|
+
const id = staged.state.releaseId;
|
|
318
|
+
if (!id) throw new PublishTargetError("publish-forge-failed", `the staged ${decl.forge} release for ${tag} has no draft id to publish`);
|
|
319
|
+
ctx.log(`publishing the draft ${decl.forge} release ${tag}`);
|
|
320
|
+
await client.publishDraft(id, tag);
|
|
321
|
+
}
|
|
322
|
+
return { target: this.name, destination: this.destination, items };
|
|
323
|
+
},
|
|
324
|
+
};
|
|
325
|
+
}
|