@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.
- package/dist/main.cjs +97 -21
- 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.
|
|
15379
|
-
tag: "v4.3.
|
|
15380
|
-
commit: "
|
|
15381
|
-
npm: "@mutmutco/cli@4.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.
|
|
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.
|
|
15405
|
-
v3Target: "v4.3.
|
|
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
|
-
|
|
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
|
-
|
|
35713
|
-
|
|
35714
|
-
|
|
35715
|
-
|
|
35716
|
-
|
|
35717
|
-
|
|
35718
|
-
|
|
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
|
-
|
|
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