@intentius/chant 0.124.28 → 0.124.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +8 -1
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +4 -2
- package/dist/change-set.d.ts +10 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +4 -3
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +6 -3
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +45 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +15 -4
- package/dist/cli/handlers/fan-out.d.ts +9 -0
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +40 -3
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +11 -3
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +134 -26
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +210 -33
- package/dist/cli/handlers/release.d.ts +62 -5
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +414 -28
- package/dist/cli/handlers/run.d.ts +11 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +67 -6
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +61 -10
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +24 -2
- package/dist/cli/registry.d.ts +15 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +3 -2
- package/dist/cli/run-events.d.ts.map +1 -1
- package/dist/cli/run-events.js +5 -3
- package/dist/components/auto-release.d.ts +2 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +1 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts +8 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +35 -0
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +50 -0
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- package/dist/components/promote.d.ts +23 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +60 -2
- package/dist/components/release-compile.d.ts +9 -0
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +9 -3
- package/dist/components/release-gate.d.ts +11 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +10 -4
- package/dist/components/wave-plan.d.ts +5 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +10 -2
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +2 -1
- package/dist/lifecycle/change-set-store.d.ts.map +1 -1
- package/dist/lifecycle/change-set-store.js +2 -1
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +10 -1
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
- package/dist/lifecycle/gate-change-set.js +35 -3
- package/dist/lifecycle/gate-ledger.d.ts +2 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +48 -1
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +116 -23
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +12 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +4 -2
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +29 -2
- package/dist/run-event-store.d.ts.map +1 -1
- package/dist/run-event-store.js +50 -3
- package/dist/run-event-stream.d.ts +2 -1
- package/dist/run-event-stream.d.ts.map +1 -1
- package/dist/run-event-stream.js +5 -1
- package/dist/run-event.d.ts +19 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/run-event.js +2 -0
- package/dist/wave-runtime.mjs +260 -229
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/change-set.schema.json +5 -1
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green.d.ts +8 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +10 -3
- package/dist/workspace/declaration.d.ts +16 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +26 -0
- package/dist/workspace/declaration.schema.json +64 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +30 -6
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +141 -21
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/reason-codes.d.ts +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +3 -0
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +9 -0
- package/dist/workspace/releases.d.ts.map +1 -1
- package/dist/workspace/releases.js +9 -0
- package/dist/workspace/releases.schema.json +123 -1
- package/dist/workspace/run-event.schema.json +10 -3
- package/dist/workspace/status-gates.d.ts +8 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +78 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +170 -3
- package/dist/workspace/status.schema.json +198 -1
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.ts +10 -2
- package/src/change-set.ts +14 -3
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +6 -3
- package/src/cli/handlers/approve-release.test.ts +5 -1
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +47 -0
- package/src/cli/handlers/build.test.ts +15 -0
- package/src/cli/handlers/build.ts +17 -4
- package/src/cli/handlers/fan-out.test.ts +180 -1
- package/src/cli/handlers/fan-out.ts +38 -3
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/pr.test.ts +148 -1
- package/src/cli/handlers/pr.ts +139 -28
- package/src/cli/handlers/promote.test.ts +280 -2
- package/src/cli/handlers/promote.ts +210 -34
- package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +146 -32
- package/src/cli/handlers/release.ts +415 -28
- package/src/cli/handlers/run-auto-release.test.ts +9 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.ts +69 -5
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +61 -10
- package/src/cli/parse-args.ts +19 -2
- package/src/cli/registry.ts +15 -3
- package/src/cli/run-events.ts +5 -2
- package/src/components/auto-release.test.ts +9 -0
- package/src/components/auto-release.ts +3 -0
- package/src/components/capability.ts +9 -0
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +36 -0
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +48 -0
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/promote.ts +68 -2
- package/src/components/release-compile.ts +17 -3
- package/src/components/release-gate.ts +18 -4
- package/src/components/wave-plan.ts +15 -2
- package/src/config.ts +8 -0
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.ts +2 -1
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.ts +37 -6
- package/src/lifecycle/gate-ledger.ts +2 -2
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +74 -2
- package/src/lifecycle/release-cut.ts +158 -22
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +15 -4
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.ts +63 -3
- package/src/run-event-stream.ts +4 -1
- package/src/run-event.ts +21 -1
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/change-set.schema.json +5 -1
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green.ts +14 -3
- package/src/workspace/declaration.schema.json +64 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +38 -0
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +148 -25
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/read-contract.e2e.test.ts +2 -1
- package/src/workspace/reason-codes.test.ts +5 -0
- package/src/workspace/reason-codes.ts +3 -0
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +123 -1
- package/src/workspace/releases.ts +11 -0
- package/src/workspace/run-event.schema.json +10 -3
- package/src/workspace/status-contract.test.ts +29 -0
- package/src/workspace/status-gates.ts +8 -0
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status.schema.json +198 -1
- package/src/workspace/status.ts +254 -3
- package/src/workspace/wip.ts +11 -2
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The why chain of a region (#4023): for each commit the intent walk read,
|
|
3
|
+
* the work item it names, its ci tag, the release that shipped it, the gate
|
|
4
|
+
* resolutions that admitted that release and the deploy records per
|
|
5
|
+
* environment. "Who asked for this, and who let it ship" in one read.
|
|
6
|
+
*
|
|
7
|
+
* Every link is made by a record that already exists, and each one states
|
|
8
|
+
* that record in `source`, so a reader can follow it:
|
|
9
|
+
*
|
|
10
|
+
* - work item: a `Chant-Record` or `Chant-Lease` trailer, judged as #4029's
|
|
11
|
+
* `judgeCommitLink` judges it. A commit in a work item's window (the
|
|
12
|
+
* intent graph's `worked` bucket) is listed apart, as a window match, and
|
|
13
|
+
* never links it.
|
|
14
|
+
* - ci tag: `ci/green/<sha>` or `ci/revoked/<sha>`, judged as `release cut`
|
|
15
|
+
* judges it (#4031, `judgedCiTags`).
|
|
16
|
+
* - release: `_releases/<digest>.json`, whose `commit` is the commit itself
|
|
17
|
+
* (`cut-from`) or one that has it in its history (`contains`).
|
|
18
|
+
* - gates and deploys: `joinRelease` (#4043), and a deploy's `admission`
|
|
19
|
+
* read back to the resolution in `_gates/<op>.jsonl` (#4020).
|
|
20
|
+
* - the plan a gate bound: the change set stored under its `planDigest`
|
|
21
|
+
* (#4022), and a deploy's release plan in `_plans/` (ws-055).
|
|
22
|
+
*
|
|
23
|
+
* A commit is "contained" by a later commit when that commit has it as an
|
|
24
|
+
* ancestor in the history read. For each commit the chain names the releases
|
|
25
|
+
* cut from it, or failing those the first release cut after it, and the first
|
|
26
|
+
* deploy per environment that shipped it, so the document stays the size of
|
|
27
|
+
* the walk. Everything is read from local git: the tags and the local
|
|
28
|
+
* `chant/lifecycle` branch. Nothing is fetched.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { execFileSync } from "node:child_process";
|
|
32
|
+
import type { GateResolutionRecord } from "../lifecycle/gate-ledger";
|
|
33
|
+
import type { ReleaseRecord, RunOrigin } from "../lifecycle/release-ledger";
|
|
34
|
+
import type { JoinedRelease } from "../lifecycle/release-join";
|
|
35
|
+
import type { ReleasePlan } from "../lifecycle/plan-ledger";
|
|
36
|
+
import type { Release, ReleaseCut } from "../lifecycle/release-store";
|
|
37
|
+
import type { CiTags, CountedCiTag, IgnoredCiTag, RevokedTagView } from "./ci-green";
|
|
38
|
+
import type { CommitLinkProblem, CommitLinkState, WorkIndex } from "./commit-links";
|
|
39
|
+
import type { CiGreen } from "./declaration";
|
|
40
|
+
import type { CommitTrailerJoins } from "./trailer-joins";
|
|
41
|
+
|
|
42
|
+
/** Where a link comes from: the record (`ref`), and the field of it whose `value` made the join. */
|
|
43
|
+
export interface TraceSource {
|
|
44
|
+
/** A commit sha, a tag ref (`refs/tags/...`), a record file, or a path on `chant/lifecycle` (`chant/lifecycle:<path>`). */
|
|
45
|
+
ref: string;
|
|
46
|
+
/** The trailer, field or edge that joins, such as `Chant-Record`, `commit`, `releaseDigest`, `admission` or `window`. */
|
|
47
|
+
field: string;
|
|
48
|
+
/** Its value, as the record holds it. */
|
|
49
|
+
value: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A work item a commit links to, or falls in the window of. */
|
|
53
|
+
export interface TraceWork {
|
|
54
|
+
/** `<kind>:<id>`. */
|
|
55
|
+
id: string;
|
|
56
|
+
/** The work node in `nodes`, or null when no kind read has the record. */
|
|
57
|
+
node: string | null;
|
|
58
|
+
source: TraceSource;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TraceTagView {
|
|
62
|
+
tag: string;
|
|
63
|
+
tagged: string | null;
|
|
64
|
+
/** The runner whose statement the tag carries, or null for a tag that counts unsigned (#4031). */
|
|
65
|
+
signer: string | null;
|
|
66
|
+
source: TraceSource;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A commit's ci tags as readers count them (#4031, ws-103). */
|
|
70
|
+
export interface TraceTag {
|
|
71
|
+
/** `revoked` when a revoked tag counts, else `green` when a green tag counts, else `none`. */
|
|
72
|
+
state: "green" | "revoked" | "none";
|
|
73
|
+
green: TraceTagView | null;
|
|
74
|
+
revoked: (TraceTagView & Pick<RevokedTagView, "phase" | "run">) | null;
|
|
75
|
+
/** Tags on the commit that exist and don't count. */
|
|
76
|
+
ignored: IgnoredCiTag[];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One gate resolution: who let it through, on which channel, when, and for which plan. */
|
|
80
|
+
export interface TraceGate {
|
|
81
|
+
op: string;
|
|
82
|
+
gate: string;
|
|
83
|
+
environment: string | null;
|
|
84
|
+
resolvedBy: string;
|
|
85
|
+
relayedBy: string | null;
|
|
86
|
+
/** `cli`, `mcp` or `acp`, the channel the resolution was authored on (#2384), or null when the record doesn't say. */
|
|
87
|
+
channel: string | null;
|
|
88
|
+
at: string;
|
|
89
|
+
planDigest: string | null;
|
|
90
|
+
/** `planDigest`, when a change set is stored under it (#4022): `chant change-set show <changeSet>` prints it. */
|
|
91
|
+
changeSet: string | null;
|
|
92
|
+
changeSetSource: TraceSource | null;
|
|
93
|
+
/** A pass over an empty change set, written by the promote itself (#4045). */
|
|
94
|
+
empty: boolean;
|
|
95
|
+
/** Whether the approval carries an ssh seal (`chant approve --sign`, #3163). `status` judges it against the signers at base. */
|
|
96
|
+
sealed: boolean;
|
|
97
|
+
url: string | null;
|
|
98
|
+
source: TraceSource;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** A release that shipped a commit of the walk. */
|
|
102
|
+
export interface TraceRelease {
|
|
103
|
+
releaseDigest: string;
|
|
104
|
+
label: string | null;
|
|
105
|
+
member: string | null;
|
|
106
|
+
/** The green commit it was cut from. */
|
|
107
|
+
commit: string;
|
|
108
|
+
/** That commit's ci tags. */
|
|
109
|
+
tag: TraceTag;
|
|
110
|
+
source: TraceSource;
|
|
111
|
+
cuts: { label: string | null; actor: string; timestamp: string; signed: boolean; runOrigin: RunOrigin | null; source: TraceSource }[];
|
|
112
|
+
/** The release gate's resolutions for it, oldest first. */
|
|
113
|
+
gates: TraceGate[];
|
|
114
|
+
/** The ids of its deploy records in `deploys`, oldest first. */
|
|
115
|
+
deploys: string[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** A deploy record (`ReleaseRecord` in `<env>/releases.jsonl`). */
|
|
119
|
+
export interface TraceDeploy {
|
|
120
|
+
/** `deploy:<member>/<env>/<component>/<runId>@<timestamp>`, with `.` for a ledger of no member. */
|
|
121
|
+
id: string;
|
|
122
|
+
member: string | null;
|
|
123
|
+
env: string;
|
|
124
|
+
component: string;
|
|
125
|
+
timestamp: string;
|
|
126
|
+
actor: string;
|
|
127
|
+
approver: string | null;
|
|
128
|
+
runId: string;
|
|
129
|
+
runOrigin: RunOrigin | null;
|
|
130
|
+
gitSha: string;
|
|
131
|
+
digest: string;
|
|
132
|
+
/** The release it was made from, by its `releaseDigest` or through the release gate resolution its admission names (#4043), or null. */
|
|
133
|
+
release: string | null;
|
|
134
|
+
releaseSource: TraceSource | null;
|
|
135
|
+
/** The gate resolution its `admission` names (#4020), or null for an ungated deploy, one recorded before 0.124.28, or one whose resolution is not on the branch. */
|
|
136
|
+
admittedBy: TraceGate | null;
|
|
137
|
+
/** The release plan its `digest` names in `_plans/` (ws-055), or null when none is stored. */
|
|
138
|
+
plan: { document: ReleasePlan; source: TraceSource } | null;
|
|
139
|
+
source: TraceSource;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface TraceCommit {
|
|
143
|
+
/** The commit node. */
|
|
144
|
+
commit: string;
|
|
145
|
+
sha: string;
|
|
146
|
+
/**
|
|
147
|
+
* The work items the commit names, judged as `check --changes` judges it
|
|
148
|
+
* (#4029): `linked`, `unlinked`, `wrong` or `merge`. Null when no work kind
|
|
149
|
+
* was read.
|
|
150
|
+
*/
|
|
151
|
+
work: { state: CommitLinkState; items: TraceWork[]; problems: CommitLinkProblem[] } | null;
|
|
152
|
+
/** The work items whose window holds the commit (the `worked` within edges). They don't link it. */
|
|
153
|
+
windowMatches: TraceWork[];
|
|
154
|
+
tag: TraceTag;
|
|
155
|
+
/** The releases cut from the commit, or when none, the first release cut from a commit that has it in its history. */
|
|
156
|
+
releases: { release: string; link: "cut-from" | "contains"; source: TraceSource }[];
|
|
157
|
+
/** Per environment, the first deploy record built from the commit or a commit that has it in its history. */
|
|
158
|
+
deploys: { deploy: string; env: string; link: "built-from" | "contains"; source: TraceSource }[];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export type TraceReasonCode = "ledger-unreadable" | "declaration-invalid";
|
|
162
|
+
|
|
163
|
+
/** The why chain (#4023): commits in walk order, then the releases and deploys they name. */
|
|
164
|
+
export interface IntentTrace {
|
|
165
|
+
commits: TraceCommit[];
|
|
166
|
+
releases: TraceRelease[];
|
|
167
|
+
deploys: TraceDeploy[];
|
|
168
|
+
/** Parts of the chain that could not be read: the ci tags (`declaration-invalid`) or a ledger (`ledger-unreadable`). The rest stands. */
|
|
169
|
+
reasons: { code: TraceReasonCode; message: string }[];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface TraceInput {
|
|
173
|
+
top: string;
|
|
174
|
+
/** The commits of the walk, in its order. */
|
|
175
|
+
commits: { id: string; sha: string; trailers: Record<string, string[]>; joins: CommitTrailerJoins }[];
|
|
176
|
+
/** The work records the walk read, by work kind, and their nodes by `<kind>:<id>`. Null when no work kind was read. */
|
|
177
|
+
work: { index: Omit<WorkIndex, "ledger">; nodes: Map<string, string> } | null;
|
|
178
|
+
/** The work items whose window holds each commit, by sha. */
|
|
179
|
+
windows: Map<string, { id: string; node: string; path: string }[]>;
|
|
180
|
+
/**
|
|
181
|
+
* The ledgers to read, each as the directory `chant/lifecycle` stores
|
|
182
|
+
* resolve it from. `within` is a path part every ledger of the member has,
|
|
183
|
+
* `_members/<name>/`, so a member with nothing on the branch is skipped
|
|
184
|
+
* unread; null when it can't be told, as for a member at the root.
|
|
185
|
+
*/
|
|
186
|
+
ledgers: { member: string | null; cwd: string; within: string | null }[];
|
|
187
|
+
green: CiGreen | null;
|
|
188
|
+
/** Whether `later` is `sha` or has it as an ancestor, in the history read. */
|
|
189
|
+
contains: (sha: string, later: string) => boolean;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function tryGit(top: string, args: string[]): string | undefined {
|
|
193
|
+
try {
|
|
194
|
+
return execFileSync("git", args, { cwd: top, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], maxBuffer: 256 * 1024 * 1024 });
|
|
195
|
+
} catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const fileOf = (digest: string): string => `${digest.replace(/:/g, "_")}.json`;
|
|
201
|
+
const onBranch = (path: string): string => `chant/lifecycle:${path}`;
|
|
202
|
+
const byTime = (a: string, b: string): number => (a < b ? -1 : a > b ? 1 : 0);
|
|
203
|
+
|
|
204
|
+
type RevokedView = (sha: string, r: CountedCiTag) => RevokedTagView;
|
|
205
|
+
|
|
206
|
+
function tagOf(sha: string, tags: CiTags | null, revokedView: RevokedView): TraceTag {
|
|
207
|
+
const green = tags?.green.get(sha);
|
|
208
|
+
const revoked = tags?.revoked.get(sha);
|
|
209
|
+
const view = (kind: "green" | "revoked", t: CountedCiTag): TraceTagView => ({
|
|
210
|
+
tag: `ci/${kind}/${sha}`,
|
|
211
|
+
tagged: t.tagged,
|
|
212
|
+
signer: t.signer,
|
|
213
|
+
source: { ref: `refs/tags/ci/${kind}/${sha}`, field: "name", value: sha },
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
state: revoked ? "revoked" : green ? "green" : "none",
|
|
217
|
+
green: green ? view("green", green) : null,
|
|
218
|
+
revoked: revoked ? { ...view("revoked", revoked), phase: revokedView(sha, revoked).phase, run: revokedView(sha, revoked).run } : null,
|
|
219
|
+
ignored: tags?.ignored.get(sha) ?? [],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Build the chain. Never throws for a ledger or tag it can't read: that is a reason. */
|
|
224
|
+
export async function buildTrace(input: TraceInput): Promise<IntentTrace> {
|
|
225
|
+
const { top } = input;
|
|
226
|
+
const reasons: IntentTrace["reasons"] = [];
|
|
227
|
+
const shas = input.commits.map((c) => c.sha);
|
|
228
|
+
|
|
229
|
+
// The ci tags, judged as release cut judges them.
|
|
230
|
+
let tags: CiTags | null = null;
|
|
231
|
+
const { revokedTagView } = await import("./ci-green");
|
|
232
|
+
try {
|
|
233
|
+
const { judgedCiTags } = await import("../lifecycle/release-revocation");
|
|
234
|
+
tags = await judgedCiTags(top, input.green);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
reasons.push({ code: "declaration-invalid", message: `the ci tags could not be judged: ${err instanceof Error ? err.message : String(err)}` });
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// The work item each commit names (#4029).
|
|
240
|
+
const { judgeCommitLink, hasLeaseLedger, mergeCommits } = await import("./commit-links");
|
|
241
|
+
const index: WorkIndex | null = input.work ? { ...input.work.index, ledger: hasLeaseLedger(top) } : null;
|
|
242
|
+
const merges = index ? mergeCommits(top, shas) : new Set<string>();
|
|
243
|
+
const workNode = (id: string) => input.work?.nodes.get(id) ?? null;
|
|
244
|
+
|
|
245
|
+
// The ledgers on the local chant/lifecycle branch.
|
|
246
|
+
const branch = tryGit(top, ["rev-parse", "--verify", "--quiet", "refs/heads/chant/lifecycle"]) !== undefined;
|
|
247
|
+
interface Ledger {
|
|
248
|
+
member: string | null;
|
|
249
|
+
loc: { cwd: string };
|
|
250
|
+
prefix: string;
|
|
251
|
+
releases: Release[];
|
|
252
|
+
deploys: { env: string; record: ReleaseRecord }[];
|
|
253
|
+
}
|
|
254
|
+
const ledgers: Ledger[] = [];
|
|
255
|
+
// One listing of the branch, so a ledger that holds no release and no deploy costs nothing more.
|
|
256
|
+
const paths = branch ? (tryGit(top, ["ls-tree", "-r", "--name-only", "--full-tree", "refs/heads/chant/lifecycle"]) ?? "").split("\n").filter(Boolean) : [];
|
|
257
|
+
const holds = (prefix: string) =>
|
|
258
|
+
paths.some((p) => p.startsWith(prefix) && (p.startsWith(`${prefix}_releases/`) || /^[^/_][^/]*\/releases\.jsonl$/.test(p.slice(prefix.length))));
|
|
259
|
+
if (branch) {
|
|
260
|
+
const { resolveMemberLedger } = await import("../lifecycle/member-ledger");
|
|
261
|
+
const { readAllReleases } = await import("../lifecycle/release-store");
|
|
262
|
+
const { listReleaseEnvironments, readReleaseLedger } = await import("../lifecycle/release-ledger");
|
|
263
|
+
for (const l of input.ledgers) {
|
|
264
|
+
if (l.within !== null && !paths.some((p) => p.includes(l.within!))) continue;
|
|
265
|
+
const loc = { cwd: l.cwd };
|
|
266
|
+
try {
|
|
267
|
+
const prefix = (await resolveMemberLedger(l.cwd)).prefix;
|
|
268
|
+
if (ledgers.some((x) => x.prefix === prefix) || !holds(prefix)) continue;
|
|
269
|
+
const { releases } = await readAllReleases(loc);
|
|
270
|
+
const deploys: Ledger["deploys"] = [];
|
|
271
|
+
for (const env of await listReleaseEnvironments(loc)) {
|
|
272
|
+
for (const record of (await readReleaseLedger(env, loc)).records) deploys.push({ env, record });
|
|
273
|
+
}
|
|
274
|
+
ledgers.push({ member: l.member, loc, prefix, releases, deploys });
|
|
275
|
+
} catch (err) {
|
|
276
|
+
reasons.push({ code: "ledger-unreadable", message: `${l.member ?? "the workspace"}'s ledger on chant/lifecycle: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}` });
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// Each commit to the releases and deploys that shipped it.
|
|
282
|
+
const firstCut = new Map<string, string>();
|
|
283
|
+
const releaseHits = new Map<Ledger, Set<string>>();
|
|
284
|
+
const deployHits = new Map<string, { ledger: Ledger; env: string; record: ReleaseRecord }>();
|
|
285
|
+
const deployId = (l: Ledger, env: string, r: ReleaseRecord) => `deploy:${l.member ?? "."}/${env}/${r.component}/${r.runId}@${r.timestamp}`;
|
|
286
|
+
const perCommit = new Map<string, { releases: TraceCommit["releases"]; deploys: TraceCommit["deploys"] }>();
|
|
287
|
+
if (ledgers.some((l) => l.releases.length > 0)) {
|
|
288
|
+
const { readCuts } = await import("../lifecycle/release-store");
|
|
289
|
+
for (const l of ledgers) {
|
|
290
|
+
if (l.releases.length === 0) continue;
|
|
291
|
+
try {
|
|
292
|
+
for (const c of (await readCuts(l.loc)).cuts) if (!firstCut.has(c.releaseDigest) || byTime(c.timestamp, firstCut.get(c.releaseDigest)!) < 0) firstCut.set(c.releaseDigest, c.timestamp);
|
|
293
|
+
} catch {
|
|
294
|
+
// A release with no readable cut sorts last; its file still joins.
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (const sha of shas) {
|
|
299
|
+
const releases: TraceCommit["releases"] = [];
|
|
300
|
+
const deploys: TraceCommit["deploys"] = [];
|
|
301
|
+
for (const l of ledgers) {
|
|
302
|
+
const releaseSource = (r: Release): TraceSource => ({ ref: onBranch(`${l.prefix}_releases/${fileOf(r.releaseDigest)}`), field: "commit", value: r.commit });
|
|
303
|
+
const cut = l.releases.filter((r) => r.commit === sha);
|
|
304
|
+
const chosen: { r: Release; link: "cut-from" | "contains" }[] = cut.map((r) => ({ r, link: "cut-from" as const }));
|
|
305
|
+
if (chosen.length === 0) {
|
|
306
|
+
const later = l.releases.filter((r) => input.contains(sha, r.commit)).sort((a, b) => byTime(firstCut.get(a.releaseDigest) ?? "", firstCut.get(b.releaseDigest) ?? "") || byTime(a.releaseDigest, b.releaseDigest));
|
|
307
|
+
if (later.length > 0) chosen.push({ r: later[0], link: "contains" });
|
|
308
|
+
}
|
|
309
|
+
for (const { r, link } of chosen) {
|
|
310
|
+
releases.push({ release: r.releaseDigest, link, source: releaseSource(r) });
|
|
311
|
+
(releaseHits.get(l) ?? releaseHits.set(l, new Set()).get(l)!).add(r.releaseDigest);
|
|
312
|
+
}
|
|
313
|
+
const first = new Map<string, { env: string; record: ReleaseRecord; link: "built-from" | "contains" }>();
|
|
314
|
+
for (const d of l.deploys) {
|
|
315
|
+
const link = d.record.gitSha === sha ? "built-from" : input.contains(sha, d.record.gitSha) ? "contains" : null;
|
|
316
|
+
if (!link) continue;
|
|
317
|
+
const had = first.get(d.env);
|
|
318
|
+
if (!had || byTime(d.record.timestamp, had.record.timestamp) < 0) first.set(d.env, { ...d, link });
|
|
319
|
+
}
|
|
320
|
+
for (const [env, d] of [...first].sort(([a], [b]) => byTime(a, b))) {
|
|
321
|
+
const id = deployId(l, env, d.record);
|
|
322
|
+
deployHits.set(id, { ledger: l, env, record: d.record });
|
|
323
|
+
deploys.push({ deploy: id, env, link: d.link, source: { ref: onBranch(`${l.prefix}${env}/releases.jsonl`), field: "gitSha", value: d.record.gitSha } });
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
perCommit.set(sha, { releases, deploys });
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// The releases, through joinRelease, with their gates and deploys.
|
|
330
|
+
const { joinRelease } = await import("../lifecycle/release-join");
|
|
331
|
+
const { readGateLedger } = await import("../lifecycle/gate-ledger");
|
|
332
|
+
const { readStoredChangeSet, changeSetFile, CHANGE_SETS_DIR } = await import("../lifecycle/change-set-store");
|
|
333
|
+
const gateLedgers = new Map<string, Promise<GateResolutionRecord[]>>();
|
|
334
|
+
const resolutionsOf = (l: Ledger, op: string) => {
|
|
335
|
+
const key = `${l.prefix}\0${op}`;
|
|
336
|
+
let hit = gateLedgers.get(key);
|
|
337
|
+
if (!hit) {
|
|
338
|
+
hit = readGateLedger(op, l.loc).then(
|
|
339
|
+
(g) => g.resolutions,
|
|
340
|
+
() => [],
|
|
341
|
+
);
|
|
342
|
+
gateLedgers.set(key, hit);
|
|
343
|
+
}
|
|
344
|
+
return hit;
|
|
345
|
+
};
|
|
346
|
+
const changeSets = new Map<string, Promise<boolean>>();
|
|
347
|
+
const gateOf = async (l: Ledger, r: GateResolutionRecord): Promise<TraceGate> => {
|
|
348
|
+
let stored: Promise<boolean> | undefined;
|
|
349
|
+
if (r.planDigest) {
|
|
350
|
+
const key = `${l.prefix}\0${r.planDigest}`;
|
|
351
|
+
stored = changeSets.get(key);
|
|
352
|
+
if (!stored) {
|
|
353
|
+
stored = readStoredChangeSet(r.planDigest, l.loc).then(
|
|
354
|
+
(x) => x.status === "found",
|
|
355
|
+
() => false,
|
|
356
|
+
);
|
|
357
|
+
changeSets.set(key, stored);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const has = stored ? await stored : false;
|
|
361
|
+
return {
|
|
362
|
+
op: r.op,
|
|
363
|
+
gate: r.gate,
|
|
364
|
+
environment: r.environment ?? null,
|
|
365
|
+
resolvedBy: r.resolvedBy,
|
|
366
|
+
relayedBy: r.relayedBy ?? null,
|
|
367
|
+
channel: r.origin ?? null,
|
|
368
|
+
at: r.timestamp,
|
|
369
|
+
planDigest: r.planDigest ?? null,
|
|
370
|
+
changeSet: has ? r.planDigest! : null,
|
|
371
|
+
changeSetSource: has ? { ref: onBranch(`${l.prefix}${CHANGE_SETS_DIR}/${changeSetFile(r.planDigest!)}`), field: "planDigest", value: r.planDigest! } : null,
|
|
372
|
+
empty: r.empty === true,
|
|
373
|
+
sealed: r.seal !== undefined,
|
|
374
|
+
url: r.url ?? null,
|
|
375
|
+
source: { ref: onBranch(`${l.prefix}_gates/${r.op}.jsonl`), field: r.releaseDigest ? "releaseDigest" : "gate", value: r.releaseDigest ?? r.gate },
|
|
376
|
+
};
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const releases: TraceRelease[] = [];
|
|
380
|
+
const joinedDeploys = new Map<string, { ledger: Ledger; env: string; record: ReleaseRecord; release: JoinedRelease }>();
|
|
381
|
+
for (const l of ledgers) {
|
|
382
|
+
const wanted = releaseHits.get(l);
|
|
383
|
+
if (!wanted || wanted.size === 0) continue;
|
|
384
|
+
const commits = [...new Set(l.releases.filter((r) => wanted.has(r.releaseDigest)).map((r) => r.commit))];
|
|
385
|
+
let joined: JoinedRelease[];
|
|
386
|
+
try {
|
|
387
|
+
joined = (await joinRelease({ commits }, l.loc)).releases;
|
|
388
|
+
} catch (err) {
|
|
389
|
+
reasons.push({ code: "ledger-unreadable", message: `joining ${l.member ?? "the workspace"}'s releases: ${err instanceof Error ? err.message.split("\n")[0] : String(err)}` });
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
for (const j of joined) {
|
|
393
|
+
for (const d of j.deploys) joinedDeploys.set(deployId(l, d.env, d), { ledger: l, env: d.env, record: d, release: j });
|
|
394
|
+
releases.push({
|
|
395
|
+
releaseDigest: j.releaseDigest,
|
|
396
|
+
label: j.label,
|
|
397
|
+
member: j.release.member,
|
|
398
|
+
commit: j.commit,
|
|
399
|
+
tag: tagOf(j.commit, tags, revokedTagView),
|
|
400
|
+
source: { ref: onBranch(`${l.prefix}_releases/${fileOf(j.releaseDigest)}`), field: "releaseDigest", value: j.releaseDigest },
|
|
401
|
+
cuts: j.cuts.map((c: ReleaseCut) => ({
|
|
402
|
+
label: c.label,
|
|
403
|
+
actor: c.actor,
|
|
404
|
+
timestamp: c.timestamp,
|
|
405
|
+
signed: c.signature !== null,
|
|
406
|
+
runOrigin: c.runOrigin,
|
|
407
|
+
source: { ref: onBranch(`${l.prefix}_releases/cuts.jsonl`), field: "releaseDigest", value: c.releaseDigest },
|
|
408
|
+
})),
|
|
409
|
+
gates: await Promise.all([...j.gates.resolutions].sort((a, b) => byTime(a.timestamp, b.timestamp)).map((r) => gateOf(l, r))),
|
|
410
|
+
deploys: j.deploys.map((d) => deployId(l, d.env, d)),
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// The deploys: those a commit names and those of each release, oldest first.
|
|
416
|
+
const { readReleasePlan } = await import("../lifecycle/plan-ledger");
|
|
417
|
+
const plans = new Map<string, Promise<ReleasePlan | null>>();
|
|
418
|
+
const all = new Map<string, { ledger: Ledger; env: string; record: ReleaseRecord; release?: JoinedRelease }>([...deployHits, ...joinedDeploys]);
|
|
419
|
+
const releaseResolutions = new Map<Ledger, GateResolutionRecord[]>();
|
|
420
|
+
const deploys: TraceDeploy[] = [];
|
|
421
|
+
for (const [id, d] of [...all].sort(([, a], [, b]) => byTime(a.record.timestamp, b.record.timestamp) || byTime(a.env, b.env))) {
|
|
422
|
+
const r = d.record;
|
|
423
|
+
const l = d.ledger;
|
|
424
|
+
let release: string | null = r.releaseDigest ?? null;
|
|
425
|
+
let releaseSource: TraceSource | null = release ? { ref: onBranch(`${l.prefix}${d.env}/releases.jsonl`), field: "releaseDigest", value: release } : null;
|
|
426
|
+
if (!release && r.admission?.op === "release") {
|
|
427
|
+
if (!releaseResolutions.has(l)) releaseResolutions.set(l, await resolutionsOf(l, "release"));
|
|
428
|
+
const { releaseOfDeploy } = await import("../lifecycle/release-join");
|
|
429
|
+
release = releaseOfDeploy(r, releaseResolutions.get(l)!) ?? null;
|
|
430
|
+
if (release) releaseSource = { ref: onBranch(`${l.prefix}${d.env}/releases.jsonl`), field: "admission", value: `${r.admission.gate}@${r.admission.resolvedAt}` };
|
|
431
|
+
}
|
|
432
|
+
let admittedBy: TraceGate | null = null;
|
|
433
|
+
if (r.admission) {
|
|
434
|
+
const found = (await resolutionsOf(l, r.admission.op)).find((x) => x.gate === r.admission!.gate && x.timestamp === r.admission!.resolvedAt);
|
|
435
|
+
if (found) admittedBy = await gateOf(l, found);
|
|
436
|
+
}
|
|
437
|
+
const planKey = `${l.prefix}\0${r.digest}`;
|
|
438
|
+
let plan = plans.get(planKey);
|
|
439
|
+
if (!plan) {
|
|
440
|
+
plan = readReleasePlan(r.digest, l.loc).catch(() => null);
|
|
441
|
+
plans.set(planKey, plan);
|
|
442
|
+
}
|
|
443
|
+
const document = await plan;
|
|
444
|
+
deploys.push({
|
|
445
|
+
id,
|
|
446
|
+
member: l.member,
|
|
447
|
+
env: d.env,
|
|
448
|
+
component: r.component,
|
|
449
|
+
timestamp: r.timestamp,
|
|
450
|
+
actor: r.actor,
|
|
451
|
+
approver: r.approver ?? null,
|
|
452
|
+
runId: r.runId,
|
|
453
|
+
runOrigin: r.runOrigin ?? null,
|
|
454
|
+
gitSha: r.gitSha,
|
|
455
|
+
digest: r.digest,
|
|
456
|
+
release,
|
|
457
|
+
releaseSource,
|
|
458
|
+
admittedBy,
|
|
459
|
+
plan: document ? { document, source: { ref: onBranch(`${l.prefix}_plans/${fileOf(r.digest)}`), field: "digest", value: r.digest } } : null,
|
|
460
|
+
source: { ref: onBranch(`${l.prefix}${d.env}/releases.jsonl`), field: "runId", value: r.runId },
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const commits: TraceCommit[] = input.commits.map((c) => {
|
|
465
|
+
let work: TraceCommit["work"] = null;
|
|
466
|
+
if (index) {
|
|
467
|
+
const judged = merges.has(c.sha) ? { state: "merge" as const, ids: [], problems: [] } : judgeCommitLink(c.trailers, c.joins, index);
|
|
468
|
+
const items: TraceWork[] = judged.ids.map((id) => {
|
|
469
|
+
const named = c.joins.records.some((r) => `${r.kind}:${r.id}` === id);
|
|
470
|
+
return {
|
|
471
|
+
id,
|
|
472
|
+
node: workNode(id),
|
|
473
|
+
source: named ? { ref: c.sha, field: "Chant-Record", value: id } : { ref: c.sha, field: "Chant-Lease", value: c.joins.lease?.token ?? "" },
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
work = { state: judged.state, items, problems: judged.problems };
|
|
477
|
+
}
|
|
478
|
+
const windowMatches: TraceWork[] = (input.windows.get(c.sha) ?? []).map((w) => ({ id: w.id, node: w.node, source: { ref: w.path, field: "window", value: "worked" } }));
|
|
479
|
+
const hit = perCommit.get(c.sha) ?? { releases: [], deploys: [] };
|
|
480
|
+
return { commit: c.id, sha: c.sha, work, windowMatches, tag: tagOf(c.sha, tags, revokedTagView), releases: hit.releases, deploys: hit.deploys };
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
return { commits, releases, deploys, reasons };
|
|
484
|
+
}
|