@mutmutco/cli 3.111.0 → 3.113.0
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 +52 -14
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -7686,14 +7686,27 @@ function validateDeadWorktreeDirCleanup(planned, inspected) {
|
|
|
7686
7686
|
}
|
|
7687
7687
|
return { ok: true };
|
|
7688
7688
|
}
|
|
7689
|
-
function
|
|
7689
|
+
function describeLeftoverEntries(entries) {
|
|
7690
|
+
const shown = entries.slice(0, 8);
|
|
7691
|
+
const more = entries.length > shown.length ? `, +${entries.length - shown.length} more` : "";
|
|
7692
|
+
return `${shown.join(", ")}${more}`;
|
|
7693
|
+
}
|
|
7694
|
+
function validateDeadWorktreeDirContent(planned, inspected, options = {}) {
|
|
7690
7695
|
if (!inspected.entries) return { ok: false, reason: inspected.error ?? "unable to inspect directory contents" };
|
|
7691
7696
|
const entries = inspected.entries.filter(Boolean);
|
|
7692
7697
|
if (planned.reason === "orphaned-folder") {
|
|
7693
|
-
|
|
7698
|
+
if (entries.length === 0 || options.force) return { ok: true };
|
|
7699
|
+
return {
|
|
7700
|
+
ok: false,
|
|
7701
|
+
reason: `orphaned worktree directory still contains files: ${describeLeftoverEntries(entries)} \u2014 re-run with --force to remove anyway`
|
|
7702
|
+
};
|
|
7694
7703
|
}
|
|
7695
7704
|
if (planned.reason === "dangling-gitdir") {
|
|
7696
|
-
|
|
7705
|
+
if (entries.length === 1 && entries[0] === ".git" || options.force) return { ok: true };
|
|
7706
|
+
return {
|
|
7707
|
+
ok: false,
|
|
7708
|
+
reason: `dangling worktree directory (git registration already severed) still contains files: ${describeLeftoverEntries(entries)} \u2014 re-run with --force to remove anyway`
|
|
7709
|
+
};
|
|
7697
7710
|
}
|
|
7698
7711
|
return { ok: false, reason: "unknown dead worktree cleanup kind" };
|
|
7699
7712
|
}
|
|
@@ -13167,6 +13180,7 @@ function scheduleRecordFromWorkflow(repo, workflowPath, yamlText) {
|
|
|
13167
13180
|
if (!crons.length && !header.schedule) return null;
|
|
13168
13181
|
const basename6 = workflowPath.split("/").pop() ?? workflowPath;
|
|
13169
13182
|
const expectedId = `${repo}/${basename6.replace(/\.ya?ml$/, "")}`;
|
|
13183
|
+
if (STRAY_CRON_EXEMPT.has(expectedId)) return null;
|
|
13170
13184
|
const missing = SCHEDULE_HEADER_FIELDS.filter((f) => !header[f]);
|
|
13171
13185
|
if (missing.length) {
|
|
13172
13186
|
throw new Error(`schedules register: ${expectedId} (${workflowPath}) is a scheduled workflow but is missing the eight-field entry-template header field(s): ${missing.join(", ")} \u2014 see docs/schedules.md "The entry template".`);
|
|
@@ -13252,7 +13266,10 @@ async function fetchTrainAuthority(repo, deps) {
|
|
|
13252
13266
|
headers: { Authorization: `Bearer ${token}` }
|
|
13253
13267
|
});
|
|
13254
13268
|
if (res.status === 426) return { ok: false, error: upgradeRequiredError(res, await res.json().catch(() => null)) };
|
|
13255
|
-
if (!res.ok)
|
|
13269
|
+
if (!res.ok) {
|
|
13270
|
+
const detail = (await res.json().catch(() => null))?.error;
|
|
13271
|
+
return { ok: false, error: detail ? `train-authority HTTP ${res.status}: ${detail}` : `train-authority HTTP ${res.status}` };
|
|
13272
|
+
}
|
|
13256
13273
|
const body = await res.json();
|
|
13257
13274
|
if (typeof body?.train !== "boolean" || !body.role) return { ok: false, error: "malformed train-authority response" };
|
|
13258
13275
|
return { ok: true, authority: body };
|
|
@@ -15618,10 +15635,10 @@ var rollout_plan_default = {
|
|
|
15618
15635
|
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)."
|
|
15619
15636
|
},
|
|
15620
15637
|
baseline: {
|
|
15621
|
-
version: "3.
|
|
15622
|
-
tag: "v3.
|
|
15623
|
-
commit: "
|
|
15624
|
-
npm: "@mutmutco/cli@3.
|
|
15638
|
+
version: "3.113.0",
|
|
15639
|
+
tag: "v3.113.0",
|
|
15640
|
+
commit: "1d1e521aa96f",
|
|
15641
|
+
npm: "@mutmutco/cli@3.113.0"
|
|
15625
15642
|
},
|
|
15626
15643
|
exitCriterion: "fleet-n-of-n",
|
|
15627
15644
|
hubOnlyShortcut: "forbidden",
|
|
@@ -15638,14 +15655,14 @@ var rollout_plan_default = {
|
|
|
15638
15655
|
repo: "mutmutco/mmi-hub",
|
|
15639
15656
|
role: "canary",
|
|
15640
15657
|
schedule: "train",
|
|
15641
|
-
v3Target: "v3.
|
|
15658
|
+
v3Target: "v3.113.0"
|
|
15642
15659
|
}
|
|
15643
15660
|
],
|
|
15644
15661
|
rollbackTrigger: "Any red inside the post-cut soak window: `devops train gate` FAIL attributable to the v4 doors, Hub endpoint health probe failure, a v3 client refused while the compat window must still admit it (SUPPORTED_MINOR_WINDOW=2, MIN_CLIENT_VERSION 0.0.0 \u2014 D6a), or npm consumer install/doctor failure on the v4 dist.",
|
|
15645
15662
|
rollback: {
|
|
15646
15663
|
independent: true,
|
|
15647
|
-
mechanism: "npm dist-tag latest -> 3.
|
|
15648
|
-
v3Target: "v3.
|
|
15664
|
+
mechanism: "npm dist-tag latest -> 3.113.0 and redeploy the Hub Lambda from tag v3.113.0 (1d1e521aa96f); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
15665
|
+
v3Target: "v3.113.0 (@mutmutco/cli@3.113.0, tag commit 1d1e521aa96f \u2014 the preserved latest-v3 distribution, D6b)"
|
|
15649
15666
|
}
|
|
15650
15667
|
},
|
|
15651
15668
|
{
|
|
@@ -32802,7 +32819,7 @@ async function applyGcPlan(plan, remote, opts = {}) {
|
|
|
32802
32819
|
result.failed.push(`${wt.path}: skipped dead-dir removal (${stillValid.reason})`);
|
|
32803
32820
|
continue;
|
|
32804
32821
|
}
|
|
32805
|
-
const contentSafe = validateDeadWorktreeDirContent(wt, inspectDeadWorktreeDirContent(wt.path));
|
|
32822
|
+
const contentSafe = validateDeadWorktreeDirContent(wt, inspectDeadWorktreeDirContent(wt.path), { force: opts.force });
|
|
32806
32823
|
if (!contentSafe.ok) {
|
|
32807
32824
|
result.failed.push(`${wt.path}: skipped dead-dir removal (${contentSafe.reason})`);
|
|
32808
32825
|
continue;
|
|
@@ -33780,6 +33797,17 @@ function landSteps(branch, hasWorktree, hasStage) {
|
|
|
33780
33797
|
steps.push("prune worktree metadata");
|
|
33781
33798
|
return steps;
|
|
33782
33799
|
}
|
|
33800
|
+
function describeLandUnresolvableWorktree(inspection) {
|
|
33801
|
+
if (inspection.gitType === "missing") {
|
|
33802
|
+
return "this directory has no .git at all \u2014 its worktree registration is gone";
|
|
33803
|
+
}
|
|
33804
|
+
if (inspection.gitType === "file" && inspection.gitDirExists === false) {
|
|
33805
|
+
const match = /^gitdir:\s*(.+)\s*$/im.exec(inspection.gitFileContent ?? "");
|
|
33806
|
+
const gitdir = match?.[1]?.trim();
|
|
33807
|
+
return `this directory's .git points at ${gitdir ? `'${gitdir}'` : "its admin dir"}, which no longer exists \u2014 its worktree registration was severed`;
|
|
33808
|
+
}
|
|
33809
|
+
return void 0;
|
|
33810
|
+
}
|
|
33783
33811
|
function orphanLandSteps(lostBranch, hasStage) {
|
|
33784
33812
|
const steps = [];
|
|
33785
33813
|
if (hasStage) steps.push("stop spawned dev stage");
|
|
@@ -34012,7 +34040,17 @@ function registerWorktreeCommands(program3) {
|
|
|
34012
34040
|
if (o.apply && o.dryRun) return fail("worktree land: choose either --dry-run or --apply");
|
|
34013
34041
|
const apply = Boolean(o.apply);
|
|
34014
34042
|
try {
|
|
34015
|
-
|
|
34043
|
+
let wtPath;
|
|
34044
|
+
try {
|
|
34045
|
+
wtPath = (await execFileP2("git", ["rev-parse", "--show-toplevel"], { timeout: GIT_TIMEOUT_MS })).stdout.trim();
|
|
34046
|
+
} catch (e) {
|
|
34047
|
+
const reason = describeLandUnresolvableWorktree(inspectSiblingWorktreeDir(process.cwd(), ""));
|
|
34048
|
+
if (!reason) throw e;
|
|
34049
|
+
return fail(
|
|
34050
|
+
`worktree land: ${reason}. It cannot be landed from inside itself \u2014 cd to the primary checkout and run \`mmi-cli worktree gc --apply\` (add --force if this directory still holds leftover files) to clear it.`,
|
|
34051
|
+
{ code: ERROR_CODES.ERR_BAD_ENUM }
|
|
34052
|
+
);
|
|
34053
|
+
}
|
|
34016
34054
|
const headBorn = await execFileP2("git", ["-C", wtPath || ".", "rev-parse", "--verify", "--quiet", "HEAD"], { timeout: GIT_TIMEOUT_MS }).then(() => true).catch(() => false);
|
|
34017
34055
|
const symbolicBranch = (await execFileP2(
|
|
34018
34056
|
"git",
|
|
@@ -39759,7 +39797,7 @@ gcCmd.command("sweep-deferred").description("retry IDE-locked deferred worktree
|
|
|
39759
39797
|
process.exit(process.exitCode ?? 0);
|
|
39760
39798
|
});
|
|
39761
39799
|
});
|
|
39762
|
-
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH ? an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
|
|
39800
|
+
gcCmd.option("--dry-run", "show what would be deleted (default)").option("--apply", "delete only the listed clean merged/closed PR local+remote branches, linked worktrees, and stale tracking refs").option("--json", "machine-readable output").option("--scratch", "prune safe local scratch (#1864) instead of git branches/refs; local plans are surfaced advisory-only, never auto-pruned").option("--remote <name>", "remote name", "origin").option("--limit <n>", "PRs to read PER BRANCH ? an effort bound, not a correctness one: merge state is resolved per branch, so no branch is ever skipped for being old (#4227)", "200").option("--force", "remove worktrees even when the ownership registry shows another session created or worked in them recently (#3580), or when a dead worktree directory still holds leftover files (#4779)").option("--root <path>", "sweep an explicitly named worktrees root instead of the authoritative ../mmi-worktrees (#3471) ? descends into this repo container when present; ownership, dead-dir, and content guards still apply").action(async (o) => {
|
|
39763
39801
|
if (o.apply && o.dryRun) return fail("worktree gc: choose either --dry-run or --apply");
|
|
39764
39802
|
if (o.scratch) {
|
|
39765
39803
|
try {
|
package/package.json
CHANGED