@intentius/chant 0.124.27 → 0.124.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/dist/__golden__/wave-loop/op-held-on.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/dist/__golden__/wave-loop/op-per-run.json +7 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
- package/dist/__golden__/wave-loop/op-sealed.json +3 -0
- package/dist/__golden__/wave-loop/op-spend.json +3 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +63 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +130 -0
- package/dist/change-set.d.ts +62 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +36 -4
- package/dist/chant-version.d.ts +9 -0
- package/dist/chant-version.d.ts.map +1 -0
- package/dist/chant-version.js +16 -0
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +15 -11
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.js +30 -2
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +13 -2
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.js +17 -8
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +64 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +43 -4
- package/dist/cli/handlers/change-set.d.ts +10 -0
- package/dist/cli/handlers/change-set.d.ts.map +1 -1
- package/dist/cli/handlers/change-set.js +51 -1
- package/dist/cli/handlers/fan-out.d.ts +10 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +54 -6
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +16 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +269 -37
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +440 -28
- package/dist/cli/handlers/release.d.ts +91 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +589 -0
- package/dist/cli/handlers/run.d.ts +38 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +234 -16
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +143 -17
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +73 -5
- package/dist/cli/registry.d.ts +43 -5
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +30 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +49 -0
- package/dist/cli/version.d.ts +3 -5
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +3 -13
- package/dist/codegen/docs-in-site.d.ts +71 -0
- package/dist/codegen/docs-in-site.d.ts.map +1 -0
- package/dist/codegen/docs-in-site.js +177 -0
- package/dist/codegen/docs-types.d.ts +14 -1
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +18 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/docs.js +40 -7
- package/dist/codegen/rehype-base-url.mjs +15 -9
- package/dist/components/auto-release.d.ts +6 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +6 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +10 -9
- package/dist/components/driver.d.ts +50 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +116 -54
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts +7 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +85 -26
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- package/dist/components/pr-run.d.ts +11 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +20 -1
- package/dist/components/promote-order.d.ts +38 -0
- package/dist/components/promote-order.d.ts.map +1 -0
- package/dist/components/promote-order.js +62 -0
- package/dist/components/promote.d.ts +38 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +77 -2
- package/dist/components/release-compile.d.ts +142 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +382 -0
- package/dist/components/release-gate.d.ts +135 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +288 -0
- package/dist/components/release-promote.d.ts +96 -0
- package/dist/components/release-promote.d.ts.map +1 -0
- package/dist/components/release-promote.js +204 -0
- package/dist/components/run-progress.d.ts +7 -0
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/wave-plan.d.ts +8 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +11 -3
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +68 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +97 -0
- package/dist/lifecycle/commit-sha.d.ts +13 -0
- package/dist/lifecycle/commit-sha.d.ts.map +1 -0
- package/dist/lifecycle/commit-sha.js +21 -0
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +78 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +138 -0
- package/dist/lifecycle/gate-ledger.d.ts +51 -4
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +154 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +366 -0
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-ledger.d.ts +54 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +53 -20
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +200 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +320 -0
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +2 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.js +4 -2
- package/dist/op/activities/source-release.d.ts.map +1 -1
- package/dist/op/activities/source-release.js +7 -3
- package/dist/op/activities/source-rollback.d.ts.map +1 -1
- package/dist/op/activities/source-rollback.js +7 -3
- package/dist/op/gate-name.d.ts +11 -2
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +12 -3
- package/dist/op/gate-run-locator.d.ts +22 -0
- package/dist/op/gate-run-locator.d.ts.map +1 -1
- package/dist/op/gate-run-locator.js +31 -0
- package/dist/op/gate.d.ts +27 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +57 -3
- package/dist/op/local-executor.d.ts +36 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +158 -52
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +4 -0
- package/dist/op/run-live.d.ts +2 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/run-live.js +1 -1
- package/dist/op/runtime.d.ts +46 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/runtimes/local.js +8 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/point-decision.d.ts +2 -0
- package/dist/point-decision.d.ts.map +1 -1
- package/dist/point-decision.js +9 -2
- package/dist/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +104 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +251 -0
- package/dist/run-event-stream.d.ts +133 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +242 -0
- package/dist/run-event.d.ts +285 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +209 -0
- package/dist/wave-runtime.mjs +262 -226
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/run-event-check.js +59 -0
- package/dist/workspace/change-set-show.d.ts +56 -0
- package/dist/workspace/change-set-show.d.ts.map +1 -0
- package/dist/workspace/change-set-show.js +54 -0
- package/dist/workspace/change-set-show.schema.json +102 -0
- package/dist/workspace/change-set.schema.json +32 -1
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes-cli.js +10 -1
- package/dist/workspace/changes.d.ts +37 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/changes.js +49 -2
- package/dist/workspace/changes.schema.json +135 -0
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green-cli.d.ts +19 -4
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +75 -12
- package/dist/workspace/ci-green-forge.d.ts +20 -3
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +6 -4
- package/dist/workspace/ci-green-workflow.d.ts +11 -0
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +6 -0
- package/dist/workspace/ci-green.d.ts +281 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +456 -38
- package/dist/workspace/ci-last-green.schema.json +32 -2
- package/dist/workspace/ci-list.schema.json +525 -0
- package/dist/workspace/commit-links.d.ts +85 -0
- package/dist/workspace/commit-links.d.ts.map +1 -0
- package/dist/workspace/commit-links.js +163 -0
- package/dist/workspace/declaration.d.ts +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +27 -1
- package/dist/workspace/declaration.schema.json +76 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/identity-gate-rule.d.ts +37 -0
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +72 -7
- package/dist/workspace/identity.d.ts +12 -0
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/identity.js +13 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +36 -7
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +192 -22
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -0
- package/dist/workspace/ls.schema.json +3 -1
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +6 -0
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/promotion-order.d.ts +41 -0
- package/dist/workspace/promotion-order.d.ts.map +1 -0
- package/dist/workspace/promotion-order.js +95 -0
- package/dist/workspace/reason-codes.d.ts +4 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +7 -1
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +81 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +103 -0
- package/dist/workspace/releases.schema.json +513 -0
- package/dist/workspace/run-event.schema.json +295 -0
- package/dist/workspace/status-gates.d.ts +34 -1
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +31 -2
- package/dist/workspace/status-stewards.d.ts +4 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +2 -0
- package/dist/workspace/status.d.ts +117 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +218 -4
- package/dist/workspace/status.schema.json +268 -2
- package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
- package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
- package/dist/workspace/trust/ci-tag-statement.js +123 -0
- package/dist/workspace/trust/op-run-statement.d.ts +91 -0
- package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
- package/dist/workspace/trust/op-run-statement.js +143 -0
- package/dist/workspace/trust/rotation.d.ts +18 -2
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.js +65 -16
- package/dist/workspace/trust/seal.d.ts +11 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +9 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/ssh-commit.js +10 -10
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/src/__golden__/wave-loop/fan-out-only.json +6 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/src/__golden__/wave-loop/op-cost-member.json +3 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/src/__golden__/wave-loop/op-held-on.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/src/__golden__/wave-loop/op-per-run.json +7 -0
- package/src/__golden__/wave-loop/op-pr-review.json +7 -0
- package/src/__golden__/wave-loop/op-sealed.json +3 -0
- package/src/__golden__/wave-loop/op-spend.json +3 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +141 -0
- package/src/change-set.ts +87 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +25 -11
- package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
- package/src/cli/commands/check-lexicon-docs.ts +39 -2
- package/src/cli/commands/check-lexicon.ts +14 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
- package/src/cli/commands/init-lexicon.test.ts +25 -0
- package/src/cli/commands/init-lexicon.ts +18 -8
- package/src/cli/handlers/approve-release.test.ts +84 -0
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +65 -2
- package/src/cli/handlers/build.test.ts +67 -0
- package/src/cli/handlers/build.ts +46 -4
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +228 -1
- package/src/cli/handlers/fan-out.ts +53 -6
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +341 -0
- package/src/cli/handlers/pr.ts +292 -47
- package/src/cli/handlers/promote.test.ts +669 -7
- package/src/cli/handlers/promote.ts +464 -29
- package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +199 -0
- package/src/cli/handlers/release.ts +593 -0
- package/src/cli/handlers/run-auto-release.test.ts +80 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +243 -18
- package/src/cli/main.test.ts +8 -0
- package/src/cli/main.ts +143 -17
- package/src/cli/mcp/never-over-mcp.test.ts +2 -0
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +56 -5
- package/src/cli/registry.ts +43 -5
- package/src/cli/run-events.ts +57 -0
- package/src/cli/version.ts +3 -13
- package/src/codegen/docs-in-site.test.ts +166 -0
- package/src/codegen/docs-in-site.ts +182 -0
- package/src/codegen/docs-site/AgentPrompt.astro +26 -1
- package/src/codegen/docs-types.ts +14 -1
- package/src/codegen/docs.ts +60 -7
- package/src/codegen/rehype-base-url.d.mts +3 -1
- package/src/codegen/rehype-base-url.mjs +15 -9
- package/src/codegen/rehype-base-url.test.ts +34 -0
- package/src/components/auto-release.test.ts +30 -0
- package/src/components/auto-release.ts +9 -1
- package/src/components/capability.ts +9 -0
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +165 -56
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +104 -35
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/pr-run.ts +30 -1
- package/src/components/promote-order.test.ts +90 -0
- package/src/components/promote-order.ts +75 -0
- package/src/components/promote.test.ts +10 -1
- package/src/components/promote.ts +94 -3
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +459 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +355 -0
- package/src/components/release-promote.test.ts +159 -0
- package/src/components/release-promote.ts +232 -0
- package/src/components/run-progress.ts +7 -0
- package/src/components/wave-plan.ts +16 -3
- package/src/config.ts +15 -1
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/index.ts +2 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +113 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +164 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +68 -3
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +483 -0
- package/src/lifecycle/release-cut.ts +503 -0
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +434 -0
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/lifecycle/run-ledger.test.ts +10 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/op/activities/change-coverage.ts +4 -1
- package/src/op/activities/source-release.ts +8 -3
- package/src/op/activities/source-rollback.ts +8 -4
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/gate-name.ts +13 -3
- package/src/op/gate-release.test.ts +126 -0
- package/src/op/gate-run-locator.test.ts +110 -0
- package/src/op/gate-run-locator.ts +35 -0
- package/src/op/gate.ts +88 -6
- package/src/op/local-executor.ts +207 -52
- package/src/op/op-waves-run.ts +4 -0
- package/src/op/run-events.test.ts +166 -0
- package/src/op/run-live.ts +1 -1
- package/src/op/runtime.ts +39 -0
- package/src/op/runtimes/local.test.ts +16 -0
- package/src/op/runtimes/local.ts +9 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/point-decision.ts +12 -2
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +273 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +309 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +471 -0
- package/src/wave-loop-golden.test.ts +9 -0
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/__fixtures__/run-event-check.ts +58 -0
- package/src/workspace/ancestry-index.test.ts +93 -0
- package/src/workspace/change-set-show.schema.json +102 -0
- package/src/workspace/change-set-show.ts +72 -0
- package/src/workspace/change-set.schema.json +32 -1
- package/src/workspace/changes-cli.ts +10 -1
- package/src/workspace/changes.schema.json +135 -0
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/changes.ts +84 -2
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green-cli.ts +75 -13
- package/src/workspace/ci-green-forge.ts +24 -4
- package/src/workspace/ci-green-runs.test.ts +242 -0
- package/src/workspace/ci-green-signed.test.ts +302 -0
- package/src/workspace/ci-green-workflow.test.ts +16 -0
- package/src/workspace/ci-green-workflow.ts +12 -0
- package/src/workspace/ci-green.test.ts +131 -2
- package/src/workspace/ci-green.ts +635 -42
- package/src/workspace/ci-last-green.schema.json +32 -2
- package/src/workspace/ci-list.schema.json +525 -0
- package/src/workspace/commit-links.test.ts +279 -0
- package/src/workspace/commit-links.ts +183 -0
- package/src/workspace/declaration.schema.json +76 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +60 -2
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +196 -26
- package/src/workspace/ls.schema.json +3 -1
- package/src/workspace/ls.ts +6 -0
- package/src/workspace/member-pipeline.test.ts +17 -0
- package/src/workspace/member-pipeline.ts +9 -0
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +5 -1
- package/src/workspace/reason-codes.test.ts +7 -1
- package/src/workspace/reason-codes.ts +7 -1
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +513 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +133 -0
- package/src/workspace/run-event.schema.json +295 -0
- package/src/workspace/status-contract.test.ts +81 -1
- package/src/workspace/status-gates.ts +64 -1
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +268 -2
- package/src/workspace/status.ts +334 -4
- package/src/workspace/trust/ci-tag-statement.ts +151 -0
- package/src/workspace/trust/op-run-statement.ts +148 -0
- package/src/workspace/trust/rotation.ts +61 -14
- package/src/workspace/trust/seal.ts +10 -0
- package/src/workspace/trust/ssh-commit.ts +10 -9
- package/src/workspace/wip.ts +11 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The release (#4026, ws-104): one green commit of one member, compiled for
|
|
3
|
+
* every environment the member declares, stored on `chant/lifecycle`.
|
|
4
|
+
*
|
|
5
|
+
* A release is a different object from a deploy record. A deploy record is
|
|
6
|
+
* one line of `<env>/releases.jsonl` (./release-ledger.ts) saying a component
|
|
7
|
+
* was deployed. A release is what is cut once from a green commit and then
|
|
8
|
+
* moves through the environments: the commit, the artifacts built once from
|
|
9
|
+
* it, and one compile target per environment, each with the template digests
|
|
10
|
+
* and the build parameters that environment was compiled with.
|
|
11
|
+
*
|
|
12
|
+
* Storage, per ws-104 question 3:
|
|
13
|
+
*
|
|
14
|
+
* _releases/<releaseDigest>.json the release, written once and never rewritten
|
|
15
|
+
* _releases/cuts.jsonl one line per cut, with the release's label
|
|
16
|
+
* _releases/templates/<digest>.txt each template's bytes, by content digest
|
|
17
|
+
*
|
|
18
|
+
* `:` is written as `_` in file names, as `_plans/` and `_builds/` do. A
|
|
19
|
+
* workspace member's copy sits under `_members/<member>/` through the same
|
|
20
|
+
* `ledgerDir` prefix every other store uses.
|
|
21
|
+
*
|
|
22
|
+
* The template bytes are kept so that a promote under a release deploys the
|
|
23
|
+
* templates the cut recorded, not whatever the promoting checkout holds
|
|
24
|
+
* (ws-104 question 5). They are named by content digest, so a template that
|
|
25
|
+
* two environments or two releases share is stored once.
|
|
26
|
+
*
|
|
27
|
+
* `releaseDigest` is `computePlanDigest("release", subject)` over the member,
|
|
28
|
+
* the commit, the artifact digests and each target's env, templates and the
|
|
29
|
+
* name and value of each param. A param's `source`, the label, the actor, the
|
|
30
|
+
* time and any signature stay outside it, so two cuts of one commit give the
|
|
31
|
+
* same digest.
|
|
32
|
+
*/
|
|
33
|
+
import { sortedJsonReplacer } from "../utils.js";
|
|
34
|
+
import { contentDigest } from "../content-digest.js";
|
|
35
|
+
import { computePlanDigest } from "./plan-digest.js";
|
|
36
|
+
import { ledgerDir, listFilesInDir, readBlobBySha, readBlobFromPath, readPathSha, RefCASConflictError, writeLedgerFiles, } from "./git.js";
|
|
37
|
+
/** The directory on `chant/lifecycle` that holds releases, cuts and template bytes. */
|
|
38
|
+
export const RELEASES_DIR = "_releases";
|
|
39
|
+
/** The cut log inside {@link RELEASES_DIR}. */
|
|
40
|
+
export const CUTS_FILE = "cuts.jsonl";
|
|
41
|
+
/** The directory inside {@link RELEASES_DIR} that holds template bytes by content digest. */
|
|
42
|
+
export const TEMPLATES_SUBDIR = "templates";
|
|
43
|
+
/** The `kind` a release digest is computed under, so it can never equal a plan digest of another kind. */
|
|
44
|
+
export const RELEASE_DIGEST_KIND = "release";
|
|
45
|
+
const byString = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
46
|
+
/** The same release with every list in the order ws-104 sets: artifacts by component then path, targets by env, templates by component then path, params by name. */
|
|
47
|
+
export function normalizeRelease(release) {
|
|
48
|
+
return {
|
|
49
|
+
...release,
|
|
50
|
+
artifactDigests: [...release.artifactDigests]
|
|
51
|
+
.map(({ component, kind, path, digest }) => ({ component, kind, path, digest }))
|
|
52
|
+
.sort((a, b) => byString(a.component, b.component) || byString(a.path, b.path)),
|
|
53
|
+
targets: [...release.targets]
|
|
54
|
+
.map((t) => ({
|
|
55
|
+
env: t.env,
|
|
56
|
+
templates: [...t.templates]
|
|
57
|
+
.map(({ component, path, digest }) => ({ component, path, digest }))
|
|
58
|
+
.sort((a, b) => byString(a.component ?? "", b.component ?? "") || byString(a.path, b.path)),
|
|
59
|
+
params: [...t.params].map(({ name, value, source }) => ({ name, value, source })).sort((a, b) => byString(a.name, b.name)),
|
|
60
|
+
}))
|
|
61
|
+
.sort((a, b) => byString(a.env, b.env)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/** The hash input of ws-104 question 3: a param's `source` is left out. */
|
|
65
|
+
export function releaseHashInput(release) {
|
|
66
|
+
const n = normalizeRelease(release);
|
|
67
|
+
return {
|
|
68
|
+
member: n.member,
|
|
69
|
+
commit: n.commit,
|
|
70
|
+
artifactDigests: n.artifactDigests,
|
|
71
|
+
targets: n.targets.map((t) => ({ env: t.env, templates: t.templates, params: t.params.map(({ name, value }) => ({ name, value })) })),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** `computePlanDigest("release", input)` over {@link releaseHashInput}. */
|
|
75
|
+
export function computeReleaseDigest(release) {
|
|
76
|
+
return computePlanDigest(RELEASE_DIGEST_KIND, releaseHashInput(release));
|
|
77
|
+
}
|
|
78
|
+
/** A release with its digest computed and its lists in order. */
|
|
79
|
+
export function makeRelease(subject) {
|
|
80
|
+
const normalized = normalizeRelease(subject);
|
|
81
|
+
return { version: 1, releaseDigest: computeReleaseDigest(normalized), ...normalized };
|
|
82
|
+
}
|
|
83
|
+
/** Whether a release's own `releaseDigest` is the digest of what it holds. */
|
|
84
|
+
export function verifyReleaseDigest(release) {
|
|
85
|
+
return release.releaseDigest === computeReleaseDigest(release);
|
|
86
|
+
}
|
|
87
|
+
const stem = (digest) => digest.replace(/:/g, "_");
|
|
88
|
+
const releaseFile = (digest) => `${stem(digest)}.json`;
|
|
89
|
+
const templateFile = (digest) => `${stem(digest)}.txt`;
|
|
90
|
+
/** A label names a release in commands such as `chant release cut <label>`, so it is one plain word. */
|
|
91
|
+
const LABEL_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._+-]{0,127}$/;
|
|
92
|
+
/** Why a label can't be used, or undefined when it can. */
|
|
93
|
+
export function invalidLabel(label) {
|
|
94
|
+
if (LABEL_PATTERN.test(label))
|
|
95
|
+
return undefined;
|
|
96
|
+
return `"${label}" is not a release label: use letters, digits and . _ + -, starting with a letter or digit, at most 128 characters`;
|
|
97
|
+
}
|
|
98
|
+
/** Thrown when a cut would give a label two digests, or a digest two labels. */
|
|
99
|
+
export class ReleaseLabelError extends Error {
|
|
100
|
+
constructor(code, message) {
|
|
101
|
+
super(message);
|
|
102
|
+
this.code = code;
|
|
103
|
+
this.name = "ReleaseLabelError";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Store a release and its template bytes in one commit. A release already
|
|
108
|
+
* stored under its digest is never rewritten, and a template already stored
|
|
109
|
+
* is not written again. Does not push.
|
|
110
|
+
*/
|
|
111
|
+
export async function persistRelease(release, templates, opts) {
|
|
112
|
+
if (!verifyReleaseDigest(release)) {
|
|
113
|
+
throw new Error(`release ${release.releaseDigest} does not hash to its own digest; refusing to store it`);
|
|
114
|
+
}
|
|
115
|
+
for (const t of templates) {
|
|
116
|
+
if (contentDigest(t.content) !== t.digest)
|
|
117
|
+
throw new Error(`template bytes do not hash to ${t.digest}; refusing to store them`);
|
|
118
|
+
}
|
|
119
|
+
const dir = await ledgerDir(RELEASES_DIR, opts);
|
|
120
|
+
const files = [];
|
|
121
|
+
const releasePath = `${dir}/${releaseFile(release.releaseDigest)}`;
|
|
122
|
+
const exists = (await readPathSha(RELEASES_DIR, releaseFile(release.releaseDigest), opts)) !== null;
|
|
123
|
+
if (!exists)
|
|
124
|
+
files.push({ path: releasePath, content: `${JSON.stringify(release, sortedJsonReplacer)}\n`, expectPriorSha: null });
|
|
125
|
+
const seen = new Set();
|
|
126
|
+
for (const t of templates) {
|
|
127
|
+
if (seen.has(t.digest))
|
|
128
|
+
continue;
|
|
129
|
+
seen.add(t.digest);
|
|
130
|
+
if ((await readPathSha(`${RELEASES_DIR}/${TEMPLATES_SUBDIR}`, templateFile(t.digest), opts)) !== null)
|
|
131
|
+
continue;
|
|
132
|
+
files.push({ path: `${dir}/${TEMPLATES_SUBDIR}/${templateFile(t.digest)}`, content: t.content, expectPriorSha: null });
|
|
133
|
+
}
|
|
134
|
+
if (files.length === 0)
|
|
135
|
+
return { commit: null, written: false };
|
|
136
|
+
try {
|
|
137
|
+
const commit = await writeLedgerFiles(files, `Release ${release.releaseDigest}`, opts);
|
|
138
|
+
return { commit, written: !exists };
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
// Another writer stored the same content-addressed files first.
|
|
142
|
+
if (err instanceof RefCASConflictError)
|
|
143
|
+
return { commit: null, written: false };
|
|
144
|
+
throw err;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** Parse a stored release file, or null when it is not a release. */
|
|
148
|
+
export function parseRelease(text) {
|
|
149
|
+
let parsed;
|
|
150
|
+
try {
|
|
151
|
+
parsed = JSON.parse(text);
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
if (!parsed || typeof parsed !== "object")
|
|
157
|
+
return null;
|
|
158
|
+
const r = parsed;
|
|
159
|
+
const ok = r.version === 1 &&
|
|
160
|
+
typeof r.releaseDigest === "string" &&
|
|
161
|
+
(r.member === null || typeof r.member === "string") &&
|
|
162
|
+
typeof r.commit === "string" &&
|
|
163
|
+
Array.isArray(r.artifactDigests) &&
|
|
164
|
+
r.artifactDigests.every((a) => a && typeof a.component === "string" && (a.kind === "image" || a.kind === "asset") && typeof a.path === "string" && typeof a.digest === "string") &&
|
|
165
|
+
Array.isArray(r.targets) &&
|
|
166
|
+
r.targets.every((t) => t &&
|
|
167
|
+
typeof t.env === "string" &&
|
|
168
|
+
Array.isArray(t.templates) &&
|
|
169
|
+
t.templates.every((x) => x && (x.component === null || typeof x.component === "string") && typeof x.path === "string" && typeof x.digest === "string") &&
|
|
170
|
+
Array.isArray(t.params) &&
|
|
171
|
+
t.params.every((p) => p && typeof p.name === "string" && ["string", "number", "boolean"].includes(typeof p.value) && typeof p.source === "string"));
|
|
172
|
+
return ok ? r : null;
|
|
173
|
+
}
|
|
174
|
+
/** Read one release by digest. Null when none is stored, or when the file is not a release whose content hashes to its digest. */
|
|
175
|
+
export async function readRelease(digest, opts) {
|
|
176
|
+
const text = await readBlobFromPath(RELEASES_DIR, releaseFile(digest), opts);
|
|
177
|
+
if (text === null)
|
|
178
|
+
return null;
|
|
179
|
+
const release = parseRelease(text);
|
|
180
|
+
if (!release || release.releaseDigest !== digest || !verifyReleaseDigest(release))
|
|
181
|
+
return null;
|
|
182
|
+
return release;
|
|
183
|
+
}
|
|
184
|
+
/** Every stored release, and how many release files could not be read as one. */
|
|
185
|
+
export async function readAllReleases(opts) {
|
|
186
|
+
const releases = [];
|
|
187
|
+
let malformed = 0;
|
|
188
|
+
for (const name of await listFilesInDir(RELEASES_DIR, opts)) {
|
|
189
|
+
if (!name.endsWith(".json"))
|
|
190
|
+
continue;
|
|
191
|
+
const digest = name.slice(0, -".json".length).replace(/_/g, ":");
|
|
192
|
+
const release = await readRelease(digest, opts);
|
|
193
|
+
if (release)
|
|
194
|
+
releases.push(release);
|
|
195
|
+
else
|
|
196
|
+
malformed++;
|
|
197
|
+
}
|
|
198
|
+
return { releases, malformed };
|
|
199
|
+
}
|
|
200
|
+
/** Parse the lines of `cuts.jsonl`. A line that is not a cut is counted and skipped. */
|
|
201
|
+
export function parseCuts(text) {
|
|
202
|
+
const cuts = [];
|
|
203
|
+
let malformed = 0;
|
|
204
|
+
for (const line of (text ?? "").split("\n").map((l) => l.trim()).filter(Boolean)) {
|
|
205
|
+
try {
|
|
206
|
+
const c = JSON.parse(line);
|
|
207
|
+
const ok = c &&
|
|
208
|
+
c.version === 1 &&
|
|
209
|
+
typeof c.releaseDigest === "string" &&
|
|
210
|
+
(c.label === null || typeof c.label === "string") &&
|
|
211
|
+
typeof c.actor === "string" &&
|
|
212
|
+
typeof c.timestamp === "string" &&
|
|
213
|
+
(c.runOrigin === null || (typeof c.runOrigin === "object" && typeof c.runOrigin?.forge === "string")) &&
|
|
214
|
+
(c.signature === null || c.signature === undefined || typeof c.signature === "string") &&
|
|
215
|
+
(c.runId === null || c.runId === undefined || typeof c.runId === "string");
|
|
216
|
+
if (!ok) {
|
|
217
|
+
malformed++;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
cuts.push({ ...c, runId: c.runId ?? null, signature: c.signature ?? null });
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
malformed++;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { cuts, malformed };
|
|
227
|
+
}
|
|
228
|
+
/** Every cut, oldest first. */
|
|
229
|
+
export async function readCuts(opts) {
|
|
230
|
+
return parseCuts(await readBlobFromPath(RELEASES_DIR, CUTS_FILE, opts));
|
|
231
|
+
}
|
|
232
|
+
/** The label each digest carries, from its labelled cuts. */
|
|
233
|
+
export function labelsByDigest(cuts) {
|
|
234
|
+
const out = new Map();
|
|
235
|
+
for (const c of cuts)
|
|
236
|
+
if (c.label !== null && !out.has(c.releaseDigest))
|
|
237
|
+
out.set(c.releaseDigest, c.label);
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
const CUT_RETRY_ATTEMPTS = 5;
|
|
241
|
+
/**
|
|
242
|
+
* Append a cut for `releaseDigest`. A label names one digest and a digest
|
|
243
|
+
* takes at most one label (ws-104 question 3): cutting a digest again is a
|
|
244
|
+
* no-op that returns its label, giving an unlabelled digest a label appends a
|
|
245
|
+
* cut, and cutting a digest under a label other than its own, or under a
|
|
246
|
+
* label another digest has, throws {@link ReleaseLabelError}. Does not push.
|
|
247
|
+
*/
|
|
248
|
+
export async function appendCut(input, opts) {
|
|
249
|
+
if (input.label !== null) {
|
|
250
|
+
const why = invalidLabel(input.label);
|
|
251
|
+
if (why)
|
|
252
|
+
throw new ReleaseLabelError("release-label-invalid", why);
|
|
253
|
+
}
|
|
254
|
+
let lastErr;
|
|
255
|
+
for (let attempt = 1; attempt <= CUT_RETRY_ATTEMPTS; attempt++) {
|
|
256
|
+
const priorSha = await readPathSha(RELEASES_DIR, CUTS_FILE, opts);
|
|
257
|
+
const existing = priorSha ? await readBlobBySha(priorSha, opts) : null;
|
|
258
|
+
const { cuts } = parseCuts(existing);
|
|
259
|
+
const labels = labelsByDigest(cuts);
|
|
260
|
+
const own = labels.get(input.releaseDigest);
|
|
261
|
+
const alreadyCut = cuts.some((c) => c.releaseDigest === input.releaseDigest);
|
|
262
|
+
if (own !== undefined) {
|
|
263
|
+
if (input.label === null || input.label === own)
|
|
264
|
+
return { appended: false, label: own };
|
|
265
|
+
throw new ReleaseLabelError("release-already-labelled", `release ${input.releaseDigest} is already labelled "${own}", and a release takes one label`);
|
|
266
|
+
}
|
|
267
|
+
if (input.label === null && alreadyCut)
|
|
268
|
+
return { appended: false, label: null };
|
|
269
|
+
if (input.label !== null) {
|
|
270
|
+
for (const [digest, label] of labels) {
|
|
271
|
+
if (label === input.label) {
|
|
272
|
+
throw new ReleaseLabelError("release-label-taken", `the label "${input.label}" already names release ${digest}`);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
const cut = {
|
|
277
|
+
version: 1,
|
|
278
|
+
releaseDigest: input.releaseDigest,
|
|
279
|
+
label: input.label,
|
|
280
|
+
actor: input.actor,
|
|
281
|
+
timestamp: input.timestamp,
|
|
282
|
+
runId: input.runId ?? null,
|
|
283
|
+
runOrigin: input.runOrigin,
|
|
284
|
+
signature: input.signature ?? null,
|
|
285
|
+
};
|
|
286
|
+
const content = `${existing ? `${existing.replace(/\n$/, "")}\n` : ""}${JSON.stringify(cut, sortedJsonReplacer)}\n`;
|
|
287
|
+
try {
|
|
288
|
+
const dir = await ledgerDir(RELEASES_DIR, opts);
|
|
289
|
+
await writeLedgerFiles([{ path: `${dir}/${CUTS_FILE}`, content, expectPriorSha: priorSha }], "Release cut", opts);
|
|
290
|
+
return { appended: true, cut };
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
if (!(err instanceof RefCASConflictError))
|
|
294
|
+
throw err;
|
|
295
|
+
lastErr = err;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
throw lastErr;
|
|
299
|
+
}
|
|
300
|
+
/** A template's bytes by digest, or null when none is stored. */
|
|
301
|
+
export async function readReleaseTemplate(digest, opts) {
|
|
302
|
+
return readBlobFromPath(`${RELEASES_DIR}/${TEMPLATES_SUBDIR}`, templateFile(digest), opts);
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Find a release by its digest or its label. Null when neither names one.
|
|
306
|
+
* A string that names a digest is read as a digest first.
|
|
307
|
+
*/
|
|
308
|
+
export async function resolveRelease(ref, opts) {
|
|
309
|
+
const { cuts } = await readCuts(opts);
|
|
310
|
+
const labels = labelsByDigest(cuts);
|
|
311
|
+
let digest = ref.includes(":") ? ref : undefined;
|
|
312
|
+
if (!digest)
|
|
313
|
+
for (const [d, label] of labels)
|
|
314
|
+
if (label === ref)
|
|
315
|
+
digest = d;
|
|
316
|
+
if (!digest)
|
|
317
|
+
return null;
|
|
318
|
+
const release = await readRelease(digest, opts);
|
|
319
|
+
return release ? { release, label: labels.get(digest) ?? null } : null;
|
|
320
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant release verify <label|digest>` (#4049): whether a release was
|
|
3
|
+
* signed by a key `.chant/allowed_signers` lists, checked offline.
|
|
4
|
+
*
|
|
5
|
+
* It reads the release and its cuts from the local `chant/lifecycle` branch,
|
|
6
|
+
* and the signers file, its signed history (#2553) and the declaration at the
|
|
7
|
+
* release's own commit, as `chant workspace evidence verify` reads the policy
|
|
8
|
+
* at the revision it is given. It fetches nothing and needs no key. A release
|
|
9
|
+
* verifies when its file's digest is the digest of what it holds and one of
|
|
10
|
+
* its cuts carries a signature in the `chant-release` namespace that verifies
|
|
11
|
+
* for the cut's actor, over the release digest, the label, the actor and the
|
|
12
|
+
* time (`releaseSealPayload`), and, when the declaration at that commit has an
|
|
13
|
+
* `identity.gates.release` entry naming a class, the actor is in it.
|
|
14
|
+
*
|
|
15
|
+
* Reading the signers at the release's commit means a key added later does
|
|
16
|
+
* not make an old release verify, and one removed later does not unmake it.
|
|
17
|
+
*/
|
|
18
|
+
import type { ReleaseSignatureCheck } from "../workspace/identity-gate-rule.js";
|
|
19
|
+
import type { ReleaseCut } from "./release-store.js";
|
|
20
|
+
/** What `release verify --json` prints. */
|
|
21
|
+
export interface ReleaseVerification {
|
|
22
|
+
releaseDigest: string;
|
|
23
|
+
label: string | null;
|
|
24
|
+
member: string | null;
|
|
25
|
+
commit: string;
|
|
26
|
+
/** Where the signers were read: the signers file's path and the commit. */
|
|
27
|
+
signers: {
|
|
28
|
+
path: string;
|
|
29
|
+
at: string;
|
|
30
|
+
active: boolean;
|
|
31
|
+
};
|
|
32
|
+
/** True when the release file's digest is the digest of what it holds. */
|
|
33
|
+
digestMatches: boolean;
|
|
34
|
+
/** `verified` when a cut's signature verifies; `unsigned` when no cut carries one; `unverified` otherwise. */
|
|
35
|
+
status: "verified" | "unsigned" | "unverified";
|
|
36
|
+
/** The cut whose signature verified, or null. */
|
|
37
|
+
verifiedBy: {
|
|
38
|
+
actor: string;
|
|
39
|
+
key: string | null;
|
|
40
|
+
} | null;
|
|
41
|
+
reason: string;
|
|
42
|
+
cuts: Array<Pick<ReleaseCut, "label" | "actor" | "timestamp" | "runId"> & {
|
|
43
|
+
signatureCheck: ReleaseSignatureCheck;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/** Why a release could not be checked at all. */
|
|
47
|
+
export interface ReleaseVerifyError {
|
|
48
|
+
code: "release-not-found" | "release-commit-missing" | "trust-policy-unreadable" | "not-a-git-repository";
|
|
49
|
+
message: string;
|
|
50
|
+
fix?: string;
|
|
51
|
+
}
|
|
52
|
+
/** The exit-1 codes of a release that was checked and did not verify. */
|
|
53
|
+
export declare function verificationCode(v: ReleaseVerification): "release-digest-mismatch" | "release-unsigned" | "release-signature-unverified" | null;
|
|
54
|
+
/** Check the release `ref` names in the member at `cwd`. Reads only the local repository. */
|
|
55
|
+
export declare function verifyRelease(req: {
|
|
56
|
+
cwd: string;
|
|
57
|
+
ref: string;
|
|
58
|
+
}): Promise<ReleaseVerification | {
|
|
59
|
+
error: ReleaseVerifyError;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=release-verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-verify.d.ts","sourceRoot":"","sources":["../../src/lifecycle/release-verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,KAAK,EAAW,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE3D,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IACvD,0EAA0E;IAC1E,aAAa,EAAE,OAAO,CAAC;IACvB,8GAA8G;IAC9G,MAAM,EAAE,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;IAC/C,iDAAiD;IACjD,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IACzD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,GAAG,WAAW,GAAG,OAAO,CAAC,GAAG;QAAE,cAAc,EAAE,qBAAqB,CAAA;KAAE,CAAC,CAAC;CACtH;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,mBAAmB,GAAG,wBAAwB,GAAG,yBAAyB,GAAG,sBAAsB,CAAC;IAC1G,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,mBAAmB,GAAG,yBAAyB,GAAG,kBAAkB,GAAG,8BAA8B,GAAG,IAAI,CAI/I;AAED,6FAA6F;AAC7F,wBAAsB,aAAa,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,mBAAmB,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAiEnI"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant release verify <label|digest>` (#4049): whether a release was
|
|
3
|
+
* signed by a key `.chant/allowed_signers` lists, checked offline.
|
|
4
|
+
*
|
|
5
|
+
* It reads the release and its cuts from the local `chant/lifecycle` branch,
|
|
6
|
+
* and the signers file, its signed history (#2553) and the declaration at the
|
|
7
|
+
* release's own commit, as `chant workspace evidence verify` reads the policy
|
|
8
|
+
* at the revision it is given. It fetches nothing and needs no key. A release
|
|
9
|
+
* verifies when its file's digest is the digest of what it holds and one of
|
|
10
|
+
* its cuts carries a signature in the `chant-release` namespace that verifies
|
|
11
|
+
* for the cut's actor, over the release digest, the label, the actor and the
|
|
12
|
+
* time (`releaseSealPayload`), and, when the declaration at that commit has an
|
|
13
|
+
* `identity.gates.release` entry naming a class, the actor is in it.
|
|
14
|
+
*
|
|
15
|
+
* Reading the signers at the release's commit means a key added later does
|
|
16
|
+
* not make an old release verify, and one removed later does not unmake it.
|
|
17
|
+
*/
|
|
18
|
+
/** The exit-1 codes of a release that was checked and did not verify. */
|
|
19
|
+
export function verificationCode(v) {
|
|
20
|
+
if (!v.digestMatches)
|
|
21
|
+
return "release-digest-mismatch";
|
|
22
|
+
if (v.status === "verified")
|
|
23
|
+
return null;
|
|
24
|
+
return v.status === "unsigned" ? "release-unsigned" : "release-signature-unverified";
|
|
25
|
+
}
|
|
26
|
+
/** Check the release `ref` names in the member at `cwd`. Reads only the local repository. */
|
|
27
|
+
export async function verifyRelease(req) {
|
|
28
|
+
const { resolveRelease, readCuts, verifyReleaseDigest } = await import("./release-store.js");
|
|
29
|
+
const { gitRoot } = await import("../workspace/record-source.js");
|
|
30
|
+
const { resolveBase } = await import("../workspace/trust/provenance.js");
|
|
31
|
+
const { scopeSource } = await import("../workspace/write-scope.js");
|
|
32
|
+
const { releaseRuleFrom } = await import("../workspace/identity.js");
|
|
33
|
+
const { checkReleaseSignature } = await import("../workspace/identity-gate-rule.js");
|
|
34
|
+
const { cwd, ref } = req;
|
|
35
|
+
const top = gitRoot(cwd);
|
|
36
|
+
if (!top)
|
|
37
|
+
return { error: { code: "not-a-git-repository", message: "a release is checked against the signers file at its commit, and this directory is not in a git repository" } };
|
|
38
|
+
const found = await resolveRelease(ref, { cwd });
|
|
39
|
+
if (!found)
|
|
40
|
+
return { error: { code: "release-not-found", message: `no release is labelled or digested "${ref}" in this member`, fix: "Fetch chant/lifecycle (git fetch origin chant/lifecycle:chant/lifecycle), then list them with: chant release list" } };
|
|
41
|
+
const release = found.release;
|
|
42
|
+
const at = resolveBase(top, release.commit).commit;
|
|
43
|
+
if (!at) {
|
|
44
|
+
return {
|
|
45
|
+
error: {
|
|
46
|
+
code: "release-commit-missing",
|
|
47
|
+
message: `the release's commit ${release.commit} is not in this clone, so the signers file at it can't be read`,
|
|
48
|
+
fix: `Fetch it: git fetch origin ${release.commit}`,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const source = scopeSource(cwd, at);
|
|
53
|
+
if (source.policy.problems.length > 0) {
|
|
54
|
+
return { error: { code: "trust-policy-unreadable", message: `the signers at ${at.slice(0, 12)} can't be read: ${source.policy.problems.join("; ")}` } };
|
|
55
|
+
}
|
|
56
|
+
const read = releaseRuleFrom(source);
|
|
57
|
+
// A declaration read from the working tree is not the commit's, so its class does not apply.
|
|
58
|
+
const rule = source.from === "base" ? read.rule : { ...read.rule, class: null };
|
|
59
|
+
const own = (await readCuts({ cwd })).cuts.filter((c) => c.releaseDigest === release.releaseDigest);
|
|
60
|
+
// The check words where it read the signers as "at base"; here that is the release's commit.
|
|
61
|
+
const atCommit = (check) => ({ ...check, reason: check.reason.split(`${rule.signers.signersPath} at base`).join(`${rule.signers.signersPath} at ${at.slice(0, 12)}`) });
|
|
62
|
+
const cuts = own.map((c) => ({
|
|
63
|
+
label: c.label,
|
|
64
|
+
actor: c.actor,
|
|
65
|
+
timestamp: c.timestamp,
|
|
66
|
+
runId: c.runId ?? null,
|
|
67
|
+
signatureCheck: atCommit(checkReleaseSignature(rule, { releaseDigest: c.releaseDigest, label: c.label, actor: c.actor, timestamp: c.timestamp, signature: c.signature })),
|
|
68
|
+
}));
|
|
69
|
+
const verified = cuts.find((c) => c.signatureCheck.status === "verified");
|
|
70
|
+
const digestMatches = verifyReleaseDigest(release);
|
|
71
|
+
const status = verified ? "verified" : cuts.every((c) => c.signatureCheck.status === "unsigned") ? "unsigned" : "unverified";
|
|
72
|
+
const where = `${rule.signers.signersPath} at ${at.slice(0, 12)}`;
|
|
73
|
+
const reason = !digestMatches
|
|
74
|
+
? `the release file's digest ${release.releaseDigest} is not the digest of what it holds, so it was changed after the cut`
|
|
75
|
+
: verified
|
|
76
|
+
? `${verified.signatureCheck.reason}, checked against ${where}`
|
|
77
|
+
: status === "unsigned"
|
|
78
|
+
? `no cut of the release carries a signature`
|
|
79
|
+
: cuts.map((c) => c.signatureCheck.reason).join("; ");
|
|
80
|
+
return {
|
|
81
|
+
releaseDigest: release.releaseDigest,
|
|
82
|
+
label: found.label,
|
|
83
|
+
member: release.member,
|
|
84
|
+
commit: release.commit,
|
|
85
|
+
signers: { path: rule.signers.signersPath, at, active: rule.signers.active },
|
|
86
|
+
digestMatches,
|
|
87
|
+
status: digestMatches ? status : "unverified",
|
|
88
|
+
verifiedBy: digestMatches && verified ? { actor: verified.actor, key: verified.signatureCheck.key } : null,
|
|
89
|
+
reason,
|
|
90
|
+
cuts,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cutting a release from a workstation as from CI (#4027).
|
|
3
|
+
*
|
|
4
|
+
* `chant release cut` runs the same steps wherever it runs. What differs
|
|
5
|
+
* between a laptop and a CI job is read here, from git and the process
|
|
6
|
+
* environment, so the CLI and its tests resolve it the same way:
|
|
7
|
+
*
|
|
8
|
+
* - trunk: the branch `ci.green.branch` names in the declaration at base,
|
|
9
|
+
* else the branch `origin/HEAD` points at, else `main`. The commit must be
|
|
10
|
+
* reachable from `origin/<branch>` as this clone last fetched it, so a
|
|
11
|
+
* commit that is on the local branch only is refused as not pushed. Nothing
|
|
12
|
+
* is fetched: a stale `origin/<branch>` can only refuse a commit that was
|
|
13
|
+
* pushed since, never admit one that wasn't.
|
|
14
|
+
* - a clean tree: no tracked change and no untracked file that git does not
|
|
15
|
+
* ignore, since the compile reads the directory and a new
|
|
16
|
+
* `*.component.ts` would be compiled into the release.
|
|
17
|
+
* - the actor: `--actor`, else the principal the signers file at base lists
|
|
18
|
+
* for the signing key, else the CI's actor, else git's `user.email`, else
|
|
19
|
+
* `$USER`.
|
|
20
|
+
* - the tag: `release/<label>`, or `release/<member>/<label>` in a workspace
|
|
21
|
+
* member, an annotated tag on the commit that is created locally and never
|
|
22
|
+
* pushed by chant.
|
|
23
|
+
*/
|
|
24
|
+
import type { Signer } from "../workspace/trust/trust-files.js";
|
|
25
|
+
export type WorkstationCheckCode = "release-commit-not-on-trunk" | "release-commit-not-pushed" | "release-worktree-dirty" | "release-tag-taken" | "release-tag-invalid";
|
|
26
|
+
/** A refusal before anything is compiled or written. */
|
|
27
|
+
export declare class WorkstationCheckError extends Error {
|
|
28
|
+
readonly code: WorkstationCheckCode;
|
|
29
|
+
readonly fix?: string | undefined;
|
|
30
|
+
constructor(code: WorkstationCheckCode, message: string, fix?: string | undefined);
|
|
31
|
+
}
|
|
32
|
+
/** The remote a release's commit must be pushed to. */
|
|
33
|
+
export declare const RELEASE_REMOTE = "origin";
|
|
34
|
+
/** The trunk branch, and where its name came from. */
|
|
35
|
+
export interface Trunk {
|
|
36
|
+
remote: string;
|
|
37
|
+
branch: string;
|
|
38
|
+
from: "ci.green.branch" | "origin/HEAD" | "default";
|
|
39
|
+
}
|
|
40
|
+
/** The trunk of the repository at `cwd`: `ci.green.branch` when declared, else what `origin/HEAD` names, else `main`. */
|
|
41
|
+
export declare function resolveTrunk(cwd: string, greenBranch: string | null): Trunk;
|
|
42
|
+
/**
|
|
43
|
+
* Refuse `commit` unless it is reachable from `<remote>/<branch>` as this
|
|
44
|
+
* clone knows it. A commit on the local branch only is `release-commit-not-pushed`;
|
|
45
|
+
* any other is `release-commit-not-on-trunk`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function checkOnTrunk(cwd: string, commit: string, trunk: Trunk): void;
|
|
48
|
+
/** Refuse a tree with a tracked change or an untracked file git does not ignore. */
|
|
49
|
+
export declare function checkCleanTree(cwd: string, commit: string): void;
|
|
50
|
+
/** Where the actor came from. */
|
|
51
|
+
export type ActorSource = "flag" | "signer" | "ci" | "git" | "user";
|
|
52
|
+
/** The public key `keyFile` holds or names, as `<type> <base64>`, or null when it can't be read without a passphrase. */
|
|
53
|
+
export declare function publicKeyOf(keyFile: string): string | null;
|
|
54
|
+
/** The one principal `signers` lists for `publicKey`, or null for none or several. */
|
|
55
|
+
export declare function signerPrincipal(publicKey: string, signers: readonly Signer[]): string | null;
|
|
56
|
+
/**
|
|
57
|
+
* Who cut the release, and where the name came from: `--actor`, else the
|
|
58
|
+
* signer the signers file at base lists for the signing key, else the CI's
|
|
59
|
+
* actor (`GITHUB_ACTOR`, `GITLAB_USER_LOGIN`), else git's `user.email`, else
|
|
60
|
+
* `$USER`. Undefined when none is set.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveCutActor(req: {
|
|
63
|
+
cwd: string;
|
|
64
|
+
explicit?: string;
|
|
65
|
+
/** The signing key's public half, when the cut is signed and it could be read. */
|
|
66
|
+
publicKey?: string | null;
|
|
67
|
+
signers?: readonly Signer[];
|
|
68
|
+
env?: NodeJS.ProcessEnv;
|
|
69
|
+
}): {
|
|
70
|
+
actor: string;
|
|
71
|
+
source: ActorSource;
|
|
72
|
+
} | undefined;
|
|
73
|
+
/** The tag a labelled release gets: `release/<label>`, or `release/<member>/<label>` for a workspace member. */
|
|
74
|
+
export declare function releaseTagName(member: string | null, label: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* Whether `tag` can name `commit`: `absent` (the cut creates it), `present`
|
|
77
|
+
* (it already names the commit), or a {@link WorkstationCheckError} for a
|
|
78
|
+
* tag that names another commit or is not a valid tag name.
|
|
79
|
+
*/
|
|
80
|
+
export declare function checkReleaseTag(cwd: string, tag: string, commit: string): "absent" | "present";
|
|
81
|
+
/**
|
|
82
|
+
* Create `tag` on `commit` as an annotated tag whose message names the
|
|
83
|
+
* release. Local only. A clone with no git identity tags as chant, as the ci
|
|
84
|
+
* tags are made.
|
|
85
|
+
*/
|
|
86
|
+
export declare function createReleaseTag(cwd: string, tag: string, commit: string, release: {
|
|
87
|
+
releaseDigest: string;
|
|
88
|
+
label: string;
|
|
89
|
+
member: string | null;
|
|
90
|
+
}): void;
|
|
91
|
+
//# sourceMappingURL=release-workstation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-workstation.d.ts","sourceRoot":"","sources":["../../src/lifecycle/release-workstation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gCAAgC,CAAC;AAE7D,MAAM,MAAM,oBAAoB,GAAG,6BAA6B,GAAG,2BAA2B,GAAG,wBAAwB,GAAG,mBAAmB,GAAG,qBAAqB,CAAC;AAExK,wDAAwD;AACxD,qBAAa,qBAAsB,SAAQ,KAAK;IAE5C,QAAQ,CAAC,IAAI,EAAE,oBAAoB;IAEnC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM;IAHvB,YACW,IAAI,EAAE,oBAAoB,EACnC,OAAO,EAAE,MAAM,EACN,GAAG,CAAC,EAAE,MAAM,YAAA,EAItB;CACF;AAaD,uDAAuD;AACvD,eAAO,MAAM,cAAc,WAAW,CAAC;AAEvC,sDAAsD;AACtD,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,iBAAiB,GAAG,aAAa,GAAG,SAAS,CAAC;CACrD;AAED,yHAAyH;AACzH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAM3E;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAwB5E;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAehE;AAED,iCAAiC;AACjC,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,IAAI,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpE,yHAAyH;AACzH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAmB1D;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAG5F;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,GAAG,SAAS,CAarD;AAED,gHAAgH;AAChH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAY9F;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,CAMzJ"}
|