@mutmutco/cli 4.3.36 → 4.3.38

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 +108 -22
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -7648,7 +7648,7 @@ function whereLabel(v) {
7648
7648
  function formatFindHits(intent, hits) {
7649
7649
  if (!hits.length) return `no catalog match for "${intent}". Browse with \`mmi-cli vault secrets catalog\`, or run \`mmi-cli vault secrets doctor\` for drift.`;
7650
7650
  const blocks = hits.map((h) => [`${h.key} (${whereLabel(h)}) \u2014 ${h.purpose}`, ` ${h.command}`].join("\n"));
7651
- return [`${hits.length} match(es) for "${intent}" (names + meaning only \u2014 consume keyless with \`secrets use\`):`, "", ...blocks].join("\n");
7651
+ return [`${hits.length} match(es) for "${intent}" (names + meaning only \u2014 consume keyless with \`mmi-cli vault secrets use\`):`, "", ...blocks].join("\n");
7652
7652
  }
7653
7653
  function formatCatalog(r) {
7654
7654
  const lines2 = [`catalog \u2014 ${r.count} secret(s) across ${r.groups.length} group(s):`, ""];
@@ -15617,10 +15617,10 @@ var rollout_plan_default = {
15617
15617
  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)."
15618
15618
  },
15619
15619
  baseline: {
15620
- version: "4.3.36",
15621
- tag: "v4.3.36",
15622
- commit: "6309473a245d",
15623
- npm: "@mutmutco/cli@4.3.36"
15620
+ version: "4.3.38",
15621
+ tag: "v4.3.38",
15622
+ commit: "da4541fdb81e",
15623
+ npm: "@mutmutco/cli@4.3.38"
15624
15624
  },
15625
15625
  exitCriterion: "fleet-n-of-n",
15626
15626
  hubOnlyShortcut: "forbidden",
@@ -15637,14 +15637,14 @@ var rollout_plan_default = {
15637
15637
  repo: "mutmutco/mmi-hub",
15638
15638
  role: "canary",
15639
15639
  schedule: "train",
15640
- v3Target: "v4.3.36"
15640
+ v3Target: "v4.3.38"
15641
15641
  }
15642
15642
  ],
15643
15643
  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.",
15644
15644
  rollback: {
15645
15645
  independent: true,
15646
- mechanism: "npm dist-tag latest -> 4.3.36 and redeploy the Hub Lambda from tag v4.3.36 (6309473a245d); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15647
- v3Target: "v4.3.36 (@mutmutco/cli@4.3.36, tag commit 6309473a245d \u2014 last known-good release carrying the repo-index v4-only contract)"
15646
+ mechanism: "npm dist-tag latest -> 4.3.38 and redeploy the Hub Lambda from tag v4.3.38 (da4541fdb81e); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15647
+ v3Target: "v4.3.38 (@mutmutco/cli@4.3.38, tag commit da4541fdb81e \u2014 last known-good release carrying the repo-index v4-only contract)"
15648
15648
  }
15649
15649
  },
15650
15650
  {
@@ -18112,6 +18112,50 @@ async function enqueueAlignmentAutoMerge(deps, ctx, prNumber, prUrl, openedNote)
18112
18112
  }
18113
18113
  return { ...base, note: manual };
18114
18114
  }
