@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
|
@@ -179,6 +179,58 @@
|
|
|
179
179
|
"$ref": "#/$defs/ciGreen"
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
+
},
|
|
183
|
+
"retention": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"patternProperties": {
|
|
187
|
+
"^x-": true
|
|
188
|
+
},
|
|
189
|
+
"description": "How long chant lifecycle gc keeps each kind of stored record (#4036). A kind or field left out keeps its default: changeSets 14 days; releases 14 days and the newest 10 of each member; runEvents 14 days and the newest 100. Whatever the policy, gc never prunes a release an environment runs, what a pending gate binds, or a change set a gate names with a kept release. Read from the declaration at base. Added in schema 1 by chant 0.124.29, so a declaration that uses it sets minReader to 0.124.29 or newer.",
|
|
190
|
+
"properties": {
|
|
191
|
+
"changeSets": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"additionalProperties": false,
|
|
194
|
+
"description": "Stored change sets, _change-sets/<digest>.json on chant/lifecycle.",
|
|
195
|
+
"properties": {
|
|
196
|
+
"days": { "$ref": "#/$defs/retentionDays" }
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"releases": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"additionalProperties": false,
|
|
202
|
+
"description": "Releases, _releases/<digest>.json on chant/lifecycle, with the template bytes only they name. A release stays promotable for days after its newest cut, and the newest keep releases of each member stay promotable at any age, unless their commit is revoked.",
|
|
203
|
+
"properties": {
|
|
204
|
+
"days": { "$ref": "#/$defs/retentionDays" },
|
|
205
|
+
"keep": { "$ref": "#/$defs/retentionKeep" }
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"runEvents": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"additionalProperties": false,
|
|
211
|
+
"description": "Run event files, <git-common-dir>/chant/run-events/<run>.jsonl on each host. A file is kept for days after its last write, and the newest keep files at any age.",
|
|
212
|
+
"properties": {
|
|
213
|
+
"days": { "$ref": "#/$defs/retentionDays" },
|
|
214
|
+
"keep": { "$ref": "#/$defs/retentionKeep" }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"metrics": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"additionalProperties": false,
|
|
222
|
+
"patternProperties": {
|
|
223
|
+
"^x-": true
|
|
224
|
+
},
|
|
225
|
+
"description": "How chant workspace metrics counts the DORA metrics (#4039). Added in schema 1 by chant 0.124.29, so a declaration that uses it sets minReader to 0.124.29 or newer.",
|
|
226
|
+
"properties": {
|
|
227
|
+
"failureWindowDays": {
|
|
228
|
+
"type": "integer",
|
|
229
|
+
"minimum": 1,
|
|
230
|
+
"maximum": 365,
|
|
231
|
+
"description": "A deploy counts as failed when, within this many days after it, the commit it deployed is revoked or the environment is rolled back from it. The default is 7."
|
|
232
|
+
}
|
|
233
|
+
}
|
|
182
234
|
}
|
|
183
235
|
},
|
|
184
236
|
"patternProperties": {
|
|
@@ -186,6 +238,18 @@
|
|
|
186
238
|
},
|
|
187
239
|
"additionalProperties": false,
|
|
188
240
|
"$defs": {
|
|
241
|
+
"retentionDays": {
|
|
242
|
+
"type": "integer",
|
|
243
|
+
"minimum": 0,
|
|
244
|
+
"maximum": 36500,
|
|
245
|
+
"description": "Days a record is kept past its last write or use. 0 lets every record go that no never-prune rule keeps."
|
|
246
|
+
},
|
|
247
|
+
"retentionKeep": {
|
|
248
|
+
"type": "integer",
|
|
249
|
+
"minimum": 0,
|
|
250
|
+
"maximum": 100000,
|
|
251
|
+
"description": "How many of the newest are kept at any age."
|
|
252
|
+
},
|
|
189
253
|
"name": {
|
|
190
254
|
"type": "string",
|
|
191
255
|
"pattern": "^[a-z0-9][a-z0-9-]{0,39}$",
|
|
@@ -47,6 +47,21 @@ export interface DeclaredKind {
|
|
|
47
47
|
* and when its records can't be read (`records` reports why).
|
|
48
48
|
*/
|
|
49
49
|
acceptance?: DeclaredAcceptance[];
|
|
50
|
+
/**
|
|
51
|
+
* For a flag kind (#4024), read with `flags: true`: each current flag
|
|
52
|
+
* record, with its `flag-stale` warning when it is due for removal. Absent
|
|
53
|
+
* for any other kind, when the kind wasn't loaded, and when its records
|
|
54
|
+
* can't be read (`records` reports why).
|
|
55
|
+
*/
|
|
56
|
+
flags?: DeclaredFlag[];
|
|
57
|
+
}
|
|
58
|
+
/** One flag record, as `check` reads it for WSP118 (#4024). */
|
|
59
|
+
export interface DeclaredFlag {
|
|
60
|
+
flag: string;
|
|
61
|
+
/** The record file on disk, absolute. */
|
|
62
|
+
file: string;
|
|
63
|
+
/** The record's `flag-stale` warning, when the flag is due for removal; null otherwise. */
|
|
64
|
+
stale: string | null;
|
|
50
65
|
}
|
|
51
66
|
/** One work record's acceptance criteria, counted, as `ls`, `status` and `check` read them (#2772). */
|
|
52
67
|
export interface DeclaredAcceptance {
|
|
@@ -70,6 +85,7 @@ export declare function declaredKindFile(declared: RecordKindDeclaration, rootOn
|
|
|
70
85
|
export declare function loadDeclaredKinds(declaration: Declaration, tree: WorkspaceTree, rootOnDisk: string, options?: {
|
|
71
86
|
load?: boolean;
|
|
72
87
|
acceptance?: boolean;
|
|
88
|
+
flags?: boolean;
|
|
73
89
|
at?: string;
|
|
74
90
|
}): Promise<DeclaredKind[]>;
|
|
75
91
|
//# sourceMappingURL=declared-kinds.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"declared-kinds.d.ts","sourceRoot":"","sources":["../../src/workspace/declared-kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAuB,KAAK,WAAW,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAElG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,oHAAoH;AACpH,eAAO,MAAM,0BAA0B,yFAA0H,CAAC;AAClK,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,uCAAuC;IACvC,MAAM,EAAE;QAAE,IAAI,EAAE,sBAAsB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;IACjF;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"declared-kinds.d.ts","sourceRoot":"","sources":["../../src/workspace/declared-kinds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,EAAuB,KAAK,WAAW,EAAE,KAAK,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAElG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAE5C,oHAAoH;AACpH,eAAO,MAAM,0BAA0B,yFAA0H,CAAC;AAClK,MAAM,MAAM,sBAAsB,GAAG,CAAC,OAAO,0BAA0B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjF,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,qBAAqB,CAAC;IAChC,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,uCAAuC;IACvC,MAAM,EAAE;QAAE,IAAI,EAAE,sBAAsB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACjE;;;;OAIG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;YAAC,OAAO,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;IACjF;;;;;OAKG;IACH,UAAU,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAClC;;;;;OAKG;IACH,KAAK,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED,+DAA+D;AAC/D,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,2FAA2F;IAC3F,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,uGAAuG;AACvG,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,4GAA4G;IAC5G,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,kFAAkF;AAClF,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,qBAAqB,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5F;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,aAAa,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAO,GACnF,OAAO,CAAC,YAAY,EAAE,CAAC,CAmCzB"}
|
|
@@ -52,6 +52,11 @@ export async function loadDeclaredKinds(declaration, tree, rootOnDisk, options =
|
|
|
52
52
|
if (counted)
|
|
53
53
|
entry.acceptance = counted;
|
|
54
54
|
}
|
|
55
|
+
if (loaded.kind.flag && options.flags) {
|
|
56
|
+
const flags = await readFlags(file, rootOnDisk, options.at);
|
|
57
|
+
if (flags)
|
|
58
|
+
entry.flags = flags;
|
|
59
|
+
}
|
|
55
60
|
out.push(entry);
|
|
56
61
|
}
|
|
57
62
|
catch (err) {
|
|
@@ -86,6 +91,26 @@ async function countAcceptance(file, rootOnDisk, at) {
|
|
|
86
91
|
throw err;
|
|
87
92
|
}
|
|
88
93
|
}
|
|
94
|
+
/** A flag kind's current records with their stale warnings (#4024), or undefined when they can't be read. */
|
|
95
|
+
async function readFlags(file, rootOnDisk, at) {
|
|
96
|
+
const { readRecordsFor } = await import("./records-cli.js");
|
|
97
|
+
const { RecordReadError } = await import("./records.js");
|
|
98
|
+
try {
|
|
99
|
+
const read = await readRecordsFor({ kind: file, cwd: rootOnDisk, current: true, ...(at !== undefined ? { at } : {}) });
|
|
100
|
+
return read.result.records
|
|
101
|
+
.filter((r) => r.id !== null)
|
|
102
|
+
.map((r) => ({
|
|
103
|
+
flag: r.id,
|
|
104
|
+
file: join(read.root, ...r.path.split("/")),
|
|
105
|
+
stale: r.warnings.find((w) => w.code === "flag-stale")?.message ?? null,
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
if (err instanceof RecordReadError)
|
|
110
|
+
return undefined;
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
89
114
|
/** An answer kind's points file (ws-058), read from `tree` and validated as `points ask` reads it. */
|
|
90
115
|
async function checkPoints(abs, rootOnDisk, tree) {
|
|
91
116
|
const file = relative(rootOnDisk, abs).split(sep).join("/");
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace metrics [--env <env>] [--member <name>] [--since <date>] [--json]`
|
|
3
|
+
* (#4039): the four DORA metrics per member and environment, from the deploy
|
|
4
|
+
* records, the releases and their revocations on `chant/lifecycle`.
|
|
5
|
+
*
|
|
6
|
+
* The `--json` document is `delivery-metrics.schema.json`, a read-contract
|
|
7
|
+
* output, so behold, a Grafana panel and terragucci's own estate read the
|
|
8
|
+
* same numbers. The metrics are computed in `../lifecycle/delivery-metrics.ts`;
|
|
9
|
+
* this module reads what they need:
|
|
10
|
+
*
|
|
11
|
+
* - each member's `<env>/releases.jsonl`, every environment it has, read in
|
|
12
|
+
* full, since deploys before the window decide lead times and deploys after
|
|
13
|
+
* a failure decide its restore;
|
|
14
|
+
* - its releases, for the commit each was cut from, and the release gate's
|
|
15
|
+
* resolutions, for a deploy that names its release only through its
|
|
16
|
+
* admission (`releaseOfDeploy`, #4043);
|
|
17
|
+
* - the counted `ci/revoked/<sha>` tags, judged as `release cut` judges them
|
|
18
|
+
* (`readRevokedCommits`, #4033), and each commit's committer time from git.
|
|
19
|
+
*
|
|
20
|
+
* Outside a workspace the project's own ledger is read as one member, `null`.
|
|
21
|
+
* It reads the local branch and the local tags, and never fetches.
|
|
22
|
+
*/
|
|
23
|
+
import type { CommandContext } from "../cli/registry.js";
|
|
24
|
+
import { type EnvironmentMetrics, type MetricsWindow } from "../lifecycle/delivery-metrics.js";
|
|
25
|
+
export declare const DELIVERY_METRICS_CONTRACT_VERSION = 1;
|
|
26
|
+
export declare const DELIVERY_METRICS_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/delivery-metrics/v1/delivery-metrics.schema.json";
|
|
27
|
+
/** The window when `--since` is not given: eight weeks back from now. */
|
|
28
|
+
export declare const DEFAULT_METRICS_DAYS = 56;
|
|
29
|
+
/** Why the read stopped. Closed. Outside a workspace there is no declaration to fail, so `declaration-missing` never comes back. */
|
|
30
|
+
export declare const METRICS_ERROR_CODES: readonly ["declaration-ambiguous", "declaration-unparseable", "declaration-invalid", "placement-invalid", "reader-too-old", "root-chant-required", "not-a-git-repository", "environment-invalid"];
|
|
31
|
+
export type MetricsErrorCode = (typeof METRICS_ERROR_CODES)[number];
|
|
32
|
+
/** Why a member's metrics are missing or partial. Closed. */
|
|
33
|
+
export declare const METRICS_REASON_CODES: readonly ["ledger-unreadable", "ledger-malformed"];
|
|
34
|
+
export type MetricsReasonCode = (typeof METRICS_REASON_CODES)[number];
|
|
35
|
+
/** One member's metrics. */
|
|
36
|
+
export interface MemberMetrics {
|
|
37
|
+
/** The member's name, or null for a project outside a workspace. */
|
|
38
|
+
member: string | null;
|
|
39
|
+
/** Each environment with a deploy ledger, sorted by name. */
|
|
40
|
+
environments: EnvironmentMetrics[];
|
|
41
|
+
/** Null when everything was read. */
|
|
42
|
+
reason: {
|
|
43
|
+
code: MetricsReasonCode;
|
|
44
|
+
message: string;
|
|
45
|
+
} | null;
|
|
46
|
+
}
|
|
47
|
+
export type DeliveryMetricsDocument = {
|
|
48
|
+
$schema: string;
|
|
49
|
+
contract: number;
|
|
50
|
+
chant: string;
|
|
51
|
+
/** The workspace's name, or null for a project outside a workspace. */
|
|
52
|
+
workspace: {
|
|
53
|
+
name: string;
|
|
54
|
+
} | null;
|
|
55
|
+
lifecycle: {
|
|
56
|
+
ref: string;
|
|
57
|
+
commit: string | null;
|
|
58
|
+
};
|
|
59
|
+
window: MetricsWindow & {
|
|
60
|
+
days: number;
|
|
61
|
+
};
|
|
62
|
+
failureWindowDays: number;
|
|
63
|
+
members: MemberMetrics[];
|
|
64
|
+
} | {
|
|
65
|
+
$schema: string;
|
|
66
|
+
contract: number;
|
|
67
|
+
chant: string;
|
|
68
|
+
error: {
|
|
69
|
+
code: MetricsErrorCode;
|
|
70
|
+
message: string;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
export interface MetricsQuery {
|
|
74
|
+
cwd: string;
|
|
75
|
+
/** Only this environment. */
|
|
76
|
+
env?: string;
|
|
77
|
+
/** Only these members. */
|
|
78
|
+
members?: string[];
|
|
79
|
+
/** ISO-8601 date or time; the default is {@link DEFAULT_METRICS_DAYS} days before `now`. */
|
|
80
|
+
since?: string;
|
|
81
|
+
/** For tests: the end of the window. */
|
|
82
|
+
now?: string;
|
|
83
|
+
}
|
|
84
|
+
/** Read the metrics document for the workspace or project holding `query.cwd`. */
|
|
85
|
+
export declare function deliveryMetrics(query: MetricsQuery): Promise<DeliveryMetricsDocument>;
|
|
86
|
+
/** The text view: one line per member and environment. */
|
|
87
|
+
export declare function formatMetrics(doc: Extract<DeliveryMetricsDocument, {
|
|
88
|
+
members: unknown;
|
|
89
|
+
}>): string;
|
|
90
|
+
export declare function runWorkspaceMetrics(ctx: CommandContext): Promise<number>;
|
|
91
|
+
//# sourceMappingURL=delivery-metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-metrics.d.ts","sourceRoot":"","sources":["../../src/workspace/delivery-metrics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAmD,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAQ7I,eAAO,MAAM,iCAAiC,IAAI,CAAC;AACnD,eAAO,MAAM,0BAA0B,kGAAkG,CAAC;AAE1I,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,oIAAoI;AACpI,eAAO,MAAM,mBAAmB,mMASU,CAAC;AAC3C,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,oDAAqF,CAAC;AACvH,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAMtE,4BAA4B;AAC5B,MAAM,WAAW,aAAa;IAC5B,oEAAoE;IACpE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,6DAA6D;IAC7D,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,qCAAqC;IACrC,MAAM,EAAE;QAAE,IAAI,EAAE,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7D;AAED,MAAM,MAAM,uBAAuB,GAC/B;IACE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAClD,MAAM,EAAE,aAAa,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B,GACD;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE7G,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,4FAA4F;IAC5F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAmBD,kFAAkF;AAClF,wBAAsB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA+D3F;AAsED,0DAA0D;AAC1D,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,CAAC,uBAAuB,EAAE;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,GAAG,MAAM,CAcjG;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB9E"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace metrics [--env <env>] [--member <name>] [--since <date>] [--json]`
|
|
3
|
+
* (#4039): the four DORA metrics per member and environment, from the deploy
|
|
4
|
+
* records, the releases and their revocations on `chant/lifecycle`.
|
|
5
|
+
*
|
|
6
|
+
* The `--json` document is `delivery-metrics.schema.json`, a read-contract
|
|
7
|
+
* output, so behold, a Grafana panel and terragucci's own estate read the
|
|
8
|
+
* same numbers. The metrics are computed in `../lifecycle/delivery-metrics.ts`;
|
|
9
|
+
* this module reads what they need:
|
|
10
|
+
*
|
|
11
|
+
* - each member's `<env>/releases.jsonl`, every environment it has, read in
|
|
12
|
+
* full, since deploys before the window decide lead times and deploys after
|
|
13
|
+
* a failure decide its restore;
|
|
14
|
+
* - its releases, for the commit each was cut from, and the release gate's
|
|
15
|
+
* resolutions, for a deploy that names its release only through its
|
|
16
|
+
* admission (`releaseOfDeploy`, #4043);
|
|
17
|
+
* - the counted `ci/revoked/<sha>` tags, judged as `release cut` judges them
|
|
18
|
+
* (`readRevokedCommits`, #4033), and each commit's committer time from git.
|
|
19
|
+
*
|
|
20
|
+
* Outside a workspace the project's own ledger is read as one member, `null`.
|
|
21
|
+
* It reads the local branch and the local tags, and never fetches.
|
|
22
|
+
*/
|
|
23
|
+
import { execFileSync } from "node:child_process";
|
|
24
|
+
import { existsSync } from "node:fs";
|
|
25
|
+
import { resolve } from "node:path";
|
|
26
|
+
import { formatError } from "../cli/format.js";
|
|
27
|
+
import { DEFAULT_FAILURE_WINDOW_DAYS, environmentMetrics } from "../lifecycle/delivery-metrics.js";
|
|
28
|
+
import { findWorkspaceRoot } from "../project-root.js";
|
|
29
|
+
import { readDeclaration, readerVersion, WorkspaceReadError } from "./declaration.js";
|
|
30
|
+
import { gitTop, workingTree } from "./tree.js";
|
|
31
|
+
import { handToRootChant } from "./which-chant.js";
|
|
32
|
+
export const DELIVERY_METRICS_CONTRACT_VERSION = 1;
|
|
33
|
+
export const DELIVERY_METRICS_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/delivery-metrics/v1/delivery-metrics.schema.json";
|
|
34
|
+
/** The window when `--since` is not given: eight weeks back from now. */
|
|
35
|
+
export const DEFAULT_METRICS_DAYS = 56;
|
|
36
|
+
/** Why the read stopped. Closed. Outside a workspace there is no declaration to fail, so `declaration-missing` never comes back. */
|
|
37
|
+
export const METRICS_ERROR_CODES = [
|
|
38
|
+
"declaration-ambiguous",
|
|
39
|
+
"declaration-unparseable",
|
|
40
|
+
"declaration-invalid",
|
|
41
|
+
"placement-invalid",
|
|
42
|
+
"reader-too-old",
|
|
43
|
+
"root-chant-required",
|
|
44
|
+
"not-a-git-repository",
|
|
45
|
+
"environment-invalid",
|
|
46
|
+
];
|
|
47
|
+
/** Why a member's metrics are missing or partial. Closed. */
|
|
48
|
+
export const METRICS_REASON_CODES = ["ledger-unreadable", "ledger-malformed"];
|
|
49
|
+
const ENV_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,99}$/;
|
|
50
|
+
const USAGE = "chant workspace metrics [dir] [--env <env>] [--member <name>] [--since <date>] [--json]";
|
|
51
|
+
class MetricsError extends Error {
|
|
52
|
+
constructor(code, message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.code = code;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function tryGit(cwd, args) {
|
|
58
|
+
try {
|
|
59
|
+
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/** Read the metrics document for the workspace or project holding `query.cwd`. */
|
|
66
|
+
export async function deliveryMetrics(query) {
|
|
67
|
+
const head = { $schema: DELIVERY_METRICS_SCHEMA_ID, contract: DELIVERY_METRICS_CONTRACT_VERSION, chant: readerVersion() };
|
|
68
|
+
try {
|
|
69
|
+
if (query.env !== undefined && (!ENV_PATTERN.test(query.env) || query.env.includes(".."))) {
|
|
70
|
+
throw new MetricsError("environment-invalid", `--env ${JSON.stringify(query.env)} can't be an environment: use letters, digits, ".", "_" and "-", starting with a letter or digit`);
|
|
71
|
+
}
|
|
72
|
+
const top = gitTop(query.cwd);
|
|
73
|
+
if (!top)
|
|
74
|
+
throw new MetricsError("not-a-git-repository", `${query.cwd} is not in a git repository, so it has no chant/lifecycle branch to read`);
|
|
75
|
+
const found = findWorkspaceRoot(query.cwd);
|
|
76
|
+
const declaration = found ? readDeclaration(workingTree(found.dir), "", { rootChant: true }) : null;
|
|
77
|
+
const root = found?.dir ?? query.cwd;
|
|
78
|
+
const failureWindowDays = declaration?.metrics?.failureWindowDays ?? DEFAULT_FAILURE_WINDOW_DAYS;
|
|
79
|
+
const until = query.now ?? new Date().toISOString();
|
|
80
|
+
const since = query.since ?? new Date(Date.parse(until) - DEFAULT_METRICS_DAYS * 86400000).toISOString();
|
|
81
|
+
const window = { since: new Date(Date.parse(since)).toISOString(), until: new Date(Date.parse(until)).toISOString() };
|
|
82
|
+
const days = Math.round(((Date.parse(window.until) - Date.parse(window.since)) / 86400000) * 100) / 100;
|
|
83
|
+
// The revoked commits, judged against ci.green at base (#4033). Tags that
|
|
84
|
+
// can't be judged stop the read: the failure rate would be wrong otherwise.
|
|
85
|
+
const { readRevokedCommits } = await import("../lifecycle/release-revocation.js");
|
|
86
|
+
const revoked = await readRevokedCommits(root);
|
|
87
|
+
const commitTimes = new Map();
|
|
88
|
+
const commitTime = (sha) => {
|
|
89
|
+
if (!commitTimes.has(sha)) {
|
|
90
|
+
const iso = tryGit(top, ["show", "-s", "--format=%cI", `${sha}^{commit}`]);
|
|
91
|
+
commitTimes.set(sha, iso ? Date.parse(iso) : null);
|
|
92
|
+
}
|
|
93
|
+
return commitTimes.get(sha);
|
|
94
|
+
};
|
|
95
|
+
let ledgers;
|
|
96
|
+
if (declaration) {
|
|
97
|
+
const wanted = query.members && query.members.length > 0 ? new Set(query.members) : null;
|
|
98
|
+
if (wanted) {
|
|
99
|
+
const unknown = [...wanted].filter((n) => !declaration.members.some((m) => m.name === n));
|
|
100
|
+
if (unknown.length > 0)
|
|
101
|
+
throw new WorkspaceReadError("declaration-invalid", `--member ${unknown.join(", ")} names no member of the workspace`);
|
|
102
|
+
}
|
|
103
|
+
ledgers = declaration.members
|
|
104
|
+
.filter((m) => !wanted || wanted.has(m.name))
|
|
105
|
+
.map((m) => ({ member: m.name, loc: { cwd: root, member: m.dir === "." ? "." : m.name } }));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
ledgers = [{ member: null, loc: { cwd: query.cwd } }];
|
|
109
|
+
}
|
|
110
|
+
const members = [];
|
|
111
|
+
for (const l of ledgers)
|
|
112
|
+
members.push(await memberMetrics(l.member, l.loc, { env: query.env, window, failureWindowDays, revoked, commitTime }));
|
|
113
|
+
return {
|
|
114
|
+
...head,
|
|
115
|
+
workspace: declaration ? { name: declaration.name } : null,
|
|
116
|
+
lifecycle: { ref: "chant/lifecycle", commit: tryGit(top, ["rev-parse", "--verify", "--quiet", "refs/heads/chant/lifecycle"]) || null },
|
|
117
|
+
window: { ...window, days },
|
|
118
|
+
failureWindowDays,
|
|
119
|
+
members,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (err instanceof WorkspaceReadError)
|
|
124
|
+
return { ...head, error: { code: err.code, message: err.message } };
|
|
125
|
+
if (err instanceof MetricsError)
|
|
126
|
+
return { ...head, error: { code: err.code, message: err.message } };
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function memberMetrics(member, loc, ctx) {
|
|
131
|
+
try {
|
|
132
|
+
const { listReleaseEnvironments, readReleaseLedger } = await import("../lifecycle/release-ledger.js");
|
|
133
|
+
const { readAllReleases, readCuts, labelsByDigest } = await import("../lifecycle/release-store.js");
|
|
134
|
+
const { readGateLedger } = await import("../lifecycle/gate-ledger.js");
|
|
135
|
+
const { releaseOfDeploy } = await import("../lifecycle/release-join.js");
|
|
136
|
+
const { RELEASE_GATE_OP } = await import("../op/gate-name.js");
|
|
137
|
+
let envs = (await listReleaseEnvironments(loc)).sort();
|
|
138
|
+
if (ctx.env !== undefined)
|
|
139
|
+
envs = envs.filter((e) => e === ctx.env);
|
|
140
|
+
if (envs.length === 0)
|
|
141
|
+
return { member, environments: [], reason: null };
|
|
142
|
+
const { releases, malformed: badReleases } = await readAllReleases(loc);
|
|
143
|
+
const commits = new Map(releases.map((r) => [r.releaseDigest, r.commit]));
|
|
144
|
+
const labels = labelsByDigest((await readCuts(loc)).cuts);
|
|
145
|
+
const { resolutions } = await readGateLedger(RELEASE_GATE_OP, loc);
|
|
146
|
+
const environments = [];
|
|
147
|
+
let badLines = 0;
|
|
148
|
+
for (const env of envs) {
|
|
149
|
+
const { records, malformed } = await readReleaseLedger(env, loc);
|
|
150
|
+
badLines += malformed;
|
|
151
|
+
environments.push(environmentMetrics(env, records, {
|
|
152
|
+
releaseOf: (r) => releaseOfDeploy(r, resolutions),
|
|
153
|
+
commitOfRelease: (d) => commits.get(d) ?? null,
|
|
154
|
+
labelOf: (d) => labels.get(d) ?? null,
|
|
155
|
+
revocationOf: (sha) => {
|
|
156
|
+
const r = ctx.revoked.get(sha);
|
|
157
|
+
return r ? { tag: r.tag, tagged: r.tagged } : null;
|
|
158
|
+
},
|
|
159
|
+
commitTime: ctx.commitTime,
|
|
160
|
+
}, ctx.window, ctx.failureWindowDays));
|
|
161
|
+
}
|
|
162
|
+
const reason = badLines + badReleases > 0
|
|
163
|
+
? { code: "ledger-malformed", message: `${badLines} deploy record line(s) and ${badReleases} release file(s) could not be read and are left out` }
|
|
164
|
+
: null;
|
|
165
|
+
return { member, environments, reason };
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
return { member, environments: [], reason: { code: "ledger-unreadable", message: `reading the ledger failed: ${err instanceof Error ? err.message : String(err)}` } };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
const seconds = (s) => {
|
|
172
|
+
if (s === null)
|
|
173
|
+
return "none";
|
|
174
|
+
if (s < 3600)
|
|
175
|
+
return `${Math.round(s / 60)}m`;
|
|
176
|
+
if (s < 86400)
|
|
177
|
+
return `${Math.round((s / 3600) * 10) / 10}h`;
|
|
178
|
+
return `${Math.round((s / 86400) * 10) / 10}d`;
|
|
179
|
+
};
|
|
180
|
+
/** The text view: one line per member and environment. */
|
|
181
|
+
export function formatMetrics(doc) {
|
|
182
|
+
const lines = [`window ${doc.window.since} to ${doc.window.until} (${doc.window.days} days), failure window ${doc.failureWindowDays} days`];
|
|
183
|
+
for (const m of doc.members) {
|
|
184
|
+
if (m.reason)
|
|
185
|
+
lines.push(`${m.member ?? "."}: ${m.reason.code}: ${m.reason.message}`);
|
|
186
|
+
for (const e of m.environments) {
|
|
187
|
+
const rate = e.changeFailure.rate === null ? "-" : `${Math.round(e.changeFailure.rate * 1000) / 10}%`;
|
|
188
|
+
lines.push(`${m.member ?? "."} ${e.env}: ${e.deployments.count} deploys (${e.deployments.perWeek}/week), lead time ${seconds(e.leadTime.medianSeconds)}, ` +
|
|
189
|
+
`change failure ${rate} (${e.changeFailure.failed}/${e.changeFailure.deployments}), restore ${seconds(e.restore.medianSeconds)}` +
|
|
190
|
+
(e.restore.open > 0 ? `, ${e.restore.open} open` : ""));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return lines.join("\n");
|
|
194
|
+
}
|
|
195
|
+
export async function runWorkspaceMetrics(ctx) {
|
|
196
|
+
const { args } = ctx;
|
|
197
|
+
if (args.since !== undefined && (args.since === "" || Number.isNaN(Date.parse(args.since)))) {
|
|
198
|
+
console.error(formatError({ message: `--since ${JSON.stringify(args.since)} is not a date: give an ISO-8601 date such as 2026-09-01`, hint: USAGE }));
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
const cwd = resolve(args.extraPositional ?? ".");
|
|
202
|
+
if (!existsSync(cwd)) {
|
|
203
|
+
console.error(formatError({ message: `${cwd} does not exist`, hint: USAGE }));
|
|
204
|
+
return 1;
|
|
205
|
+
}
|
|
206
|
+
const handed = await handToRootChant(cwd, undefined);
|
|
207
|
+
if (handed !== undefined)
|
|
208
|
+
return handed;
|
|
209
|
+
const doc = await deliveryMetrics({ cwd, ...(args.env ? { env: args.env } : {}), ...(args.members ? { members: args.members } : {}), ...(args.since ? { since: args.since } : {}) });
|
|
210
|
+
if (args.json)
|
|
211
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
212
|
+
else if ("error" in doc)
|
|
213
|
+
console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
|
|
214
|
+
else
|
|
215
|
+
console.log(formatMetrics(doc));
|
|
216
|
+
return "error" in doc ? 1 : 0;
|
|
217
|
+
}
|