@intentius/chant 0.124.27 → 0.124.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/dist/__golden__/wave-loop/op-held-on.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/dist/__golden__/wave-loop/op-per-run.json +7 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
- package/dist/__golden__/wave-loop/op-sealed.json +3 -0
- package/dist/__golden__/wave-loop/op-spend.json +3 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +63 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +130 -0
- package/dist/change-set.d.ts +62 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +36 -4
- package/dist/chant-version.d.ts +9 -0
- package/dist/chant-version.d.ts.map +1 -0
- package/dist/chant-version.js +16 -0
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +15 -11
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.js +30 -2
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +13 -2
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.js +17 -8
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +64 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +43 -4
- package/dist/cli/handlers/change-set.d.ts +10 -0
- package/dist/cli/handlers/change-set.d.ts.map +1 -1
- package/dist/cli/handlers/change-set.js +51 -1
- package/dist/cli/handlers/fan-out.d.ts +10 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +54 -6
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +16 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +269 -37
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +440 -28
- package/dist/cli/handlers/release.d.ts +91 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +589 -0
- package/dist/cli/handlers/run.d.ts +38 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +234 -16
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +143 -17
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +73 -5
- package/dist/cli/registry.d.ts +43 -5
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +30 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +49 -0
- package/dist/cli/version.d.ts +3 -5
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +3 -13
- package/dist/codegen/docs-in-site.d.ts +71 -0
- package/dist/codegen/docs-in-site.d.ts.map +1 -0
- package/dist/codegen/docs-in-site.js +177 -0
- package/dist/codegen/docs-types.d.ts +14 -1
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +18 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/docs.js +40 -7
- package/dist/codegen/rehype-base-url.mjs +15 -9
- package/dist/components/auto-release.d.ts +6 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +6 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +10 -9
- package/dist/components/driver.d.ts +50 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +116 -54
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts +7 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +85 -26
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- package/dist/components/pr-run.d.ts +11 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +20 -1
- package/dist/components/promote-order.d.ts +38 -0
- package/dist/components/promote-order.d.ts.map +1 -0
- package/dist/components/promote-order.js +62 -0
- package/dist/components/promote.d.ts +38 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +77 -2
- package/dist/components/release-compile.d.ts +142 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +382 -0
- package/dist/components/release-gate.d.ts +135 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +288 -0
- package/dist/components/release-promote.d.ts +96 -0
- package/dist/components/release-promote.d.ts.map +1 -0
- package/dist/components/release-promote.js +204 -0
- package/dist/components/run-progress.d.ts +7 -0
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/wave-plan.d.ts +8 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +11 -3
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +68 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +97 -0
- package/dist/lifecycle/commit-sha.d.ts +13 -0
- package/dist/lifecycle/commit-sha.d.ts.map +1 -0
- package/dist/lifecycle/commit-sha.js +21 -0
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +78 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +138 -0
- package/dist/lifecycle/gate-ledger.d.ts +51 -4
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +154 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +366 -0
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-ledger.d.ts +54 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +53 -20
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +200 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +320 -0
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +2 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.js +4 -2
- package/dist/op/activities/source-release.d.ts.map +1 -1
- package/dist/op/activities/source-release.js +7 -3
- package/dist/op/activities/source-rollback.d.ts.map +1 -1
- package/dist/op/activities/source-rollback.js +7 -3
- package/dist/op/gate-name.d.ts +11 -2
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +12 -3
- package/dist/op/gate-run-locator.d.ts +22 -0
- package/dist/op/gate-run-locator.d.ts.map +1 -1
- package/dist/op/gate-run-locator.js +31 -0
- package/dist/op/gate.d.ts +27 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +57 -3
- package/dist/op/local-executor.d.ts +36 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +158 -52
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +4 -0
- package/dist/op/run-live.d.ts +2 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/run-live.js +1 -1
- package/dist/op/runtime.d.ts +46 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/runtimes/local.js +8 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/point-decision.d.ts +2 -0
- package/dist/point-decision.d.ts.map +1 -1
- package/dist/point-decision.js +9 -2
- package/dist/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +104 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +251 -0
- package/dist/run-event-stream.d.ts +133 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +242 -0
- package/dist/run-event.d.ts +285 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +209 -0
- package/dist/wave-runtime.mjs +262 -226
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/run-event-check.js +59 -0
- package/dist/workspace/change-set-show.d.ts +56 -0
- package/dist/workspace/change-set-show.d.ts.map +1 -0
- package/dist/workspace/change-set-show.js +54 -0
- package/dist/workspace/change-set-show.schema.json +102 -0
- package/dist/workspace/change-set.schema.json +32 -1
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes-cli.js +10 -1
- package/dist/workspace/changes.d.ts +37 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/changes.js +49 -2
- package/dist/workspace/changes.schema.json +135 -0
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green-cli.d.ts +19 -4
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +75 -12
- package/dist/workspace/ci-green-forge.d.ts +20 -3
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +6 -4
- package/dist/workspace/ci-green-workflow.d.ts +11 -0
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +6 -0
- package/dist/workspace/ci-green.d.ts +281 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +456 -38
- package/dist/workspace/ci-last-green.schema.json +32 -2
- package/dist/workspace/ci-list.schema.json +525 -0
- package/dist/workspace/commit-links.d.ts +85 -0
- package/dist/workspace/commit-links.d.ts.map +1 -0
- package/dist/workspace/commit-links.js +163 -0
- package/dist/workspace/declaration.d.ts +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +27 -1
- package/dist/workspace/declaration.schema.json +76 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/identity-gate-rule.d.ts +37 -0
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +72 -7
- package/dist/workspace/identity.d.ts +12 -0
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/identity.js +13 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +36 -7
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +192 -22
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -0
- package/dist/workspace/ls.schema.json +3 -1
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +6 -0
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/promotion-order.d.ts +41 -0
- package/dist/workspace/promotion-order.d.ts.map +1 -0
- package/dist/workspace/promotion-order.js +95 -0
- package/dist/workspace/reason-codes.d.ts +4 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +7 -1
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +81 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +103 -0
- package/dist/workspace/releases.schema.json +513 -0
- package/dist/workspace/run-event.schema.json +295 -0
- package/dist/workspace/status-gates.d.ts +34 -1
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +31 -2
- package/dist/workspace/status-stewards.d.ts +4 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +2 -0
- package/dist/workspace/status.d.ts +117 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +218 -4
- package/dist/workspace/status.schema.json +268 -2
- package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
- package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
- package/dist/workspace/trust/ci-tag-statement.js +123 -0
- package/dist/workspace/trust/op-run-statement.d.ts +91 -0
- package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
- package/dist/workspace/trust/op-run-statement.js +143 -0
- package/dist/workspace/trust/rotation.d.ts +18 -2
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.js +65 -16
- package/dist/workspace/trust/seal.d.ts +11 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +9 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/ssh-commit.js +10 -10
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/src/__golden__/wave-loop/fan-out-only.json +6 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/src/__golden__/wave-loop/op-cost-member.json +3 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/src/__golden__/wave-loop/op-held-on.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/src/__golden__/wave-loop/op-per-run.json +7 -0
- package/src/__golden__/wave-loop/op-pr-review.json +7 -0
- package/src/__golden__/wave-loop/op-sealed.json +3 -0
- package/src/__golden__/wave-loop/op-spend.json +3 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +141 -0
- package/src/change-set.ts +87 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +25 -11
- package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
- package/src/cli/commands/check-lexicon-docs.ts +39 -2
- package/src/cli/commands/check-lexicon.ts +14 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
- package/src/cli/commands/init-lexicon.test.ts +25 -0
- package/src/cli/commands/init-lexicon.ts +18 -8
- package/src/cli/handlers/approve-release.test.ts +84 -0
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +65 -2
- package/src/cli/handlers/build.test.ts +67 -0
- package/src/cli/handlers/build.ts +46 -4
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +228 -1
- package/src/cli/handlers/fan-out.ts +53 -6
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +341 -0
- package/src/cli/handlers/pr.ts +292 -47
- package/src/cli/handlers/promote.test.ts +669 -7
- package/src/cli/handlers/promote.ts +464 -29
- package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +199 -0
- package/src/cli/handlers/release.ts +593 -0
- package/src/cli/handlers/run-auto-release.test.ts +80 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +243 -18
- package/src/cli/main.test.ts +8 -0
- package/src/cli/main.ts +143 -17
- package/src/cli/mcp/never-over-mcp.test.ts +2 -0
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +56 -5
- package/src/cli/registry.ts +43 -5
- package/src/cli/run-events.ts +57 -0
- package/src/cli/version.ts +3 -13
- package/src/codegen/docs-in-site.test.ts +166 -0
- package/src/codegen/docs-in-site.ts +182 -0
- package/src/codegen/docs-site/AgentPrompt.astro +26 -1
- package/src/codegen/docs-types.ts +14 -1
- package/src/codegen/docs.ts +60 -7
- package/src/codegen/rehype-base-url.d.mts +3 -1
- package/src/codegen/rehype-base-url.mjs +15 -9
- package/src/codegen/rehype-base-url.test.ts +34 -0
- package/src/components/auto-release.test.ts +30 -0
- package/src/components/auto-release.ts +9 -1
- package/src/components/capability.ts +9 -0
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +165 -56
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +104 -35
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/pr-run.ts +30 -1
- package/src/components/promote-order.test.ts +90 -0
- package/src/components/promote-order.ts +75 -0
- package/src/components/promote.test.ts +10 -1
- package/src/components/promote.ts +94 -3
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +459 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +355 -0
- package/src/components/release-promote.test.ts +159 -0
- package/src/components/release-promote.ts +232 -0
- package/src/components/run-progress.ts +7 -0
- package/src/components/wave-plan.ts +16 -3
- package/src/config.ts +15 -1
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/index.ts +2 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +113 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +164 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +68 -3
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +483 -0
- package/src/lifecycle/release-cut.ts +503 -0
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +434 -0
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/lifecycle/run-ledger.test.ts +10 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/op/activities/change-coverage.ts +4 -1
- package/src/op/activities/source-release.ts +8 -3
- package/src/op/activities/source-rollback.ts +8 -4
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/gate-name.ts +13 -3
- package/src/op/gate-release.test.ts +126 -0
- package/src/op/gate-run-locator.test.ts +110 -0
- package/src/op/gate-run-locator.ts +35 -0
- package/src/op/gate.ts +88 -6
- package/src/op/local-executor.ts +207 -52
- package/src/op/op-waves-run.ts +4 -0
- package/src/op/run-events.test.ts +166 -0
- package/src/op/run-live.ts +1 -1
- package/src/op/runtime.ts +39 -0
- package/src/op/runtimes/local.test.ts +16 -0
- package/src/op/runtimes/local.ts +9 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/point-decision.ts +12 -2
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +273 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +309 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +471 -0
- package/src/wave-loop-golden.test.ts +9 -0
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/__fixtures__/run-event-check.ts +58 -0
- package/src/workspace/ancestry-index.test.ts +93 -0
- package/src/workspace/change-set-show.schema.json +102 -0
- package/src/workspace/change-set-show.ts +72 -0
- package/src/workspace/change-set.schema.json +32 -1
- package/src/workspace/changes-cli.ts +10 -1
- package/src/workspace/changes.schema.json +135 -0
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/changes.ts +84 -2
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green-cli.ts +75 -13
- package/src/workspace/ci-green-forge.ts +24 -4
- package/src/workspace/ci-green-runs.test.ts +242 -0
- package/src/workspace/ci-green-signed.test.ts +302 -0
- package/src/workspace/ci-green-workflow.test.ts +16 -0
- package/src/workspace/ci-green-workflow.ts +12 -0
- package/src/workspace/ci-green.test.ts +131 -2
- package/src/workspace/ci-green.ts +635 -42
- package/src/workspace/ci-last-green.schema.json +32 -2
- package/src/workspace/ci-list.schema.json +525 -0
- package/src/workspace/commit-links.test.ts +279 -0
- package/src/workspace/commit-links.ts +183 -0
- package/src/workspace/declaration.schema.json +76 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +60 -2
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +196 -26
- package/src/workspace/ls.schema.json +3 -1
- package/src/workspace/ls.ts +6 -0
- package/src/workspace/member-pipeline.test.ts +17 -0
- package/src/workspace/member-pipeline.ts +9 -0
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +5 -1
- package/src/workspace/reason-codes.test.ts +7 -1
- package/src/workspace/reason-codes.ts +7 -1
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +513 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +133 -0
- package/src/workspace/run-event.schema.json +295 -0
- package/src/workspace/status-contract.test.ts +81 -1
- package/src/workspace/status-gates.ts +64 -1
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +268 -2
- package/src/workspace/status.ts +334 -4
- package/src/workspace/trust/ci-tag-statement.ts +151 -0
- package/src/workspace/trust/op-run-statement.ts +148 -0
- package/src/workspace/trust/rotation.ts +61 -14
- package/src/workspace/trust/seal.ts +10 -0
- package/src/workspace/trust/ssh-commit.ts +10 -9
- package/src/workspace/wip.ts +11 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test stand-ins for the publish targets (#4047): an OCI registry fake that
|
|
3
|
+
* listens on 127.0.0.1 inside the test process and answers with a Bearer
|
|
4
|
+
* challenge as GHCR and Docker Hub do, an OCI image layout writer, a fake
|
|
5
|
+
* `npm` that records each call and keeps a registry with dist-tags in a file,
|
|
6
|
+
* and a forge fake with draft releases (#4048). Nothing here reaches a network or a real
|
|
7
|
+
* registry, and every server a test starts is closed by it.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "node:crypto";
|
|
10
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { createServer } from "node:http";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
const sha = (b) => `sha256:${createHash("sha256").update(b).digest("hex")}`;
|
|
14
|
+
/** A registry fake with a Bearer token service that accepts `username` and `password`. */
|
|
15
|
+
export async function startFakeRegistry(creds) {
|
|
16
|
+
const blobs = new Map();
|
|
17
|
+
const manifests = new Map();
|
|
18
|
+
const requests = [];
|
|
19
|
+
const state = { uploads: 0, port: 0 };
|
|
20
|
+
const read = (req) => new Promise((res) => {
|
|
21
|
+
const parts = [];
|
|
22
|
+
req.on("data", (c) => parts.push(c));
|
|
23
|
+
req.on("end", () => res(Buffer.concat(parts)));
|
|
24
|
+
});
|
|
25
|
+
const server = createServer(async (req, res) => {
|
|
26
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${state.port}`);
|
|
27
|
+
const body = await read(req);
|
|
28
|
+
requests.push({ method: req.method ?? "", path: url.pathname, authorization: req.headers.authorization ?? null });
|
|
29
|
+
const send = (status, headers = {}, text = "") => {
|
|
30
|
+
res.writeHead(status, headers);
|
|
31
|
+
res.end(text);
|
|
32
|
+
};
|
|
33
|
+
if (url.pathname === "/token") {
|
|
34
|
+
const basic = `Basic ${Buffer.from(`${creds.username}:${creds.password}`).toString("base64")}`;
|
|
35
|
+
if (req.headers.authorization !== basic)
|
|
36
|
+
return send(401, {}, "bad credentials");
|
|
37
|
+
return send(200, { "content-type": "application/json" }, JSON.stringify({ token: creds.token }));
|
|
38
|
+
}
|
|
39
|
+
if (req.headers.authorization !== `Bearer ${creds.token}`) {
|
|
40
|
+
return send(401, { "www-authenticate": `Bearer realm="http://127.0.0.1:${state.port}/token",service="fake-registry"` }, "unauthorized");
|
|
41
|
+
}
|
|
42
|
+
let m = url.pathname.match(/^\/v2\/(.+)\/blobs\/uploads\/(.*)$/);
|
|
43
|
+
if (m) {
|
|
44
|
+
if (req.method === "POST")
|
|
45
|
+
return send(202, { location: `/v2/${m[1]}/blobs/uploads/u${requests.length}?_state=s` });
|
|
46
|
+
const digest = url.searchParams.get("digest") ?? "";
|
|
47
|
+
if (req.method === "PUT" && sha(body) === digest && url.searchParams.get("_state") === "s") {
|
|
48
|
+
blobs.set(digest, body);
|
|
49
|
+
state.uploads++;
|
|
50
|
+
return send(201, { "docker-content-digest": digest });
|
|
51
|
+
}
|
|
52
|
+
return send(400, {}, "digest invalid");
|
|
53
|
+
}
|
|
54
|
+
m = url.pathname.match(/^\/v2\/(.+)\/blobs\/(sha256:[0-9a-f]+)$/);
|
|
55
|
+
if (m && req.method === "HEAD")
|
|
56
|
+
return send(blobs.has(m[2]) ? 200 : 404);
|
|
57
|
+
m = url.pathname.match(/^\/v2\/(.+)\/manifests\/(.+)$/);
|
|
58
|
+
if (m && (req.method === "HEAD" || req.method === "GET")) {
|
|
59
|
+
const found = manifests.get(m[2]);
|
|
60
|
+
return found ? send(200, { "content-type": found.type, "docker-content-digest": sha(found.bytes) }, req.method === "GET" ? found.bytes.toString("utf-8") : "") : send(404);
|
|
61
|
+
}
|
|
62
|
+
if (m && req.method === "PUT") {
|
|
63
|
+
const ref = m[2];
|
|
64
|
+
if (ref.startsWith("sha256:") && sha(body) !== ref)
|
|
65
|
+
return send(400, {}, "manifest digest invalid");
|
|
66
|
+
const parsed = JSON.parse(body.toString("utf-8"));
|
|
67
|
+
for (const d of [parsed.config, ...(parsed.layers ?? [])])
|
|
68
|
+
if (d && !blobs.has(d.digest))
|
|
69
|
+
return send(400, {}, `blob unknown ${d.digest}`);
|
|
70
|
+
manifests.set(ref, { name: m[1], type: String(req.headers["content-type"]), bytes: body });
|
|
71
|
+
return send(201, { "docker-content-digest": sha(body) });
|
|
72
|
+
}
|
|
73
|
+
return send(404, {}, "not found");
|
|
74
|
+
});
|
|
75
|
+
await new Promise((r) => server.listen(0, "127.0.0.1", () => r()));
|
|
76
|
+
state.port = server.address().port;
|
|
77
|
+
return {
|
|
78
|
+
host: `127.0.0.1:${state.port}`,
|
|
79
|
+
blobs,
|
|
80
|
+
manifests,
|
|
81
|
+
requests,
|
|
82
|
+
get uploads() {
|
|
83
|
+
return state.uploads;
|
|
84
|
+
},
|
|
85
|
+
close: () => new Promise((r) => server.close(() => r())),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Write a one-image OCI layout at `dir`, returning the manifest's and the config's digests. */
|
|
89
|
+
export function writeOciLayout(dir, content = "layer bytes") {
|
|
90
|
+
const blobsDir = join(dir, "blobs", "sha256");
|
|
91
|
+
mkdirSync(blobsDir, { recursive: true });
|
|
92
|
+
const put = (bytes) => {
|
|
93
|
+
const d = sha(bytes);
|
|
94
|
+
writeFileSync(join(blobsDir, d.slice(7)), bytes);
|
|
95
|
+
return { digest: d, size: Buffer.byteLength(bytes) };
|
|
96
|
+
};
|
|
97
|
+
const layer = put(content);
|
|
98
|
+
const config = put(JSON.stringify({ architecture: "amd64", os: "linux", rootfs: { type: "layers", diff_ids: [layer.digest] } }));
|
|
99
|
+
const manifest = put(JSON.stringify({
|
|
100
|
+
schemaVersion: 2,
|
|
101
|
+
mediaType: "application/vnd.oci.image.manifest.v1+json",
|
|
102
|
+
config: { mediaType: "application/vnd.oci.image.config.v1+json", ...config },
|
|
103
|
+
layers: [{ mediaType: "application/vnd.oci.image.layer.v1.tar", ...layer }],
|
|
104
|
+
}));
|
|
105
|
+
writeFileSync(join(dir, "oci-layout"), JSON.stringify({ imageLayoutVersion: "1.0.0" }));
|
|
106
|
+
writeFileSync(join(dir, "index.json"), JSON.stringify({ schemaVersion: 2, manifests: [{ mediaType: "application/vnd.oci.image.manifest.v1+json", ...manifest }] }));
|
|
107
|
+
return { digest: manifest.digest, configDigest: config.digest };
|
|
108
|
+
}
|
|
109
|
+
/** The registry file a fake npm writing calls to `log` keeps its state in. */
|
|
110
|
+
export const fakeNpmRegistryFile = (log) => `${log}.registry.json`;
|
|
111
|
+
/** Read the fake npm's registry. */
|
|
112
|
+
export function readFakeNpmRegistry(log) {
|
|
113
|
+
const file = fakeNpmRegistryFile(log);
|
|
114
|
+
return existsSync(file) ? JSON.parse(readFileSync(file, "utf-8")) : { packages: {} };
|
|
115
|
+
}
|
|
116
|
+
/** Seed the fake npm's registry, such as with an earlier version as `latest`. */
|
|
117
|
+
export function seedFakeNpmRegistry(log, registry) {
|
|
118
|
+
writeFileSync(fakeNpmRegistryFile(log), JSON.stringify(registry));
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Write a fake `npm` at `<binDir>/npm` that appends `{ args, tokenSet }` to
|
|
122
|
+
* `log` for each call and prints the token it was given (so a test can show
|
|
123
|
+
* it is masked). It keeps a registry in `<log>.registry.json`, as npm's does:
|
|
124
|
+
*
|
|
125
|
+
* - `publish <dir|tgz> [--tag <t>]` adds the version under `<t>` (default
|
|
126
|
+
* `latest`), and refuses a version already there with a 403. As npm's
|
|
127
|
+
* registry does, a package's first version is also `latest`. It exits 1
|
|
128
|
+
* when the package path contains `$FAKE_NPM_FAIL`.
|
|
129
|
+
* - `view <name>@<version> version` prints the version when it is there.
|
|
130
|
+
* - `dist-tag add <name>@<version> <tag>` points the tag at a version that is
|
|
131
|
+
* there. It exits 1 when the name contains `$FAKE_NPM_FAIL_TAG`.
|
|
132
|
+
* - `install` (#4049) checks that every dependency in `./package.json` is in
|
|
133
|
+
* its registry at that version, and installs nothing.
|
|
134
|
+
* - `audit signatures` (#4049) prints what npm prints for verified
|
|
135
|
+
* signatures and attestations, and exits 1 when `$FAKE_NPM_AUDIT_FAIL` is set.
|
|
136
|
+
*/
|
|
137
|
+
export function writeFakeNpm(binDir, log) {
|
|
138
|
+
mkdirSync(binDir, { recursive: true });
|
|
139
|
+
const file = join(binDir, "npm");
|
|
140
|
+
writeFileSync(file, `#!${process.execPath}
|
|
141
|
+
const fs = require("fs");
|
|
142
|
+
const path = require("path");
|
|
143
|
+
const { execFileSync } = require("child_process");
|
|
144
|
+
const args = process.argv.slice(2);
|
|
145
|
+
const token = process.env.NODE_AUTH_TOKEN || "";
|
|
146
|
+
const regFile = ${JSON.stringify(log)} + ".registry.json";
|
|
147
|
+
fs.appendFileSync(${JSON.stringify(log)}, JSON.stringify({ args, tokenSet: token !== "" }) + "\\n");
|
|
148
|
+
console.log("npm notice using //registry.example/:_authToken=" + token);
|
|
149
|
+
console.error("npm notice token " + token);
|
|
150
|
+
const reg = fs.existsSync(regFile) ? JSON.parse(fs.readFileSync(regFile, "utf-8")) : { packages: {} };
|
|
151
|
+
const save = () => fs.writeFileSync(regFile, JSON.stringify(reg));
|
|
152
|
+
const flag = (name) => { const i = args.indexOf(name); return i < 0 ? null : args[i + 1]; };
|
|
153
|
+
const split = (spec) => { const at = spec.lastIndexOf("@"); return [spec.slice(0, at), spec.slice(at + 1)]; };
|
|
154
|
+
if (args[0] === "publish") {
|
|
155
|
+
if (process.env.FAKE_NPM_FAIL && args[1] && args[1].includes(process.env.FAKE_NPM_FAIL)) { console.error("npm error 403 Forbidden"); process.exit(1); }
|
|
156
|
+
const text = fs.statSync(args[1]).isDirectory() ? fs.readFileSync(path.join(args[1], "package.json"), "utf-8") : execFileSync("tar", ["-xzOf", args[1], "package/package.json"], { encoding: "utf-8" });
|
|
157
|
+
const { name, version } = JSON.parse(text);
|
|
158
|
+
const p = reg.packages[name] || (reg.packages[name] = { versions: [], distTags: {} });
|
|
159
|
+
if (p.versions.includes(version)) { console.error("npm error 403 You cannot publish over the previously published versions: " + version); process.exit(1); }
|
|
160
|
+
p.versions.push(version);
|
|
161
|
+
p.distTags[flag("--tag") || "latest"] = version;
|
|
162
|
+
if (!p.distTags.latest) p.distTags.latest = version;
|
|
163
|
+
save();
|
|
164
|
+
console.log("+ " + name + "@" + version);
|
|
165
|
+
} else if (args[0] === "view") {
|
|
166
|
+
const [name, version] = split(args[1]);
|
|
167
|
+
const p = reg.packages[name];
|
|
168
|
+
if (!p) { console.error("npm error 404 Not Found - " + name); process.exit(1); }
|
|
169
|
+
if (p.versions.includes(version)) console.log(version);
|
|
170
|
+
} else if (args[0] === "dist-tag" && args[1] === "add") {
|
|
171
|
+
const [name, version] = split(args[2]);
|
|
172
|
+
if (process.env.FAKE_NPM_FAIL_TAG && name.includes(process.env.FAKE_NPM_FAIL_TAG)) { console.error("npm error 403 Forbidden - no permission to change dist-tags"); process.exit(1); }
|
|
173
|
+
const p = reg.packages[name];
|
|
174
|
+
if (!p || !p.versions.includes(version)) { console.error("npm error 404 " + args[2] + " is not in the npm registry"); process.exit(1); }
|
|
175
|
+
p.distTags[args[3]] = version;
|
|
176
|
+
save();
|
|
177
|
+
console.log("+" + args[3] + ": " + args[2]);
|
|
178
|
+
} else if (args[0] === "install") {
|
|
179
|
+
const deps = JSON.parse(fs.readFileSync("package.json", "utf-8")).dependencies || {};
|
|
180
|
+
for (const [name, version] of Object.entries(deps)) {
|
|
181
|
+
const p = reg.packages[name];
|
|
182
|
+
if (!p || !p.versions.includes(version)) { console.error("npm error 404 " + name + "@" + version + " is not in this registry"); process.exit(1); }
|
|
183
|
+
}
|
|
184
|
+
console.log("added " + Object.keys(deps).length + " packages");
|
|
185
|
+
} else if (args[0] === "audit" && args[1] === "signatures") {
|
|
186
|
+
const n = Object.keys(JSON.parse(fs.readFileSync("package.json", "utf-8")).dependencies || {}).length;
|
|
187
|
+
if (process.env.FAKE_NPM_AUDIT_FAIL) { console.error(n + " packages have invalid attestations"); process.exit(1); }
|
|
188
|
+
console.log("audited " + n + " packages");
|
|
189
|
+
console.log(n + " packages have verified registry signatures");
|
|
190
|
+
console.log(n + " packages have verified attestations");
|
|
191
|
+
} else {
|
|
192
|
+
console.error("fake npm: unknown command " + args.join(" ")); process.exit(2);
|
|
193
|
+
}
|
|
194
|
+
`);
|
|
195
|
+
chmodSync(file, 0o755);
|
|
196
|
+
return file;
|
|
197
|
+
}
|
|
198
|
+
/** A forge fake at `api` for `repo`, which holds `releases` to begin with. */
|
|
199
|
+
export function startFakeForge(forge, api, repo, releases = []) {
|
|
200
|
+
const calls = [];
|
|
201
|
+
const packages = new Map();
|
|
202
|
+
const failures = [];
|
|
203
|
+
let nextId = 100 + releases.length;
|
|
204
|
+
const root = forge === "gitlab" ? `${api}/projects/${encodeURIComponent(repo)}` : `${api}/repos/${repo}`;
|
|
205
|
+
const uploads = forge === "github" ? "https://uploads.forge.test" : api;
|
|
206
|
+
const json = (status, body) => ({ ok: status < 300, status, headers: { get: () => null }, text: async () => (body === undefined ? "" : JSON.stringify(body)) });
|
|
207
|
+
const view = (r) => forge === "gitlab"
|
|
208
|
+
? { tag_name: r.tag_name, assets: { links: r.assets.map((a) => ({ name: a.name })) } }
|
|
209
|
+
: { id: r.id, tag_name: r.tag_name, draft: r.draft, upload_url: `${uploads}/repos/${repo}/releases/${r.id}/assets{?name,label}`, assets: r.assets.map((a) => ({ name: a.name, ...(forge === "github" ? { digest: a.digest } : {}) })) };
|
|
210
|
+
const text = (b) => (b === undefined ? "" : typeof b === "string" ? b : Buffer.from(b).toString("utf-8"));
|
|
211
|
+
const fetch = async (url, init) => {
|
|
212
|
+
calls.push({ method: init.method, url, headers: init.headers, ...(init.body !== undefined ? { body: init.body } : {}) });
|
|
213
|
+
const key = `${init.method} ${url}`;
|
|
214
|
+
const f = failures.findIndex((p) => key.startsWith(p));
|
|
215
|
+
if (f >= 0) {
|
|
216
|
+
failures.splice(f, 1);
|
|
217
|
+
return json(500, { message: "Internal Server Error" });
|
|
218
|
+
}
|
|
219
|
+
const u = new URL(url);
|
|
220
|
+
const path = url.split("?")[0];
|
|
221
|
+
if (forge === "gitlab") {
|
|
222
|
+
if (init.method === "PUT" && path.startsWith(`${root}/packages/generic/`)) {
|
|
223
|
+
packages.set(path, text(init.body));
|
|
224
|
+
return json(201, { message: "201 Created" });
|
|
225
|
+
}
|
|
226
|
+
const byTag = path.startsWith(`${root}/releases/`) ? decodeURIComponent(path.slice(`${root}/releases/`.length)) : null;
|
|
227
|
+
if (init.method === "GET" && byTag !== null) {
|
|
228
|
+
const r = releases.find((x) => x.tag_name === byTag);
|
|
229
|
+
return r ? json(200, view(r)) : json(404, { message: "404 Not Found" });
|
|
230
|
+
}
|
|
231
|
+
if (init.method === "POST" && path === `${root}/releases`) {
|
|
232
|
+
const b = JSON.parse(text(init.body));
|
|
233
|
+
if (releases.some((x) => x.tag_name === b.tag_name))
|
|
234
|
+
return json(409, { message: "Release already exists" });
|
|
235
|
+
const r = { id: nextId++, tag_name: b.tag_name, draft: false, target: b.ref, assets: (b.assets?.links ?? []).map((l) => ({ name: l.name, digest: null, bytes: packages.get(l.url) ?? "" })) };
|
|
236
|
+
releases.push(r);
|
|
237
|
+
return json(201, view(r));
|
|
238
|
+
}
|
|
239
|
+
return json(404, { message: "404 Not Found" });
|
|
240
|
+
}
|
|
241
|
+
if (init.method === "GET" && path.startsWith(`${root}/releases/tags/`)) {
|
|
242
|
+
const tag = decodeURIComponent(path.slice(`${root}/releases/tags/`.length));
|
|
243
|
+
const r = releases.find((x) => x.tag_name === tag && !x.draft);
|
|
244
|
+
return r ? json(200, view(r)) : json(404, { message: "Not Found" });
|
|
245
|
+
}
|
|
246
|
+
if (init.method === "GET" && path === `${root}/releases`)
|
|
247
|
+
return json(200, releases.map(view));
|
|
248
|
+
if (init.method === "POST" && path === `${root}/releases`) {
|
|
249
|
+
const b = JSON.parse(text(init.body));
|
|
250
|
+
const r = { id: nextId++, tag_name: b.tag_name, draft: b.draft === true, target: b.target_commitish, assets: [] };
|
|
251
|
+
releases.push(r);
|
|
252
|
+
return json(201, view(r));
|
|
253
|
+
}
|
|
254
|
+
const asset = path.match(/\/releases\/(\d+)\/assets$/);
|
|
255
|
+
if (init.method === "POST" && asset && (path.startsWith(uploads) || path.startsWith(root))) {
|
|
256
|
+
const r = releases.find((x) => x.id === Number(asset[1]));
|
|
257
|
+
const name = u.searchParams.get("name") ?? "";
|
|
258
|
+
if (!r)
|
|
259
|
+
return json(404, { message: "Not Found" });
|
|
260
|
+
if (r.assets.some((a) => a.name === name))
|
|
261
|
+
return json(422, { message: "already_exists" });
|
|
262
|
+
let bytes = text(init.body);
|
|
263
|
+
if (forge === "forgejo")
|
|
264
|
+
bytes = bytes.split("\r\n\r\n")[1]?.split("\r\n--")[0] ?? "";
|
|
265
|
+
r.assets.push({ name, digest: sha(bytes), bytes });
|
|
266
|
+
return json(201, { name, browser_download_url: `https://forge.test/${repo}/releases/download/${r.tag_name}/${name}` });
|
|
267
|
+
}
|
|
268
|
+
const edit = path.match(/\/releases\/(\d+)$/);
|
|
269
|
+
if (init.method === "PATCH" && edit) {
|
|
270
|
+
const r = releases.find((x) => x.id === Number(edit[1]));
|
|
271
|
+
if (!r)
|
|
272
|
+
return json(404, { message: "Not Found" });
|
|
273
|
+
const b = JSON.parse(text(init.body));
|
|
274
|
+
if (b.draft === false)
|
|
275
|
+
r.draft = false;
|
|
276
|
+
return json(200, view(r));
|
|
277
|
+
}
|
|
278
|
+
return json(404, { message: "Not Found" });
|
|
279
|
+
};
|
|
280
|
+
return {
|
|
281
|
+
fetch,
|
|
282
|
+
calls,
|
|
283
|
+
releases,
|
|
284
|
+
packages,
|
|
285
|
+
visible: () => releases.filter((r) => !r.draft).map((r) => ({ tag: r.tag_name, assets: r.assets.map((a) => a.name) })),
|
|
286
|
+
failOnce: (prefix) => void failures.push(prefix),
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/** A small SPDX document, as `npm sbom --sbom-format spdx` writes one. */
|
|
290
|
+
export function spdxDocument(name) {
|
|
291
|
+
return `${JSON.stringify({ spdxVersion: "SPDX-2.3", dataLicense: "CC0-1.0", SPDXID: "SPDXRef-DOCUMENT", name, documentNamespace: `https://example.test/${name}`, creationInfo: { created: "2026-10-17T00:00:00Z", creators: ["Tool: npm"] }, packages: [] }, null, 2)}\n`;
|
|
292
|
+
}
|
|
293
|
+
/** Write an npm package directory, with an `sbom.spdx.json` at its root unless `sbom` is false (#4049). */
|
|
294
|
+
export function writePackage(dir, name, version, sbom = true) {
|
|
295
|
+
mkdirSync(dir, { recursive: true });
|
|
296
|
+
writeFileSync(join(dir, "package.json"), JSON.stringify({ name, version }, null, 2));
|
|
297
|
+
if (sbom)
|
|
298
|
+
writeFileSync(join(dir, "sbom.spdx.json"), spdxDocument(name));
|
|
299
|
+
}
|
|
300
|
+
/** Write a `package-lock.json` at `dir`, which the OCI and forge targets write their SBOM from (#4049). */
|
|
301
|
+
export function writeLockfile(dir) {
|
|
302
|
+
mkdirSync(dir, { recursive: true });
|
|
303
|
+
writeFileSync(join(dir, "package-lock.json"), JSON.stringify({ name: "acme", lockfileVersion: 3, packages: { "": { name: "acme" }, "node_modules/left-pad": { version: "1.3.0" } } }, null, 2));
|
|
304
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stored change sets (#4022): each change-set document a gate binds, kept on
|
|
3
|
+
* `chant/lifecycle` under its digest so the plan an approval bound can be read
|
|
4
|
+
* once the run that planned it is gone.
|
|
5
|
+
*
|
|
6
|
+
* A pull-request gate, a release gate and an `empty` resolution (ws-104
|
|
7
|
+
* question 2 and 6) bind the change-set document's `digest`
|
|
8
|
+
* (`../change-set.ts`) in `planDigest`. Only that value reaches the gate
|
|
9
|
+
* ledger, so this store keeps the document beside it:
|
|
10
|
+
*
|
|
11
|
+
* _change-sets/<digest>.json the document, written once and never rewritten
|
|
12
|
+
*
|
|
13
|
+
* `:` is written as `_` in the file name, as `_plans/`, `_builds/` and
|
|
14
|
+
* `_releases/` do. A workspace member's copy sits under
|
|
15
|
+
* `_members/<member>/_change-sets/` through the same `ledgerDir` prefix the
|
|
16
|
+
* gate ledger uses, so a gate and its change set are read from one member.
|
|
17
|
+
*
|
|
18
|
+
* The stored document is the one the gate bound, with each entry's graph
|
|
19
|
+
* `node` (`../change-set-graph.ts`) and the `graph` it came from. Neither is
|
|
20
|
+
* hashed, so the stored document still verifies against its digest. A
|
|
21
|
+
* component gate in a plain `run --components` binds a lifecycle-plan digest
|
|
22
|
+
* and stores nothing here.
|
|
23
|
+
*
|
|
24
|
+
* `chant lifecycle gc` removes a stored change set from the branch's tree
|
|
25
|
+
* once #4036's retention policy lets it go (./retention.ts); history keeps it.
|
|
26
|
+
*/
|
|
27
|
+
import { type ChangeSetDocument } from "../change-set.js";
|
|
28
|
+
import { type LedgerLocation } from "./git.js";
|
|
29
|
+
/** The directory on `chant/lifecycle` that holds stored change sets. */
|
|
30
|
+
export declare const CHANGE_SETS_DIR = "_change-sets";
|
|
31
|
+
/** The file a change set with `digest` is stored in, inside {@link CHANGE_SETS_DIR}. */
|
|
32
|
+
export declare function changeSetFile(digest: string): string;
|
|
33
|
+
/** Thrown when a document can't be stored: it is not a change-set document, or its content does not hash to its digest. */
|
|
34
|
+
export declare class ChangeSetStoreError extends Error {
|
|
35
|
+
constructor(message: string);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Store a change-set document under its digest. A document already stored
|
|
39
|
+
* under the digest is never rewritten. Refuses a document whose content does
|
|
40
|
+
* not hash to its own `digest`. Does not push: call `pushLifecycleReport`
|
|
41
|
+
* (./git.ts) after, or let the gate's own pending fact push it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function persistChangeSet(doc: ChangeSetDocument, opts?: {
|
|
44
|
+
cwd?: string;
|
|
45
|
+
}): Promise<{
|
|
46
|
+
commit: string | null;
|
|
47
|
+
written: boolean;
|
|
48
|
+
}>;
|
|
49
|
+
/** What reading one stored change set found. */
|
|
50
|
+
export type StoredChangeSetRead = {
|
|
51
|
+
status: "found";
|
|
52
|
+
document: ChangeSetDocument;
|
|
53
|
+
} | {
|
|
54
|
+
status: "missing";
|
|
55
|
+
}
|
|
56
|
+
/** A file is stored under the digest, and it is not a change-set document whose content hashes to that digest. */
|
|
57
|
+
| {
|
|
58
|
+
status: "malformed";
|
|
59
|
+
message: string;
|
|
60
|
+
};
|
|
61
|
+
/** Parse a stored file, checking it is the document `digest` names. */
|
|
62
|
+
export declare function parseStoredChangeSet(text: string, digest: string): StoredChangeSetRead;
|
|
63
|
+
/**
|
|
64
|
+
* Read the change set stored under `digest`. Reads the local `chant/lifecycle`
|
|
65
|
+
* branch and never fetches. Throws only when git itself fails.
|
|
66
|
+
*/
|
|
67
|
+
export declare function readStoredChangeSet(digest: string, opts?: LedgerLocation): Promise<StoredChangeSetRead>;
|
|
68
|
+
//# sourceMappingURL=change-set-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-set-store.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAoE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACzH,OAAO,EAAmF,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAE7H,wEAAwE;AACxE,eAAO,MAAM,eAAe,iBAAiB,CAAC;AAE9C,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED,2HAA2H;AAC3H,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,iBAAiB,EACtB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACtB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CAsBtD;AAED,gDAAgD;AAChD,MAAM,MAAM,mBAAmB,GAC3B;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,iBAAiB,CAAA;CAAE,GAChD;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE;AACvB,kHAAkH;GAChH;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,uEAAuE;AACvE,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CActF;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAI7G"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stored change sets (#4022): each change-set document a gate binds, kept on
|
|
3
|
+
* `chant/lifecycle` under its digest so the plan an approval bound can be read
|
|
4
|
+
* once the run that planned it is gone.
|
|
5
|
+
*
|
|
6
|
+
* A pull-request gate, a release gate and an `empty` resolution (ws-104
|
|
7
|
+
* question 2 and 6) bind the change-set document's `digest`
|
|
8
|
+
* (`../change-set.ts`) in `planDigest`. Only that value reaches the gate
|
|
9
|
+
* ledger, so this store keeps the document beside it:
|
|
10
|
+
*
|
|
11
|
+
* _change-sets/<digest>.json the document, written once and never rewritten
|
|
12
|
+
*
|
|
13
|
+
* `:` is written as `_` in the file name, as `_plans/`, `_builds/` and
|
|
14
|
+
* `_releases/` do. A workspace member's copy sits under
|
|
15
|
+
* `_members/<member>/_change-sets/` through the same `ledgerDir` prefix the
|
|
16
|
+
* gate ledger uses, so a gate and its change set are read from one member.
|
|
17
|
+
*
|
|
18
|
+
* The stored document is the one the gate bound, with each entry's graph
|
|
19
|
+
* `node` (`../change-set-graph.ts`) and the `graph` it came from. Neither is
|
|
20
|
+
* hashed, so the stored document still verifies against its digest. A
|
|
21
|
+
* component gate in a plain `run --components` binds a lifecycle-plan digest
|
|
22
|
+
* and stores nothing here.
|
|
23
|
+
*
|
|
24
|
+
* `chant lifecycle gc` removes a stored change set from the branch's tree
|
|
25
|
+
* once #4036's retention policy lets it go (./retention.ts); history keeps it.
|
|
26
|
+
*/
|
|
27
|
+
import { sortedJsonReplacer } from "../utils.js";
|
|
28
|
+
import { CHANGE_SET_CONTRACT, CHANGE_SET_SCHEMA_ID, verifyChangeSetDigest } from "../change-set.js";
|
|
29
|
+
import { ledgerDir, readBlobFromPath, readPathSha, RefCASConflictError, writeLedgerFiles } from "./git.js";
|
|
30
|
+
/** The directory on `chant/lifecycle` that holds stored change sets. */
|
|
31
|
+
export const CHANGE_SETS_DIR = "_change-sets";
|
|
32
|
+
/** The file a change set with `digest` is stored in, inside {@link CHANGE_SETS_DIR}. */
|
|
33
|
+
export function changeSetFile(digest) {
|
|
34
|
+
return `${digest.replace(/:/g, "_")}.json`;
|
|
35
|
+
}
|
|
36
|
+
/** Thrown when a document can't be stored: it is not a change-set document, or its content does not hash to its digest. */
|
|
37
|
+
export class ChangeSetStoreError extends Error {
|
|
38
|
+
constructor(message) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "ChangeSetStoreError";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Store a change-set document under its digest. A document already stored
|
|
45
|
+
* under the digest is never rewritten. Refuses a document whose content does
|
|
46
|
+
* not hash to its own `digest`. Does not push: call `pushLifecycleReport`
|
|
47
|
+
* (./git.ts) after, or let the gate's own pending fact push it.
|
|
48
|
+
*/
|
|
49
|
+
export async function persistChangeSet(doc, opts) {
|
|
50
|
+
if (doc?.$schema !== CHANGE_SET_SCHEMA_ID || doc.contract !== CHANGE_SET_CONTRACT) {
|
|
51
|
+
throw new ChangeSetStoreError(`not a change-set document: its $schema is not ${CHANGE_SET_SCHEMA_ID} with contract ${CHANGE_SET_CONTRACT}`);
|
|
52
|
+
}
|
|
53
|
+
if (!verifyChangeSetDigest(doc)) {
|
|
54
|
+
throw new ChangeSetStoreError(`change set ${doc.digest} does not hash to its own digest; refusing to store it`);
|
|
55
|
+
}
|
|
56
|
+
const file = changeSetFile(doc.digest);
|
|
57
|
+
if ((await readPathSha(CHANGE_SETS_DIR, file, opts)) !== null)
|
|
58
|
+
return { commit: null, written: false };
|
|
59
|
+
const dir = await ledgerDir(CHANGE_SETS_DIR, opts);
|
|
60
|
+
try {
|
|
61
|
+
const commit = await writeLedgerFiles([{ path: `${dir}/${file}`, content: `${JSON.stringify(doc, sortedJsonReplacer)}\n`, expectPriorSha: null }], `Change set ${doc.digest}`, opts);
|
|
62
|
+
return { commit, written: true };
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// Another writer stored the same digest first; what is there is the same document.
|
|
66
|
+
if (err instanceof RefCASConflictError)
|
|
67
|
+
return { commit: null, written: false };
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Parse a stored file, checking it is the document `digest` names. */
|
|
72
|
+
export function parseStoredChangeSet(text, digest) {
|
|
73
|
+
let doc;
|
|
74
|
+
try {
|
|
75
|
+
doc = JSON.parse(text);
|
|
76
|
+
}
|
|
77
|
+
catch (err) {
|
|
78
|
+
return { status: "malformed", message: `the file stored under ${digest} is not JSON: ${err instanceof Error ? err.message : String(err)}` };
|
|
79
|
+
}
|
|
80
|
+
if (!doc || typeof doc !== "object" || doc.$schema !== CHANGE_SET_SCHEMA_ID || doc.contract !== CHANGE_SET_CONTRACT || !Array.isArray(doc.entries) || !Array.isArray(doc.members)) {
|
|
81
|
+
return { status: "malformed", message: `the file stored under ${digest} is not a change-set document` };
|
|
82
|
+
}
|
|
83
|
+
if (doc.digest !== digest || !verifyChangeSetDigest(doc)) {
|
|
84
|
+
return { status: "malformed", message: `the change set stored under ${digest} does not hash to that digest` };
|
|
85
|
+
}
|
|
86
|
+
return { status: "found", document: doc };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Read the change set stored under `digest`. Reads the local `chant/lifecycle`
|
|
90
|
+
* branch and never fetches. Throws only when git itself fails.
|
|
91
|
+
*/
|
|
92
|
+
export async function readStoredChangeSet(digest, opts) {
|
|
93
|
+
const text = await readBlobFromPath(CHANGE_SETS_DIR, changeSetFile(digest), opts);
|
|
94
|
+
if (text === null)
|
|
95
|
+
return { status: "missing" };
|
|
96
|
+
return parseStoredChangeSet(text, digest);
|
|
97
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The commit a run, a gate or a deploy is for (#4020), as one answer for every
|
|
3
|
+
* record that carries it. The checkout's `HEAD` is what the process builds
|
|
4
|
+
* from, so it comes first; a CI job whose checkout has no git (a bare
|
|
5
|
+
* artifact job) falls back to the commit the forge says it started for.
|
|
6
|
+
* Undefined when neither is known, so a record leaves the field out rather
|
|
7
|
+
* than guessing.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveCommitSha(opts?: {
|
|
10
|
+
cwd?: string;
|
|
11
|
+
env?: Record<string, string | undefined>;
|
|
12
|
+
}): Promise<string | undefined>;
|
|
13
|
+
//# sourceMappingURL=commit-sha.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-sha.d.ts","sourceRoot":"","sources":["../../src/lifecycle/commit-sha.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,wBAAsB,gBAAgB,CACpC,IAAI,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;CAAO,GACpE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAQ7B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The commit a run, a gate or a deploy is for (#4020), as one answer for every
|
|
3
|
+
* record that carries it. The checkout's `HEAD` is what the process builds
|
|
4
|
+
* from, so it comes first; a CI job whose checkout has no git (a bare
|
|
5
|
+
* artifact job) falls back to the commit the forge says it started for.
|
|
6
|
+
* Undefined when neither is known, so a record leaves the field out rather
|
|
7
|
+
* than guessing.
|
|
8
|
+
*/
|
|
9
|
+
import { ciCommitFromEnv } from "../op/gate-run-locator.js";
|
|
10
|
+
import { getHeadCommit } from "./git.js";
|
|
11
|
+
export async function resolveCommitSha(opts = {}) {
|
|
12
|
+
try {
|
|
13
|
+
const head = await getHeadCommit(opts.cwd ? { cwd: opts.cwd } : undefined);
|
|
14
|
+
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
15
|
+
return head;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
// Not a checkout, or no git on the path: the environment may still say.
|
|
19
|
+
}
|
|
20
|
+
return ciCommitFromEnv(opts.env);
|
|
21
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The four DORA metrics of one environment's deploy records (#4039): how
|
|
3
|
+
* often it deploys, the lead time from commit to running release, the change
|
|
4
|
+
* failure rate and the time to restore.
|
|
5
|
+
*
|
|
6
|
+
* Everything here is pure. The caller reads the ledgers, the release files
|
|
7
|
+
* and the ci tags, and hands this module one environment's records with
|
|
8
|
+
* lookups for the rest; `workspace/delivery-metrics.ts` does that for
|
|
9
|
+
* `chant workspace metrics`.
|
|
10
|
+
*
|
|
11
|
+
* A deploy is one record in `<env>/releases.jsonl`: one component deployed
|
|
12
|
+
* once. A redeploy, which puts the running release back, is counted apart and
|
|
13
|
+
* is not a deploy for the metrics. A deploy fails when, within the failure window after it, the commit
|
|
14
|
+
* it deployed is revoked (a counted `ci/revoked/<sha>` tag, #4033) or the
|
|
15
|
+
* environment is rolled back from it. Which records are rollbacks, and which
|
|
16
|
+
* put the current release back, is decided in {@link rollbackOf} and
|
|
17
|
+
* {@link isRedeploy} and nowhere else, so a new kind of rollback record
|
|
18
|
+
* (#4035) changes those two functions only.
|
|
19
|
+
*/
|
|
20
|
+
import type { ReleaseRecord, ReleaseRef } from "./release-ledger.js";
|
|
21
|
+
/** The failure window when the declaration's `metrics.failureWindowDays` is not set. */
|
|
22
|
+
export declare const DEFAULT_FAILURE_WINDOW_DAYS = 7;
|
|
23
|
+
/**
|
|
24
|
+
* The earlier deploy a rollback record restores, or null when the record is
|
|
25
|
+
* not a rollback. Today a rollback is a record `chant components rollback`
|
|
26
|
+
* wrote, which names what it restores in `restores` (#2531). Every rule below
|
|
27
|
+
* that asks whether a record rolled an environment back asks this function.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rollbackOf(record: Pick<ReleaseRecord, "restores">): ReleaseRef | null;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the record put the current release back (`chant components
|
|
32
|
+
* redeploy`, #2604). A redeploy deploys the same release again, so it neither
|
|
33
|
+
* restores a failed deploy nor stands between a deploy and its rollback.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isRedeploy(record: Pick<ReleaseRecord, "redeploys">): boolean;
|
|
36
|
+
/** A counted revoked tag on a commit, with the time it was made, or null for a lightweight tag. */
|
|
37
|
+
export interface CommitRevocation {
|
|
38
|
+
tag: string;
|
|
39
|
+
tagged: string | null;
|
|
40
|
+
}
|
|
41
|
+
/** What the caller knows beyond the records, as lookups. */
|
|
42
|
+
export interface MetricsLookups {
|
|
43
|
+
/** The release a deploy record names: its `releaseDigest`, else the one its release-gate admission names. Undefined when it names none. */
|
|
44
|
+
releaseOf: (record: ReleaseRecord) => string | undefined;
|
|
45
|
+
/** The commit a release was cut from, or null when its file can't be read here. */
|
|
46
|
+
commitOfRelease: (releaseDigest: string) => string | null;
|
|
47
|
+
/** The release's label, or null. */
|
|
48
|
+
labelOf: (releaseDigest: string) => string | null;
|
|
49
|
+
/** The counted revoked tag on a commit, or null when the commit is not revoked. */
|
|
50
|
+
revocationOf: (commit: string) => CommitRevocation | null;
|
|
51
|
+
/** The commit's committer time in milliseconds, or null when the commit is not in this repository. */
|
|
52
|
+
commitTime: (commit: string) => number | null;
|
|
53
|
+
}
|
|
54
|
+
/** The span of time the metrics count deploys in. */
|
|
55
|
+
export interface MetricsWindow {
|
|
56
|
+
/** ISO-8601, inclusive. */
|
|
57
|
+
since: string;
|
|
58
|
+
/** ISO-8601, inclusive. */
|
|
59
|
+
until: string;
|
|
60
|
+
}
|
|
61
|
+
/** A record named the way `restores` names one. */
|
|
62
|
+
export interface DeployRef {
|
|
63
|
+
runId: string;
|
|
64
|
+
timestamp: string;
|
|
65
|
+
}
|
|
66
|
+
/** One failed deploy, and how it was restored. */
|
|
67
|
+
export interface DeployFailure {
|
|
68
|
+
component: string;
|
|
69
|
+
timestamp: string;
|
|
70
|
+
runId: string;
|
|
71
|
+
/** The release the deploy came from, or null for a deploy made from no release. */
|
|
72
|
+
releaseDigest: string | null;
|
|
73
|
+
label: string | null;
|
|
74
|
+
/** The commit it deployed: the release's commit, or the record's `gitSha` for a deploy made from no release. */
|
|
75
|
+
commit: string;
|
|
76
|
+
/** Why it counts as failed: `revoked`, `rolled-back`, or both. */
|
|
77
|
+
causes: Array<"revoked" | "rolled-back">;
|
|
78
|
+
/** The revoked tag on the commit, when that is a cause. */
|
|
79
|
+
revoked: CommitRevocation | null;
|
|
80
|
+
/** The rollback record that rolled the environment back from it, when that is a cause. */
|
|
81
|
+
rolledBackBy: DeployRef | null;
|
|
82
|
+
/** The next deploy of the component in the environment whose commit is not revoked, or null while there is none. */
|
|
83
|
+
restoredBy: DeployRef | null;
|
|
84
|
+
/** Seconds from the failed deploy to `restoredBy`, or null while it is not restored. */
|
|
85
|
+
restoreSeconds: number | null;
|
|
86
|
+
}
|
|
87
|
+
/** The four metrics of one environment. */
|
|
88
|
+
export interface EnvironmentMetrics {
|
|
89
|
+
env: string;
|
|
90
|
+
deployments: {
|
|
91
|
+
/** Deploy records in the window, one per component deployed, leaving out redeploys. Rollbacks count. */
|
|
92
|
+
count: number;
|
|
93
|
+
/** Redeploys in the window: records that put the running release back, counted apart from `count`. */
|
|
94
|
+
redeploys: number;
|
|
95
|
+
/** Distinct `runId` values among them, so a run that deploys several components counts once. */
|
|
96
|
+
runs: number;
|
|
97
|
+
/** Of `count`, the deploys made from a release. */
|
|
98
|
+
fromRelease: number;
|
|
99
|
+
/** `count` over the window in weeks, to two decimals. */
|
|
100
|
+
perWeek: number;
|
|
101
|
+
};
|
|
102
|
+
leadTime: {
|
|
103
|
+
/** Changes whose first deploy here falls in the window and whose commit time is known. */
|
|
104
|
+
changes: number;
|
|
105
|
+
/** Of `changes`, releases (the first deploy naming the release). */
|
|
106
|
+
fromRelease: number;
|
|
107
|
+
/** Of `changes`, commits deployed from no release (the first deploy with that `gitSha`). */
|
|
108
|
+
fromCommit: number;
|
|
109
|
+
/** Changes left out because their commit is not in this repository. */
|
|
110
|
+
unknown: number;
|
|
111
|
+
/** The median, in seconds, or null when `changes` is 0. */
|
|
112
|
+
medianSeconds: number | null;
|
|
113
|
+
};
|
|
114
|
+
changeFailure: {
|
|
115
|
+
deployments: number;
|
|
116
|
+
failed: number;
|
|
117
|
+
/** Failed deploys whose commit was revoked within the window after them. */
|
|
118
|
+
revoked: number;
|
|
119
|
+
/** Failed deploys the environment was rolled back from within the window after them. */
|
|
120
|
+
rolledBack: number;
|
|
121
|
+
/** `failed` over `deployments`, to four decimals, or null with no deployments. */
|
|
122
|
+
rate: number | null;
|
|
123
|
+
};
|
|
124
|
+
restore: {
|
|
125
|
+
restored: number;
|
|
126
|
+
open: number;
|
|
127
|
+
medianSeconds: number | null;
|
|
128
|
+
};
|
|
129
|
+
/** The failed deploys in the window, oldest first. */
|
|
130
|
+
failures: DeployFailure[];
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* The metrics of one environment's records. `records` is the whole ledger of
|
|
134
|
+
* the environment, every component, in ledger order: deploys before the
|
|
135
|
+
* window still decide lead times, and deploys after a failure decide its
|
|
136
|
+
* restore. Only deploys whose `timestamp` falls in `window` are counted.
|
|
137
|
+
*/
|
|
138
|
+
export declare function environmentMetrics(env: string, records: readonly ReleaseRecord[], lookups: MetricsLookups, window: MetricsWindow, failureWindowDays?: number): EnvironmentMetrics;
|
|
139
|
+
//# sourceMappingURL=delivery-metrics.d.ts.map
|