18115
+ var ALIGNMENT_LAND_POLL_MS = 15e3;
18116
+ var ALIGNMENT_LAND_TIMEOUT_MS = 20 * 6e4;
18117
+ async function awaitAlignmentLanded(deps, ctx, leg, base, opts = {}) {
18118
+ if (leg.status !== "pr-pending" || !leg.autoMergeEnqueued || leg.prNumber === void 0) return leg;
18119
+ const sleep2 = resolveSleep(deps);
18120
+ const now = deps.now ?? Date.now;
18121
+ const startedAt = now();
18122
+ const timeoutMs = opts.timeoutMs ?? ALIGNMENT_LAND_TIMEOUT_MS;
18123
+ const n = leg.prNumber;
18124
+ deps.warn?.(`watching alignment PR #${n} (${ctx.repo}) until it lands on ${base}`);
18125
+ for (; ; ) {
18126
+ let view;
18127
+ try {
18128
+ view = JSON.parse(await deps.run("gh", ["pr", "view", String(n), "--repo", ctx.repo, "--json", "state,mergeCommit"]));
18129
+ } catch (e) {
18130
+ deps.warn?.(`alignment PR #${n}: read failed (${describeReadError(e)}) \u2014 retrying`);
18131
+ }
18132
+ if (view?.state === "MERGED") {
18133
+ const oid = typeof view.mergeCommit?.oid === "string" ? view.mergeCommit.oid : void 0;
18134
+ let parents;
18135
+ if (oid) {
18136
+ try {
18137
+ parents = Number(clean2(await deps.run("gh", ["api", `repos/${ctx.repo}/commits/${oid}`, "--jq", ".parents | length"])));
18138
+ } catch {
18139
+ parents = void 0;
18140
+ }
18141
+ }
18142
+ if (parents === 1) {
18143
+ const squashNote = `alignment PR #${n} landed as a SQUASH (${oid?.slice(0, 12)} has one parent), not the true merge the Merge floor requires \u2014 re-align ${base} by true merge before the next train`;
18144
+ deps.warn?.(squashNote);
18145
+ return { ...leg, note: `${leg.note} \u2014 ${squashNote}` };
18146
+ }
18147
+ return { status: "aligned", alignment: "already-merged", prNumber: n, ...leg.prUrl ? { prUrl: leg.prUrl } : {}, note: `alignment PR #${n} landed on ${base} by true merge (watched)` };
18148
+ }
18149
+ if (view?.state === "CLOSED") {
18150
+ return { ...leg, note: `${leg.note} \u2014 alignment PR #${n} was CLOSED without merging; rerun the roll-forward` };
18151
+ }
18152
+ if (now() - startedAt >= timeoutMs) {
18153
+ deps.warn?.(`alignment PR #${n} still open after ${Math.round(timeoutMs / 6e4)}m \u2014 leaving the alignment leg pending; fold it later with mmi-cli devops release --resume --watch`);
18154
+ return leg;
18155
+ }
18156
+ await sleep2(ALIGNMENT_LAND_POLL_MS);
18157
+ }
18158
+ }
18115
18159
  async function alignRcForward(deps, ctx, tag) {
18116
18160
  await runGitRemoteRead(deps, ["fetch", "origin", "main", "rc"]);
18117
18161
  const required = await discoverRequiredCheckContexts(deps, ctx, "rc");
@@ -22484,7 +22528,7 @@ async function recordRcandDeployLegs(ledger, deps, anchors, deployModel, d, opts
22484
22528
  await recordPhase(ledger, deps, anchors, "alignment", phaseEntry({ state: "skipped", sha, note: "rcand aligns no branch \u2014 alignment rides the release" }), { ...opts, landed });
22485
22529
  return dispatch;
22486
22530
  }
22487
- async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe, branchHints, ledger) {
22531
+ async function completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, startBranch, preFold, tagProbe, branchHints, ledger, alignEarly) {
22488
22532
  const resumeCommand = options.dev ? "mmi-cli devops release --dev --apply" : "mmi-cli devops release --apply";
22489
22533
  const ledgerAnchors = { repo: ctx.repo, tag, tagSha: releaseSha };
22490
22534
  try {
@@ -22589,6 +22633,7 @@ ${recovery.note}`), recoveryInput);
22589
22633
  await verifyPublishedRelease(deps, ctx.repo, tag, "main", releaseSha);
22590
22634
  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" });
22591
22635
  const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
22636
+ let alignment = alignEarly ? await alignEarly() : void 0;
22592
22637
  const autoRunSince = (deps.now ?? Date.now)();
22593
22638
  const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, releaseSha, "report", meta.publishDir);
22594
22639
  const deployRunRepo = releaseRunRepoFor(deployModel, ctx.repo);
@@ -22633,7 +22678,13 @@ ${recovery.note}`), recoveryInput);
22633
22678
  });
22634
22679
  let dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
22635
22680
  if (publishSkipNote) dispatch = { ...dispatch, note: `${dispatch.note}; tenant-publish.yml skipped (${publishSkipNote})` };
22636
- return { checks, releaseUrl, announceNote, dispatch, ledgerAnchors };
22681
+ if (watch && alignment) {
22682
+ alignment = {
22683
+ dev: await awaitAlignmentLanded(deps, ctx, alignment.dev, "development"),
22684
+ ...alignment.rc ? { rc: await awaitAlignmentLanded(deps, ctx, alignment.rc, "rc") } : {}
22685
+ };
22686
+ }
22687
+ return { checks, releaseUrl, announceNote, dispatch, ledgerAnchors, ...alignment ? { alignment } : {} };
22637
22688
  }
