@mutmutco/cli 4.3.52 → 4.3.53

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.
Files changed (2) hide show
  1. package/dist/main.cjs +117 -22
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -15858,10 +15858,10 @@ var rollout_plan_default = {
15858
15858
  note: "The v4.0.0 stamp happens at cut time (D6e #4463); until then the candidate is the origin/development head artifacts (built cli/dist + npm pack), identity proven by dist content hash (D6a)."
15859
15859
  },
15860
15860
  baseline: {
15861
- version: "4.3.52",
15862
- tag: "v4.3.52",
15863
- commit: "6219f52caf87",
15864
- npm: "@mutmutco/cli@4.3.52"
15861
+ version: "4.3.53",
15862
+ tag: "v4.3.53",
15863
+ commit: "8877c0336b18",
15864
+ npm: "@mutmutco/cli@4.3.53"
15865
15865
  },
15866
15866
  exitCriterion: "fleet-n-of-n",
15867
15867
  hubOnlyShortcut: "forbidden",
@@ -15878,14 +15878,14 @@ var rollout_plan_default = {
15878
15878
  repo: "mutmutco/mmi-hub",
15879
15879
  role: "canary",
15880
15880
  schedule: "train",
15881
- v3Target: "v4.3.52"
15881
+ v3Target: "v4.3.53"
15882
15882
  }
15883
15883
  ],
15884
15884
  rollbackTrigger: "Any red inside the post-contract soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a pre-v4 client admitted instead of receiving actionable HTTP 426, or npm consumer install/doctor failure on the v4-only dist.",
15885
15885
  rollback: {
15886
15886
  independent: true,
15887
- mechanism: "npm dist-tag latest -> 4.3.52 and redeploy the Hub Lambda from tag v4.3.52 (6219f52caf87); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15888
- v3Target: "v4.3.52 (@mutmutco/cli@4.3.52, tag commit 6219f52caf87 \u2014 last known-good release carrying the repo-index v4-only contract)"
15887
+ mechanism: "npm dist-tag latest -> 4.3.53 and redeploy the Hub Lambda from tag v4.3.53 (8877c0336b18); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15888
+ v3Target: "v4.3.53 (@mutmutco/cli@4.3.53, tag commit 8877c0336b18 \u2014 last known-good release carrying the repo-index v4-only contract)"
15889
15889
  }
15890
15890
  },
