@intentius/chant 0.124.28 → 0.124.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +8 -1
- package/dist/change-set-graph.d.ts.map +1 -1
- package/dist/change-set-graph.js +4 -2
- package/dist/change-set.d.ts +10 -0
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +4 -3
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +6 -3
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +45 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +15 -4
- package/dist/cli/handlers/fan-out.d.ts +9 -0
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +40 -3
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +11 -3
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +134 -26
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +210 -33
- package/dist/cli/handlers/release.d.ts +62 -5
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +414 -28
- package/dist/cli/handlers/run.d.ts +11 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +67 -6
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +61 -10
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +24 -2
- package/dist/cli/registry.d.ts +15 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +3 -2
- package/dist/cli/run-events.d.ts.map +1 -1
- package/dist/cli/run-events.js +5 -3
- package/dist/components/auto-release.d.ts +2 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +1 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts +8 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +35 -0
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +50 -0
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- package/dist/components/promote.d.ts +23 -0
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +60 -2
- package/dist/components/release-compile.d.ts +9 -0
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +9 -3
- package/dist/components/release-gate.d.ts +11 -1
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +10 -4
- package/dist/components/wave-plan.d.ts +5 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +10 -2
- package/dist/config.d.ts +38 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +2 -1
- package/dist/lifecycle/change-set-store.d.ts.map +1 -1
- package/dist/lifecycle/change-set-store.js +2 -1
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +10 -1
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -1
- package/dist/lifecycle/gate-change-set.js +35 -3
- package/dist/lifecycle/gate-ledger.d.ts +2 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +48 -1
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +116 -23
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +12 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +4 -2
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +29 -2
- package/dist/run-event-store.d.ts.map +1 -1
- package/dist/run-event-store.js +50 -3
- package/dist/run-event-stream.d.ts +2 -1
- package/dist/run-event-stream.d.ts.map +1 -1
- package/dist/run-event-stream.js +5 -1
- package/dist/run-event.d.ts +19 -1
- package/dist/run-event.d.ts.map +1 -1
- package/dist/run-event.js +2 -0
- package/dist/wave-runtime.mjs +260 -229
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/change-set.schema.json +5 -1
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green.d.ts +8 -0
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +10 -3
- package/dist/workspace/declaration.d.ts +16 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +26 -0
- package/dist/workspace/declaration.schema.json +64 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +30 -6
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +141 -21
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/reason-codes.d.ts +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +3 -0
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +9 -0
- package/dist/workspace/releases.d.ts.map +1 -1
- package/dist/workspace/releases.js +9 -0
- package/dist/workspace/releases.schema.json +123 -1
- package/dist/workspace/run-event.schema.json +10 -3
- package/dist/workspace/status-gates.d.ts +8 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +78 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +170 -3
- package/dist/workspace/status.schema.json +198 -1
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.ts +10 -2
- package/src/change-set.ts +14 -3
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +6 -3
- package/src/cli/handlers/approve-release.test.ts +5 -1
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +47 -0
- package/src/cli/handlers/build.test.ts +15 -0
- package/src/cli/handlers/build.ts +17 -4
- package/src/cli/handlers/fan-out.test.ts +180 -1
- package/src/cli/handlers/fan-out.ts +38 -3
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/pr.test.ts +148 -1
- package/src/cli/handlers/pr.ts +139 -28
- package/src/cli/handlers/promote.test.ts +280 -2
- package/src/cli/handlers/promote.ts +210 -34
- package/src/cli/handlers/release-implicit.e2e.test.ts +12 -4
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +146 -32
- package/src/cli/handlers/release.ts +415 -28
- package/src/cli/handlers/run-auto-release.test.ts +9 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.ts +69 -5
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +61 -10
- package/src/cli/parse-args.ts +19 -2
- package/src/cli/registry.ts +15 -3
- package/src/cli/run-events.ts +5 -2
- package/src/components/auto-release.test.ts +9 -0
- package/src/components/auto-release.ts +3 -0
- package/src/components/capability.ts +9 -0
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +36 -0
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +48 -0
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/promote.ts +68 -2
- package/src/components/release-compile.ts +17 -3
- package/src/components/release-gate.ts +18 -4
- package/src/components/wave-plan.ts +15 -2
- package/src/config.ts +8 -0
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.ts +2 -1
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.ts +37 -6
- package/src/lifecycle/gate-ledger.ts +2 -2
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +74 -2
- package/src/lifecycle/release-cut.ts +158 -22
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +15 -4
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.ts +63 -3
- package/src/run-event-stream.ts +4 -1
- package/src/run-event.ts +21 -1
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/change-set.schema.json +5 -1
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green.ts +14 -3
- package/src/workspace/declaration.schema.json +64 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +38 -0
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +148 -25
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/read-contract.e2e.test.ts +2 -1
- package/src/workspace/reason-codes.test.ts +5 -0
- package/src/workspace/reason-codes.ts +3 -0
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +123 -1
- package/src/workspace/releases.ts +11 -0
- package/src/workspace/run-event.schema.json +10 -3
- package/src/workspace/status-contract.test.ts +29 -0
- package/src/workspace/status-gates.ts +8 -0
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status.schema.json +198 -1
- package/src/workspace/status.ts +254 -3
- package/src/workspace/wip.ts +11 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time parameters from workspace flags (#4024, decided in #4016).
|
|
3
|
+
*
|
|
4
|
+
* A member of a workspace may have flag records: records of a kind with a
|
|
5
|
+
* `flag` block, each naming the member, one of its declared build
|
|
6
|
+
* parameters and a value per environment (`workspace/flags.ts`). A build
|
|
7
|
+
* for an environment (`chant build --env <env>`, `chant run --components
|
|
8
|
+
* --env <env>`, and each environment a release cut compiles) reads them, and
|
|
9
|
+
* a flag-backed parameter takes the flag's value for that environment,
|
|
10
|
+
* recorded as `source: "flag:<name>"` on `BuildResult.buildParams` and on the
|
|
11
|
+
* release target's `params`.
|
|
12
|
+
*
|
|
13
|
+
* The precedence rule: where the environment has a gate, `--param`,
|
|
14
|
+
* `--params-file` and the parameter's env var can't set a flag-backed
|
|
15
|
+
* parameter, and the build refuses. An environment has a gate when a
|
|
16
|
+
* component enabled in it declares a gate step, the same test `components
|
|
17
|
+
* promote --release` uses to bind a release gate. The test runs only when one
|
|
18
|
+
* of those sources did set a flag-backed parameter, since it imports the
|
|
19
|
+
* member's components; a project with no components has no gate.
|
|
20
|
+
*
|
|
21
|
+
* Level 0 pays one upward `existsSync` walk here and nothing more. Workspace
|
|
22
|
+
* code is imported only once a declaration is found (#2525 rule 5, pinned by
|
|
23
|
+
* #2526's goldens).
|
|
24
|
+
*/
|
|
25
|
+
import { findWorkspaceRoot } from "./project-root.js";
|
|
26
|
+
import { flagGateRefusal } from "./build-params.js";
|
|
27
|
+
import { echoBuildParams, resolveCliBuildParams } from "./cli/build-params-cli.js";
|
|
28
|
+
import { formatError } from "./cli/format.js";
|
|
29
|
+
/** The flags backing parameters of the member at `projectDir` in `env`, or why they can't be read. `flags` is undefined outside a workspace, without an environment, or when no flag names the member. */
|
|
30
|
+
export async function loadBuildParamFlags(projectDir, env) {
|
|
31
|
+
if (!env)
|
|
32
|
+
return { flags: undefined, errors: [] };
|
|
33
|
+
const found = findWorkspaceRoot(projectDir);
|
|
34
|
+
if (!found)
|
|
35
|
+
return { flags: undefined, errors: [] };
|
|
36
|
+
const { memberFlagBindings } = await import("./workspace/flags-read.js");
|
|
37
|
+
try {
|
|
38
|
+
const bindings = await memberFlagBindings(found.dir, projectDir, env);
|
|
39
|
+
if (!bindings)
|
|
40
|
+
return { flags: undefined, errors: [] };
|
|
41
|
+
if (bindings.errors.length > 0)
|
|
42
|
+
return { flags: undefined, errors: bindings.errors };
|
|
43
|
+
if (Object.keys(bindings.byParam).length === 0)
|
|
44
|
+
return { flags: undefined, errors: [] };
|
|
45
|
+
return { flags: { env, byParam: bindings.byParam }, errors: [] };
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
const { WorkspaceReadError } = await import("./workspace/declaration.js");
|
|
49
|
+
const { RecordReadError } = await import("./workspace/records.js");
|
|
50
|
+
if (err instanceof WorkspaceReadError || err instanceof RecordReadError) {
|
|
51
|
+
return { flags: undefined, errors: [`the workspace's flag records can't be read, so this build can't tell which parameters they set: ${err.message}`] };
|
|
52
|
+
}
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/** The parameters a flag backs that `--param`, `--params-file` or an env var set instead. */
|
|
57
|
+
export function overriddenFlagParams(provenance, flags) {
|
|
58
|
+
if (!flags)
|
|
59
|
+
return [];
|
|
60
|
+
return provenance.filter((p) => flags.byParam[p.name] !== undefined && (p.source === "cli" || p.source === "params-file" || p.source === "env"));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The gates the member's components declare in the environment the process
|
|
64
|
+
* builds for (`CHANT_ENV`), with the parameters as resolved, or why the
|
|
65
|
+
* components can't be read. Only components enabled under those parameters
|
|
66
|
+
* count.
|
|
67
|
+
*/
|
|
68
|
+
export async function environmentGates(projectDir, provenance) {
|
|
69
|
+
const { discoverComponents } = await import("./components/discover.js");
|
|
70
|
+
const { withoutDisabled } = await import("./components/cli-support.js");
|
|
71
|
+
const { gateNamesOf } = await import("./components/release-gate.js");
|
|
72
|
+
try {
|
|
73
|
+
const discovered = await discoverComponents(projectDir, { buildParams: provenance });
|
|
74
|
+
if (discovered.errors.length > 0)
|
|
75
|
+
return { error: discovered.errors.map((e) => e.message).join("; ") };
|
|
76
|
+
const all = [...discovered.components.values()].map((d) => d.component);
|
|
77
|
+
const plain = all.map((c) => ({ name: c.name, dependsOn: c.dependsOn, deploy: c.deploy }));
|
|
78
|
+
return { gates: gateNamesOf(withoutDisabled(all, plain)) };
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
return { error: err instanceof Error ? err.message : String(err) };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve the build parameters of the project at `projectDir` for `env`,
|
|
86
|
+
* with the workspace's flags, as `resolveCliBuildParams` does without them.
|
|
87
|
+
* Refuses when a flag record can't be read, and when `--param`,
|
|
88
|
+
* `--params-file` or an env var sets a flag-backed parameter in an
|
|
89
|
+
* environment with a gate. Prints the summary line only on success.
|
|
90
|
+
*/
|
|
91
|
+
export async function resolveFlaggedBuildParams(projectDir, buildParamsConfig, args, env) {
|
|
92
|
+
const loaded = await loadBuildParamFlags(projectDir, env);
|
|
93
|
+
if (loaded.errors.length > 0) {
|
|
94
|
+
return { success: false, provenance: [], errors: loaded.errors.map((message) => formatError({ message })) };
|
|
95
|
+
}
|
|
96
|
+
const { flags } = loaded;
|
|
97
|
+
const resolution = resolveCliBuildParams(buildParamsConfig, { ...args, ...(flags ? { flags } : {}), echo: false });
|
|
98
|
+
if (!resolution.success || !flags) {
|
|
99
|
+
if (resolution.success && args.echo !== false)
|
|
100
|
+
echoBuildParams(resolution.provenance, args.verbose);
|
|
101
|
+
return resolution;
|
|
102
|
+
}
|
|
103
|
+
const overridden = overriddenFlagParams(resolution.provenance, flags);
|
|
104
|
+
if (overridden.length > 0) {
|
|
105
|
+
const found = await environmentGates(projectDir, resolution.provenance);
|
|
106
|
+
if ("error" in found) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
provenance: [],
|
|
110
|
+
errors: [
|
|
111
|
+
formatError({
|
|
112
|
+
message: `${overridden.map((p) => `"${p.name}"`).join(", ")} ${overridden.length === 1 ? "is" : "are"} backed by a flag and set by ${[...new Set(overridden.map((p) => p.source))].join(", ")}, ` +
|
|
113
|
+
`and whether "${flags.env}" has a gate can't be told, because its components can't be read: ${found.error}`,
|
|
114
|
+
}),
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (found.gates.length > 0) {
|
|
119
|
+
const around = (p) => p.source === "cli" ? "--param" : p.source === "params-file" ? "--params-file" : `the env var ${buildParamsConfig?.[p.name]?.env ?? "(unknown)"}`;
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
provenance: [],
|
|
123
|
+
errors: overridden.map((p) => formatError({ message: `${flagGateRefusal(p.name, flags.byParam[p.name].flag, flags.env, around(p))} (gates: ${found.gates.join(", ")})` })),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (args.echo !== false)
|
|
128
|
+
echoBuildParams(resolution.provenance, args.verbose);
|
|
129
|
+
return resolution;
|
|
130
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
export interface FakeRegistry {
|
|
10
|
+
/** `127.0.0.1:<port>`, for a repository reference. */
|
|
11
|
+
host: string;
|
|
12
|
+
blobs: Map<string, Buffer>;
|
|
13
|
+
manifests: Map<string, {
|
|
14
|
+
name: string;
|
|
15
|
+
type: string;
|
|
16
|
+
bytes: Buffer;
|
|
17
|
+
}>;
|
|
18
|
+
/** Each request: method, path and the authorization header it carried. */
|
|
19
|
+
requests: Array<{
|
|
20
|
+
method: string;
|
|
21
|
+
path: string;
|
|
22
|
+
authorization: string | null;
|
|
23
|
+
}>;
|
|
24
|
+
uploads: number;
|
|
25
|
+
close(): Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
/** A registry fake with a Bearer token service that accepts `username` and `password`. */
|
|
28
|
+
export declare function startFakeRegistry(creds: {
|
|
29
|
+
username: string;
|
|
30
|
+
password: string;
|
|
31
|
+
token: string;
|
|
32
|
+
}): Promise<FakeRegistry>;
|
|
33
|
+
/** Write a one-image OCI layout at `dir`, returning the manifest's and the config's digests. */
|
|
34
|
+
export declare function writeOciLayout(dir: string, content?: string): {
|
|
35
|
+
digest: string;
|
|
36
|
+
configDigest: string;
|
|
37
|
+
};
|
|
38
|
+
/** What the fake npm's registry holds: each package's versions and dist-tags. */
|
|
39
|
+
export interface FakeNpmRegistry {
|
|
40
|
+
packages: Record<string, {
|
|
41
|
+
versions: string[];
|
|
42
|
+
distTags: Record<string, string>;
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
/** The registry file a fake npm writing calls to `log` keeps its state in. */
|
|
46
|
+
export declare const fakeNpmRegistryFile: (log: string) => string;
|
|
47
|
+
/** Read the fake npm's registry. */
|
|
48
|
+
export declare function readFakeNpmRegistry(log: string): FakeNpmRegistry;
|
|
49
|
+
/** Seed the fake npm's registry, such as with an earlier version as `latest`. */
|
|
50
|
+
export declare function seedFakeNpmRegistry(log: string, registry: FakeNpmRegistry): void;
|
|
51
|
+
/**
|
|
52
|
+
* Write a fake `npm` at `<binDir>/npm` that appends `{ args, tokenSet }` to
|
|
53
|
+
* `log` for each call and prints the token it was given (so a test can show
|
|
54
|
+
* it is masked). It keeps a registry in `<log>.registry.json`, as npm's does:
|
|
55
|
+
*
|
|
56
|
+
* - `publish <dir|tgz> [--tag <t>]` adds the version under `<t>` (default
|
|
57
|
+
* `latest`), and refuses a version already there with a 403. As npm's
|
|
58
|
+
* registry does, a package's first version is also `latest`. It exits 1
|
|
59
|
+
* when the package path contains `$FAKE_NPM_FAIL`.
|
|
60
|
+
* - `view <name>@<version> version` prints the version when it is there.
|
|
61
|
+
* - `dist-tag add <name>@<version> <tag>` points the tag at a version that is
|
|
62
|
+
* there. It exits 1 when the name contains `$FAKE_NPM_FAIL_TAG`.
|
|
63
|
+
* - `install` (#4049) checks that every dependency in `./package.json` is in
|
|
64
|
+
* its registry at that version, and installs nothing.
|
|
65
|
+
* - `audit signatures` (#4049) prints what npm prints for verified
|
|
66
|
+
* signatures and attestations, and exits 1 when `$FAKE_NPM_AUDIT_FAIL` is set.
|
|
67
|
+
*/
|
|
68
|
+
export declare function writeFakeNpm(binDir: string, log: string): string;
|
|
69
|
+
/** A forge release as {@link startFakeForge} keeps it. */
|
|
70
|
+
export interface FakeForgeRelease {
|
|
71
|
+
id: number;
|
|
72
|
+
tag_name: string;
|
|
73
|
+
draft: boolean;
|
|
74
|
+
target: string;
|
|
75
|
+
assets: Array<{
|
|
76
|
+
name: string;
|
|
77
|
+
digest: string | null;
|
|
78
|
+
bytes: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
/** A forge fake answering through `fetch`: GitHub or Forgejo with draft releases, or GitLab with releases, links and a generic package registry. */
|
|
82
|
+
export interface FakeForge {
|
|
83
|
+
fetch: (url: string, init: {
|
|
84
|
+
method: string;
|
|
85
|
+
headers: Record<string, string>;
|
|
86
|
+
body?: Uint8Array | string;
|
|
87
|
+
}) => Promise<{
|
|
88
|
+
ok: boolean;
|
|
89
|
+
status: number;
|
|
90
|
+
headers: {
|
|
91
|
+
get(name: string): string | null;
|
|
92
|
+
};
|
|
93
|
+
text(): Promise<string>;
|
|
94
|
+
}>;
|
|
95
|
+
calls: Array<{
|
|
96
|
+
method: string;
|
|
97
|
+
url: string;
|
|
98
|
+
headers: Record<string, string>;
|
|
99
|
+
body?: Uint8Array | string;
|
|
100
|
+
}>;
|
|
101
|
+
releases: FakeForgeRelease[];
|
|
102
|
+
/** GitLab: the package registry's files, by URL. */
|
|
103
|
+
packages: Map<string, string>;
|
|
104
|
+
/** The releases a consumer sees: published (not draft) ones, with their asset names. */
|
|
105
|
+
visible(): Array<{
|
|
106
|
+
tag: string;
|
|
107
|
+
assets: string[];
|
|
108
|
+
}>;
|
|
109
|
+
/** Make the next request whose `METHOD url` starts with `prefix` answer 500. */
|
|
110
|
+
failOnce(prefix: string): void;
|
|
111
|
+
}
|
|
112
|
+
/** A forge fake at `api` for `repo`, which holds `releases` to begin with. */
|
|
113
|
+
export declare function startFakeForge(forge: "github" | "forgejo" | "gitlab", api: string, repo: string, releases?: FakeForgeRelease[]): FakeForge;
|
|
114
|
+
/** A small SPDX document, as `npm sbom --sbom-format spdx` writes one. */
|
|
115
|
+
export declare function spdxDocument(name: string): string;
|
|
116
|
+
/** Write an npm package directory, with an `sbom.spdx.json` at its root unless `sbom` is false (#4049). */
|
|
117
|
+
export declare function writePackage(dir: string, name: string, version: string, sbom?: boolean): void;
|
|
118
|
+
/** Write a `package-lock.json` at `dir`, which the OCI and forge targets write their SBOM from (#4049). */
|
|
119
|
+
export declare function writeLockfile(dir: string): void;
|
|
120
|
+
//# sourceMappingURL=publish-fakes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-fakes.d.ts","sourceRoot":"","sources":["../../../src/lifecycle/__fixtures__/publish-fakes.ts"],"names":[],"mappings":"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"}
|
|
@@ -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
|
+
}
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* component gate in a plain `run --components` binds a lifecycle-plan digest
|
|
22
22
|
* and stores nothing here.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
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.
|
|
25
26
|
*/
|
|
26
27
|
import { type ChangeSetDocument } from "../change-set.js";
|
|
27
28
|
import { type LedgerLocation } from "./git.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-set-store.d.ts","sourceRoot":"","sources":["../../src/lifecycle/change-set-store.ts"],"names":[],"mappings":"AAAA
|
|
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"}
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
* component gate in a plain `run --components` binds a lifecycle-plan digest
|
|
22
22
|
* and stores nothing here.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
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.
|
|
25
26
|
*/
|
|
26
27
|
import { sortedJsonReplacer } from "../utils.js";
|
|
27
28
|
import { CHANGE_SET_CONTRACT, CHANGE_SET_SCHEMA_ID, verifyChangeSetDigest } from "../change-set.js";
|