22638
22689
  function resumeStateOf(status) {
22639
22690
  return status === "complete" ? "aligned" : status;
@@ -23021,6 +23072,8 @@ ${recovery.note}`), recoveryInput);
23021
23072
  steps.push("verified the Release published against the tagged commit");
23022
23073
  await recordPhase(ledger, deps, anchors, "githubRelease", phaseEntry({ state: "complete", sha: tagSha, runUrl: releaseUrl, note: "verified the Release published against the tagged commit" }), { strict: ledgerMode === "strict", landed: "the immutable tag, the re-proven wall, and the origin/main push" });
23023
23074
  const announceNote = deps.announce ? (await deps.announce({ repo: ctx.repo, tag, summaryFile: options.announceSummaryFile })).note : void 0;
23075
+ let devRollForward = await rollDevelopmentForward(deps, ctx, tag);
23076
+ let rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
23024
23077
  const autoRunSince = (deps.now ?? Date.now)();
23025
23078
  const deployDispatch0 = await dispatchDeploy(deps, ctx, "main", "main", deployModel, watch, autoRunSince, tagSha, "report", meta.publishDir);
23026
23079
  const recoveredDeployDispatch = await recoverFailedDispatchPhase(
@@ -23056,9 +23109,11 @@ ${recovery.note}`), recoveryInput);
23056
23109
  landed: "the immutable tag, the re-proven wall, origin/main, the verified GitHub Release, and the dispatched deploy path"
23057
23110
  });
23058
23111
  const dispatch = appendPublishDispatch(deployDispatch, publishDispatch);
23059
- const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
23112
+ if (watch) {
23113
+ devRollForward = await awaitAlignmentLanded(deps, ctx, devRollForward, "development");
23114
+ if (rcAlignment) rcAlignment = await awaitAlignmentLanded(deps, ctx, rcAlignment, "rc");
23115
+ }
23060
23116
  steps.push(`development roll-forward: ${devRollForward.status}`);
23061
- const rcAlignment = !directTrack && branchHints.hasRcBranch ? await alignRcForward(deps, ctx, tag) : void 0;
23062
23117
  if (rcAlignment) steps.push(`rc alignment: ${rcAlignment.status}`);
23063
23118
  const followUp = deriveTrainFollowUpStatus({
23064
23119
  projectInfo: "ok",
@@ -23399,9 +23454,25 @@ ${spent.note}`), rcandRecovery(tag2));
23399
23454
  const releaseSha3 = requireValue(clean2(await deps.run("git", ["rev-parse", "main"])), "release sha");
23400
23455
  return { versionFold: versionFold3, releaseSha: releaseSha3 };
23401
23456
  });
23402
- const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2, ledgerAnchors: ledgerAnchors2 } = await completeMainRelease(deps, ctx, meta, deployModel2, watch, options, tag2, releaseSha2, "development", preFold2, tagProbe2, branchHints, ledger);
23403
- const devRollForward2 = await rollDevelopmentForward(deps, ctx, tag2);
23404
- await recordPhase(ledger, deps, ledgerAnchors2, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
23457
+ const { checks: checks2, releaseUrl: releaseUrl2, announceNote: announceNote2, dispatch: d2, ledgerAnchors: ledgerAnchors2, alignment: alignment2 } = await completeMainRelease(
23458
+ deps,
23459
+ ctx,
23460
+ meta,
23461
+ deployModel2,
23462
+ watch,
23463
+ options,
23464
+ tag2,
23465
+ releaseSha2,
23466
+ "development",
23467
+ preFold2,
23468
+ tagProbe2,
23469
+ branchHints,
23470
+ ledger,
23471
+ // #6468: alignment PRs open before the publish wait; rc aligns only on the --dev lane of a repo that has one.
23472
+ async () => ({ dev: await rollDevelopmentForward(deps, ctx, tag2), ...!directTrack && hasRcBranch ? { rc: await alignRcForward(deps, ctx, tag2) } : {} })
23473
+ );
23474
+ const devRollForward2 = alignment2?.dev ?? await rollDevelopmentForward(deps, ctx, tag2);
23475
+ await recordPhase(ledger, deps, ledgerAnchors2, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2, alignment2?.rc)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
23405
23476
  const ledgerReport2 = releaseLedgerReport(await ledger?.load(ledgerAnchors2).catch(() => void 0), ledger?.path ?? "(no ledger)");
23406
23477
  if (directTrack) {
23407
23478
  return {
@@ -23430,8 +23501,7 @@ ${spent.note}`), rcandRecovery(tag2));
23430
23501
  };
23431
23502
  }
23432
23503
  const retirement2 = hasRcBranch ? await retireRcRuntime(deps, ctx, deployModel2, d2.deployStatus, rcShaAtRelease) : { status: "not-applicable", note: "no origin/rc branch \u2014 rc runtime retirement skipped" };
