@mutmutco/cli 4.3.3 → 4.3.5

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 +97 -21
  2. package/package.json +1 -1
package/dist/main.cjs CHANGED
@@ -15375,10 +15375,10 @@ var rollout_plan_default = {
15375
15375
  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)."
15376
15376
  },
15377
15377
  baseline: {
15378
- version: "4.3.3",
15379
- tag: "v4.3.3",
15380
- commit: "851ec4c56e34",
15381
- npm: "@mutmutco/cli@4.3.3"
15378
+ version: "4.3.5",
15379
+ tag: "v4.3.5",
15380
+ commit: "125c3bf83846",
15381
+ npm: "@mutmutco/cli@4.3.5"
15382
15382
  },
15383
15383
  exitCriterion: "fleet-n-of-n",
15384
15384
  hubOnlyShortcut: "forbidden",
@@ -15395,14 +15395,14 @@ var rollout_plan_default = {
15395
15395
  repo: "mutmutco/mmi-hub",
15396
15396
  role: "canary",
15397
15397
  schedule: "train",
15398
- v3Target: "v4.3.3"
15398
+ v3Target: "v4.3.5"
15399
15399
  }
15400
15400
  ],
15401
15401
  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.",
15402
15402
  rollback: {
15403
15403
  independent: true,
15404
- mechanism: "npm dist-tag latest -> 4.3.3 and redeploy the Hub Lambda from tag v4.3.3 (851ec4c56e34); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15405
- v3Target: "v4.3.3 (@mutmutco/cli@4.3.3, tag commit 851ec4c56e34 \u2014 last known-good release carrying the repo-index v4-only contract)"
15404
+ mechanism: "npm dist-tag latest -> 4.3.5 and redeploy the Hub Lambda from tag v4.3.5 (125c3bf83846); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
15405
+ v3Target: "v4.3.5 (@mutmutco/cli@4.3.5, tag commit 125c3bf83846 \u2014 last known-good release carrying the repo-index v4-only contract)"
15406
15406
  }
15407
15407
  },
15408
15408
  {
@@ -19902,6 +19902,29 @@ async function startReleaseLedger(ledger, anchors, opts = {}) {
19902
19902
  return { ok: false, error: e instanceof Error ? e.message : String(e) };
19903
19903
  }
19904
19904
  }
