@intentius/chant 0.124.28 → 0.124.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +8 -1
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +4 -2
- package/dist/change-set.d.ts +10 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +4 -3
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +6 -3
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +45 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +18 -4
- package/dist/cli/handlers/fan-out.d.ts +9 -0
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +67 -8
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +11 -3
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +134 -26
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +210 -33
- package/dist/cli/handlers/release.d.ts +62 -5
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +414 -28
- package/dist/cli/handlers/run.d.ts +11 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +67 -6
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +91 -10
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +73 -2
- package/dist/cli/registry.d.ts +32 -4
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +3 -2
- package/dist/cli/run-events.d.ts.map +1 -1
- package/dist/cli/run-events.js +5 -3
- package/dist/components/auto-release.d.ts +2 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +1 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts +8 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +35 -0
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +50 -0
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +49 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +38 -4
- package/dist/components/pr-run.d.ts +20 -2
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +26 -13
- package/dist/components/promote.d.ts +23 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +60 -2
- package/dist/components/release-compile.d.ts +9 -0
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +9 -3
- package/dist/components/release-gate.d.ts +11 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +10 -4
- package/dist/components/wave-plan.d.ts +16 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +36 -2
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +2 -1
- package/dist/lifecycle/change-set-store.d.ts.map +1 -1
- package/dist/lifecycle/change-set-store.js +2 -1
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +10 -1
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
- package/dist/lifecycle/gate-change-set.js +35 -3
- package/dist/lifecycle/gate-ledger.d.ts +2 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +48 -1
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +116 -23
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-ledger.d.ts +8 -3
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +2 -0
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +12 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +4 -2
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/lifecycle/run-ledger.d.ts +1 -1
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +9 -0
- package/dist/op/activities/activity-contracts.d.ts +1 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +1 -0
- package/dist/op/activities/shell.d.ts +6 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +1 -1
- package/dist/op/local-executor.d.ts +23 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +9 -3
- package/dist/op/runtime.d.ts +27 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtime.js +20 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/pr-forge.d.ts +23 -0
- package/dist/pr-forge.d.ts.map +1 -1
- package/dist/pr-forge.js +44 -0
- package/dist/pr-loop.d.ts +17 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +30 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +29 -2
- package/dist/run-event-store.d.ts.map +1 -1
- package/dist/run-event-store.js +50 -3
- package/dist/run-event-stream.d.ts +4 -3
- package/dist/run-event-stream.d.ts.map +1 -1
- package/dist/run-event-stream.js +7 -3
- package/dist/run-event.d.ts +34 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/run-event.js +4 -0
- package/dist/wave-runtime.mjs +260 -229
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/change-set.schema.json +5 -1
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green-forge.d.ts +2 -0
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green.d.ts +10 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +14 -5
- package/dist/workspace/ci-run.d.ts +222 -0
- package/dist/workspace/ci-run.d.ts.map +1 -0
- package/dist/workspace/ci-run.js +464 -0
- package/dist/workspace/ci-serve.d.ts +214 -0
- package/dist/workspace/ci-serve.d.ts.map +1 -0
- package/dist/workspace/ci-serve.js +412 -0
- package/dist/workspace/declaration.d.ts +16 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +26 -0
- package/dist/workspace/declaration.schema.json +64 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +30 -6
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +141 -21
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/pr-report.schema.json +5 -0
- package/dist/workspace/reason-codes.d.ts +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +3 -0
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +9 -0
- package/dist/workspace/releases.d.ts.map +1 -1
- package/dist/workspace/releases.js +9 -0
- package/dist/workspace/releases.schema.json +123 -1
- package/dist/workspace/run-event.schema.json +24 -6
- package/dist/workspace/status-gates.d.ts +8 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-stewards.d.ts +2 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +1 -0
- package/dist/workspace/status.d.ts +78 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +170 -3
- package/dist/workspace/status.schema.json +210 -2
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.ts +10 -2
- package/src/change-set.ts +14 -3
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +6 -3
- package/src/cli/handlers/approve-release.test.ts +5 -1
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +47 -0
- package/src/cli/handlers/build.test.ts +30 -0
- package/src/cli/handlers/build.ts +20 -4
- package/src/cli/handlers/fan-out.test.ts +262 -1
- package/src/cli/handlers/fan-out.ts +72 -8
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/pr.test.ts +148 -1
- package/src/cli/handlers/pr.ts +144 -28
- package/src/cli/handlers/promote.test.ts +280 -2
- package/src/cli/handlers/promote.ts +210 -34
- package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +146 -32
- package/src/cli/handlers/release.ts +415 -28
- package/src/cli/handlers/run-auto-release.test.ts +9 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.ts +69 -5
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +91 -10
- package/src/cli/parse-args.ts +54 -2
- package/src/cli/registry.ts +32 -4
- package/src/cli/run-events.ts +5 -2
- package/src/components/auto-release.test.ts +9 -0
- package/src/components/auto-release.ts +3 -0
- package/src/components/capability.ts +9 -0
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +36 -0
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +48 -0
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +67 -4
- package/src/components/pr-run.test.ts +127 -0
- package/src/components/pr-run.ts +32 -12
- package/src/components/promote.ts +68 -2
- package/src/components/release-compile.ts +17 -3
- package/src/components/release-gate.ts +18 -4
- package/src/components/wave-plan.ts +45 -2
- package/src/config.ts +8 -0
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.ts +2 -1
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.ts +37 -6
- package/src/lifecycle/gate-ledger.ts +2 -2
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +74 -2
- package/src/lifecycle/release-cut.ts +158 -22
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-ledger.ts +9 -3
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +15 -4
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/lifecycle/run-ledger.ts +7 -1
- package/src/op/activities/activity-contracts.ts +1 -0
- package/src/op/activities/shell.ts +7 -1
- package/src/op/local-executor.ts +27 -4
- package/src/op/runtime.ts +42 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/pr-forge.test.ts +25 -0
- package/src/pr-forge.ts +62 -0
- package/src/pr-loop.test.ts +13 -1
- package/src/pr-loop.ts +41 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.ts +63 -3
- package/src/run-event-stream.ts +8 -4
- package/src/run-event.ts +38 -1
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/change-set.schema.json +5 -1
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green-forge.ts +2 -0
- package/src/workspace/ci-green.ts +20 -5
- package/src/workspace/ci-run.test.ts +416 -0
- package/src/workspace/ci-run.ts +571 -0
- package/src/workspace/ci-serve.test.ts +268 -0
- package/src/workspace/ci-serve.ts +521 -0
- package/src/workspace/declaration.schema.json +64 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +38 -0
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +148 -25
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/pr-report.schema.json +5 -0
- package/src/workspace/read-contract.e2e.test.ts +2 -1
- package/src/workspace/reason-codes.test.ts +5 -0
- package/src/workspace/reason-codes.ts +3 -0
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +123 -1
- package/src/workspace/releases.ts +11 -0
- package/src/workspace/run-event.schema.json +24 -6
- package/src/workspace/status-contract.test.ts +29 -0
- package/src/workspace/status-gates.ts +8 -0
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status-stewards.ts +3 -0
- package/src/workspace/status.schema.json +210 -2
- package/src/workspace/status.ts +254 -3
- package/src/workspace/wip.ts +11 -2
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/** #4036: `chant lifecycle gc [--yes] [--push] [--json]`, in temp repos. */
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
8
|
+
import type { ParsedArgs } from "../registry";
|
|
9
|
+
import { runLifecycleGc } from "./lifecycle-gc";
|
|
10
|
+
import { writeLedgerFiles } from "../../lifecycle/git";
|
|
11
|
+
import { makeRelease } from "../../lifecycle/release-store";
|
|
12
|
+
import type { GcReport } from "../../lifecycle/retention";
|
|
13
|
+
|
|
14
|
+
function args(o: Partial<ParsedArgs>): ParsedArgs {
|
|
15
|
+
return { command: "lifecycle", path: ".", format: "", fix: false, watch: false, verbose: false, help: false, live: false, ...o };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const git = (cwd: string, ...a: string[]) => execFileSync("git", a, { cwd, encoding: "utf-8" }).trim();
|
|
19
|
+
|
|
20
|
+
let dir: string;
|
|
21
|
+
let cwd: string;
|
|
22
|
+
let out: string[];
|
|
23
|
+
let err: string[];
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
cwd = process.cwd();
|
|
26
|
+
dir = realpathSync(mkdtempSync(join(tmpdir(), "lifecycle-gc-")));
|
|
27
|
+
vi.stubEnv("CHANT_RUN_EVENTS_DIR", join(dir, ".events"));
|
|
28
|
+
git(dir, "init", "-q", "-b", "main");
|
|
29
|
+
git(dir, "config", "user.email", "t@t");
|
|
30
|
+
git(dir, "config", "user.name", "t");
|
|
31
|
+
process.chdir(dir);
|
|
32
|
+
out = [];
|
|
33
|
+
err = [];
|
|
34
|
+
vi.spyOn(console, "log").mockImplementation((s: string) => void out.push(String(s)));
|
|
35
|
+
vi.spyOn(console, "error").mockImplementation((s: string) => void err.push(String(s)));
|
|
36
|
+
});
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
vi.unstubAllEnvs();
|
|
40
|
+
process.chdir(cwd);
|
|
41
|
+
rmSync(dir, { recursive: true, force: true });
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
function commitAll(): string {
|
|
45
|
+
writeFileSync(join(dir, "README.md"), "x\n");
|
|
46
|
+
git(dir, "add", ".");
|
|
47
|
+
git(dir, "commit", "-q", "-m", "init");
|
|
48
|
+
return git(dir, "rev-parse", "HEAD");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const changeSet = `_change-sets/jcs1-sha256_${"1".repeat(64)}.json`;
|
|
52
|
+
const ctx = (o: Partial<ParsedArgs>) => ({ args: args(o), plugins: [], serializers: [] });
|
|
53
|
+
const report = (): GcReport => JSON.parse(out.join("\n")) as GcReport;
|
|
54
|
+
|
|
55
|
+
describe("chant lifecycle gc", () => {
|
|
56
|
+
test("a dry run by default; --yes prunes in one local commit and pushes nothing", async () => {
|
|
57
|
+
writeFileSync(join(dir, "chant.workspace.json"), JSON.stringify({ name: "w", schema: 1, members: [], retention: { changeSets: { days: 0 } } }));
|
|
58
|
+
commitAll();
|
|
59
|
+
await writeLedgerFiles([{ path: changeSet, content: "{}\n", expectPriorSha: null }], "fixture", { cwd: dir });
|
|
60
|
+
const tip = git(dir, "rev-parse", "chant/lifecycle");
|
|
61
|
+
|
|
62
|
+
expect(await runLifecycleGc(ctx({}))).toBe(0);
|
|
63
|
+
expect(out[0]).toMatch(/^chant\/lifecycle at [0-9a-f]{12}: would remove 1 change set\(s\), 0 release\(s\) and 0 template\(s\)/);
|
|
64
|
+
expect(err.join("\n")).toContain("Dry run: nothing was removed");
|
|
65
|
+
expect(git(dir, "rev-parse", "chant/lifecycle")).toBe(tip);
|
|
66
|
+
|
|
67
|
+
out = [];
|
|
68
|
+
expect(await runLifecycleGc(ctx({ yes: true, json: true }))).toBe(0);
|
|
69
|
+
const r = report();
|
|
70
|
+
expect(r.dryRun).toBe(false);
|
|
71
|
+
expect(r.policy.changeSets.days).toBe(0);
|
|
72
|
+
expect(r.lifecycle.remove.map((f) => f.path)).toEqual([changeSet]);
|
|
73
|
+
expect(r.lifecycle.commit).toBe(git(dir, "rev-parse", "chant/lifecycle"));
|
|
74
|
+
expect(git(dir, "rev-parse", "chant/lifecycle^")).toBe(tip);
|
|
75
|
+
expect(r.lifecycle.pushed).toBe(false);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("outside a workspace the defaults apply, and a release of a revoked commit is not promotable", async () => {
|
|
79
|
+
const head = commitAll();
|
|
80
|
+
const cut = (commit: string) => {
|
|
81
|
+
const r = makeRelease({ member: null, commit, artifactDigests: [], targets: [{ env: "prod", templates: [], params: [] }] });
|
|
82
|
+
return { r, line: JSON.stringify({ version: 1, releaseDigest: r.releaseDigest, label: null, actor: "a", timestamp: new Date().toISOString(), runOrigin: null, signature: null }) };
|
|
83
|
+
};
|
|
84
|
+
const revoked = cut(head);
|
|
85
|
+
const fine = cut("b".repeat(40));
|
|
86
|
+
const file = (d: string) => `_releases/${d.replace(/:/g, "_")}.json`;
|
|
87
|
+
await writeLedgerFiles(
|
|
88
|
+
[
|
|
89
|
+
{ path: changeSet, content: "{}\n" },
|
|
90
|
+
{ path: file(revoked.r.releaseDigest), content: `${JSON.stringify(revoked.r)}\n` },
|
|
91
|
+
{ path: file(fine.r.releaseDigest), content: `${JSON.stringify(fine.r)}\n` },
|
|
92
|
+
{ path: "_releases/cuts.jsonl", content: `${revoked.line}\n${fine.line}\n` },
|
|
93
|
+
],
|
|
94
|
+
"fixture",
|
|
95
|
+
{ cwd: dir },
|
|
96
|
+
);
|
|
97
|
+
git(dir, "tag", `ci/revoked/${head}`, head);
|
|
98
|
+
|
|
99
|
+
expect(await runLifecycleGc(ctx({ json: true }))).toBe(0);
|
|
100
|
+
const r = report();
|
|
101
|
+
expect(r.policy.releases).toEqual({ days: 14, keep: 10 });
|
|
102
|
+
expect(r.lifecycle.remove.map((f) => f.path)).toEqual([file(revoked.r.releaseDigest)]);
|
|
103
|
+
expect(r.lifecycle.keep.map((f) => [f.path, f.reasons.map((x) => x.code)])).toEqual(
|
|
104
|
+
expect.arrayContaining([[changeSet, ["within-window"]], [file(fine.r.releaseDigest), ["newest", "within-window"]]]),
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("a workspace whose declaration can't be read prunes nothing", async () => {
|
|
109
|
+
writeFileSync(join(dir, "chant.workspace.json"), JSON.stringify({ name: "w", schema: 1, members: [], retention: { changeSets: { days: -1 } } }));
|
|
110
|
+
commitAll();
|
|
111
|
+
expect(await runLifecycleGc(ctx({ yes: true }))).toBe(1);
|
|
112
|
+
expect(err.join("\n")).toContain("its retention policy is unknown and nothing is pruned");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("a directory outside git is refused", async () => {
|
|
116
|
+
const plain = realpathSync(mkdtempSync(join(tmpdir(), "lifecycle-gc-plain-")));
|
|
117
|
+
try {
|
|
118
|
+
process.chdir(plain);
|
|
119
|
+
expect(await runLifecycleGc(ctx({}))).toBe(1);
|
|
120
|
+
expect(err.join("\n")).toContain("not in a git repository");
|
|
121
|
+
} finally {
|
|
122
|
+
process.chdir(dir);
|
|
123
|
+
rmSync(plain, { recursive: true, force: true });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant lifecycle gc [--yes] [--push] [--json] [--verbose]` (#4036): prune
|
|
3
|
+
* stored change sets, releases and their templates from `chant/lifecycle`, and
|
|
4
|
+
* run event files from this host, by the retention policy.
|
|
5
|
+
*
|
|
6
|
+
* Without `--yes` it is a dry run: it prints what it would remove and changes
|
|
7
|
+
* nothing. With `--yes` it writes one commit to the local `chant/lifecycle`
|
|
8
|
+
* that removes those files, and deletes the run event files. It pushes only
|
|
9
|
+
* with `--push`. History is never rewritten, so a pruned file stays in the
|
|
10
|
+
* branch's earlier commits. The rules are in ../../lifecycle/retention.ts.
|
|
11
|
+
*
|
|
12
|
+
* The policy is the defaults, overridden by the `retention` block of the
|
|
13
|
+
* workspace declaration at base when the checkout is a workspace. The prune
|
|
14
|
+
* covers the whole branch, every member, from wherever in the repository it
|
|
15
|
+
* runs.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { execFileSync } from "node:child_process";
|
|
19
|
+
import { existsSync } from "node:fs";
|
|
20
|
+
import { dirname, join, resolve } from "node:path";
|
|
21
|
+
import { formatError, formatSuccess, formatWarning } from "../format";
|
|
22
|
+
import type { CommandContext } from "../registry";
|
|
23
|
+
import type { RetentionPolicy } from "../../lifecycle/retention-policy";
|
|
24
|
+
|
|
25
|
+
const USAGE = "chant lifecycle gc [--yes] [--push] [--json] [--verbose]";
|
|
26
|
+
|
|
27
|
+
function tryGit(cwd: string, args: string[]): string | null {
|
|
28
|
+
try {
|
|
29
|
+
return execFileSync("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Whether a workspace declaration sits in `cwd` or a directory above it, up to the repository's top. */
|
|
36
|
+
function inWorkspace(cwd: string, top: string): boolean {
|
|
37
|
+
for (let dir = cwd; ; dir = dirname(dir)) {
|
|
38
|
+
if (existsSync(join(dir, "chant.workspace.json")) || existsSync(join(dir, "chant.workspace.jsonc"))) return true;
|
|
39
|
+
if (dir === top || dirname(dir) === dir) return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Every commit with a `ci/revoked/<sha>` tag, counted as `chant release cut` counts it. */
|
|
44
|
+
async function revokedCommits(cwd: string, top: string, green: import("../../workspace/declaration").CiGreen | null, workspace: boolean): Promise<Set<string>> {
|
|
45
|
+
if (workspace) {
|
|
46
|
+
const { readCiTags } = await import("../../workspace/ci-green");
|
|
47
|
+
const base = green
|
|
48
|
+
? (tryGit(top, ["rev-parse", "--verify", "--quiet", `refs/remotes/origin/${green.branch}^{commit}`]) ??
|
|
49
|
+
tryGit(top, ["rev-parse", "--verify", "--quiet", `refs/heads/${green.branch}^{commit}`]) ??
|
|
50
|
+
tryGit(top, ["rev-parse", "HEAD"]))
|
|
51
|
+
: null;
|
|
52
|
+
return new Set(readCiTags(top, green, base).revoked.keys());
|
|
53
|
+
}
|
|
54
|
+
// With no declaration every tag counts, as readCiTags counts them without `ci.green`.
|
|
55
|
+
const out = tryGit(cwd, ["for-each-ref", "--format=%(refname:lstrip=4)", "refs/tags/ci/revoked/"]) ?? "";
|
|
56
|
+
return new Set(out.split("\n").filter((s) => /^[0-9a-f]{40,64}$/.test(s)));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const kb = (bytes: number): string => (bytes >= 1024 * 1024 ? `${(bytes / 1024 / 1024).toFixed(1)} MB` : `${(bytes / 1024).toFixed(1)} KB`);
|
|
60
|
+
|
|
61
|
+
export async function runLifecycleGc(ctx: CommandContext): Promise<number> {
|
|
62
|
+
const { args } = ctx;
|
|
63
|
+
if (args.extraPositional) {
|
|
64
|
+
console.error(formatError({ message: `lifecycle gc takes no argument, and was given "${args.extraPositional}"`, hint: USAGE }));
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
const cwd = resolve(".");
|
|
68
|
+
const top = tryGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
69
|
+
if (!top) {
|
|
70
|
+
console.error(formatError({ message: "lifecycle gc prunes the chant/lifecycle branch, and this directory is not in a git repository", hint: USAGE }));
|
|
71
|
+
return 1;
|
|
72
|
+
}
|
|
73
|
+
const dryRun = args.yes !== true;
|
|
74
|
+
|
|
75
|
+
const { resolveRetention } = await import("../../lifecycle/retention-policy");
|
|
76
|
+
let policy: RetentionPolicy = resolveRetention(null);
|
|
77
|
+
let green: import("../../workspace/declaration").CiGreen | null = null;
|
|
78
|
+
const workspace = inWorkspace(cwd, top);
|
|
79
|
+
if (workspace) {
|
|
80
|
+
const { scopeSource } = await import("../../workspace/write-scope");
|
|
81
|
+
const source = scopeSource(cwd);
|
|
82
|
+
if (!source.declaration) {
|
|
83
|
+
console.error(formatError({ message: `the workspace declaration can't be read, so its retention policy is unknown and nothing is pruned: ${source.failure?.message ?? "no declaration"}` }));
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
policy = resolveRetention(source.declaration.retention);
|
|
87
|
+
green = source.declaration.ci?.green ?? null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Read the remote's facts first: a prune planned on a stale branch could
|
|
91
|
+
// remove what a newer gate binds, and a forked branch must not be pushed over.
|
|
92
|
+
const { fetchLifecycleStatus } = await import("../../lifecycle/git");
|
|
93
|
+
const fetched = await fetchLifecycleStatus({ cwd });
|
|
94
|
+
if (fetched.status === "diverged" || fetched.status === "unreachable") {
|
|
95
|
+
const why = fetched.status === "diverged" ? `the local chant/lifecycle and ${fetched.remote}'s have forked` : `${fetched.remote} did not answer: ${fetched.stderr}`;
|
|
96
|
+
if (!dryRun) {
|
|
97
|
+
console.error(formatError({ message: `nothing was pruned: ${why}`, hint: "Bring chant/lifecycle up to date with its remote, then run again." }));
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
100
|
+
if (!args.json) console.error(formatWarning({ message: `planned on the local branch only: ${why}` }));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const { runLifecycleGc: run } = await import("../../lifecycle/retention");
|
|
104
|
+
const report = await run({
|
|
105
|
+
cwd,
|
|
106
|
+
policy,
|
|
107
|
+
now: new Date(),
|
|
108
|
+
revokedCommits: await revokedCommits(cwd, top, green, workspace),
|
|
109
|
+
dryRun,
|
|
110
|
+
push: args.push === true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (args.json) {
|
|
114
|
+
console.log(JSON.stringify(report, null, 2));
|
|
115
|
+
return 0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const lc = report.lifecycle;
|
|
119
|
+
const count = (list: ReadonlyArray<{ kind: string }>, kind: string) => list.filter((f) => f.kind === kind).length;
|
|
120
|
+
const bytes = lc.remove.reduce((n, f) => n + f.bytes, 0);
|
|
121
|
+
const verb = dryRun ? "would remove" : "removed";
|
|
122
|
+
if (!lc.tip) {
|
|
123
|
+
console.error("No chant/lifecycle branch: nothing stored to prune.");
|
|
124
|
+
} else {
|
|
125
|
+
console.log(
|
|
126
|
+
`chant/lifecycle at ${lc.tip.slice(0, 12)}: ${verb} ${count(lc.remove, "change-set")} change set(s), ${count(lc.remove, "release")} release(s) and ${count(lc.remove, "template")} template(s), ${kb(bytes)}; ` +
|
|
127
|
+
`keeps ${count(lc.keep, "change-set")}, ${count(lc.keep, "release")} and ${count(lc.keep, "template")}`,
|
|
128
|
+
);
|
|
129
|
+
console.log(` tree at the tip: ${lc.before.files} file(s), ${kb(lc.before.bytes)} before; ${lc.after.files} file(s), ${kb(lc.after.bytes)} after. The ledgers (${kb(lc.before.ledgers.bytes)}) are never pruned, and history keeps every removed file.`);
|
|
130
|
+
if (args.verbose) for (const f of lc.remove) console.log(` - ${f.path}`);
|
|
131
|
+
}
|
|
132
|
+
const ev = report.runEvents;
|
|
133
|
+
if (ev.dir) {
|
|
134
|
+
console.log(` run events in ${ev.dir}: ${verb} ${ev.remove.length} file(s), ${kb(ev.remove.reduce((n, f) => n + f.bytes, 0))}; keeps ${ev.kept.files}`);
|
|
135
|
+
if (args.verbose) for (const f of ev.remove) console.log(` - ${f.file}`);
|
|
136
|
+
}
|
|
137
|
+
if (dryRun) {
|
|
138
|
+
console.error("Dry run: nothing was removed. Run again with --yes to prune.");
|
|
139
|
+
} else if (lc.commit) {
|
|
140
|
+
console.error(formatSuccess(`Pruned in commit ${lc.commit.slice(0, 12)} on chant/lifecycle`));
|
|
141
|
+
if (lc.pushed) console.error(" pushed");
|
|
142
|
+
else if (lc.pushWarning) console.error(formatWarning({ message: lc.pushWarning }));
|
|
143
|
+
else console.error(" not pushed: run with --push, or push chant/lifecycle yourself");
|
|
144
|
+
}
|
|
145
|
+
return 0;
|
|
146
|
+
}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { mkdtemp, rm } from "node:fs/promises";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
11
12
|
import { join } from "node:path";
|
|
12
13
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
|
13
14
|
import type { DriverComponent } from "../../components/driver";
|
|
@@ -58,6 +59,8 @@ vi.mock("../../op/gate", async () => {
|
|
|
58
59
|
});
|
|
59
60
|
|
|
60
61
|
const failing = new Set<string>();
|
|
62
|
+
/** Called as a component's apply starts, before it can fail. */
|
|
63
|
+
let onRun: (root: string) => void = () => {};
|
|
61
64
|
vi.mock("../../components/fan-out-support", async () => {
|
|
62
65
|
const { CapabilityRegistry } = await import("../../components/capability");
|
|
63
66
|
const { planFanOut } = await import("../../components/fan-out");
|
|
@@ -78,6 +81,7 @@ vi.mock("../../components/fan-out-support", async () => {
|
|
|
78
81
|
return {};
|
|
79
82
|
},
|
|
80
83
|
async run(_ctx: DeployContext, input: { root: string }) {
|
|
84
|
+
onRun(input.root);
|
|
81
85
|
if (failing.has(input.root)) throw new Error(`${input.root} failed`);
|
|
82
86
|
return { outputs: {} };
|
|
83
87
|
},
|
|
@@ -106,6 +110,7 @@ const { checkRunEventLines, stepTrail } = await import("../../workspace/__fixtur
|
|
|
106
110
|
|
|
107
111
|
let dir: string;
|
|
108
112
|
let out: string[];
|
|
113
|
+
let errors: string[];
|
|
109
114
|
|
|
110
115
|
const ctx = (a: Partial<ParsedArgs>) =>
|
|
111
116
|
({ args: { command: "components", path: "pr-apply", format: "", fix: false, watch: false, verbose: false, help: false, live: false, base: "main", env: "prod", output: dir, eventsJson: true, ...a } as ParsedArgs, plugins: [] }) as never;
|
|
@@ -113,10 +118,12 @@ const ctx = (a: Partial<ParsedArgs>) =>
|
|
|
113
118
|
beforeEach(async () => {
|
|
114
119
|
dir = await mkdtemp(join(tmpdir(), "pr-events-"));
|
|
115
120
|
out = [];
|
|
121
|
+
errors = [];
|
|
116
122
|
failing.clear();
|
|
123
|
+
onRun = () => {};
|
|
117
124
|
gateLedger = memoryGateLedgerPort();
|
|
118
125
|
vi.spyOn(process.stdout, "write").mockImplementation((chunk: string | Uint8Array) => { out.push(String(chunk)); return true; });
|
|
119
|
-
vi.spyOn(console, "error").mockImplementation(() => {});
|
|
126
|
+
vi.spyOn(console, "error").mockImplementation((...m: unknown[]) => { errors.push(m.map(String).join(" ")); });
|
|
120
127
|
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
121
128
|
});
|
|
122
129
|
|
|
@@ -182,6 +189,146 @@ describe("pr-apply --events-json", () => {
|
|
|
182
189
|
});
|
|
183
190
|
});
|
|
184
191
|
|
|
192
|
+
/** Plan pull request 12 and approve its digest, as a reviewer would. */
|
|
193
|
+
async function planAndApprove(): Promise<void> {
|
|
194
|
+
await runComponentsPrPlan(ctx({ pr: 12 }));
|
|
195
|
+
const digest = await plannedDigest();
|
|
196
|
+
gateLedger = memoryGateLedgerPort({
|
|
197
|
+
resolutions: [{ version: 1, kind: "resolution", op: "pr-12", gate: "pr-apply", resolvedBy: "github:alice", timestamp: new Date().toISOString(), planDigest: digest }],
|
|
198
|
+
});
|
|
199
|
+
out = [];
|
|
200
|
+
errors = [];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** The job log's wave and component lines, without colour. */
|
|
204
|
+
const logLines = (): string[] =>
|
|
205
|
+
errors.map((l) => l.replace(/\u001b\[[0-9;]*m/g, "")).filter((l) => /^wave \d|^ {2}\S+: /.test(l));
|
|
206
|
+
|
|
207
|
+
describe("pr-apply's job log (#4007)", () => {
|
|
208
|
+
test("without --events-json, a line as each wave starts and as each component settles, in order", async () => {
|
|
209
|
+
await planAndApprove();
|
|
210
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, eventsJson: false }))).toBe(0);
|
|
211
|
+
expect(out).toEqual([]);
|
|
212
|
+
expect(logLines()).toEqual(["wave 1/2: net", " net: applied", "wave 2/2: app", " app: applied"]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a failed dependency: the component behind it settles blocked, naming it, in the log and the stream", async () => {
|
|
216
|
+
await planAndApprove();
|
|
217
|
+
failing.add("net");
|
|
218
|
+
expect(await runComponentsPrApply(ctx({ pr: 12 }))).toBe(1);
|
|
219
|
+
expect(logLines()).toEqual(["wave 1/2: net", " net: failed", " app: blocked by net, not run"]);
|
|
220
|
+
const events = checkRunEventLines(out);
|
|
221
|
+
const blocked = events.find((e) => e.type === "component-settle" && e.component === "app");
|
|
222
|
+
expect(blocked).toMatchObject({ type: "component-settle", wave: 2, component: "app", status: "blocked", blockedBy: "net" });
|
|
223
|
+
expect(events.some((e) => e.type === "component-start" && e.component === "app")).toBe(false);
|
|
224
|
+
expect(events.some((e) => e.type === "wave-start" && e.wave === 2)).toBe(false);
|
|
225
|
+
expect(events.at(-1)).toMatchObject({ type: "run-settle", status: "failed" });
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("a resumed apply settles what the earlier attempt applied as already-applied, before the gate", async () => {
|
|
229
|
+
await planAndApprove();
|
|
230
|
+
const resume = join(dir, "attempt.json");
|
|
231
|
+
failing.add("app");
|
|
232
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume, eventsJson: false }))).toBe(1);
|
|
233
|
+
failing.clear();
|
|
234
|
+
out = [];
|
|
235
|
+
errors = [];
|
|
236
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume }))).toBe(0);
|
|
237
|
+
const events = checkRunEventLines(out);
|
|
238
|
+
const settles = events.filter((e) => e.type === "component-settle").map((e) => [e.component, (e as { status: string }).status, (e as { wave: number }).wave]);
|
|
239
|
+
expect(settles).toEqual([["net", "already-applied", 1], ["app", "ok", 1]]);
|
|
240
|
+
const gate = events.findIndex((e) => e.type === "phase-start" && e.phase === "gate");
|
|
241
|
+
expect(events.findIndex((e) => e.type === "component-settle" && e.component === "net")).toBeLessThan(gate);
|
|
242
|
+
expect(logLines()).toEqual([" net: already applied in an earlier attempt", "wave 1/1: app", " app: applied"]);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe("--resume carries only an unchanged input (#4081)", () => {
|
|
247
|
+
test("a component whose input moved since the attempt runs again, and says why", async () => {
|
|
248
|
+
await planAndApprove();
|
|
249
|
+
const resume = join(dir, "attempt.json");
|
|
250
|
+
failing.add("app");
|
|
251
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume, eventsJson: false }))).toBe(1);
|
|
252
|
+
expect(JSON.parse(readFileSync(resume, "utf8")).inputs).toEqual({ net: expect.stringMatching(/^jcs1-sha256:/) });
|
|
253
|
+
failing.clear();
|
|
254
|
+
errors = [];
|
|
255
|
+
const step = ESTATE[0]!.deploy[0]!.steps[0] as Record<string, unknown>;
|
|
256
|
+
step.note = "a different change of the same shape";
|
|
257
|
+
const ran: string[] = [];
|
|
258
|
+
onRun = (name) => ran.push(name);
|
|
259
|
+
try {
|
|
260
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume, eventsJson: false }))).toBe(0);
|
|
261
|
+
} finally {
|
|
262
|
+
delete step.note;
|
|
263
|
+
}
|
|
264
|
+
expect(ran).toEqual(["net", "app"]);
|
|
265
|
+
expect(errors.join("\n")).toContain("net: changed since the earlier attempt, so it runs again");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("a record without input digests does not carry", async () => {
|
|
269
|
+
await planAndApprove();
|
|
270
|
+
const resume = join(dir, "attempt.json");
|
|
271
|
+
failing.add("app");
|
|
272
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume, eventsJson: false }))).toBe(1);
|
|
273
|
+
const { writeFileSync } = await import("node:fs");
|
|
274
|
+
const record = JSON.parse(readFileSync(resume, "utf8"));
|
|
275
|
+
delete record.inputs;
|
|
276
|
+
writeFileSync(resume, JSON.stringify(record));
|
|
277
|
+
failing.clear();
|
|
278
|
+
errors = [];
|
|
279
|
+
const ran: string[] = [];
|
|
280
|
+
onRun = (name) => ran.push(name);
|
|
281
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, resume, eventsJson: false }))).toBe(0);
|
|
282
|
+
expect(ran).toEqual(["net", "app"]);
|
|
283
|
+
expect(errors.join("\n")).toMatch(/does not carry: it does not record what each component applied/);
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe("--report-url (#4007)", () => {
|
|
288
|
+
test("refuses a URL it can't upload to, before anything plans", async () => {
|
|
289
|
+
expect(await runComponentsPrPlan(ctx({ pr: 12, reportUrl: "https://example.invalid/reports" }))).toBe(1);
|
|
290
|
+
expect(errors.join("\n")).toMatch(/takes an s3:\/\/<bucket>\/<prefix> or file:\/\/<directory> URL/);
|
|
291
|
+
expect(out).toEqual([]);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("file://: the report, the change set and the events land under <pr>/<commit>/, the events as each wave settles", async () => {
|
|
295
|
+
const { pathToFileURL } = await import("node:url");
|
|
296
|
+
const { readFile } = await import("node:fs/promises");
|
|
297
|
+
const reports = join(dir, "reports");
|
|
298
|
+
const reportUrl = pathToFileURL(reports).href;
|
|
299
|
+
await runComponentsPrPlan(ctx({ pr: 12, reportUrl }));
|
|
300
|
+
const at = join(reports, "12", HEAD);
|
|
301
|
+
expect(JSON.parse(await readFile(join(at, "pr-plan.json"), "utf8"))).toMatchObject({ stage: "plan", pr: 12, status: "planned" });
|
|
302
|
+
expect(checkRunEventLines((await readFile(join(at, "pr-plan.events.jsonl"), "utf8")).split(/(?<=\n)/))[0]).toMatchObject({ type: "run-start" });
|
|
303
|
+
|
|
304
|
+
await planAndApprove();
|
|
305
|
+
// What a reader of the URL sees while wave 2 applies: wave 1 settled.
|
|
306
|
+
let seenMidRun: string[] = [];
|
|
307
|
+
onRun = (root) => {
|
|
308
|
+
if (root === "app") seenMidRun = readFileSync(join(at, "pr-apply.events.jsonl"), "utf8").split(/(?<=\n)/);
|
|
309
|
+
};
|
|
310
|
+
expect(await runComponentsPrApply(ctx({ pr: 12, reportUrl }))).toBe(0);
|
|
311
|
+
const mid = seenMidRun.map((l) => JSON.parse(l) as { type: string; wave?: number; status?: string });
|
|
312
|
+
expect(mid.some((e) => e.type === "wave-settle" && e.wave === 1 && e.status === "ok")).toBe(true);
|
|
313
|
+
expect(mid.some((e) => e.type === "run-settle")).toBe(false);
|
|
314
|
+
|
|
315
|
+
const final = checkRunEventLines((await readFile(join(at, "pr-apply.events.jsonl"), "utf8")).split(/(?<=\n)/));
|
|
316
|
+
expect(final.at(-1)).toMatchObject({ type: "run-settle", status: "ok" });
|
|
317
|
+
expect(final.map((e) => e.type).join("\n")).toBe(out.map((l) => JSON.parse(l).type).join("\n"));
|
|
318
|
+
expect(JSON.parse(await readFile(join(at, "pr-apply.json"), "utf8"))).toMatchObject({ stage: "apply", status: "applied" });
|
|
319
|
+
expect(JSON.parse(await readFile(join(at, "change-set.json"), "utf8"))).toHaveProperty("digest");
|
|
320
|
+
expect(errors.join("\n")).toContain(`report : ${reportUrl}/12/${HEAD}/`);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("without --events-json, only the report and the change set are copied", async () => {
|
|
324
|
+
const { pathToFileURL } = await import("node:url");
|
|
325
|
+
const { readdir } = await import("node:fs/promises");
|
|
326
|
+
const reports = join(dir, "reports");
|
|
327
|
+
await runComponentsPrPlan(ctx({ pr: 12, reportUrl: pathToFileURL(reports).href, eventsJson: false }));
|
|
328
|
+
expect((await readdir(join(reports, "12", HEAD))).sort()).toEqual(["change-set.json", "pr-plan.json"]);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
|
|
185
332
|
describe("--events-json beside --json", () => {
|
|
186
333
|
test("is refused on pr-plan and pr-apply before anything plans, since both print to stdout", async () => {
|
|
187
334
|
const errors: string[] = [];
|