15891
15891
  {
@@ -17819,6 +17819,7 @@ rollback stays available: mmi-cli devops train enforce --apply --disarm`
17819
17819
 
17820
17820
  // src/train-apply-correlation.ts
17821
17821
  async function verifyPublishedRelease(deps, repo, tag, expectedTarget, expectedSha) {
17822
+ deps.warn?.("Release: Verifying the published release");
17822
17823
  const out = await deps.run("gh", ["release", "view", tag, "--repo", repo, "--json", "tagName,targetCommitish"]);
17823
17824
  let release;
17824
17825
  try {
@@ -17954,6 +17955,7 @@ async function currentBranch(deps) {
17954
17955
  return clean2(await deps.run("git", ["rev-parse", "--abbrev-ref", "HEAD"])) || "(unknown)";
17955
17956
  }
17956
17957
  async function restoreCheckoutAfterRelease(deps, startBranch) {
17958
+ deps.warn?.("Release: Returning to the working branch");
17957
17959
  const status = await deps.run("git", ["status", "--porcelain"]);
17958
17960
  if (porcelainHasBlockingChanges(status)) {
17959
17961
  return {
@@ -18194,6 +18196,7 @@ async function correlateWorkflowRun(deps, args) {
18194
18196
  }
18195
18197
  async function watchTenantRun(deps, runId, repo = HUB_REPO2) {
18196
18198
  if (runId == null) return "pending";
18199
+ deps.warn?.("Release: Waiting for deployment or publishing to finish");
18197
18200
  let watchSaidSuccess = true;
18198
18201
  const startedAt = Date.now();
18199
18202
  deps.warn?.(`watching workflow run ${runId} (${repo})`);
@@ -18304,6 +18307,7 @@ function parsePrNumber(url) {
18304
18307
  return Number.isFinite(n) ? n : void 0;
18305
18308
  }
18306
18309
  async function rollDevelopmentForward(deps, ctx, tag) {
18310
+ deps.warn?.("Release: Bringing development up to date");
18307
18311
  await runGitRemoteRead(deps, ["fetch", "origin", "main", "development"]);
18308
18312
  const required = await discoverRequiredCheckContexts(deps, ctx, "development");
18309
18313
  if (required.length === 0) {
@@ -18368,6 +18372,7 @@ var ALIGNMENT_LAND_POLL_MS = 15e3;
18368
18372
  var ALIGNMENT_LAND_TIMEOUT_MS = 20 * 6e4;
18369
18373
  async function awaitAlignmentLanded(deps, ctx, leg, base, opts = {}) {
18370
18374
  if (leg.status !== "pr-pending" || !leg.autoMergeEnqueued || leg.prNumber === void 0) return leg;
18375
+ deps.warn?.(`Release: Waiting for ${base} alignment to finish`);
18371
18376
  const sleep2 = resolveSleep(deps);
18372
18377
  const now = deps.now ?? Date.now;
18373
18378
  const startedAt = now();
@@ -18409,6 +18414,7 @@ async function awaitAlignmentLanded(deps, ctx, leg, base, opts = {}) {
18409
18414
  }
18410
18415
  }
18411
18416
  async function alignRcForward(deps, ctx, tag) {
18417
+ deps.warn?.("Release: Bringing the release candidate up to date");
18412
18418
  await runGitRemoteRead(deps, ["fetch", "origin", "main", "rc"]);
18413
18419
  const required = await discoverRequiredCheckContexts(deps, ctx, "rc");
18414
18420
  if (required.length === 0) {
@@ -18471,6 +18477,7 @@ async function waitForRequiredTrainChecks(deps, ctx, sha, required, freshSince)
18471
18477
  if (required.length === 0) {
18472
18478
  return "no required status checks configured on the target branch \u2014 check wait skipped (GitHub push gate is the backstop)";
18473
18479
  }
18480
+ deps.warn?.("Release: Waiting for required checks");
18474
18481
  let fleetNote = "";
18475
18482
  let fleetPosted;
18476
18483
  if (required.includes(FLEET_LOCKSTEP_CONTEXT)) {
@@ -18764,6 +18771,7 @@ async function reconcilePublishFailure(deps, runId) {
18764
18771
  return "failure";
18765
18772
  }
18766
18773
  async function dispatchTenantPublish(deps, ctx, stage, ref, watch, dispatchFailure = "throw", publishDir) {
18774
+ deps.warn?.("Release: Starting package publishing");
18767
18775
  const since = (deps.now ?? Date.now)();
18768
18776
  const dispatchArgs = [
18769
18777
  "workflow",
@@ -18989,6 +18997,7 @@ async function discoverShaWorkflowRuns(deps, repo, headSha, seenRunIds) {
18989
18997
  }
18990
18998
  async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince, autoRunHeadSha, dispatchFailure = "throw", publishDir) {
18991
18999
  if (isCentralDispatchModel(model)) {
19000
+ deps.warn?.("Release: Starting deployment");
18992
19001
  const since = (deps.now ?? Date.now)();
18993
19002
  try {
18994
19003
  await dispatchTenantDeployWithRetry(deps, { repo: ctx.repo, slug: ctx.slug, ref, stage });
@@ -19018,6 +19027,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
19018
19027
  if (model === "registry-publish") {
19019
19028
  const note = ref === "rc" ? "no dispatch on rc: registry-publish repos have no rc-stage Release to publish from" : "no central dispatch: this repo's own publish.yml auto-fires on the published Release (#2428)";
19020
19029
  if (ref === "rc" || !autoRunHeadSha) return { note, deployStatus: "pending" };
19030
+ deps.warn?.("Release: Checking package publishing progress");
19021
19031
  if (!watch) {
19022
19032
  const listed = await listOwnWorkflowRuns(deps, ctx.repo, prodReleaseTargets(model), autoRunHeadSha);
19023
19033
  return { note, workflowRuns: listed, deployStatus: aggregateWorkflowRuns(listed) };
@@ -19037,6 +19047,7 @@ async function dispatchDeploy(deps, ctx, stage, ref, model, watch, autoRunSince,
19037
19047
  if (model === "hub-serverless") {
19038
19048
  const note = ref === "rc" ? "no manual dispatch: deploy.yml auto-fires on the rc push (rc stage)" : "no manual dispatch: deploy.yml + publish.yml auto-fire on the published Release (prod)";
19039
19049
  if (!autoRunHeadSha) return { note, deployStatus: "pending" };
19050
+ deps.warn?.("Release: Checking deployment and publishing progress");
19040
19051
  if (!watch) {
19041
19052
  if (ref === "rc") return { note, deployStatus: "pending" };
19042
19053
  const listed = await listOwnWorkflowRuns(deps, HUB_REPO2, prodReleaseTargets(model), autoRunHeadSha);
@@ -22261,6 +22272,7 @@ function enforceGateBudget(deps, repo) {
22261
22272
  }
22262
22273
  }
22263
22274
  async function preflight(deps, ctx, stage, meta, lane) {
22275
+ deps.warn?.("Release: Checking release prerequisites");
22264
22276
  const model = requireDeployModel(meta, ctx.repo);
22265
22277
  if (model === "content") {
22266
22278
  throw new Error(`${ctx.repo} is a content repo (deployModel=content) \u2014 the release train does not apply (trunk-based; PR to main)`);
@@ -22287,6 +22299,7 @@ async function preflightMergeToMain(deps, deployModel, remoteRef, blockingPrefix
22287
22299
  return { foldPaths, tolerated, predicted };
22288
22300
  }
22289
22301
  async function executeMergeToMain(deps, sourceRef, mergeLabel, tolerated, predicted, preFold) {
22302
+ deps.warn?.("Release: Preparing the release version");
22290
22303
  await deps.run("git", ["checkout", "main"]);
22291
22304
  await ffOnlyPull(deps, "main");
22292
22305
  preFold.mainSha = clean2(await deps.run("git", ["rev-parse", "main"]));
@@ -22808,6 +22821,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
22808
22821
  const resumeCommand = options.dev ? "mmi-cli devops release --dev --apply" : "mmi-cli devops release --apply";
22809
22822
  const ledgerAnchors = { repo: ctx.repo, tag, tagSha: releaseSha };
22810
22823
  try {
22824
+ deps.warn?.("Release: Checking the package before publishing");
22811
22825
  await verifyPublishDryRun(deps, ctx, meta, deployModel);
22812
22826
  } catch (e) {
22813
22827
  throw await recoverFailedFold(deps, e, startBranch, preFold.mainSha, resumeCommand);
@@ -22849,6 +22863,7 @@ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options,
22849
22863
  }
22850
22864
  tagLedgerStarted = true;
22851
22865
  }
22866
+ deps.warn?.("Release: Publishing the release tag");
22852
22867
  tagPush = await ensureTagPushed(deps, tag, releaseSha, tagProbe, ctx.repo);
22853
22868
  } catch (e) {
22854
22869
  if (ledger && tagLedgerStarted) {
@@ -22899,12 +22914,14 @@ ${recovery.note}`), recoveryInput);
22899
22914
  });