23433
- const rcAlignment2 = hasRcBranch ? await alignRcForward(deps, ctx, tag2) : void 0;
23434
- await recordPhase(ledger, deps, ledgerAnchors2, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward2, rcAlignment2)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
23504
+ const rcAlignment2 = alignment2?.rc;
23435
23505
  const fullLedgerReport = releaseLedgerReport(await ledger?.load(ledgerAnchors2).catch(() => void 0), ledger?.path ?? "(no ledger)");
23436
23506
  const environments2 = await buildEnvironments(deps, ctx, deployModel2, d2.deployStatus, retirement2);
23437
23507
  return {
@@ -23493,10 +23563,26 @@ ${spent.note}`), rcandRecovery(tag2));
23493
23563
  const releaseSha2 = requireValue(clean2(await deps.run("git", ["rev-parse", "main"])), "release sha");
23494
23564
  return { versionFold: versionFold2, releaseSha: releaseSha2 };
23495
23565
  });
23496
- const { checks, releaseUrl, announceNote, dispatch: d, ledgerAnchors } = await completeMainRelease(deps, ctx, meta, deployModel, watch, options, tag, releaseSha, "rc", preFold, tagProbe, branchHints, ledger);
23566
+ const { checks, releaseUrl, announceNote, dispatch: d, ledgerAnchors, alignment } = await completeMainRelease(
23567
+ deps,
23568
+ ctx,
23569
+ meta,
23570
+ deployModel,
23571
+ watch,
23572
+ options,
23573
+ tag,
23574
+ releaseSha,
23575
+ "rc",
23576
+ preFold,
23577
+ tagProbe,
23578
+ branchHints,
23579
+ ledger,
23580
+ // #6468: both alignment PRs open before the publish wait.
23581
+ async () => ({ dev: await rollDevelopmentForward(deps, ctx, tag), rc: await alignRcForward(deps, ctx, tag) })
23582
+ );
23497
23583
  const retirement = await retireRcRuntime(deps, ctx, deployModel, d.deployStatus, releasedRcSha);
23498
- const devRollForward = await rollDevelopmentForward(deps, ctx, tag);
23499
- const rcAlignment = await alignRcForward(deps, ctx, tag);
23584
+ const devRollForward = alignment?.dev ?? await rollDevelopmentForward(deps, ctx, tag);
23585
+ const rcAlignment = alignment?.rc ?? await alignRcForward(deps, ctx, tag);
23500
23586
  await recordPhase(ledger, deps, ledgerAnchors, "alignment", phaseEntry(alignmentPhaseEntry(devRollForward, rcAlignment)), { landed: "the immutable tag, the green wall, origin/main, the verified Release, and the deploy/publish path" });
23501
23587
  const ledgerReport = releaseLedgerReport(await ledger?.load(ledgerAnchors).catch(() => void 0), ledger?.path ?? "(no ledger)");
23502
23588
  const environments = await buildEnvironments(deps, ctx, deployModel, d.deployStatus, retirement);
@@ -44922,7 +45008,7 @@ project.command("list").description("list all projects (identity + board, never
44922
45008
  console.log(`${p.slug ?? "?"} - ${p.name ?? ""}${p.division ? ` [${p.division}]` : ""}${p.class ? ` (${p.class})` : ""}${p.projectType ? ` <${p.projectType}>` : ""}${p.deployModel ? ` {${p.deployModel}}` : ""}`);
44923
45009
  }
44924
45010
  });
44925
- project.command("get [owner/repo]").description("a project's META (board ids + pointers) by repo or slug; defaults to the current repo \u2014 identity, NOT deploy coords").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
45011
+ project.command("get [owner/repo]").description("a project's META (board ids + pointers) by repo or slug; defaults to the current repo \u2014 identity, NOT deploy coords").option("--json", "machine-readable output").action(async (repoOrSlug) => {
44926
45012
  const cfg = await loadConfig();
44927
45013
  let target;
44928
45014
  try {
@@ -44938,7 +45024,7 @@ project.command("get [owner/repo]").description("a project's META (board ids + p
44938
45024
  return failGraceful(`org project get: no registry META for ${target} (unknown or unbootstrapped)`);
44939
45025
  }
44940
45026
  console.log(JSON.stringify(read.project));
44941
- if (!o.json) {
45027
+ if (!rawFlag("--json")) {
44942
45028
  const m = read.project;
44943
45029
  const track = resolveReleaseTrack(m, void 0, target);
44944
45030
  const stages = branchesForTrack(track).join(" -> ");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.36",
3
+ "version": "4.3.38",
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",