@intentius/chant 0.124.26 → 0.124.28
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/change-set-graph.d.ts +56 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +128 -0
- package/dist/change-set.d.ts +52 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +35 -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/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +9 -8
- 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 +19 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +28 -0
- 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 +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +14 -3
- package/dist/cli/handlers/pr.d.ts +8 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +148 -24
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +256 -21
- package/dist/cli/handlers/release.d.ts +34 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +203 -0
- package/dist/cli/handlers/run.d.ts +28 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +170 -13
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +88 -13
- 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 +49 -3
- package/dist/cli/registry.d.ts +29 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +29 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +47 -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 +4 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +2 -0
- 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 +42 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +81 -54
- 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 +35 -26
- 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 +15 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +17 -0
- package/dist/components/release-compile.d.ts +133 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +376 -0
- package/dist/components/release-gate.d.ts +125 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +282 -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 +3 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -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/change-set-store.d.ts +67 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +96 -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/gate-change-set.d.ts +69 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +106 -0
- package/dist/lifecycle/gate-ledger.d.ts +49 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/release-cut.d.ts +107 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +273 -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-store.d.ts +190 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +318 -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/activity-registry.d.ts +6 -3
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.js +7 -5
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +14 -7
- 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 +12 -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/run-event-store.d.ts +77 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +204 -0
- package/dist/run-event-stream.d.ts +132 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +238 -0
- package/dist/run-event.d.ts +267 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +207 -0
- package/dist/wave-runtime.mjs +225 -242
- 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 +28 -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/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 +273 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +449 -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 +19 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +1 -1
- package/dist/workspace/declaration.schema.json +12 -0
- 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.d.ts +6 -1
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +51 -1
- 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/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 +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +4 -1
- package/dist/workspace/releases.d.ts +72 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +94 -0
- package/dist/workspace/releases.schema.json +391 -0
- package/dist/workspace/run-event.schema.json +288 -0
- package/dist/workspace/status-gates.d.ts +26 -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 +39 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +48 -1
- package/dist/workspace/status.schema.json +70 -1
- 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/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/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +133 -0
- package/src/change-set.ts +76 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/commands/build.ts +19 -8
- 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/compiled-cli.e2e.test.ts +31 -6
- package/src/cli/handlers/approve-release.test.ts +80 -0
- package/src/cli/handlers/approve.ts +18 -2
- package/src/cli/handlers/build.test.ts +52 -0
- package/src/cli/handlers/build.ts +29 -0
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +48 -0
- package/src/cli/handlers/fan-out.ts +15 -3
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +194 -0
- package/src/cli/handlers/pr.ts +168 -34
- package/src/cli/handlers/promote.test.ts +389 -5
- package/src/cli/handlers/promote.ts +279 -20
- package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
- package/src/cli/handlers/release.e2e.test.ts +85 -0
- package/src/cli/handlers/release.ts +206 -0
- package/src/cli/handlers/run-auto-release.test.ts +71 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +176 -15
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +88 -13
- 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 +37 -3
- package/src/cli/registry.ts +29 -3
- package/src/cli/run-events.ts +54 -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 +21 -0
- package/src/components/auto-release.ts +6 -1
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.ts +129 -56
- package/src/components/fan-out-run.ts +56 -35
- 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 +26 -1
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +445 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +341 -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 +1 -1
- package/src/config.ts +7 -1
- package/src/index.ts +2 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +112 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +133 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +66 -1
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +411 -0
- package/src/lifecycle/release-cut.ts +367 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-store.ts +423 -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/activity-registry.ts +7 -5
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/discover-root.test.ts +39 -0
- package/src/op/discover.ts +15 -8
- 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 +13 -2
- package/src/point-decision.ts +12 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +213 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +306 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +451 -0
- package/src/wave-loop-golden.test.ts +9 -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 +28 -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/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 +624 -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 +12 -0
- package/src/workspace/declaration.ts +22 -2
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent.ts +48 -1
- 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/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +4 -1
- package/src/workspace/reason-codes.test.ts +2 -1
- package/src/workspace/reason-codes.ts +4 -1
- package/src/workspace/releases.schema.json +391 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +122 -0
- package/src/workspace/run-event.schema.json +288 -0
- package/src/workspace/status-contract.test.ts +52 -1
- package/src/workspace/status-gates.ts +56 -1
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +70 -1
- package/src/workspace/status.ts +80 -1
- 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/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,190 @@
|
|
|
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 { type LedgerLocation } from "./git.js";
|
|
34
|
+
import type { RunOrigin } from "./release-ledger.js";
|
|
35
|
+
/** The directory on `chant/lifecycle` that holds releases, cuts and template bytes. */
|
|
36
|
+
export declare const RELEASES_DIR = "_releases";
|
|
37
|
+
/** The cut log inside {@link RELEASES_DIR}. */
|
|
38
|
+
export declare const CUTS_FILE = "cuts.jsonl";
|
|
39
|
+
/** The directory inside {@link RELEASES_DIR} that holds template bytes by content digest. */
|
|
40
|
+
export declare const TEMPLATES_SUBDIR = "templates";
|
|
41
|
+
/** The `kind` a release digest is computed under, so it can never equal a plan digest of another kind. */
|
|
42
|
+
export declare const RELEASE_DIGEST_KIND = "release";
|
|
43
|
+
/** An artifact built once from the release's commit: an `image` or `asset` archive entry. */
|
|
44
|
+
export interface ReleaseArtifact {
|
|
45
|
+
component: string;
|
|
46
|
+
kind: "image" | "asset";
|
|
47
|
+
/** The archive path the build step wrote. */
|
|
48
|
+
path: string;
|
|
49
|
+
digest: string;
|
|
50
|
+
}
|
|
51
|
+
/** One template of a target: a file a component's deploy reads, compiled for the target's environment. */
|
|
52
|
+
export interface ReleaseTemplate {
|
|
53
|
+
/** The component whose deploy reads it, or null for a project with no components. */
|
|
54
|
+
component: string | null;
|
|
55
|
+
/** The path the deploy step names, relative to the member's directory. */
|
|
56
|
+
path: string;
|
|
57
|
+
/** `sha256:<hex>` of the file's bytes. */
|
|
58
|
+
digest: string;
|
|
59
|
+
}
|
|
60
|
+
/** One resolved build parameter of a target. */
|
|
61
|
+
export interface ReleaseParam {
|
|
62
|
+
name: string;
|
|
63
|
+
value: string | number | boolean;
|
|
64
|
+
/** Where the value came from: `cli`, `params-file`, `env` or `default`. Outside the digest. */
|
|
65
|
+
source: string;
|
|
66
|
+
}
|
|
67
|
+
/** What one environment compiles to. */
|
|
68
|
+
export interface ReleaseTarget {
|
|
69
|
+
env: string;
|
|
70
|
+
templates: ReleaseTemplate[];
|
|
71
|
+
params: ReleaseParam[];
|
|
72
|
+
}
|
|
73
|
+
/** The release file, `_releases/<releaseDigest>.json`. */
|
|
74
|
+
export interface Release {
|
|
75
|
+
version: 1;
|
|
76
|
+
releaseDigest: string;
|
|
77
|
+
/** The workspace member, its path of names joined with `/` when nested, or null outside a workspace. */
|
|
78
|
+
member: string | null;
|
|
79
|
+
/** The full sha of the green commit. */
|
|
80
|
+
commit: string;
|
|
81
|
+
artifactDigests: ReleaseArtifact[];
|
|
82
|
+
targets: ReleaseTarget[];
|
|
83
|
+
}
|
|
84
|
+
/** One line of `_releases/cuts.jsonl`. */
|
|
85
|
+
export interface ReleaseCut {
|
|
86
|
+
version: 1;
|
|
87
|
+
releaseDigest: string;
|
|
88
|
+
/** The human name of the release, such as `v1.4.0`, or null for a cut with none. */
|
|
89
|
+
label: string | null;
|
|
90
|
+
actor: string;
|
|
91
|
+
timestamp: string;
|
|
92
|
+
/** The CI run the cut ran in, or null outside CI. */
|
|
93
|
+
runOrigin: RunOrigin | null;
|
|
94
|
+
/**
|
|
95
|
+
* The cut's signature (#4031): an armored ssh signature by the actor's key
|
|
96
|
+
* in the `chant-release` namespace over the release digest, the label, the
|
|
97
|
+
* actor and the time, one per line (`releaseSealPayload`). Null for an
|
|
98
|
+
* unsigned cut, and on every cut written before 0.124.28.
|
|
99
|
+
*/
|
|
100
|
+
signature: string | null;
|
|
101
|
+
}
|
|
102
|
+
/** The parts of a release a digest is computed from. */
|
|
103
|
+
export type ReleaseSubject = Pick<Release, "member" | "commit" | "artifactDigests" | "targets">;
|
|
104
|
+
/** 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. */
|
|
105
|
+
export declare function normalizeRelease<T extends ReleaseSubject>(release: T): T;
|
|
106
|
+
/** The hash input of ws-104 question 3: a param's `source` is left out. */
|
|
107
|
+
export declare function releaseHashInput(release: ReleaseSubject): unknown;
|
|
108
|
+
/** `computePlanDigest("release", input)` over {@link releaseHashInput}. */
|
|
109
|
+
export declare function computeReleaseDigest(release: ReleaseSubject): string;
|
|
110
|
+
/** A release with its digest computed and its lists in order. */
|
|
111
|
+
export declare function makeRelease(subject: ReleaseSubject): Release;
|
|
112
|
+
/** Whether a release's own `releaseDigest` is the digest of what it holds. */
|
|
113
|
+
export declare function verifyReleaseDigest(release: Release): boolean;
|
|
114
|
+
/** Why a label can't be used, or undefined when it can. */
|
|
115
|
+
export declare function invalidLabel(label: string): string | undefined;
|
|
116
|
+
/** Thrown when a cut would give a label two digests, or a digest two labels. */
|
|
117
|
+
export declare class ReleaseLabelError extends Error {
|
|
118
|
+
readonly code: "release-label-taken" | "release-already-labelled" | "release-label-invalid";
|
|
119
|
+
constructor(code: "release-label-taken" | "release-already-labelled" | "release-label-invalid", message: string);
|
|
120
|
+
}
|
|
121
|
+
/** A release's template bytes, as the cut read them. */
|
|
122
|
+
export interface TemplateContent {
|
|
123
|
+
digest: string;
|
|
124
|
+
content: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Store a release and its template bytes in one commit. A release already
|
|
128
|
+
* stored under its digest is never rewritten, and a template already stored
|
|
129
|
+
* is not written again. Does not push.
|
|
130
|
+
*/
|
|
131
|
+
export declare function persistRelease(release: Release, templates: readonly TemplateContent[], opts?: {
|
|
132
|
+
cwd?: string;
|
|
133
|
+
}): Promise<{
|
|
134
|
+
commit: string | null;
|
|
135
|
+
written: boolean;
|
|
136
|
+
}>;
|
|
137
|
+
/** Parse a stored release file, or null when it is not a release. */
|
|
138
|
+
export declare function parseRelease(text: string): Release | null;
|
|
139
|
+
/** Read one release by digest. Null when none is stored, or when the file is not a release whose content hashes to its digest. */
|
|
140
|
+
export declare function readRelease(digest: string, opts?: LedgerLocation): Promise<Release | null>;
|
|
141
|
+
/** Every stored release, and how many release files could not be read as one. */
|
|
142
|
+
export declare function readAllReleases(opts?: LedgerLocation): Promise<{
|
|
143
|
+
releases: Release[];
|
|
144
|
+
malformed: number;
|
|
145
|
+
}>;
|
|
146
|
+
/** Parse the lines of `cuts.jsonl`. A line that is not a cut is counted and skipped. */
|
|
147
|
+
export declare function parseCuts(text: string | null): {
|
|
148
|
+
cuts: ReleaseCut[];
|
|
149
|
+
malformed: number;
|
|
150
|
+
};
|
|
151
|
+
/** Every cut, oldest first. */
|
|
152
|
+
export declare function readCuts(opts?: LedgerLocation): Promise<{
|
|
153
|
+
cuts: ReleaseCut[];
|
|
154
|
+
malformed: number;
|
|
155
|
+
}>;
|
|
156
|
+
/** The label each digest carries, from its labelled cuts. */
|
|
157
|
+
export declare function labelsByDigest(cuts: readonly ReleaseCut[]): Map<string, string>;
|
|
158
|
+
/** What {@link appendCut} did. */
|
|
159
|
+
export type CutOutcome = {
|
|
160
|
+
appended: true;
|
|
161
|
+
cut: ReleaseCut;
|
|
162
|
+
}
|
|
163
|
+
/** The digest was already cut; nothing was written. `label` is the label it carries, or null. */
|
|
164
|
+
| {
|
|
165
|
+
appended: false;
|
|
166
|
+
label: string | null;
|
|
167
|
+
};
|
|
168
|
+
/**
|
|
169
|
+
* Append a cut for `releaseDigest`. A label names one digest and a digest
|
|
170
|
+
* takes at most one label (ws-104 question 3): cutting a digest again is a
|
|
171
|
+
* no-op that returns its label, giving an unlabelled digest a label appends a
|
|
172
|
+
* cut, and cutting a digest under a label other than its own, or under a
|
|
173
|
+
* label another digest has, throws {@link ReleaseLabelError}. Does not push.
|
|
174
|
+
*/
|
|
175
|
+
export declare function appendCut(input: Omit<ReleaseCut, "version" | "signature"> & {
|
|
176
|
+
signature?: string | null;
|
|
177
|
+
}, opts?: {
|
|
178
|
+
cwd?: string;
|
|
179
|
+
}): Promise<CutOutcome>;
|
|
180
|
+
/** A template's bytes by digest, or null when none is stored. */
|
|
181
|
+
export declare function readReleaseTemplate(digest: string, opts?: LedgerLocation): Promise<string | null>;
|
|
182
|
+
/**
|
|
183
|
+
* Find a release by its digest or its label. Null when neither names one.
|
|
184
|
+
* A string that names a digest is read as a digest first.
|
|
185
|
+
*/
|
|
186
|
+
export declare function resolveRelease(ref: string, opts?: LedgerLocation): Promise<{
|
|
187
|
+
release: Release;
|
|
188
|
+
label: string | null;
|
|
189
|
+
} | null>;
|
|
190
|
+
//# sourceMappingURL=release-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"release-store.d.ts","sourceRoot":"","sources":["../../src/lifecycle/release-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,318 @@
|
|
|
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
|
+
if (!ok) {
|
|
216
|
+
malformed++;
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
cuts.push({ ...c, signature: c.signature ?? null });
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
malformed++;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return { cuts, malformed };
|
|
226
|
+
}
|
|
227
|
+
/** Every cut, oldest first. */
|
|
228
|
+
export async function readCuts(opts) {
|
|
229
|
+
return parseCuts(await readBlobFromPath(RELEASES_DIR, CUTS_FILE, opts));
|
|
230
|
+
}
|
|
231
|
+
/** The label each digest carries, from its labelled cuts. */
|
|
232
|
+
export function labelsByDigest(cuts) {
|
|
233
|
+
const out = new Map();
|
|
234
|
+
for (const c of cuts)
|
|
235
|
+
if (c.label !== null && !out.has(c.releaseDigest))
|
|
236
|
+
out.set(c.releaseDigest, c.label);
|
|
237
|
+
return out;
|
|
238
|
+
}
|
|
239
|
+
const CUT_RETRY_ATTEMPTS = 5;
|
|
240
|
+
/**
|
|
241
|
+
* Append a cut for `releaseDigest`. A label names one digest and a digest
|
|
242
|
+
* takes at most one label (ws-104 question 3): cutting a digest again is a
|
|
243
|
+
* no-op that returns its label, giving an unlabelled digest a label appends a
|
|
244
|
+
* cut, and cutting a digest under a label other than its own, or under a
|
|
245
|
+
* label another digest has, throws {@link ReleaseLabelError}. Does not push.
|
|
246
|
+
*/
|
|
247
|
+
export async function appendCut(input, opts) {
|
|
248
|
+
if (input.label !== null) {
|
|
249
|
+
const why = invalidLabel(input.label);
|
|
250
|
+
if (why)
|
|
251
|
+
throw new ReleaseLabelError("release-label-invalid", why);
|
|
252
|
+
}
|
|
253
|
+
let lastErr;
|
|
254
|
+
for (let attempt = 1; attempt <= CUT_RETRY_ATTEMPTS; attempt++) {
|
|
255
|
+
const priorSha = await readPathSha(RELEASES_DIR, CUTS_FILE, opts);
|
|
256
|
+
const existing = priorSha ? await readBlobBySha(priorSha, opts) : null;
|
|
257
|
+
const { cuts } = parseCuts(existing);
|
|
258
|
+
const labels = labelsByDigest(cuts);
|
|
259
|
+
const own = labels.get(input.releaseDigest);
|
|
260
|
+
const alreadyCut = cuts.some((c) => c.releaseDigest === input.releaseDigest);
|
|
261
|
+
if (own !== undefined) {
|
|
262
|
+
if (input.label === null || input.label === own)
|
|
263
|
+
return { appended: false, label: own };
|
|
264
|
+
throw new ReleaseLabelError("release-already-labelled", `release ${input.releaseDigest} is already labelled "${own}", and a release takes one label`);
|
|
265
|
+
}
|
|
266
|
+
if (input.label === null && alreadyCut)
|
|
267
|
+
return { appended: false, label: null };
|
|
268
|
+
if (input.label !== null) {
|
|
269
|
+
for (const [digest, label] of labels) {
|
|
270
|
+
if (label === input.label) {
|
|
271
|
+
throw new ReleaseLabelError("release-label-taken", `the label "${input.label}" already names release ${digest}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
const cut = {
|
|
276
|
+
version: 1,
|
|
277
|
+
releaseDigest: input.releaseDigest,
|
|
278
|
+
label: input.label,
|
|
279
|
+
actor: input.actor,
|
|
280
|
+
timestamp: input.timestamp,
|
|
281
|
+
runOrigin: input.runOrigin,
|
|
282
|
+
signature: input.signature ?? null,
|
|
283
|
+
};
|
|
284
|
+
const content = `${existing ? `${existing.replace(/\n$/, "")}\n` : ""}${JSON.stringify(cut, sortedJsonReplacer)}\n`;
|
|
285
|
+
try {
|
|
286
|
+
const dir = await ledgerDir(RELEASES_DIR, opts);
|
|
287
|
+
await writeLedgerFiles([{ path: `${dir}/${CUTS_FILE}`, content, expectPriorSha: priorSha }], "Release cut", opts);
|
|
288
|
+
return { appended: true, cut };
|
|
289
|
+
}
|
|
290
|
+
catch (err) {
|
|
291
|
+
if (!(err instanceof RefCASConflictError))
|
|
292
|
+
throw err;
|
|
293
|
+
lastErr = err;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
throw lastErr;
|
|
297
|
+
}
|
|
298
|
+
/** A template's bytes by digest, or null when none is stored. */
|
|
299
|
+
export async function readReleaseTemplate(digest, opts) {
|
|
300
|
+
return readBlobFromPath(`${RELEASES_DIR}/${TEMPLATES_SUBDIR}`, templateFile(digest), opts);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Find a release by its digest or its label. Null when neither names one.
|
|
304
|
+
* A string that names a digest is read as a digest first.
|
|
305
|
+
*/
|
|
306
|
+
export async function resolveRelease(ref, opts) {
|
|
307
|
+
const { cuts } = await readCuts(opts);
|
|
308
|
+
const labels = labelsByDigest(cuts);
|
|
309
|
+
let digest = ref.includes(":") ? ref : undefined;
|
|
310
|
+
if (!digest)
|
|
311
|
+
for (const [d, label] of labels)
|
|
312
|
+
if (label === ref)
|
|
313
|
+
digest = d;
|
|
314
|
+
if (!digest)
|
|
315
|
+
return null;
|
|
316
|
+
const release = await readRelease(digest, opts);
|
|
317
|
+
return release ? { release, label: labels.get(digest) ?? null } : null;
|
|
318
|
+
}
|
|
@@ -26,6 +26,10 @@ export declare function buildRunRecord(config: Pick<OpConfig, "name" | "labels">
|
|
|
26
26
|
steward?: string;
|
|
27
27
|
/** The gate the run resumed at, for a run that resumed (#3947). */
|
|
28
28
|
resumed?: OpRunRecord["resumed"];
|
|
29
|
+
/** The commit the run built from (#4020). */
|
|
30
|
+
gitSha?: string;
|
|
31
|
+
/** The CI run the Op ran in (#4020), when it ran in CI. */
|
|
32
|
+
runOrigin?: OpRunRecord["runOrigin"];
|
|
29
33
|
/** Each phase's wall-clock time, by name, as the executor measured it. */
|
|
30
34
|
phaseDurations?: Record<string, number>;
|
|
31
35
|
}): OpRunRecordInput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/run-ledger.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAqC,MAAM,eAAe,CAAC;AAStG,0EAA0E;AAC1E,eAAO,MAAM,eAAe,UAAU,CAAC;AAMvC,8FAA8F;AAC9F,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,CAEjE;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC,EACzC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,GACA,gBAAgB,
|
|
1
|
+
{"version":3,"file":"run-ledger.d.ts","sourceRoot":"","sources":["../../src/lifecycle/run-ledger.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAqC,MAAM,eAAe,CAAC;AAStG,0EAA0E;AAC1E,eAAO,MAAM,eAAe,UAAU,CAAC;AAMvC,8FAA8F;AAC9F,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,CAEjE;AAQD;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,CAAC,EACzC,OAAO,EAAE,SAAS,UAAU,EAAE,EAC9B,KAAK,EAAE;IACL,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3B,KAAK,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,WAAW,CAAC,SAAS,CAAC,CAAC;IACjC,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC;IACrC,0EAA0E;IAC1E,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC,GACA,gBAAgB,CAiDlB;AAED;;;;;;;;;GASG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,gBAAgB,EACvB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC,CAsBlD;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,EAAE,EAAE,MAAM,EACV,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA4BxD"}
|
|
@@ -114,6 +114,8 @@ export function buildRunRecord(config, records, times) {
|
|
|
114
114
|
...(times.point ? { point: times.point } : {}),
|
|
115
115
|
...(times.steward ? { steward: times.steward } : {}),
|
|
116
116
|
...(times.resumed ? { resumed: times.resumed } : {}),
|
|
117
|
+
...(times.gitSha ? { gitSha: times.gitSha } : {}),
|
|
118
|
+
...(times.runOrigin ? { runOrigin: times.runOrigin } : {}),
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-coverage.d.ts","sourceRoot":"","sources":["../../../src/op/activities/change-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC;IACjG,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,SAAS,CAAC,CAAC;CAClE;AAED,wBAAsB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,oBAAoB,CAAC,
|
|
1
|
+
{"version":3,"file":"change-coverage.d.ts","sourceRoot":"","sources":["../../../src/op/activities/change-coverage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,WAAW,kBAAkB;IACjC,gFAAgF;IAChF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,QAAQ,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;IAClC,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC,EAAE,CAAC;IACjG,OAAO,EAAE,OAAO,CAAC,eAAe,EAAE;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC,SAAS,CAAC,CAAC;CAClE;AAED,wBAAsB,cAAc,CAAC,IAAI,GAAE,kBAAuB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAsBjG"}
|
|
@@ -34,7 +34,9 @@ export async function changeCoverage(args = {}) {
|
|
|
34
34
|
findings: doc.findings.map(({ code, path, message, severity, records, triage }) => ({ code, path, message, severity, records, triage })),
|
|
35
35
|
summary: doc.summary,
|
|
36
36
|
};
|
|
37
|
-
if (!doc.ok)
|
|
38
|
-
|
|
37
|
+
if (!doc.ok) {
|
|
38
|
+
const all = [...doc.findings.map((f) => `${f.code} ${f.path}`), ...(doc.links?.findings ?? []).map((f) => `${f.code} ${f.commit.slice(0, 8)}`)];
|
|
39
|
+
throw new Error(`changeCoverage: ${all.length} change ${all.length === 1 ? "finding" : "findings"} at severity fail: ${all.join(", ")}`);
|
|
40
|
+
}
|
|
39
41
|
return result;
|
|
40
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-release.d.ts","sourceRoot":"","sources":["../../../src/op/activities/source-release.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;
|
|
1
|
+
{"version":3,"file":"source-release.d.ts","sourceRoot":"","sources":["../../../src/op/activities/source-release.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,EAAmC,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIxF,MAAM,WAAW,iBAAiB;IAChC,mGAAmG;IACnG,IAAI,EAAE,MAAM,CAAC;IACb,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,mBAAmB,GAAG,aAAa,CAAC;AAEhD,gFAAgF;AAChF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAEzF;AAID,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAGvE;AAED,qDAAqD;AACrD,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,iBAAiB,CAAC,CASnF;AAED,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAM3G;AAID,MAAM,WAAW,iBAAiB;IAChC,wGAAwG;IACxG,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,2EAA2E;IAC3E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,mEAAmE;AACnE,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAmDzF"}
|
|
@@ -60,15 +60,18 @@ export async function releaseRecord(args) {
|
|
|
60
60
|
const component = plan.component;
|
|
61
61
|
const env = plan.env;
|
|
62
62
|
const gitSha = plan.gitSha;
|
|
63
|
-
const { readReleaseLedger, latestPerComponent, appendReleaseRecord, resolveRunId } = await import("../../lifecycle/release-ledger.js");
|
|
63
|
+
const { readReleaseLedger, latestPerComponent, appendReleaseRecord, resolveRunId, admissionOf } = await import("../../lifecycle/release-ledger.js");
|
|
64
64
|
const { persistReleasePlan } = await import("../../lifecycle/plan-ledger.js");
|
|
65
65
|
const { pushLifecycle } = await import("../../lifecycle/git.js");
|
|
66
66
|
let approver = null;
|
|
67
|
+
let admission;
|
|
67
68
|
if (args.approval) {
|
|
68
69
|
const { readGateLedger } = await import("../../lifecycle/gate-ledger.js");
|
|
69
70
|
const { resolutions } = await readGateLedger(args.approval.op, opts);
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
// #4020: the approver and the gate resolution that admitted the release.
|
|
72
|
+
const admitted = admissionOf(resolutions, { ...args.approval, planDigest: plan.digest });
|
|
73
|
+
approver = admitted?.approver ?? null;
|
|
74
|
+
admission = admitted?.admission;
|
|
72
75
|
}
|
|
73
76
|
const latest = latestPerComponent((await readReleaseLedger(env, opts)).records).get(component);
|
|
74
77
|
if (latest?.digest === plan.digest && latest.gitSha === gitSha) {
|
|
@@ -91,6 +94,7 @@ export async function releaseRecord(args) {
|
|
|
91
94
|
timestamp: new Date().toISOString(),
|
|
92
95
|
actor,
|
|
93
96
|
...(approver ? { approver } : {}),
|
|
97
|
+
...(admission ? { admission } : {}),
|
|
94
98
|
}, opts);
|
|
95
99
|
await pushLifecycle(opts);
|
|
96
100
|
return { recorded: true, digest: plan.digest, env, component, approver };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-rollback.d.ts","sourceRoot":"","sources":["../../../src/op/activities/source-rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"source-rollback.d.ts","sourceRoot":"","sources":["../../../src/op/activities/source-rollback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,OAAO,KAAK,EAAoB,aAAa,EAAc,MAAM,gCAAgC,CAAC;AAMlG,MAAM,WAAW,uBAAuB;IACtC,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ,2GAA2G;IAC3G,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,iHAAiH;IACjH,EAAE,EAAE,MAAM,CAAC;IACX,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,aAAa,EAAE,MAAM,CAAC;IACtB,uCAAuC;IACvC,GAAG,EAAE,MAAM,CAAC;CACb;AAKD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,aAAa,EAAE,EACxB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,EAAE,CAAC,EAAE,MAAM,GACV;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,aAAa,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAmBxE;AAED,kIAAkI;AAClI,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,yBAAyB,CAAC,CA0D3G;AAID,MAAM,WAAW,yBAAyB;IACxC,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,2BAA2B;IAC1C,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAC;IAClB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,qDAAqD;AACrD,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,yBAAyB,GAAG,OAAO,CAAC,2BAA2B,CAAC,CAkDjH"}
|
|
@@ -131,14 +131,17 @@ export async function releaseRollbackRecord(args) {
|
|
|
131
131
|
const component = plan.component;
|
|
132
132
|
const env = plan.env;
|
|
133
133
|
const gitSha = plan.gitSha;
|
|
134
|
-
const { readReleaseLedger, latestPerComponent, appendReleaseRecord, resolveRunId } = await import("../../lifecycle/release-ledger.js");
|
|
134
|
+
const { readReleaseLedger, latestPerComponent, appendReleaseRecord, resolveRunId, admissionOf } = await import("../../lifecycle/release-ledger.js");
|
|
135
135
|
const { pushLifecycle } = await import("../../lifecycle/git.js");
|
|
136
136
|
let approver = null;
|
|
137
|
+
let admission;
|
|
137
138
|
if (args.approval) {
|
|
138
139
|
const { readGateLedger } = await import("../../lifecycle/gate-ledger.js");
|
|
139
140
|
const { resolutions } = await readGateLedger(args.approval.op, opts);
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
// #4020: the approver and the gate resolution that admitted the release.
|
|
142
|
+
const admitted = admissionOf(resolutions, { ...args.approval, planDigest: plan.digest });
|
|
143
|
+
approver = admitted?.approver ?? null;
|
|
144
|
+
admission = admitted?.admission;
|
|
142
145
|
}
|
|
143
146
|
const latest = latestPerComponent((await readReleaseLedger(env, opts)).records.filter((r) => r.component === component)).get(component);
|
|
144
147
|
if (latest?.digest === rollback.to && latest.restores?.runId === rollback.restores.runId && latest.restores.timestamp === rollback.restores.timestamp) {
|
|
@@ -158,6 +161,7 @@ export async function releaseRollbackRecord(args) {
|
|
|
158
161
|
timestamp: new Date().toISOString(),
|
|
159
162
|
actor,
|
|
160
163
|
...(approver ? { approver } : {}),
|
|
164
|
+
...(admission ? { admission } : {}),
|
|
161
165
|
restores: rollback.restores,
|
|
162
166
|
}, opts);
|
|
163
167
|
await pushLifecycle(opts);
|