@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
|
@@ -119,6 +119,23 @@ vi.mock("../../lifecycle/release-store", async () => {
|
|
|
119
119
|
resolveRelease: async (ref: string) => (releaseFixture && (ref === releaseFixture.label || ref === releaseFixture.release.releaseDigest) ? releaseFixture : null),
|
|
120
120
|
readReleaseTemplate: async () => storedTemplate,
|
|
121
121
|
readRelease: async (digest: string) => knownReleases.get(digest) ?? null,
|
|
122
|
+
// #4035: labels come from the fixture's cuts, never from this checkout's branch.
|
|
123
|
+
readCuts: async () => ({
|
|
124
|
+
cuts: [...knownLabels].map(([releaseDigest, label]) => ({ version: 1, releaseDigest, label, actor: "alice", timestamp: "2026-01-01T00:00:00.000Z", runOrigin: null, signature: null })),
|
|
125
|
+
malformed: 0,
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
});
|
|
129
|
+
const knownLabels = new Map<string, string>();
|
|
130
|
+
|
|
131
|
+
// #4035: deploy outcomes live in memory.
|
|
132
|
+
const outcomes: import("../../lifecycle/deploy-outcome").DeployOutcome[] = [];
|
|
133
|
+
vi.mock("../../lifecycle/deploy-outcome", async () => {
|
|
134
|
+
const actual = await vi.importActual<typeof import("../../lifecycle/deploy-outcome")>("../../lifecycle/deploy-outcome");
|
|
135
|
+
return {
|
|
136
|
+
...actual,
|
|
137
|
+
appendDeployOutcome: async (o: import("../../lifecycle/deploy-outcome").DeployOutcome) => { outcomes.push(o); return "c0ffee1"; },
|
|
138
|
+
readDeployOutcomes: async (env: string) => outcomes.filter((o) => o.env === env),
|
|
122
139
|
};
|
|
123
140
|
});
|
|
124
141
|
vi.mock("../../components/release-promote", async () => {
|
|
@@ -130,6 +147,13 @@ vi.mock("../../components/release-promote", async () => {
|
|
|
130
147
|
};
|
|
131
148
|
});
|
|
132
149
|
|
|
150
|
+
// #4033: the revoked commits come from a map, not from this checkout's tags.
|
|
151
|
+
const revokedCommits = new Map<string, import("../../lifecycle/release-revocation").ReleaseRevocation>();
|
|
152
|
+
vi.mock("../../lifecycle/release-revocation", async () => {
|
|
153
|
+
const actual = await vi.importActual<typeof import("../../lifecycle/release-revocation")>("../../lifecycle/release-revocation");
|
|
154
|
+
return { ...actual, readRevokedCommits: async () => revokedCommits };
|
|
155
|
+
});
|
|
156
|
+
|
|
133
157
|
// The run handler pulls in every runtime; the promote needs only its exit code.
|
|
134
158
|
vi.mock("./run", () => ({ GATED_EXIT_CODE: 3 }));
|
|
135
159
|
|
|
@@ -185,6 +209,9 @@ beforeEach(() => {
|
|
|
185
209
|
storedChangeSets.clear();
|
|
186
210
|
storeFails = false;
|
|
187
211
|
knownReleases.clear();
|
|
212
|
+
knownLabels.clear();
|
|
213
|
+
outcomes.length = 0;
|
|
214
|
+
revokedCommits.clear();
|
|
188
215
|
removedTemplates.length = 0;
|
|
189
216
|
ran.length = 0;
|
|
190
217
|
stepInputs.length = 0;
|
|
@@ -499,6 +526,33 @@ describe("chant components promote --release binds its gate (#4045)", () => {
|
|
|
499
526
|
});
|
|
500
527
|
});
|
|
501
528
|
|
|
529
|
+
test("a release cut from a revoked commit is refused before anything runs, so its pending gate never passes (#4033)", async () => {
|
|
530
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
531
|
+
const pending = releaseGatePort!.appended[0];
|
|
532
|
+
await releaseGatePort!.appendResolution!({
|
|
533
|
+
op: "release", gate: "prod-release", resolvedBy: "alice", timestamp: new Date(Date.now() + 1000).toISOString(),
|
|
534
|
+
planDigest: pending.planDigest, environment: "prod", releaseDigest: r1.releaseDigest,
|
|
535
|
+
});
|
|
536
|
+
// The commit went red after the approval: a run from chant runs (#4044) names its run in opRun, with id null.
|
|
537
|
+
revokedCommits.set(r1.commit, {
|
|
538
|
+
commit: r1.commit,
|
|
539
|
+
tag: `ci/revoked/${r1.commit}`,
|
|
540
|
+
tagged: "2026-10-16T12:00:00.000Z",
|
|
541
|
+
phase: "e2e",
|
|
542
|
+
run: { name: "smoke", id: null, conclusion: "failure", url: null, opRun: { id: "r-12", env: "staging", signer: "runner:ci" } },
|
|
543
|
+
signer: "runner:ci",
|
|
544
|
+
});
|
|
545
|
+
ran.length = 0;
|
|
546
|
+
errors.length = 0;
|
|
547
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(1);
|
|
548
|
+
const said = errors.join("\n");
|
|
549
|
+
expect(said).toMatch(/release v1\.0\.0 was cut from commit abc123000000, which ci\/revoked\/abc1230{34} revokes \(phase e2e failed in smoke \(Op run r-12 in staging\)\)/);
|
|
550
|
+
expect(said).toMatch(/\[release-revoked\]/);
|
|
551
|
+
expect(said).toMatch(/chant ci last-green/);
|
|
552
|
+
expect(ran).toEqual([]);
|
|
553
|
+
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
554
|
+
});
|
|
555
|
+
|
|
502
556
|
test("an approval for one release can't be used by another", async () => {
|
|
503
557
|
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
504
558
|
const pending = releaseGatePort!.appended[0];
|
|
@@ -585,7 +639,7 @@ describe("chant components rollback", () => {
|
|
|
585
639
|
|
|
586
640
|
test("an environment and --component are required", async () => {
|
|
587
641
|
expect(await runComponentsRollback(rb({ component: undefined }))).toBe(1);
|
|
588
|
-
expect(errors.join("\n")).toMatch(/--component <name> are required/);
|
|
642
|
+
expect(errors.join("\n")).toMatch(/--component <name> or --release <label\|digest> are required/);
|
|
589
643
|
});
|
|
590
644
|
|
|
591
645
|
test("redeploys the previous release from its recorded digest and records what it restored", async () => {
|
|
@@ -602,6 +656,24 @@ describe("chant components rollback", () => {
|
|
|
602
656
|
expect(appendReleaseRecordMock.mock.calls[0][0]).not.toHaveProperty("promotedFrom");
|
|
603
657
|
});
|
|
604
658
|
|
|
659
|
+
test("copies the release off the record it restores, and writes none for a record of no release (#4043)", async () => {
|
|
660
|
+
const RELEASE = `jcs1-sha256:${"b".repeat(64)}`;
|
|
661
|
+
readReleaseLedgerMock.mockResolvedValue({
|
|
662
|
+
records: [{ ...prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1"), releaseDigest: RELEASE }, prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2")],
|
|
663
|
+
malformed: 0,
|
|
664
|
+
});
|
|
665
|
+
expect(await runComponentsRollback(rb({}))).toBe(0);
|
|
666
|
+
expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ digest: "sha256:old", releaseDigest: RELEASE });
|
|
667
|
+
|
|
668
|
+
appendReleaseRecordMock.mockClear();
|
|
669
|
+
readReleaseLedgerMock.mockResolvedValue({
|
|
670
|
+
records: [prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1"), prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2")],
|
|
671
|
+
malformed: 0,
|
|
672
|
+
});
|
|
673
|
+
expect(await runComponentsRollback(rb({}))).toBe(0);
|
|
674
|
+
expect(appendReleaseRecordMock.mock.calls[0][0]).not.toHaveProperty("releaseDigest");
|
|
675
|
+
});
|
|
676
|
+
|
|
605
677
|
test("a comma list is refused: rollback acts on one component (#3950)", async () => {
|
|
606
678
|
expect(await runComponentsRollback(rb({ component: "api,web" }))).toBe(1);
|
|
607
679
|
expect(errors.join("\n")).toMatch(/--component takes one component name, not a list/);
|
|
@@ -668,6 +740,16 @@ describe("chant components redeploy", () => {
|
|
|
668
740
|
expect(errors.join("\n")).toMatch(/Redeployed .*api.* in prod at sha256:new/);
|
|
669
741
|
});
|
|
670
742
|
|
|
743
|
+
test("copies the release off the record it redeploys (#4043)", async () => {
|
|
744
|
+
const RELEASE = `jcs1-sha256:${"b".repeat(64)}`;
|
|
745
|
+
readReleaseLedgerMock.mockResolvedValue({
|
|
746
|
+
records: [{ ...prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2"), releaseDigest: RELEASE }],
|
|
747
|
+
malformed: 0,
|
|
748
|
+
});
|
|
749
|
+
expect(await runComponentsRedeploy(rd({}))).toBe(0);
|
|
750
|
+
expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({ digest: "sha256:new", releaseDigest: RELEASE, redeploys: { runId: "run-2" } });
|
|
751
|
+
});
|
|
752
|
+
|
|
671
753
|
test("the same digest rollback refuses is the one redeploy accepts", async () => {
|
|
672
754
|
expect(await runComponentsRollback(rd({ path: "rollback", digest: "sha256:new" }))).toBe(1);
|
|
673
755
|
expect(errors.join("\n")).toMatch(/already the current release/);
|
|
@@ -704,10 +786,13 @@ describe("chant components redeploy", () => {
|
|
|
704
786
|
expect(errors.join("\n")).toMatch(/then run the same redeploy again/);
|
|
705
787
|
});
|
|
706
788
|
|
|
707
|
-
test("a failed redeploy records
|
|
789
|
+
test("a failed redeploy records no release, records the component failed (#4035), and exits 1", async () => {
|
|
708
790
|
failApplyFor = "api";
|
|
709
791
|
expect(await runComponentsRedeploy(rd({}))).toBe(1);
|
|
710
792
|
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
793
|
+
expect(outcomes).toEqual([
|
|
794
|
+
expect.objectContaining({ env: "prod", verb: "redeploy", status: "fail", components: [{ component: "api", outcome: "failed", digest: "sha256:new", releaseDigest: null }] }),
|
|
795
|
+
]);
|
|
711
796
|
expect(errors.join("\n")).toMatch(/redeploy failed at "api"/);
|
|
712
797
|
});
|
|
713
798
|
});
|
|
@@ -780,3 +865,196 @@ describe("--events-json on promote, rollback and redeploy (#4041)", () => {
|
|
|
780
865
|
expect(out.filter((l) => l.startsWith('{"$schema"'))).toEqual([]);
|
|
781
866
|
});
|
|
782
867
|
});
|
|
868
|
+
|
|
869
|
+
describe("chant components rollback --release (#4035)", () => {
|
|
870
|
+
const prodTemplate = `{"env":"prod"}\n`;
|
|
871
|
+
const gated: DriverComponent = {
|
|
872
|
+
...api,
|
|
873
|
+
deploy: [
|
|
874
|
+
...api.deploy.slice(0, 2),
|
|
875
|
+
{ phase: "Apply", steps: [{ kind: "gate", gate: "prod-release" }, { kind: "apply", template: "dist/api.template.json", imageRef: "@Publish.digest" }] },
|
|
876
|
+
],
|
|
877
|
+
};
|
|
878
|
+
const releaseAt = (commit: string) =>
|
|
879
|
+
makeRelease({
|
|
880
|
+
member: null,
|
|
881
|
+
commit: commit.padEnd(40, "0"),
|
|
882
|
+
artifactDigests: [{ component: "api", kind: "image", path: "api.tar", digest: "sha256:api" }],
|
|
883
|
+
targets: [{ env: "prod", templates: [{ component: "api", path: "dist/api.template.json", digest: contentDigest(prodTemplate) }], params: [] }],
|
|
884
|
+
});
|
|
885
|
+
const r1 = releaseAt("abc123");
|
|
886
|
+
const r2 = releaseAt("def456");
|
|
887
|
+
const prod = (digest: string, timestamp: string, runId: string, release: { releaseDigest: string }): ReleaseRecord =>
|
|
888
|
+
({ ...staging, env: "prod", digest, timestamp, runId, releaseDigest: release.releaseDigest });
|
|
889
|
+
let ledger: ReleaseRecord[];
|
|
890
|
+
const revoke = (r: { commit: string }) =>
|
|
891
|
+
revokedCommits.set(r.commit, { commit: r.commit, tag: `ci/revoked/${r.commit}`, tagged: "2026-10-16T12:00:00.000Z", phase: "e2e", run: null, signer: null });
|
|
892
|
+
|
|
893
|
+
beforeEach(async () => {
|
|
894
|
+
const { memoryGateLedgerPort } = await vi.importActual<typeof import("../../op/gate")>("../../op/gate");
|
|
895
|
+
releaseGatePort = memoryGateLedgerPort();
|
|
896
|
+
resolveTargetsMock.mockResolvedValue({ success: true, targets: [gated] });
|
|
897
|
+
releaseFixture = { release: r1, label: "v1.0.0" };
|
|
898
|
+
storedTemplate = prodTemplate;
|
|
899
|
+
for (const [r, label] of [[r1, "v1.0.0"], [r2, "v1.1.0"]] as const) {
|
|
900
|
+
knownReleases.set(r.releaseDigest, r);
|
|
901
|
+
knownLabels.set(r.releaseDigest, label);
|
|
902
|
+
}
|
|
903
|
+
ledger = [prod("sha256:old", "2026-01-01T00:00:00.000Z", "run-1", r1), prod("sha256:new", "2026-01-02T00:00:00.000Z", "run-2", r2)];
|
|
904
|
+
readReleaseLedgerMock.mockImplementation(async (env: string) => ({ records: env === "prod" ? ledger : [], malformed: 0 }));
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
const rb = (a: Partial<ParsedArgs>) => ctx({ migrateFrom: undefined, migrateTo: undefined, path: "rollback", extraPositional: "prod", ...a });
|
|
908
|
+
|
|
909
|
+
test("restores every component of the release through the release gate, and records the release it restored", async () => {
|
|
910
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(3);
|
|
911
|
+
expect(ran).toEqual([]);
|
|
912
|
+
const [doc] = [...storedChangeSets.values()];
|
|
913
|
+
expect(doc).toMatchObject({ releaseDigest: r1.releaseDigest, environment: "prod" });
|
|
914
|
+
expect(doc.entries[0]).toMatchObject({ address: "api", action: "update" });
|
|
915
|
+
expect(releaseGatePort!.appended).toEqual([
|
|
916
|
+
expect.objectContaining({ op: "release", gate: "prod-release", environment: "prod", planDigest: doc.digest, releaseDigest: r1.releaseDigest }),
|
|
917
|
+
]);
|
|
918
|
+
expect(errors.join("\n")).toMatch(/chant approve release prod-release --env prod/);
|
|
919
|
+
expect(errors.join("\n")).toMatch(/then run the same rollback again/);
|
|
920
|
+
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
921
|
+
// Stopped at its gate with nothing deployed: the environment is unchanged, so no outcome.
|
|
922
|
+
expect(outcomes).toEqual([]);
|
|
923
|
+
|
|
924
|
+
await releaseGatePort!.appendResolution!({
|
|
925
|
+
op: "release", gate: "prod-release", resolvedBy: "alice", timestamp: new Date(Date.now() + 1000).toISOString(),
|
|
926
|
+
planDigest: doc.digest, environment: "prod", releaseDigest: r1.releaseDigest,
|
|
927
|
+
});
|
|
928
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(0);
|
|
929
|
+
// No build and no publish: the recorded digest of the release, and the release's own template.
|
|
930
|
+
expect(ran).toEqual(["prod:apply"]);
|
|
931
|
+
const pinned = `.chant/release-templates/${r1.targets[0].templates[0].digest.replace(/:/g, "_")}/api.template.json`;
|
|
932
|
+
expect(stepInputs.find((x) => x.kind === "apply")?.input).toMatchObject({ template: pinned, imageRef: "sha256:old" });
|
|
933
|
+
expect(appendReleaseRecordMock).toHaveBeenCalledTimes(1);
|
|
934
|
+
expect(appendReleaseRecordMock.mock.calls[0][0]).toMatchObject({
|
|
935
|
+
component: "api",
|
|
936
|
+
env: "prod",
|
|
937
|
+
digest: "sha256:old",
|
|
938
|
+
releaseDigest: r1.releaseDigest,
|
|
939
|
+
restores: { env: "prod", runId: "run-1", timestamp: "2026-01-01T00:00:00.000Z" },
|
|
940
|
+
approver: "alice",
|
|
941
|
+
admission: { op: "release", gate: "prod-release" },
|
|
942
|
+
});
|
|
943
|
+
expect(errors.join("\n")).toMatch(/Rolled back .*api.* in prod to sha256:old/);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
test("--release takes neither --component nor --digest", async () => {
|
|
947
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0", component: "api" }))).toBe(1);
|
|
948
|
+
expect(errors.join("\n")).toMatch(/--release rolls back every component of a release, so it does not take --component/);
|
|
949
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0", digest: "sha256:old" }))).toBe(1);
|
|
950
|
+
expect(errors.join("\n")).toMatch(/--digest picks one release of one component/);
|
|
951
|
+
expect(ran).toEqual([]);
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
test("the release the environment runs is refused, unless a run left one of its components unsettled", async () => {
|
|
955
|
+
releaseFixture = { release: r2, label: "v1.1.0" };
|
|
956
|
+
expect(await runComponentsRollback(rb({ release: "v1.1.0" }))).toBe(1);
|
|
957
|
+
expect(errors.join("\n")).toMatch(/"prod" already runs release v1\.1\.0; nothing to roll back/);
|
|
958
|
+
expect(storedChangeSets.size).toBe(0);
|
|
959
|
+
|
|
960
|
+
outcomes.push({
|
|
961
|
+
version: 1, env: "prod", verb: "promote", runId: "run-3", timestamp: "2026-01-03T00:00:00.000Z", actor: "bob",
|
|
962
|
+
releaseDigest: `jcs1-sha256:${"9".repeat(64)}`, status: "fail",
|
|
963
|
+
components: [{ component: "api", outcome: "failed", digest: "sha256:newer", releaseDigest: `jcs1-sha256:${"9".repeat(64)}` }],
|
|
964
|
+
});
|
|
965
|
+
// The ledger records v1.1.0, but the failed run left api unknown: a hole, so the gate waits for an approval.
|
|
966
|
+
expect(await runComponentsRollback(rb({ release: "v1.1.0" }))).toBe(3);
|
|
967
|
+
const [doc] = [...storedChangeSets.values()];
|
|
968
|
+
expect(doc.entries).toEqual([]);
|
|
969
|
+
expect(doc.members[0].holes).toEqual([expect.objectContaining({ address: "api", reason: expect.stringMatching(/the promote run run-3 left api failed in "prod"/) })]);
|
|
970
|
+
expect(releaseGatePort!.resolved).toEqual([]);
|
|
971
|
+
expect(releaseGatePort!.appended.at(-1)).toMatchObject({ releaseDigest: r2.releaseDigest, environment: "prod", planDigest: doc.digest });
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
test("a release that never deployed a component to the environment is refused", async () => {
|
|
975
|
+
ledger = [ledger[1]];
|
|
976
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(1);
|
|
977
|
+
expect(errors.join("\n")).toMatch(/release v1\.0\.0 never deployed "api" to "prod", so no recorded digest can be restored; promote it instead: chant components promote --to prod --release v1\.0\.0/);
|
|
978
|
+
expect(ran).toEqual([]);
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
test("a revoked release is refused before anything runs, naming a release to restore instead", async () => {
|
|
982
|
+
revoke(r1);
|
|
983
|
+
expect(await runComponentsRollback(rb({ release: "v1.0.0" }))).toBe(1);
|
|
984
|
+
const said = errors.join("\n");
|
|
985
|
+
expect(said).toMatch(/release v1\.0\.0 was cut from commit abc123000000, which ci\/revoked\/abc1230{34} revokes .*\[release-revoked\]/);
|
|
986
|
+
expect(said).toMatch(/chant components rollback prod --release v1\.1\.0/);
|
|
987
|
+
expect(ran).toEqual([]);
|
|
988
|
+
expect(storedChangeSets.size).toBe(0);
|
|
989
|
+
});
|
|
990
|
+
|
|
991
|
+
test("rollback --component and redeploy refuse to put back a revoked release (#4033)", async () => {
|
|
992
|
+
revoke(r2);
|
|
993
|
+
const rd = ctx({ migrateFrom: undefined, migrateTo: undefined, path: "redeploy", extraPositional: "prod", component: "api" });
|
|
994
|
+
expect(await runComponentsRedeploy(rd)).toBe(1);
|
|
995
|
+
expect(errors.join("\n")).toMatch(/release v1\.1\.0 was cut from commit def456000000, .*\[release-revoked\]/);
|
|
996
|
+
expect(errors.join("\n")).toMatch(/Restore a release cut from a green commit instead: chant components rollback prod --release v1\.0\.0/);
|
|
997
|
+
|
|
998
|
+
revokedCommits.clear();
|
|
999
|
+
revoke(r1);
|
|
1000
|
+
errors.length = 0;
|
|
1001
|
+
expect(await runComponentsRollback(rb({ component: "api" }))).toBe(1);
|
|
1002
|
+
expect(errors.join("\n")).toMatch(/release v1\.0\.0 was cut from commit abc123000000, .*\[release-revoked\]/);
|
|
1003
|
+
expect(ran).toEqual([]);
|
|
1004
|
+
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
describe("a promote that applies partly records what the environment runs (#4035)", () => {
|
|
1009
|
+
const web: DriverComponent = { ...api, name: "web" };
|
|
1010
|
+
const release = makeRelease({
|
|
1011
|
+
member: null,
|
|
1012
|
+
commit: "abc123".padEnd(40, "0"),
|
|
1013
|
+
artifactDigests: ["api", "web"].map((component) => ({ component, kind: "image", path: `${component}.tar`, digest: `sha256:${component}` })),
|
|
1014
|
+
targets: [{ env: "prod", templates: [], params: [] }],
|
|
1015
|
+
});
|
|
1016
|
+
|
|
1017
|
+
beforeEach(() => {
|
|
1018
|
+
resolveTargetsMock.mockResolvedValue({ success: true, targets: [api, web] });
|
|
1019
|
+
readReleaseLedgerMock.mockResolvedValue({ records: [staging, { ...staging, component: "web" }], malformed: 0 });
|
|
1020
|
+
releaseFixture = { release, label: "v2.0.0" };
|
|
1021
|
+
});
|
|
1022
|
+
|
|
1023
|
+
test("the component that deployed is recorded, the one that failed is recorded failed, and the lines that settle it are printed", async () => {
|
|
1024
|
+
failApplyFor = "web";
|
|
1025
|
+
expect(await runComponentsPromote(ctx({ release: "v2.0.0" }))).toBe(1);
|
|
1026
|
+
expect(appendReleaseRecordMock).toHaveBeenCalledTimes(1);
|
|
1027
|
+
const [record] = appendReleaseRecordMock.mock.calls[0];
|
|
1028
|
+
expect(record).toMatchObject({ component: "api", env: "prod", releaseDigest: release.releaseDigest });
|
|
1029
|
+
expect(outcomes).toEqual([{
|
|
1030
|
+
version: 1,
|
|
1031
|
+
env: "prod",
|
|
1032
|
+
verb: "promote",
|
|
1033
|
+
runId: "run-9",
|
|
1034
|
+
timestamp: record.timestamp,
|
|
1035
|
+
actor: "bob",
|
|
1036
|
+
releaseDigest: release.releaseDigest,
|
|
1037
|
+
status: "fail",
|
|
1038
|
+
components: [
|
|
1039
|
+
{ component: "api", outcome: "deployed", digest: "sha256:api", releaseDigest: release.releaseDigest },
|
|
1040
|
+
{ component: "web", outcome: "failed", digest: "sha256:api", releaseDigest: release.releaseDigest },
|
|
1041
|
+
],
|
|
1042
|
+
}]);
|
|
1043
|
+
expect(pushLifecycleMock).toHaveBeenCalledTimes(1);
|
|
1044
|
+
const said = errors.join("\n");
|
|
1045
|
+
expect(said).toMatch(/"prod" now runs a mix: api deployed, web failed/);
|
|
1046
|
+
expect(said).toMatch(/settle web: chant components redeploy prod --component web, or run the same promote again/);
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
test("--json carries the outcome", async () => {
|
|
1050
|
+
failApplyFor = "web";
|
|
1051
|
+
expect(await runComponentsPromote(ctx({ release: "v2.0.0", json: true }))).toBe(1);
|
|
1052
|
+
expect(JSON.parse(logs.join("\n")).outcome).toMatchObject({ status: "fail", components: [{ outcome: "deployed" }, { outcome: "failed" }] });
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
test("a promote that deploys everything records no outcome", async () => {
|
|
1056
|
+
expect(await runComponentsPromote(ctx({ release: "v2.0.0" }))).toBe(0);
|
|
1057
|
+
expect(appendReleaseRecordMock).toHaveBeenCalledTimes(2);
|
|
1058
|
+
expect(outcomes).toEqual([]);
|
|
1059
|
+
});
|
|
1060
|
+
});
|