22900
22915
  if (trueMergeGateNote) checks = `${trueMergeGateNote}; ${checks}`;
22901
22916
  try {
22917
+ deps.warn?.("Release: Promoting the verified release");
22902
22918
  await runGitPush(deps, ["push", "origin", "main"]);
22903
22919
  } catch (e) {
22904
22920
  await recordPhase(ledger, deps, ledgerAnchors, "promotion", phaseEntry({ state: "failed", sha: releaseSha, error: `origin/main push failed after ${tag} was pushed: ${e instanceof Error ? e.message : String(e)}` }), { landed: "the immutable tag pushed at " + releaseSha.slice(0, 12) });
22905
22921
  throw partialTrainRecoveryError(e, recoveryInput);
22906
22922
  }
22907
22923
  await recordPhase(ledger, deps, ledgerAnchors, "promotion", phaseEntry({ state: "complete", sha: releaseSha, note: `immutable ${tag} pushed at ${releaseSha.slice(0, 12)}; origin/main fast-forwarded to it` }), { landed: "the immutable tag and the green required-check wall" });
22924
+ deps.warn?.("Release: Creating the release announcement");
22908
22925
  const releaseUrl = clean2(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
22909
22926
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
22910
22927
  await recordPhase(ledger, deps, ledgerAnchors, "githubRelease", phaseEntry({ state: "complete", sha: releaseSha, runUrl: releaseUrl, note: `verified GitHub Release ${tag} against ${releaseSha.slice(0, 12)}` }), { landed: "the immutable tag, the green required-check wall, and the origin/main fast-forward" });
@@ -23239,6 +23256,7 @@ Nothing was written. Inspect ${ledger.path} (or clear it only after proving the
23239
23256
  steps2.push(`development alignment: ${devRollForward2.status}`);
23240
23257
  const rcAlignment2 = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
23241
23258
  if (rcAlignment2) steps2.push(`rc alignment: ${rcAlignment2.status}`);
23259
+ deps.warn?.("Release: Checking deployment and publishing progress");
23242
23260
  const historicalTargets = prodReleaseTargets(deployModel);
23243
23261
  const historicalRepo = releaseRunRepoFor(deployModel, ctx.repo);
23244
23262
  let historicalRows = historicalTargets.length ? await listOwnWorkflowRuns(deps, historicalRepo, historicalTargets, tagSha) : [];
@@ -23341,9 +23359,11 @@ ${recovery.note}`), recoveryInput);
23341
23359
  }
23342
23360
  }
23343
23361
  await recordPhase(ledger, deps, anchors, "ordinaryCi", phaseEntry({ state: "complete", sha: tagSha, note: "re-verified the required-check wall live on this resume" }), { strict: ledgerMode === "strict", landed: "the immutable tag on origin at the resumed SHA" });
23362
+ deps.warn?.("Release: Promoting the verified release");
23344
23363
  await runGitPush(deps, ["push", "origin", `${tagSha}:refs/heads/main`]);
23345
23364
  steps.push(`pushed ${tagSha.slice(0, 12)} to origin/main`);
23346
23365
  await recordPhase(ledger, deps, anchors, "promotion", phaseEntry({ state: "complete", sha: tagSha, note: `resumed: ${tagSha.slice(0, 12)} pushed to origin/main (tag preserved, never re-cut)` }), { strict: ledgerMode === "strict", landed: "the immutable tag and the re-proven required-check wall" });
23366
+ deps.warn?.("Release: Creating the release announcement");
23347
23367
  const releaseUrl = clean2(await deps.run("gh", ["release", "create", tag, "--target", "main", "--generate-notes", "--latest", "--repo", ctx.repo])) || void 0;
23348
23368
  steps.push(`created the GitHub Release for ${tag}`);
23349
23369
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", tagSha);
@@ -44049,6 +44069,36 @@ function registerTrainCommands(program3, trainDeps) {
44049
44069
  });
44050
44070
  }
44051
44071
 
44072
+ // src/release-progress.ts
44073
+ async function withReleaseProgress(deps, initial, action) {
44074
+ let phase = initial;
44075
+ let startedAt = Date.now();
44076
+ const progressDeps = {
44077
+ ...deps,
44078
+ warn(message2) {
44079
+ if (message2.startsWith("Release: ")) {
44080
+ phase = message2.slice("Release: ".length);
44081
+ startedAt = Date.now();
44082
+ }
44083
+ deps.warn?.(message2);
44084
+ }
44085
+ };
44086
+ progressDeps.warn(`Release: ${initial}`);
44087
+ const heartbeat = setInterval(() => {
44088
+ const seconds = Math.floor((Date.now() - startedAt) / 1e3);
44089
+ deps.warn?.(`Release: ${phase} \u2014 ${seconds}s elapsed`);
44090
+ }, 3e4);
44091
+ heartbeat.unref();
44092
+ try {
44093
+ return await action(progressDeps);
44094
+ } finally {
44095
+ clearInterval(heartbeat);
44096
+ }
44097
+ }
44098
+ function releaseProgressVerdict(status) {
44099
+ return status === "complete" ? "Release complete" : status === "failed" ? "Release failed \u2014 a follow-up needs attention" : "Release pending \u2014 waiting for unfinished follow-ups";
44100
+ }
44101
+
44052
44102
  // src/command-register-train-operations.ts
44053
44103
  var INVOKED_ARGV2 = process.argv.slice(2);
44054
44104
  var isWin3 = process.platform === "win32";
@@ -44226,6 +44276,9 @@ function renderReleaseResume(r) {
44226
44276
  if (r.rcAlignment) lines2.push(` rc: ${r.rcAlignment.note}`);
44227
44277
  if (r.checkout) lines2.push(` checkout: ${r.checkout.note}`);
44228
44278
  if (r.projectInfoSync) lines2.push(` project info: ${r.projectInfoSync.note}`);
44279
+ if (r.releaseVerdict) {
44280
+ lines2.push(` release verdict: ${r.releaseVerdict.releaseStatus.toUpperCase()} (published); follow-up: ${r.releaseVerdict.followUpStatus.toUpperCase()}`);
44281
+ }
44229
44282
  if (r.ledger) lines2.push(...formatReleaseLedgerReport(r.ledger).map((l, i) => i === 0 ? ` ${l}` : ` ${l}`));
44230
44283
  return lines2.join("\n");
44231
44284
  }
@@ -44268,11 +44321,11 @@ function releaseFollowUpLegs(result, projectInfoSync) {
44268
44321
  status: followUpLegStatus(run.conclusion),
44269
44322
  ...run.conclusion === "failure" ? { error: run.workflow === "jerv-gateway" ? result.dispatch : `${run.workflow} reported failure` } : {}
44270
44323
  })));
44271
- } else if (result.deployStatus !== "success") {
44324
+ } else if (result.deployStatus && result.deployStatus !== "success") {
44272
44325
  legs.push({
44273
44326
  leg: "deploy",
44274
44327
  status: followUpLegStatus(result.deployStatus),
44275
- ...result.deployStatus === "failure" ? { error: result.dispatch } : {}
44328
+ ...result.deployStatus === "failure" ? { error: result.dispatch ?? "deploy reported failure" } : {}
44276
44329
  });
44277
44330
  }
44278
44331
  if (result.rcRetirement) {
@@ -44360,6 +44413,37 @@ function buildReleaseVerdict(commandName, result, projectInfoSync) {
44360
44413
  } : void 0
44361
44414
  };
44362
44415
  }
44416
+ function buildResumeReleaseVerdict(result, projectInfoSync) {
44417
+ const resumeStatus = resumeFollowUpOf(result.state);
44418
+ const followUpStatus = deriveTrainFollowUpStatus({
44419
+ projectInfo: projectInfoOutcome(projectInfoSync),
44420
+ deploy: "ok",
44421
+ rcRetirement: "ok",
44422
+ alignment: resumeStatus === "failed" ? "failure" : resumeStatus === "pending" ? "unresolved" : "ok",
44423
+ foldPort: "ok"
44424
+ });
44425
+ const legInput = {
44426
+ ...result.dispatch ? {
44427
+ dispatch: result.dispatch.note,
44428
+ deployStatus: result.dispatch.deployStatus,
44429
+ ...result.dispatch.workflowRuns ? { workflowRuns: result.dispatch.workflowRuns } : {}
44430
+ } : {},
44431
+ ...result.devRollForward ? { devRollForward: result.devRollForward } : {},
44432
+ ...result.rcAlignment ? { rcAlignment: result.rcAlignment } : {}
44433
+ };
44434
+ return {
44435
+ followUpStatus,
44436
+ releaseVerdict: {
44437
+ releaseStatus: "succeeded",
44438
+ followUpStatus,
44439
+ promoted: true,
44440
+ tag: result.tag,
44441
+ // Alignment-only carries no Release URL of its own; `null` is the honest absence.
44442
+ releaseUrl: result.releaseUrl ?? null,
44443
+ legs: releaseFollowUpLegs(legInput, projectInfoSync)
44444
+ }
44445
+ };
44446
+ }
44363
44447
  function renderTrainApply(commandName, r) {
44364
44448
  let base = `mmi-cli ${commandName}: promoted ${r.repo} \u2014 ${r.stage} at ${r.tag} [${r.deployModel}]; ${renderDeployLine(r)}`;
44365
44449
  if (r.versionFold) base = `${base}; ${r.versionFold}`;
@@ -44541,22 +44625,26 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
44541
44625
  }));
44542
44626
  return;
44543
44627
  }
44544
- const raw = await runReleaseResume(trainApplyDeps(), { watch: o.watch, announceSummaryFile: o.announceSummaryFile });
44628
+ const raw = await withReleaseProgress(
44629
+ trainApplyDeps(),
44630
+ "Checking the existing release",
44631
+ (deps) => runReleaseResume(deps, { watch: o.watch, announceSummaryFile: o.announceSummaryFile })
44632
+ );
44545
44633
  const result = raw.dispatch?.workflowRuns ? { ...raw, dispatch: { ...raw.dispatch, workflowRuns: raw.dispatch.workflowRuns.map(workflowRunWithEvidence) } } : raw;
44546
- const projectInfoSync = await runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo);
44547
- const resumed = { ...result, projectInfoSync };
44634
+ const projectInfoSync = await withReleaseProgress(
44635
+ trainApplyDeps(),
44636
+ "Updating project information",
44637
+ () => runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo)
44638
+ );
44639
+ const { followUpStatus, releaseVerdict } = buildResumeReleaseVerdict(result, projectInfoSync);
44640
+ const resumed = { ...result, projectInfoSync, releaseVerdict };
44548
44641
  emitTrainResult("release --resume", o.json ? JSON.stringify(resumed, null, 2) : renderReleaseResume(resumed), o.out);
44549
- const resumeStatus = resumeFollowUpOf(result.state);
44550
- applyTrainFollowUpExit(deriveTrainFollowUpStatus({
44551
- projectInfo: projectInfoOutcome(projectInfoSync),
44552
- deploy: "ok",
44553
- rcRetirement: "ok",
44554
- alignment: resumeStatus === "failed" ? "failure" : resumeStatus === "pending" ? "unresolved" : "ok",
44555
- foldPort: "ok"
44556
- }));
44642
+ consoleIo.err(`mmi-cli: Release: ${releaseProgressVerdict(followUpStatus)}`);
44643
+ applyTrainFollowUpExit(followUpStatus);
44557
44644
  return;
44558
44645
  } catch (e) {
44559
44646
  applyTrainFollowUpExit("failed");
44647
+ if (commandName === "release") consoleIo.err("mmi-cli: Release: Release blocked \u2014 see the error below");
44560
44648
  return failGraceful(`${commandName} --resume: ${e.message}`);
44561
44649
  }
44562
44650
  }
@@ -44599,11 +44687,16 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
44599
44687
  if (o.apply) {
44600
44688
  try {
44601
44689
  const ack = (o.ack ?? "").split(",").map((s) => s.trim()).filter(Boolean);
44602
- const raw = await runTrainApply(commandName, trainApplyDeps(), { watch: o.watch, announceSummaryFile: o.announceSummaryFile, ack, dev: o.dev });
44690
+ const apply = (deps) => runTrainApply(commandName, deps, { watch: o.watch, announceSummaryFile: o.announceSummaryFile, ack, dev: o.dev });
44691
+ const raw = commandName === "release" ? await withReleaseProgress(trainApplyDeps(), "Preparing release", apply) : await apply(trainApplyDeps());
44603
44692
  const result = raw.workflowRuns ? { ...raw, workflowRuns: raw.workflowRuns.map(workflowRunWithEvidence) } : raw;
44604
44693
  let projectInfoSync;
44605
44694
  if (commandName === "release") {
44606
- projectInfoSync = await runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo);
44695
+ projectInfoSync = await withReleaseProgress(
44696
+ trainApplyDeps(),
44697
+ "Updating project information",
44698
+ () => runProjectInfoSyncLeg(runProjectInfoSyncCallback, result.repo)
44699
+ );
44607
44700
  }
44608
44701
  const { followUpStatus, releaseVerdict } = buildReleaseVerdict(commandName, result, projectInfoSync);
44609
44702
  const reported = {
@@ -44613,10 +44706,12 @@ function registerTrainOperationsCommands(program3, runProjectInfoSyncCallback) {
44613
44706
  };
44614
44707
  const output = o.json ? JSON.stringify(reported, null, 2) : renderTrainApply(commandName, reported);
44615
44708
  emitTrainResult(`${commandName} apply`, output, o.out);
44709
+ if (commandName === "release") consoleIo.err(`mmi-cli: Release: ${releaseProgressVerdict(followUpStatus)}`);
44616
44710
  applyTrainFollowUpExit(followUpStatus);
44617
44711
  return;
44618
44712
  } catch (e) {
44619
44713
  process.exitCode = 1;
44714
+ if (commandName === "release") consoleIo.err("mmi-cli: Release: Release blocked \u2014 see the error below");
44620
44715
  return await failGraceful(`${commandName}: ${e.message}`);
44621
44716
  }
44622
44717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.52",
3
+ "version": "4.3.53",
4
4
  "description": "MMI Future CLI — the org dev toolbox and shared cross-IDE engine for every registry-declared MMI coding surface.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",