19905
+ function alignmentPrNumbers(note) {
19906
+ return [...new Set([...(note ?? "").matchAll(/\/pull\/(\d+)/g)].map((m) => Number.parseInt(m[1], 10)))];
19907
+ }
19908
+ async function alignmentLegClosedLive(deps, prior) {
19909
+ const leg = prior.phases.alignment;
19910
+ if (leg.state !== "pending") return void 0;
19911
+ const numbers = alignmentPrNumbers(leg.note);
19912
+ if (numbers.length === 0) return void 0;
19913
+ for (const n of numbers) {
19914
+ let state = "";
19915
+ try {
19916
+ state = JSON.parse(await deps.run("gh", ["pr", "view", String(n), "--repo", prior.repo, "--json", "state"])).state;
19917
+ } catch {
19918
+ return void 0;
19919
+ }
19920
+ if (state !== "MERGED") return void 0;
19921
+ }
19922
+ return {
19923
+ state: "complete",
19924
+ note: `alignment: already-merged \u2014 alignment PR ${numbers.map((n) => `#${n}`).join(", ")} merged on GitHub after the train returned; closed live at the next release's pre-flight (#6061)`,
19925
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
19926
+ };
19927
+ }
19905
19928
  async function beginReleaseLedger(deps, cwd, targetTag) {
19906
19929
  let path2;
19907
19930
  try {
@@ -19924,7 +19947,15 @@ async function beginReleaseLedger(deps, cwd, targetTag) {
19924
19947
  return { ok: false, error: `the active release ledger at ${path2} is unreadable (${e instanceof Error ? e.message : String(e)}) \u2014 resolve it by hand before starting a release (#5987)` };
19925
19948
  }
19926
19949
  if (targetTag && prior.tag === targetTag) return { ok: true };
19927
- const derived = deriveReleasePhaseStatus(prior);
19950
+ let derived = deriveReleasePhaseStatus(prior);
19951
+ if (derived.phaseStatus === "phases-pending" && derived.legs.every((l) => l.state !== "pending" || l.phase === "alignment")) {
19952
+ const closed = await alignmentLegClosedLive(deps, prior);
19953
+ if (closed) {
19954
+ prior = { ...prior, phases: { ...prior.phases, alignment: closed }, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
19955
+ atomicWriteJson(path2, prior);
19956
+ derived = deriveReleasePhaseStatus(prior);
19957
+ }
19958
+ }
19928
19959
  if (derived.phaseStatus !== "phases-green") {
19929
19960
  const open2 = derived.legs.filter((l) => l.state === "pending" || l.state === "failed");
19930
19961
  return {
@@ -35429,6 +35460,10 @@ function ghPrMergeLocalBranchDeleteWarning(message) {
35429
35460
  function mergeAutoRejectedPrAlreadyClean(message) {
35430
35461
  return /clean status \(enablePullRequestAutoMerge\)|is in clean status/i.test(message);
35431
35462
  }
35463
+ function mergeRejectedBaseBranchModified(message) {
35464
+ return /base branch was modified\. review and try the merge again/i.test(message);
35465
+ }
35466
+ var PR_MERGE_BASE_RACE_RETRY_DELAY_MS = 3e3;
35432
35467
  function assertRemoteBranchPreservedOnMerge(repo, deleteBranchOnMerge) {
35433
35468
  if (deleteBranchOnMerge === false) return;
35434
35469
  throw new Error(
@@ -35709,14 +35744,16 @@ async function deleteMergedRemoteBranch(options) {
35709
35744
  if (options.existedBefore === false) {
35710
35745
  const exists2 = await options.branchExists(options.branch);
35711
35746
  if (exists2 === false) return { branch: options.branch, existedBefore: false, attempted: false, status: "already-gone" };
35712
- return {
35713
- branch: options.branch,
35714
- existedBefore: false,
35715
- attempted: false,
35716
- status: "failed",
35717
- error: exists2 ? `origin reports ${options.branch} after merge` : `could not verify absence of origin/${options.branch}`,
35718
- remediation
35719
- };
35747
+ if (exists2 !== true) {
35748
+ return {
35749
+ branch: options.branch,
35750
+ existedBefore: false,
35751
+ attempted: false,
35752
+ status: "failed",
35753
+ error: `could not verify absence of origin/${options.branch}`,
35754
+ remediation
35755
+ };
35756
+ }
35720
35757
  }
35721
35758
  try {
35722
35759
  await options.execGit(["push", "origin", "--delete", options.branch]);
@@ -36389,6 +36426,22 @@ function samePath(a, b) {
36389
36426
  const nb = normPath2(b);
36390
36427
  return process.platform === "win32" ? na.toLowerCase() === nb.toLowerCase() : na === nb;
36391
36428
  }
36429
+ function isPathAtOrWithin(path2, worktreePath) {
36430
+ if (!path2) return false;
36431
+ const candidate = normPath2(path2);
36432
+ const target = normPath2(worktreePath);
36433
+ const normalize = (value) => process.platform === "win32" ? value.toLowerCase() : value;
36434
+ const normalizedCandidate = normalize(candidate);
36435
+ const normalizedTarget = normalize(target);
36436
+ return normalizedCandidate === normalizedTarget || normalizedCandidate.startsWith(`${normalizedTarget}/`);
36437
+ }
36438
+ function isWindowsCwdLockResidueError(error) {
36439
+ return process.platform === "win32" && /\bEPERM\b|access(?: is)? denied/i.test(error);
36440
+ }
36441
+ function deferredCwdLockRemediation(primaryRoot, wtPath) {
36442
+ const quote = (path2) => path2.replace(/'/g, "''");
36443
+ return `After this command exits: Set-Location -LiteralPath '${quote(primaryRoot)}'; Remove-Item -LiteralPath '${quote(wtPath)}' -Recurse -Force`;
36444
+ }
36392
36445
  function selectPrMergeCleanupWorktree(branch, before, after, startingPath) {
36393
36446
  if (!branch) return void 0;
36394
36447
  const current = after.find((w) => w.branch === branch)?.path;
@@ -36559,7 +36612,7 @@ async function removeWorktreeWithReconcile(wtPath, git3, listWorktrees, pathExis
36559
36612
  }
36560
36613
  function isPrMergeWorktreePartial(cleanup) {
36561
36614
  const worktree = cleanup?.worktree;
36562
- return Boolean(worktree?.path && worktree.status !== "removed" && worktree.status !== "preserved");
36615
+ return Boolean(worktree?.path && worktree.status !== "removed" && worktree.status !== "preserved" && worktree.status !== "retained-locked");
36563
36616
  }
36564
36617
  function prMergeLocalCleanupExitCode(cleanup) {
36565
36618
  return cleanup?.worktree?.status === "failed" || cleanup?.localBranch?.status === "failed" ? 1 : void 0;
@@ -36731,11 +36784,17 @@ async function cleanupPrMergeLocalBranch(branch, options) {
36731
36784
  if (residue.ok) {
36732
36785
  report.worktree.residue = "swept";
36733
36786
  } else {
36734
- report.worktree.status = "failed";
36735
- report.worktree.reason = "residue-remains";
36736
36787
  report.worktree.residue = "left";
36737
36788
  report.worktree.residueError = residue.error;
36738
- report.worktree.remediation = `Remove-Item -LiteralPath '${wtPath.replace(/'/g, "''")}' -Force`;
36789
+ if (isWindowsCwdLockResidueError(residue.error) && isPathAtOrWithin(options.startingPath, wtPath)) {
36790
+ report.worktree.status = "retained-locked";
36791
+ report.worktree.reason = "parent-cwd-lock";
36792
+ report.worktree.remediation = deferredCwdLockRemediation(options.primaryRoot, wtPath);
36793
+ } else {
36794
+ report.worktree.status = "failed";
36795
+ report.worktree.reason = "residue-remains";
36796
+ report.worktree.remediation = `Remove-Item -LiteralPath '${wtPath.replace(/'/g, "''")}' -Force`;
36797
+ }
36739
36798
  }
36740
36799
  }
36741
36800
  try {
@@ -36764,8 +36823,10 @@ function renderPrMergeCleanupLines(cleanup) {
36764
36823
  lines.push(`pr merge: worktree ${wt.path} removal failed${wt.error ? ` \u2014 ${wt.error}` : ""}`);
36765
36824
  } else if (wt.status === "preserved") {
36766
36825
  lines.push(`pr merge: preserved worktree ${wt.path} (--preserve-worktree)`);
36826
+ } else if (wt.status === "retained-locked") {
36827
+ lines.push(`pr merge: worktree ${wt.path} cleanup deferred \u2014 its Windows parent shell still holds the cwd; ${wt.remediation ?? "exit the shell and remove the residue from the primary checkout"}`);
36767
36828
  }
36768
- if (wt.residue === "left") {
36829
+ if (wt.residue === "left" && wt.status !== "retained-locked") {
36769
36830
  lines.push(`pr merge: worktree ${wt.path} registration is gone but residue remains \u2014 ${wt.residueError ?? wt.path}; remediate: ${wt.remediation ?? wt.path}`);
36770
36831
  }
36771
36832
  if (wt.artifactsArchive?.status === "archived" && wt.artifactsArchive.path) {
@@ -37979,6 +38040,21 @@ ${list}`);
37979
38040
  });
37980
38041
  return;
37981
38042
  }
38043
+ if (!o.auto && mergeRejectedBaseBranchModified(message)) {
38044
+ console.warn(`pr merge: the base branch was modified by a concurrent merge \u2014 waiting ${PR_MERGE_BASE_RACE_RETRY_DELAY_MS / 1e3}s and retrying PR #${number}'s merge once (#6052).`);
38045
+ await new Promise((resolve7) => setTimeout(resolve7, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
38046
+ await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
38047
+ const m2 = String(e2.message || "");
38048
+ if (/already been merged/i.test(m2)) {
38049
+ remoteNotAttemptedReason = "pr-already-merged";
38050
+ return;
38051
+ }
38052
+ const note2 = timeoutKillNote(e2, GH_MUTATION_TIMEOUT_MS);
38053
+ if (note2) throw new Error(`gh pr merge ${number}: ${note2}`);
38054
+ if (!ghPrMergeLocalBranchDeleteWarning(m2)) throw e2;
38055
+ });
38056
+ return;
38057
+ }
37982
38058
  if (o.auto && mergeAutoRejectedPrAlreadyClean(message)) {
37983
38059
  await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
37984
38060
  const m2 = String(e2.message || "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mutmutco/cli",
3
- "version": "4.3.3",
3
+ "version": "4.3.5",
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",