@intentius/chant 0.124.32 → 0.124.34
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/ci-jobs.d.ts +4 -0
- package/dist/ci-jobs.d.ts.map +1 -1
- package/dist/ci-jobs.js +9 -2
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +18 -3
- package/dist/cli/handlers/lifecycle-health.d.ts +16 -0
- package/dist/cli/handlers/lifecycle-health.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-health.js +150 -0
- package/dist/cli/handlers/lifecycle.d.ts +26 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.js +1 -1
- package/dist/cli/handlers/pr.d.ts +2 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +124 -2
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +277 -74
- package/dist/cli/handlers/release.d.ts +16 -1
- package/dist/cli/handlers/release.d.ts.map +1 -1
- package/dist/cli/handlers/release.js +83 -11
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +99 -22
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +30 -1
- package/dist/cli/registry.d.ts +21 -5
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/components/deploy-inputs.d.ts +91 -0
- package/dist/components/deploy-inputs.d.ts.map +1 -0
- package/dist/components/deploy-inputs.js +184 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +9 -0
- package/dist/components/gate-plan.d.ts +8 -0
- package/dist/components/gate-plan.d.ts.map +1 -1
- package/dist/components/gate-plan.js +6 -0
- package/dist/components/pr-pipeline.d.ts +33 -10
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +42 -11
- package/dist/components/pr-run.d.ts +1 -1
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/promote-rounds.d.ts +102 -0
- package/dist/components/promote-rounds.d.ts.map +1 -0
- package/dist/components/promote-rounds.js +174 -0
- package/dist/components/release-compile.d.ts +37 -2
- package/dist/components/release-compile.d.ts.map +1 -1
- package/dist/components/release-compile.js +64 -8
- package/dist/components/release-gate.d.ts +30 -0
- package/dist/components/release-gate.d.ts.map +1 -1
- package/dist/components/release-gate.js +92 -42
- package/dist/components/release-promote.d.ts +17 -3
- package/dist/components/release-promote.d.ts.map +1 -1
- package/dist/components/release-promote.js +79 -18
- package/dist/components/verbs/bom-writer.d.ts +1 -1
- package/dist/components/verbs/bom-writer.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +20 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/lockfile-sbom-generator.js +41 -4
- package/dist/components/wave-plan.d.ts +10 -0
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +32 -4
- package/dist/graph-declared.d.ts +4 -0
- package/dist/graph-declared.d.ts.map +1 -1
- package/dist/graph-declared.js +11 -1
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-detail.js +32 -7
- package/dist/health.d.ts +110 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +105 -0
- package/dist/health.schema.json +191 -0
- package/dist/lexicon.d.ts +27 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/merge-record.d.ts +104 -0
- package/dist/lifecycle/merge-record.d.ts.map +1 -0
- package/dist/lifecycle/merge-record.js +102 -0
- package/dist/lifecycle/publish-attachments.d.ts +8 -5
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -1
- package/dist/lifecycle/publish-attachments.js +27 -13
- package/dist/lifecycle/publish-npm.d.ts +3 -2
- package/dist/lifecycle/publish-npm.d.ts.map +1 -1
- package/dist/lifecycle/publish-npm.js +4 -3
- package/dist/lifecycle/release-at.d.ts +40 -0
- package/dist/lifecycle/release-at.d.ts.map +1 -0
- package/dist/lifecycle/release-at.js +83 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -1
- package/dist/lifecycle/release-cut.js +23 -12
- package/dist/lifecycle/release-publish.d.ts +28 -12
- package/dist/lifecycle/release-publish.d.ts.map +1 -1
- package/dist/lifecycle/release-publish.js +29 -17
- package/dist/lifecycle/release-store.d.ts +25 -2
- package/dist/lifecycle/release-store.d.ts.map +1 -1
- package/dist/lifecycle/release-store.js +40 -13
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +1 -0
- package/dist/lint/rules/op/index.d.ts +2 -1
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.js +4 -2
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts +10 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.d.ts.map +1 -0
- package/dist/lint/rules/op/ops018-shell-image-digest.js +38 -0
- package/dist/op/activities/activity-contracts.d.ts +5 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.js +3 -0
- package/dist/op/activities/shell.d.ts +38 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/shell.js +89 -3
- package/dist/op/builders.d.ts +7 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/builders.js +7 -0
- package/dist/op/runtime.d.ts +2 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/pr-loop.d.ts +14 -3
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +15 -5
- package/dist/pr-report-url.d.ts +2 -2
- package/dist/pr-report-url.d.ts.map +1 -1
- package/dist/runner-start.d.ts +179 -0
- package/dist/runner-start.d.ts.map +1 -0
- package/dist/runner-start.js +277 -0
- package/dist/wave-runtime.mjs +236 -229
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.js +13 -2
- package/dist/workspace/check.schema.json +88 -2
- package/dist/workspace/checks/ci-workflow.d.ts +17 -0
- package/dist/workspace/checks/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/checks/ci-workflow.js +123 -0
- package/dist/workspace/checks/security.d.ts +50 -0
- package/dist/workspace/checks/security.d.ts.map +1 -0
- package/dist/workspace/checks/security.js +305 -0
- package/dist/workspace/checks.d.ts +2 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +8 -0
- package/dist/workspace/ci-builds.d.ts +163 -0
- package/dist/workspace/ci-builds.d.ts.map +1 -0
- package/dist/workspace/ci-builds.js +315 -0
- package/dist/workspace/ci-green-cli.d.ts +2 -1
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +25 -5
- package/dist/workspace/ci-green-forge.d.ts +12 -0
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +12 -0
- package/dist/workspace/ci-green-workflow.d.ts +55 -1
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +128 -1
- package/dist/workspace/ci-green.d.ts +89 -4
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +310 -23
- package/dist/workspace/ci-init.d.ts +100 -0
- package/dist/workspace/ci-init.d.ts.map +1 -0
- package/dist/workspace/ci-init.js +434 -0
- package/dist/workspace/ci-last-green.schema.json +49 -0
- package/dist/workspace/ci-list.schema.json +35 -1
- package/dist/workspace/ci-run-index.d.ts +1 -1
- package/dist/workspace/ci-run-index.d.ts.map +1 -1
- package/dist/workspace/ci-run-index.schema.json +1 -1
- package/dist/workspace/ci-run.d.ts +12 -6
- package/dist/workspace/ci-run.d.ts.map +1 -1
- package/dist/workspace/ci-run.js +72 -16
- package/dist/workspace/ci-runner-start.d.ts +77 -0
- package/dist/workspace/ci-runner-start.d.ts.map +1 -0
- package/dist/workspace/ci-runner-start.js +185 -0
- package/dist/workspace/ci-serve.d.ts +76 -7
- package/dist/workspace/ci-serve.d.ts.map +1 -1
- package/dist/workspace/ci-serve.js +153 -19
- package/dist/workspace/ci-workflow.d.ts +91 -0
- package/dist/workspace/ci-workflow.d.ts.map +1 -0
- package/dist/workspace/ci-workflow.js +33 -0
- package/dist/workspace/declaration.d.ts +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +12 -2
- package/dist/workspace/declaration.schema.json +51 -0
- package/dist/workspace/init.js +2 -2
- package/dist/workspace/ls.d.ts +2 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +1 -1
- package/dist/workspace/ls.schema.json +2 -1
- package/dist/workspace/member-pipeline.d.ts +1 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +2 -0
- package/dist/workspace/merge-record.schema.json +49 -0
- package/dist/workspace/pr-report.schema.json +3 -3
- package/dist/workspace/reason-codes.d.ts +26 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +28 -0
- package/dist/workspace/release-candidates.d.ts +129 -0
- package/dist/workspace/release-candidates.d.ts.map +1 -0
- package/dist/workspace/release-candidates.js +233 -0
- package/dist/workspace/release-candidates.schema.json +353 -0
- package/dist/workspace/release-workflow.d.ts +31 -0
- package/dist/workspace/release-workflow.d.ts.map +1 -0
- package/dist/workspace/release-workflow.js +150 -0
- package/dist/workspace/security.d.ts +40 -0
- package/dist/workspace/security.d.ts.map +1 -0
- package/dist/workspace/security.js +99 -0
- package/package.json +8 -1
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +1 -1
- package/src/ci-jobs.ts +11 -2
- package/src/cli/handlers/build.test.ts +36 -0
- package/src/cli/handlers/build.ts +18 -3
- package/src/cli/handlers/lifecycle-health.test.ts +123 -0
- package/src/cli/handlers/lifecycle-health.ts +157 -0
- package/src/cli/handlers/lifecycle.ts +1 -1
- package/src/cli/handlers/pr-deploy-inputs.test.ts +209 -0
- package/src/cli/handlers/pr-record.test.ts +201 -0
- package/src/cli/handlers/pr.test.ts +22 -0
- package/src/cli/handlers/pr.ts +127 -4
- package/src/cli/handlers/promote.test.ts +17 -0
- package/src/cli/handlers/promote.ts +296 -84
- package/src/cli/handlers/release-cut-at.e2e.test.ts +115 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +2 -2
- package/src/cli/handlers/release-verify.e2e.test.ts +12 -10
- package/src/cli/handlers/release.e2e.test.ts +19 -28
- package/src/cli/handlers/release.ts +81 -11
- package/src/cli/main.ts +99 -22
- package/src/cli/parse-args.ts +22 -1
- package/src/cli/registry.ts +21 -5
- package/src/components/deploy-inputs.test.ts +108 -0
- package/src/components/deploy-inputs.ts +225 -0
- package/src/components/driver.ts +11 -0
- package/src/components/gate-plan.ts +9 -0
- package/src/components/pr-pipeline.ts +56 -15
- package/src/components/pr-run.ts +1 -1
- package/src/components/promote-rounds.test.ts +220 -0
- package/src/components/promote-rounds.ts +232 -0
- package/src/components/release-compile.test.ts +34 -0
- package/src/components/release-compile.ts +89 -8
- package/src/components/release-gate.test.ts +47 -0
- package/src/components/release-gate.ts +111 -46
- package/src/components/release-promote.test.ts +55 -2
- package/src/components/release-promote.ts +93 -23
- package/src/components/verbs/bom-writer.ts +1 -1
- package/src/components/verbs/lockfile-sbom-generator.test.ts +36 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +43 -5
- package/src/components/wave-plan.ts +32 -4
- package/src/graph-declared-exports.test.ts +40 -0
- package/src/graph-declared.ts +9 -1
- package/src/graph-detail.test.ts +57 -0
- package/src/graph-detail.ts +33 -7
- package/src/health.schema.json +191 -0
- package/src/health.test.ts +62 -0
- package/src/health.ts +178 -0
- package/src/lexicon.ts +41 -1
- package/src/lifecycle/merge-record.test.ts +111 -0
- package/src/lifecycle/merge-record.ts +154 -0
- package/src/lifecycle/publish-attachments.ts +25 -13
- package/src/lifecycle/publish-npm.ts +4 -3
- package/src/lifecycle/publish-targets.test.ts +34 -4
- package/src/lifecycle/release-at.test.ts +48 -0
- package/src/lifecycle/release-at.ts +100 -0
- package/src/lifecycle/release-cut.test.ts +41 -0
- package/src/lifecycle/release-cut.ts +22 -11
- package/src/lifecycle/release-publish.test.ts +43 -13
- package/src/lifecycle/release-publish.ts +38 -18
- package/src/lifecycle/release-store.ts +57 -13
- package/src/lifecycle/run-ledger.test.ts +13 -0
- package/src/lifecycle/run-ledger.ts +1 -0
- package/src/lint/rules/op/index.ts +4 -2
- package/src/lint/rules/op/ops018-shell-image-digest.test.ts +60 -0
- package/src/lint/rules/op/ops018-shell-image-digest.ts +48 -0
- package/src/op/activities/activity-contracts.ts +3 -0
- package/src/op/activities/shell-container.e2e.test.ts +67 -0
- package/src/op/activities/shell.test.ts +44 -1
- package/src/op/activities/shell.ts +110 -3
- package/src/op/builders.ts +7 -0
- package/src/op/runtime.ts +2 -0
- package/src/op/step-console.test.ts +4 -2
- package/src/pr-loop.test.ts +29 -0
- package/src/pr-loop.ts +26 -7
- package/src/pr-report-url.ts +2 -2
- package/src/runner-start-bundle.test.ts +29 -0
- package/src/runner-start.test.ts +179 -0
- package/src/runner-start.ts +329 -0
- package/src/workspace/__fixtures__/contract-repo.ts +11 -2
- package/src/workspace/check-contract.test.ts +4 -2
- package/src/workspace/check.schema.json +88 -2
- package/src/workspace/checks/ci-workflow.test.ts +150 -0
- package/src/workspace/checks/ci-workflow.ts +129 -0
- package/src/workspace/checks/security.test.ts +138 -0
- package/src/workspace/checks/security.ts +305 -0
- package/src/workspace/checks.ts +8 -0
- package/src/workspace/ci-builds.e2e.test.ts +208 -0
- package/src/workspace/ci-builds.test.ts +174 -0
- package/src/workspace/ci-builds.ts +385 -0
- package/src/workspace/ci-green-builds.test.ts +180 -0
- package/src/workspace/ci-green-cli.test.ts +54 -0
- package/src/workspace/ci-green-cli.ts +26 -5
- package/src/workspace/ci-green-forge.ts +24 -0
- package/src/workspace/ci-green-workflow.test.ts +84 -1
- package/src/workspace/ci-green-workflow.ts +139 -2
- package/src/workspace/ci-green.test.ts +5 -2
- package/src/workspace/ci-green.ts +359 -27
- package/src/workspace/ci-init.test.ts +159 -0
- package/src/workspace/ci-init.ts +445 -0
- package/src/workspace/ci-last-green.schema.json +49 -0
- package/src/workspace/ci-list.schema.json +35 -1
- package/src/workspace/ci-report-url.test.ts +6 -1
- package/src/workspace/ci-run-index.schema.json +1 -1
- package/src/workspace/ci-run-index.ts +1 -1
- package/src/workspace/ci-run.test.ts +24 -7
- package/src/workspace/ci-run.ts +78 -16
- package/src/workspace/ci-runner-start.test.ts +151 -0
- package/src/workspace/ci-runner-start.ts +193 -0
- package/src/workspace/ci-serve-builds.test.ts +86 -0
- package/src/workspace/ci-serve.test.ts +59 -0
- package/src/workspace/ci-serve.ts +177 -22
- package/src/workspace/ci-workflow.ts +71 -0
- package/src/workspace/declaration.schema.json +51 -0
- package/src/workspace/declaration.test.ts +17 -0
- package/src/workspace/declaration.ts +53 -3
- package/src/workspace/init.ts +2 -2
- package/src/workspace/ls.schema.json +2 -1
- package/src/workspace/ls.ts +3 -2
- package/src/workspace/member-pipeline.ts +2 -1
- package/src/workspace/merge-record.schema.json +49 -0
- package/src/workspace/pr-report.schema.json +3 -3
- package/src/workspace/reason-codes.test.ts +4 -0
- package/src/workspace/reason-codes.ts +28 -0
- package/src/workspace/release-candidates.schema.json +353 -0
- package/src/workspace/release-candidates.test.ts +106 -0
- package/src/workspace/release-candidates.ts +315 -0
- package/src/workspace/release-workflow.ts +167 -0
- package/src/workspace/security.test.ts +70 -0
- package/src/workspace/security.ts +118 -0
package/src/cli/handlers/pr.ts
CHANGED
|
@@ -153,7 +153,7 @@ const fail = (message: string, hint?: string): number => {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
/** Everything both stages share: the change and the derivation. Each stage plans the members itself. */
|
|
156
|
-
async function prepare(ctx: CommandContext, stage: "plan" | "apply"): Promise<Prepared | number> {
|
|
156
|
+
async function prepare(ctx: CommandContext, stage: "plan" | "apply" | "record"): Promise<Prepared | number> {
|
|
157
157
|
const { args } = ctx;
|
|
158
158
|
const projectPath = resolve(".");
|
|
159
159
|
const { config } = await loadChantConfig(projectPath).catch(() => ({ config: {} as ChantConfig }));
|
|
@@ -297,6 +297,7 @@ function settleOf(status: PrReport["status"]): RunEventSettleStatus {
|
|
|
297
297
|
switch (status) {
|
|
298
298
|
case "nothing":
|
|
299
299
|
case "planned":
|
|
300
|
+
case "recorded":
|
|
300
301
|
case "applied":
|
|
301
302
|
return "ok";
|
|
302
303
|
case "refused":
|
|
@@ -405,7 +406,7 @@ async function withStageEvents(stage: string, ctx: CommandContext, run: (events:
|
|
|
405
406
|
* Open the stage's `--report-url` upload under its pull request and commit,
|
|
406
407
|
* when the flag was given. Its events are the lines `openCliRunEvents` writes.
|
|
407
408
|
*/
|
|
408
|
-
function openUpload(events: StageEvents, stage: "plan" | "apply", pr: number | null, p: Prepared): PrReportUpload | undefined {
|
|
409
|
+
function openUpload(events: StageEvents, stage: "plan" | "apply" | "record", pr: number | null, p: Prepared): PrReportUpload | undefined {
|
|
409
410
|
if (!events.target) return undefined;
|
|
410
411
|
events.upload = openPrReportUpload({
|
|
411
412
|
target: events.target,
|
|
@@ -484,6 +485,11 @@ export function runComponentsPrApply(ctx: CommandContext): Promise<number> {
|
|
|
484
485
|
}
|
|
485
486
|
|
|
486
487
|
async function prApply(ctx: CommandContext, stage: StageEvents): Promise<number> {
|
|
488
|
+
// #4168: the review-rules security group turns --require-review on, read from the declaration at base.
|
|
489
|
+
const { securityGroupsAt } = await import("../../workspace/security");
|
|
490
|
+
const reviewRules = !ctx.args.requireReview && securityGroupsAt(process.cwd(), ctx.args.base).has("review-rules");
|
|
491
|
+
if (reviewRules && !ctx.args.forge) return fail("the workspace opts into review-rules, which reads the pull request's reviews, so pr-apply needs --forge <kind>");
|
|
492
|
+
if (reviewRules) ctx = { ...ctx, args: { ...ctx.args, requireReview: true } };
|
|
487
493
|
const { args } = ctx;
|
|
488
494
|
if (args.requireReview && !args.forge) return fail("--require-review reads the pull request's reviews, so it needs --forge <kind>");
|
|
489
495
|
if (args.wave !== undefined && !args.waveGate) return fail("--wave runs one wave of a --wave-gate apply, so it needs --wave-gate <name>");
|
|
@@ -723,12 +729,129 @@ async function prApply(ctx: CommandContext, stage: StageEvents): Promise<number>
|
|
|
723
729
|
return code;
|
|
724
730
|
}
|
|
725
731
|
|
|
732
|
+
/** chant components pr-record --base <ref> [--pr <n>] [--env <env>] [--gate <name>] [--forge <kind>] [--output <dir>] [--json] [--events-json] [--report-url <url>] */
|
|
733
|
+
export function runComponentsPrRecord(ctx: CommandContext): Promise<number> {
|
|
734
|
+
if (refusesEventsWithJson(ctx.args, "the stage's report")) return Promise.resolve(1);
|
|
735
|
+
return withStageEvents("pr-record", ctx, (events) => prRecord(ctx, events));
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/**
|
|
739
|
+
* The record stage (#4168 step 1): what a merge does unless apply on merge
|
|
740
|
+
* is opted in. It plans the members the change reaches on the merged
|
|
741
|
+
* commit, stores the change set, decides the pull request's gate against
|
|
742
|
+
* it without writing to the ledger, and writes the merge record
|
|
743
|
+
* (`../../lifecycle/merge-record.ts`). It applies nothing and refuses
|
|
744
|
+
* nothing. A plan that differs from the approved one is recorded, and the
|
|
745
|
+
* `chant/record` status fails (decision 1). It exits 1 only when a member
|
|
746
|
+
* failed to plan.
|
|
747
|
+
*/
|
|
748
|
+
async function prRecord(ctx: CommandContext, stage: StageEvents): Promise<number> {
|
|
749
|
+
const { args } = ctx;
|
|
750
|
+
const p = await prepare(ctx, "record");
|
|
751
|
+
if (typeof p === "number") return p;
|
|
752
|
+
|
|
753
|
+
let pr: number | null = args.pr ?? null;
|
|
754
|
+
if (pr === null && p.forge) {
|
|
755
|
+
try {
|
|
756
|
+
pr = await p.forge.pullRequestFor(p.head);
|
|
757
|
+
} catch (err) {
|
|
758
|
+
console.error(formatWarning({ message: `could not ask ${p.forge.kind} which pull request merged ${p.head}, so the record names none: ${err instanceof Error ? err.message : String(err)}` }));
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
const op = pr === null ? "pr-unknown" : prOp(pr, p.member);
|
|
762
|
+
const upload = openUpload(stage, "record", pr, p);
|
|
763
|
+
const events = await openCliRunEvents(args, { kind: "component", name: `pr-record ${op}`, cwd: resolve(".") }, upload?.line);
|
|
764
|
+
stage.stream = events;
|
|
765
|
+
events?.emit({ type: "run-start", waves: p.plan.waves });
|
|
766
|
+
const set = await planAll(p, events?.emit);
|
|
767
|
+
const nothing = p.plan.order.length === 0;
|
|
768
|
+
|
|
769
|
+
let approval: PrApproval = { status: "pending" };
|
|
770
|
+
if (!nothing && !set.failed) {
|
|
771
|
+
await storeChangeSetForGate(ctx, p, set.doc);
|
|
772
|
+
if (pr !== null) {
|
|
773
|
+
try {
|
|
774
|
+
const check = await emitGateStep(events?.emit, { op, gate: p.gate }, () =>
|
|
775
|
+
decidePrGate(readOnlyLedger(gitGateLedgerPort()), { op, gate: p.gate, digest: set.doc.digest }),
|
|
776
|
+
);
|
|
777
|
+
approval = approvalOf(check);
|
|
778
|
+
} catch (err) {
|
|
779
|
+
console.error(formatWarning({ message: `could not read the gate ledger, so the record says no approval: ${err instanceof Error ? err.message : String(err)}` }));
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// #4000: the record keeps an approval by someone who wrote the change, which review rules would not count.
|
|
785
|
+
let authorApproved: string[] | undefined;
|
|
786
|
+
if (pr !== null && p.forge && approval.status === "approved") {
|
|
787
|
+
try {
|
|
788
|
+
const authors = (await p.forge.authors(pr)).map((login) => p.forge!.principalOf(login));
|
|
789
|
+
authorApproved = approversWhoAuthored(approval.approvedBy ?? [], authors);
|
|
790
|
+
} catch (err) {
|
|
791
|
+
console.error(formatWarning({ message: `could not read the authors of #${pr}: ${err instanceof Error ? err.message : String(err)}` }));
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
const status = nothing ? "nothing" : set.failed ? "plan-failed" : "recorded";
|
|
796
|
+
const report = prReport({
|
|
797
|
+
stage: "record",
|
|
798
|
+
pr,
|
|
799
|
+
env: p.env,
|
|
800
|
+
...(p.member ? { member: p.member } : {}),
|
|
801
|
+
base: p.base,
|
|
802
|
+
head: p.head,
|
|
803
|
+
op,
|
|
804
|
+
gate: p.gate,
|
|
805
|
+
plan: p.plan,
|
|
806
|
+
signal: p.signal,
|
|
807
|
+
set,
|
|
808
|
+
approval,
|
|
809
|
+
status,
|
|
810
|
+
...(status === "plan-failed" ? { message: "A member failed to plan on the merged commit, so the record has no plan to compare with the approved one. Nothing was applied." } : {}),
|
|
811
|
+
...(status === "recorded" && approval.status === "changed"
|
|
812
|
+
? { message: `${describePlanChanged({ op, gate: p.gate, digest: set.doc.digest, approval })} The merge stands and nothing was applied; the release gate decides before this deploys.` }
|
|
813
|
+
: {}),
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
const { mergeSourceOf, writeMergeRecord, MERGE_RECORD_SCHEMA_ID, MERGE_RECORD_CONTRACT } = await import("../../lifecycle/merge-record");
|
|
817
|
+
const { pushLifecycleReport } = await import("../../lifecycle/git");
|
|
818
|
+
try {
|
|
819
|
+
const written = await writeMergeRecord({
|
|
820
|
+
$schema: MERGE_RECORD_SCHEMA_ID,
|
|
821
|
+
contract: MERGE_RECORD_CONTRACT,
|
|
822
|
+
commit: p.head,
|
|
823
|
+
base: p.base,
|
|
824
|
+
env: p.env,
|
|
825
|
+
...(p.member ? { member: p.member } : {}),
|
|
826
|
+
source: mergeSourceOf(resolve("."), p.head, pr),
|
|
827
|
+
op: pr === null ? null : op,
|
|
828
|
+
gate: p.gate,
|
|
829
|
+
digest: nothing ? null : set.doc.digest,
|
|
830
|
+
plan: nothing ? "nothing" : set.failed ? "plan-failed" : approval.status === "approved" ? "approved" : approval.status === "changed" ? "changed" : "unapproved",
|
|
831
|
+
...(approval.status === "changed" && approval.approved ? { approvedDigest: approval.approved } : {}),
|
|
832
|
+
approvedBy: approval.status === "approved" ? (approval.approvedBy ?? []) : [],
|
|
833
|
+
...(authorApproved && authorApproved.length > 0 ? { authorApproved } : {}),
|
|
834
|
+
components: p.plan.order,
|
|
835
|
+
recordedAt: new Date().toISOString(),
|
|
836
|
+
});
|
|
837
|
+
if (written.written) {
|
|
838
|
+
const pushed = await pushLifecycleReport();
|
|
839
|
+
if (pushed.pushWarning) console.error(formatWarning({ message: pushed.pushWarning }));
|
|
840
|
+
}
|
|
841
|
+
} catch (err) {
|
|
842
|
+
console.error(formatWarning({ message: `could not write the merge record for ${p.head}: ${err instanceof Error ? err.message : String(err)}` }));
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const stop = stopper(ctx, p, events, upload);
|
|
846
|
+
return stop(report, status === "plan-failed" ? 1 : 0);
|
|
847
|
+
}
|
|
848
|
+
|
|
726
849
|
/** How a stage stops: settle its events, write and publish the report, say how it ended, and return `code`. */
|
|
727
850
|
function stopper(ctx: CommandContext, p: Prepared, events: RunEventStream | undefined, upload: PrReportUpload | undefined) {
|
|
728
851
|
return async (report: PrReport, code: number, settle?: RunEventSettleStatus): Promise<number> => {
|
|
729
|
-
events?.settle(settle ?? settleOf(report.status), report.status === "plan-failed" || report.status === "failed" ? report.message ?? `pr
|
|
852
|
+
events?.settle(settle ?? settleOf(report.status), report.status === "plan-failed" || report.status === "failed" ? report.message ?? `pr-${report.stage} ${report.status}` : undefined);
|
|
730
853
|
await finish(ctx, p, report, upload);
|
|
731
|
-
console.error((code === 0 ? formatInfo : (m: string) => formatWarning({ message: m }))(`pr
|
|
854
|
+
console.error((code === 0 ? formatInfo : (m: string) => formatWarning({ message: m }))(`pr-${report.stage}: ${report.status}${report.refusal ? ` (${report.refusal})` : ""}`));
|
|
732
855
|
if (report.message) console.error(report.message);
|
|
733
856
|
printMembers(report);
|
|
734
857
|
return code;
|
|
@@ -156,6 +156,12 @@ vi.mock("../../lifecycle/release-revocation", async () => {
|
|
|
156
156
|
return { ...actual, readRevokedCommits: async () => revokedCommits };
|
|
157
157
|
});
|
|
158
158
|
|
|
159
|
+
// #4168: the security groups and the release verification, set per test.
|
|
160
|
+
let groups: string[] = [];
|
|
161
|
+
let verification: { status: "verified" | "unsigned" | "unverified"; digestMatches: boolean; reason: string } = { status: "unsigned", digestMatches: true, reason: "no cut of the release carries a signature" };
|
|
162
|
+
vi.mock("../../workspace/security", async (original) => ({ ...(await original<typeof import("../../workspace/security")>()), securityGroupsAt: () => new Set(groups) }));
|
|
163
|
+
vi.mock("../../lifecycle/release-verify", async (original) => ({ ...(await original<typeof import("../../lifecycle/release-verify")>()), verifyRelease: async () => verification }));
|
|
164
|
+
|
|
159
165
|
// The run handler pulls in every runtime; the promote needs only its exit code.
|
|
160
166
|
vi.mock("./run", () => ({ GATED_EXIT_CODE: 3 }));
|
|
161
167
|
|
|
@@ -214,6 +220,8 @@ beforeEach(() => {
|
|
|
214
220
|
knownLabels.clear();
|
|
215
221
|
outcomes.length = 0;
|
|
216
222
|
revokedCommits.clear();
|
|
223
|
+
groups = [];
|
|
224
|
+
verification = { status: "unsigned", digestMatches: true, reason: "no cut of the release carries a signature" };
|
|
217
225
|
removedTemplates.length = 0;
|
|
218
226
|
ran.length = 0;
|
|
219
227
|
stepInputs.length = 0;
|
|
@@ -571,6 +579,15 @@ describe("chant components promote --release binds its gate (#4045)", () => {
|
|
|
571
579
|
expect(appendReleaseRecordMock).not.toHaveBeenCalled();
|
|
572
580
|
});
|
|
573
581
|
|
|
582
|
+
test("under verified-releases a release that does not verify is refused before anything runs, and one that does goes on to its gate (#4168)", async () => {
|
|
583
|
+
groups = ["verified-releases"];
|
|
584
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(1);
|
|
585
|
+
expect(errors.join("\n")).toMatch(/release-unsigned: release v1\.0\.0 does not verify \(no cut of the release carries a signature\), and the workspace opts into verified-releases/);
|
|
586
|
+
expect(ran).toEqual([]);
|
|
587
|
+
verification = { status: "verified", digestMatches: true, reason: "signed by alice" };
|
|
588
|
+
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
589
|
+
});
|
|
590
|
+
|
|
574
591
|
test("an approval for one release can't be used by another", async () => {
|
|
575
592
|
expect(await runComponentsPromote(ctx({ release: "v1.0.0" }))).toBe(3);
|
|
576
593
|
const pending = releaseGatePort!.appended[0];
|