@mutmutco/cli 4.5.124 → 4.5.125
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 +293 -202
- package/package.json +1 -1
package/dist/main.cjs
CHANGED
|
@@ -76,19 +76,19 @@ function hardExit(code) {
|
|
|
76
76
|
process.exit(code);
|
|
77
77
|
}
|
|
78
78
|
function flushStream(stream) {
|
|
79
|
-
return new Promise((
|
|
79
|
+
return new Promise((resolve8) => {
|
|
80
80
|
try {
|
|
81
|
-
stream.write("", () =>
|
|
81
|
+
stream.write("", () => resolve8());
|
|
82
82
|
} catch {
|
|
83
|
-
|
|
83
|
+
resolve8();
|
|
84
84
|
}
|
|
85
85
|
});
|
|
86
86
|
}
|
|
87
87
|
async function flushStdio(timeoutMs = STDIO_FLUSH_TIMEOUT_MS) {
|
|
88
88
|
await Promise.race([
|
|
89
89
|
Promise.all([flushStream(process.stdout), flushStream(process.stderr)]),
|
|
90
|
-
new Promise((
|
|
91
|
-
setTimeout(
|
|
90
|
+
new Promise((resolve8) => {
|
|
91
|
+
setTimeout(resolve8, timeoutMs).unref?.();
|
|
92
92
|
})
|
|
93
93
|
]);
|
|
94
94
|
}
|
|
@@ -96,7 +96,7 @@ async function cleanExit(code) {
|
|
|
96
96
|
process.exitCode = code;
|
|
97
97
|
await closeHttpPool();
|
|
98
98
|
await flushStdio();
|
|
99
|
-
await new Promise((
|
|
99
|
+
await new Promise((resolve8) => setImmediate(resolve8));
|
|
100
100
|
return void 0;
|
|
101
101
|
}
|
|
102
102
|
async function finishCliRun(watchdogMs = CLI_EXIT_WATCHDOG_MS) {
|
|
@@ -644,7 +644,7 @@ function rateLimitedReceipt(opts) {
|
|
|
644
644
|
};
|
|
645
645
|
}
|
|
646
646
|
async function runWithRateLimitBackoff(operation, opts) {
|
|
647
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
647
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
648
648
|
const now = opts.now ?? Date.now;
|
|
649
649
|
const log = opts.log ?? ((message2) => console.warn(message2));
|
|
650
650
|
const capMs = opts.capMs ?? RATE_LIMIT_WAIT_CAP_MS;
|
|
@@ -734,7 +734,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
734
734
|
const attempts = opts.attempts ?? 3;
|
|
735
735
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
736
736
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
737
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
737
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
738
738
|
let lastErr;
|
|
739
739
|
for (let i = 0; i < attempts; i++) {
|
|
740
740
|
const isLast = i === attempts - 1;
|
|
@@ -1057,8 +1057,8 @@ async function readStdin(opts = {}) {
|
|
|
1057
1057
|
})().catch(() => {
|
|
1058
1058
|
});
|
|
1059
1059
|
let timer;
|
|
1060
|
-
const timeout = new Promise((
|
|
1061
|
-
timer = setTimeout(
|
|
1060
|
+
const timeout = new Promise((resolve8) => {
|
|
1061
|
+
timer = setTimeout(resolve8, timeoutMs);
|
|
1062
1062
|
});
|
|
1063
1063
|
try {
|
|
1064
1064
|
await Promise.race([drain, timeout]);
|
|
@@ -1125,12 +1125,12 @@ function killProcessTree(pid) {
|
|
|
1125
1125
|
function execFileHard(file, args, options) {
|
|
1126
1126
|
const { timeout, step, ...rest } = options;
|
|
1127
1127
|
const started = Date.now();
|
|
1128
|
-
return new Promise((
|
|
1128
|
+
return new Promise((resolve8, reject) => {
|
|
1129
1129
|
const child2 = (0, import_node_child_process2.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
|
|
1130
1130
|
clearTimeout(timer);
|
|
1131
1131
|
if (expired) return;
|
|
1132
1132
|
if (error) reject(error);
|
|
1133
|
-
else
|
|
1133
|
+
else resolve8({ stdout: String(stdout), stderr: String(stderr) });
|
|
1134
1134
|
});
|
|
1135
1135
|
let expired = false;
|
|
1136
1136
|
const timer = setTimeout(() => {
|
|
@@ -5656,7 +5656,7 @@ function resolveVerbAliasShim(argv) {
|
|
|
5656
5656
|
|
|
5657
5657
|
// src/command-composition.ts
|
|
5658
5658
|
var import_node_os25 = require("node:os");
|
|
5659
|
-
var
|
|
5659
|
+
var import_node_path56 = require("node:path");
|
|
5660
5660
|
|
|
5661
5661
|
// src/board-read.ts
|
|
5662
5662
|
var import_node_fs14 = require("node:fs");
|
|
@@ -6735,7 +6735,7 @@ async function readRestPrSnapshotWithRetry(prNumber, repo, gh2 = defaultGhApi, o
|
|
|
6735
6735
|
const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
|
|
6736
6736
|
const untilMs = options?.retryTransientUntilMs;
|
|
6737
6737
|
const delayMs = options?.delayMs ?? (untilMs !== void 0 ? PR_CHECKS_POLL_MS : PR_SNAPSHOT_READ_DELAY_MS);
|
|
6738
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
6738
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
6739
6739
|
const now = options?.now ?? (() => Date.now());
|
|
6740
6740
|
let lastError = "no attempt completed";
|
|
6741
6741
|
let attempt = 0;
|
|
@@ -10588,7 +10588,7 @@ async function createPrViaRestFallback(args, swappedArgs, deps, knownPools) {
|
|
|
10588
10588
|
}
|
|
10589
10589
|
async function ghCreate(args, deps = {}) {
|
|
10590
10590
|
const exec3 = deps.exec ?? execFileP;
|
|
10591
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
10591
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
10592
10592
|
const now = deps.now ?? Date.now;
|
|
10593
10593
|
const read = deps.readFile ?? import_promises.readFile;
|
|
10594
10594
|
const restCreatePr = deps.restCreatePr ?? ((input) => defaultRestCreatePr(exec3, input));
|
|
@@ -11416,8 +11416,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
|
11416
11416
|
async function drainHttpPoolBeforeSpawn() {
|
|
11417
11417
|
const drained = await Promise.race([
|
|
11418
11418
|
closeHttpPool().then(() => true),
|
|
11419
|
-
new Promise((
|
|
11420
|
-
setTimeout(() =>
|
|
11419
|
+
new Promise((resolve8) => {
|
|
11420
|
+
setTimeout(() => resolve8(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
|
|
11421
11421
|
})
|
|
11422
11422
|
]);
|
|
11423
11423
|
if (!drained) destroyHttpPool();
|
|
@@ -15004,7 +15004,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
|
|
|
15004
15004
|
await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
|
|
15005
15005
|
return cliPriorityToFieldName(priority);
|
|
15006
15006
|
}
|
|
15007
|
-
var defaultRetrySleep = (ms) => new Promise((
|
|
15007
|
+
var defaultRetrySleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
15008
15008
|
async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
|
|
15009
15009
|
const attempts = Math.max(1, opts.attempts ?? 5);
|
|
15010
15010
|
const delayMs = opts.delayMs ?? 300;
|
|
@@ -18243,10 +18243,10 @@ var rollout_plan_default = {
|
|
|
18243
18243
|
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)."
|
|
18244
18244
|
},
|
|
18245
18245
|
baseline: {
|
|
18246
|
-
version: "4.5.
|
|
18247
|
-
tag: "v4.5.
|
|
18248
|
-
commit: "
|
|
18249
|
-
npm: "@mutmutco/cli@4.5.
|
|
18246
|
+
version: "4.5.125",
|
|
18247
|
+
tag: "v4.5.125",
|
|
18248
|
+
commit: "00accba6d8e0",
|
|
18249
|
+
npm: "@mutmutco/cli@4.5.125"
|
|
18250
18250
|
},
|
|
18251
18251
|
exitCriterion: "fleet-n-of-n",
|
|
18252
18252
|
hubOnlyShortcut: "forbidden",
|
|
@@ -18263,14 +18263,14 @@ var rollout_plan_default = {
|
|
|
18263
18263
|
repo: "mutmutco/mmi-hub",
|
|
18264
18264
|
role: "canary",
|
|
18265
18265
|
schedule: "train",
|
|
18266
|
-
v3Target: "v4.5.
|
|
18266
|
+
v3Target: "v4.5.125"
|
|
18267
18267
|
}
|
|
18268
18268
|
],
|
|
18269
18269
|
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.",
|
|
18270
18270
|
rollback: {
|
|
18271
18271
|
independent: true,
|
|
18272
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
18273
|
-
v3Target: "v4.5.
|
|
18272
|
+
mechanism: "npm dist-tag latest -> 4.5.125 and redeploy the Hub Lambda from tag v4.5.125 (00accba6d8e0); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
18273
|
+
v3Target: "v4.5.125 (@mutmutco/cli@4.5.125, tag commit 00accba6d8e0 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
18274
18274
|
}
|
|
18275
18275
|
},
|
|
18276
18276
|
{
|
|
@@ -20353,7 +20353,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
20353
20353
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
20354
20354
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
20355
20355
|
}
|
|
20356
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
20356
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve8, extraTolerated = []) {
|
|
20357
20357
|
try {
|
|
20358
20358
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
20359
20359
|
return;
|
|
@@ -20367,7 +20367,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, ex
|
|
|
20367
20367
|
unmerged.length === 0 ? `${label} merge failed without conflicted paths \u2014 merge aborted; inspect the repo state and rerun` : `${label} merge conflicts on untolerated path(s): ${blocking.join(", ")} \u2014 merge aborted (the train is misaligned; reconcile the branches via an approved alignment PR, then rerun)`
|
|
20368
20368
|
);
|
|
20369
20369
|
}
|
|
20370
|
-
await deps.run("git", ["checkout", `--${
|
|
20370
|
+
await deps.run("git", ["checkout", `--${resolve8}`, "--", ...unmerged]);
|
|
20371
20371
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
20372
20372
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
20373
20373
|
}
|
|
@@ -20547,7 +20547,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
|
20547
20547
|
var CORRELATE_PAGE_LIMIT = 50;
|
|
20548
20548
|
var RUN_CONFIRM_ATTEMPTS = 3;
|
|
20549
20549
|
var RUN_CONFIRM_DELAY_MS = 1e3;
|
|
20550
|
-
var defaultSleep = (ms) => new Promise((
|
|
20550
|
+
var defaultSleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
20551
20551
|
function resolveSleep(deps) {
|
|
20552
20552
|
return deps.sleep ?? defaultSleep;
|
|
20553
20553
|
}
|
|
@@ -23726,7 +23726,7 @@ function lockTokenPid(token) {
|
|
|
23726
23726
|
const m = /^(\d+)-/.exec(token);
|
|
23727
23727
|
return m ? Number(m[1]) : void 0;
|
|
23728
23728
|
}
|
|
23729
|
-
var sleep = (ms) => new Promise((
|
|
23729
|
+
var sleep = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
23730
23730
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
23731
23731
|
var FileLockBusyError = class extends Error {
|
|
23732
23732
|
lockPath;
|
|
@@ -24602,7 +24602,7 @@ async function defaultWatchRun(deps, runId, repo) {
|
|
|
24602
24602
|
}
|
|
24603
24603
|
let confirmError;
|
|
24604
24604
|
for (let attempt = 0; attempt < RUN_CONFIRM_ATTEMPTS2; attempt++) {
|
|
24605
|
-
if (attempt > 0) await new Promise((
|
|
24605
|
+
if (attempt > 0) await new Promise((resolve8) => setTimeout(resolve8, RUN_CONFIRM_DELAY_MS2));
|
|
24606
24606
|
try {
|
|
24607
24607
|
const parsed = JSON.parse(await deps.run("gh", ["run", "view", String(runId), "--repo", repo, "--json", "status,conclusion"]));
|
|
24608
24608
|
if (parsed.status === "completed") return parsed.conclusion === "success" ? "success" : "failure";
|
|
@@ -25644,11 +25644,11 @@ var defaultDeps = {
|
|
|
25644
25644
|
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
25645
25645
|
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
25646
25646
|
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
25647
|
-
reexec: (argv) => new Promise((
|
|
25647
|
+
reexec: (argv) => new Promise((resolve8) => {
|
|
25648
25648
|
const args = [...argv];
|
|
25649
25649
|
const child2 = isWin2 ? (0, import_node_child_process8.spawn)("cmd.exe", ["/c", "mmi-cli", ...args], { stdio: "inherit", windowsHide: true }) : (0, import_node_child_process8.spawn)("mmi-cli", args, { stdio: "inherit", windowsHide: true });
|
|
25650
|
-
child2.on("exit", (code) =>
|
|
25651
|
-
child2.on("error", () =>
|
|
25650
|
+
child2.on("exit", (code) => resolve8(code ?? 1));
|
|
25651
|
+
child2.on("error", () => resolve8(1));
|
|
25652
25652
|
}),
|
|
25653
25653
|
// stderr only — stdout may be carrying --json output.
|
|
25654
25654
|
warn: (line) => console.error(line)
|
|
@@ -25754,7 +25754,7 @@ var defaultDeps2 = {
|
|
|
25754
25754
|
}
|
|
25755
25755
|
},
|
|
25756
25756
|
registryWhoami: probeRegistryWhoami,
|
|
25757
|
-
sleep: (ms) => new Promise((
|
|
25757
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
25758
25758
|
env: process.env
|
|
25759
25759
|
};
|
|
25760
25760
|
function message(e) {
|
|
@@ -37162,7 +37162,7 @@ function readSuiteCut(path2) {
|
|
|
37162
37162
|
} catch {
|
|
37163
37163
|
throw new Error(`suite cut record unreadable at ${path2}; repair it, never recut`);
|
|
37164
37164
|
}
|
|
37165
|
-
if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0) || m.ruleset !== void 0 && (!["rc", "main"].includes(m.ruleset.after) || typeof m.ruleset.ok !== "boolean" || !Array.isArray(m.ruleset.lines))))
|
|
37165
|
+
if (record.schema !== SCHEMA || record.lead !== SUITE_REPO || !SHA2.test(record.leadDevelopment) || !SHA2.test(record.leadPreviousRc) || !Array.isArray(record.members) || record.members.length === 0 || record.pinPr !== void 0 && (typeof record.pinPr !== "string" || !record.pinPr.startsWith("https://github.com/mutmutco/MM-Work/pull/")) || record.pinsUnchanged !== void 0 && (record.pinsUnchanged !== true || record.pinPr !== void 0) || record.pinMerged !== void 0 && (record.pinMerged !== true && record.pinMerged !== false || !record.pinPr && !record.pinsUnchanged) || record.leadRcTag !== void 0 && (!RC_TAG.test(record.leadRcTag) || !SHA2.test(record.leadRcSha ?? "") || !record.pinMerged) || record.leadReleased !== void 0 && (record.leadReleased !== true || !record.leadRcTag) || record.announcement !== void 0 && (!["pending", "sent", "skipped"].includes(record.announcement) || !record.leadReleased) || record.heldMembers !== void 0 && (!Array.isArray(record.heldMembers) || record.heldMembers.some((h) => !h.id || !h.repo || typeof h.reason !== "string" || !h.reason.trim() || h.since !== void 0 && typeof h.since !== "string" || record.members.some((m) => m.id === h.id))) || record.members.some((m) => !m.id || !m.repo || !m.backend || !m.host || !SHA2.test(m.development) || !SHA2.test(m.previousRc) || m.rcWasAbsent !== void 0 && m.rcWasAbsent !== true || m.tag !== void 0 && (!RC_TAG.test(m.tag) || !SHA2.test(m.sha ?? "")) || m.carriedPublished !== void 0 && (m.carriedPublished !== true || !m.carried) || m.published !== void 0 && (m.published !== true || !m.tag) || m.released !== void 0 && (m.released !== true || !m.published) || m.mainPublished !== void 0 && (m.mainPublished !== true || !m.released || !!m.carried) || m.backendAfterRelease !== void 0 && (!m.released || typeof m.backendAfterRelease !== "string") || m.carried !== void 0 && (m.carried !== true || m.tag !== void 0 || m.published !== void 0 || !isPin(m.rcPin) || !isPin(m.mainPin)) || m.carried === void 0 && (m.rcPin !== void 0 || m.mainPin !== void 0) || m.ruleset !== void 0 && (!["rc", "main"].includes(m.ruleset.after) || typeof m.ruleset.ok !== "boolean" || !Array.isArray(m.ruleset.lines))))
|
|
37166
37166
|
throw new Error(`suite cut record has unknown or invalid state at ${path2}; repair it, never recut`);
|
|
37167
37167
|
return record;
|
|
37168
37168
|
}
|
|
@@ -37274,7 +37274,22 @@ async function pendingBackend(deps, m, dev) {
|
|
|
37274
37274
|
const files = (await deps.changedPaths(m.repo, live, dev)).filter((f) => !NOT_BACKEND.test(f));
|
|
37275
37275
|
return files.length ? `${files.length} backend file(s) changed since live ${version}, e.g. ${files[0]}` : "";
|
|
37276
37276
|
}
|
|
37277
|
+
function existingCutDescription(record) {
|
|
37278
|
+
const tags = [...record.members.flatMap((m) => m.tag ? [m.tag] : []), ...record.leadRcTag ? [record.leadRcTag] : []];
|
|
37279
|
+
return `from ${record.leadDevelopment.slice(0, 12)}; tags: ${tags.join(", ") || "none"}; pin PR: ${record.pinPr ?? "none"}`;
|
|
37280
|
+
}
|
|
37277
37281
|
async function suiteRcand(deps, opts) {
|
|
37282
|
+
const existing = readSuiteCut(deps.recordPath);
|
|
37283
|
+
try {
|
|
37284
|
+
return await suiteRcandRun(deps, opts);
|
|
37285
|
+
} catch (e) {
|
|
37286
|
+
if (existing && !opts.dryRun && opts.ownerGo && readSuiteCut(deps.recordPath))
|
|
37287
|
+
throw new Error(`${e.message}
|
|
37288
|
+
An existing cut record ${existingCutDescription(existing)} was resumed. mmi-cli devops suite abandon --owner-go retires it (subject to abandon safety checks).`);
|
|
37289
|
+
throw e;
|
|
37290
|
+
}
|
|
37291
|
+
}
|
|
37292
|
+
async function suiteRcandRun(deps, opts) {
|
|
37278
37293
|
if (!opts.ownerGo && !opts.dryRun) throw new Error("suite rcand requires --owner-go (rc uses live data); nothing was written");
|
|
37279
37294
|
const members = suiteMembers(await deps.registry());
|
|
37280
37295
|
const modules = trainModules(members);
|
|
@@ -37288,6 +37303,8 @@ async function suiteRcand(deps, opts) {
|
|
|
37288
37303
|
const lead = await deps.head(SUITE_REPO, "development");
|
|
37289
37304
|
plan.push(await kitCheck(deps, lead, active.map((m) => ({ id: m.id, url: m.bundles.dev })), "dev bundles"));
|
|
37290
37305
|
if (opts.dryRun) {
|
|
37306
|
+
const existing = readSuiteCut(deps.recordPath);
|
|
37307
|
+
if (existing) plan.push(`existing cut record: ${deps.recordPath} (${existingCutDescription(existing)})`);
|
|
37291
37308
|
plan.push(...await Promise.all(modules.map(async (m) => `${m.repo}: ${heldById.has(m.id) ? "HELD" : await dryRunStatus(deps, m)}`)));
|
|
37292
37309
|
return { plan };
|
|
37293
37310
|
}
|
|
@@ -37372,7 +37389,7 @@ async function suiteRcand(deps, opts) {
|
|
|
37372
37389
|
}
|
|
37373
37390
|
const refusals = [];
|
|
37374
37391
|
for (const member of record.members) {
|
|
37375
|
-
if (member.published) continue;
|
|
37392
|
+
if (member.published || member.carriedPublished) continue;
|
|
37376
37393
|
try {
|
|
37377
37394
|
await deps.preflight(member.repo);
|
|
37378
37395
|
} catch (e) {
|
|
@@ -37386,28 +37403,60 @@ async function suiteRcand(deps, opts) {
|
|
|
37386
37403
|
${refusals.join("\n")}
|
|
37387
37404
|
Each module must ship a live release that bind-mounts <deployPath>/data read-only and serves <data>/rc/module.js at /rc/module.js (docs/Guides/suite-trains.md).`);
|
|
37388
37405
|
}
|
|
37389
|
-
|
|
37390
|
-
|
|
37391
|
-
|
|
37392
|
-
|
|
37393
|
-
|
|
37394
|
-
|
|
37395
|
-
}
|
|
37396
|
-
|
|
37397
|
-
|
|
37398
|
-
|
|
37399
|
-
|
|
37400
|
-
|
|
37401
|
-
|
|
37402
|
-
|
|
37403
|
-
|
|
37404
|
-
|
|
37405
|
-
|
|
37406
|
-
await deps.
|
|
37407
|
-
|
|
37408
|
-
|
|
37406
|
+
const cutRecord = record;
|
|
37407
|
+
let writes = Promise.resolve();
|
|
37408
|
+
const persist = (mutate) => {
|
|
37409
|
+
const next = writes.then(() => {
|
|
37410
|
+
mutate();
|
|
37411
|
+
writeSuiteCut(deps.recordPath, cutRecord);
|
|
37412
|
+
});
|
|
37413
|
+
writes = next.catch(() => {
|
|
37414
|
+
});
|
|
37415
|
+
return next;
|
|
37416
|
+
};
|
|
37417
|
+
let running = 0;
|
|
37418
|
+
const waiting = [];
|
|
37419
|
+
const results = await Promise.allSettled(record.members.map(async (member) => {
|
|
37420
|
+
if (running >= 4) await new Promise((resolve8) => waiting.push(resolve8));
|
|
37421
|
+
else running++;
|
|
37422
|
+
try {
|
|
37423
|
+
if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
|
|
37424
|
+
throw new Error(`${member.repo}: development moved after the cut snapshot; refuse to add untested code`);
|
|
37425
|
+
if (member.carried) {
|
|
37426
|
+
if (!member.carriedPublished) {
|
|
37427
|
+
await deps.publish(member.repo, pinVersion(member.rcPin));
|
|
37428
|
+
await persist(() => {
|
|
37429
|
+
member.carriedPublished = true;
|
|
37430
|
+
});
|
|
37431
|
+
}
|
|
37432
|
+
return;
|
|
37433
|
+
}
|
|
37434
|
+
if (!member.tag) {
|
|
37435
|
+
const cut = await deps.rcand(member.repo, member.previousRc, member.rcWasAbsent);
|
|
37436
|
+
if (!RC_TAG.test(cut.tag) || !SHA2.test(cut.sha) || cut.sha.toLowerCase() === member.previousRc.toLowerCase()) throw new Error(`${member.repo}: rcand did not return a new immutable rc tag/SHA`);
|
|
37437
|
+
if (!await deps.contains(member.repo, member.development, cut.sha)) throw new Error(`${member.repo}: rc tag does not carry the snapshotted development SHA; refuse`);
|
|
37438
|
+
await persist(() => {
|
|
37439
|
+
member.tag = cut.tag;
|
|
37440
|
+
member.sha = cut.sha;
|
|
37441
|
+
});
|
|
37442
|
+
}
|
|
37443
|
+
const tag = member.tag;
|
|
37444
|
+
if ((await deps.tagSha(member.repo, tag))?.toLowerCase() !== member.sha.toLowerCase()) throw new Error(`${member.repo}: rc tag ${member.tag} moved or is unreadable; stop`);
|
|
37445
|
+
if (!member.published) {
|
|
37446
|
+
await deps.publish(member.repo, tag);
|
|
37447
|
+
await persist(() => {
|
|
37448
|
+
member.published = true;
|
|
37449
|
+
});
|
|
37450
|
+
}
|
|
37451
|
+
} finally {
|
|
37452
|
+
const next = waiting.shift();
|
|
37453
|
+
if (next) next();
|
|
37454
|
+
else running--;
|
|
37409
37455
|
}
|
|
37410
|
-
}
|
|
37456
|
+
}));
|
|
37457
|
+
const failures = results.flatMap((result, i) => result.status === "rejected" ? [`${record.members[i].repo}: ${result.reason instanceof Error ? result.reason.message : String(result.reason)}`] : []);
|
|
37458
|
+
if (failures.length) throw new Error(`suite member cuts failed:
|
|
37459
|
+
${failures.join("\n")}`);
|
|
37411
37460
|
for (const member of record.members) {
|
|
37412
37461
|
const [live, devHead] = await Promise.all([
|
|
37413
37462
|
deps.backend({ repo: member.repo, id: member.id, bundles: {} }),
|
|
@@ -37852,8 +37901,10 @@ async function runRcDataSwitch(deps, opts) {
|
|
|
37852
37901
|
}
|
|
37853
37902
|
|
|
37854
37903
|
// src/suite-train-io.ts
|
|
37855
|
-
var
|
|
37904
|
+
var import_node_child_process18 = require("node:child_process");
|
|
37856
37905
|
var import_node_path44 = require("node:path");
|
|
37906
|
+
var import_node_fs45 = require("node:fs");
|
|
37907
|
+
var import_node_path45 = require("node:path");
|
|
37857
37908
|
init_cli_shared();
|
|
37858
37909
|
|
|
37859
37910
|
// src/command-register-train-operations.ts
|
|
@@ -38035,7 +38086,7 @@ function clean3(out) {
|
|
|
38035
38086
|
return out.trim();
|
|
38036
38087
|
}
|
|
38037
38088
|
function sleeper(deps) {
|
|
38038
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
38089
|
+
return deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
38039
38090
|
}
|
|
38040
38091
|
function normalizeHotfixVersion(input) {
|
|
38041
38092
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -39707,7 +39758,7 @@ async function updatePrHeadFromBase(input) {
|
|
|
39707
39758
|
} catch (e) {
|
|
39708
39759
|
throw new Error(`GitHub could not update ${input.head} from ${input.base} (${e.message.trim()}) \u2014 ${remedy}`);
|
|
39709
39760
|
}
|
|
39710
|
-
const sleep2 = input.sleep ?? ((ms) => new Promise((
|
|
39761
|
+
const sleep2 = input.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
39711
39762
|
for (let attempt = 0; attempt < PR_HEAD_UPDATE_API_READ_RETRIES; attempt++) {
|
|
39712
39763
|
const to = await readHead2();
|
|
39713
39764
|
if (to && to !== from) return { from, to, base: input.base, via: "update-branch-api" };
|
|
@@ -39725,7 +39776,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
|
39725
39776
|
if (first.mergeStatus !== "failed") return first;
|
|
39726
39777
|
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
|
|
39727
39778
|
if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
|
|
39728
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
39779
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
39729
39780
|
await sleep2(PR_LAND_MERGE_RETRY_DELAY_MS);
|
|
39730
39781
|
const retried = await deps.mergeAuto(prNumber, repo);
|
|
39731
39782
|
if (retried.mergeStatus !== "failed") return retried;
|
|
@@ -39736,7 +39787,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
|
|
|
39736
39787
|
async function confirmAutoMergeEnqueued(deps, options) {
|
|
39737
39788
|
const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
|
|
39738
39789
|
const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
|
|
39739
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
39790
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
39740
39791
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
39741
39792
|
if (await deps.readMerged().catch(() => false)) return "merged";
|
|
39742
39793
|
const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
|
|
@@ -39753,7 +39804,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
|
|
|
39753
39804
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
39754
39805
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
39755
39806
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
39756
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
39807
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve8) => setTimeout(resolve8, ms)));
|
|
39757
39808
|
let lastError = "empty state";
|
|
39758
39809
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
39759
39810
|
try {
|
|
@@ -43381,7 +43432,7 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
43381
43432
|
}
|
|
43382
43433
|
}
|
|
43383
43434
|
function computePrPatchId(number, repo) {
|
|
43384
|
-
return new Promise((
|
|
43435
|
+
return new Promise((resolve8, reject) => {
|
|
43385
43436
|
const gh2 = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
43386
43437
|
const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
43387
43438
|
let out = "";
|
|
@@ -43419,7 +43470,7 @@ function computePrPatchId(number, repo) {
|
|
|
43419
43470
|
if (gitCode !== 0) return reject(new Error(`git patch-id --stable exited ${gitCode}: ${gitErr.trim()}`));
|
|
43420
43471
|
const id = out.trim().split(/\s+/)[0];
|
|
43421
43472
|
if (!/^[0-9a-f]{40}$/.test(id ?? "")) return reject(new Error(`patch-id: PR #${number} diff is empty or unreadable (gh: ${ghErr.trim() || "no stderr"})`));
|
|
43422
|
-
|
|
43473
|
+
resolve8(id);
|
|
43423
43474
|
};
|
|
43424
43475
|
gh2.on("close", (code) => {
|
|
43425
43476
|
ghCode = code;
|
|
@@ -43470,7 +43521,7 @@ init_cli_shared();
|
|
|
43470
43521
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
43471
43522
|
function catFileBatch(root, ref, paths) {
|
|
43472
43523
|
if (paths.length === 0) return Promise.resolve([]);
|
|
43473
|
-
return new Promise((
|
|
43524
|
+
return new Promise((resolve8) => {
|
|
43474
43525
|
const chunks = [];
|
|
43475
43526
|
let settled = false;
|
|
43476
43527
|
const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
@@ -43478,7 +43529,7 @@ function catFileBatch(root, ref, paths) {
|
|
|
43478
43529
|
if (settled) return;
|
|
43479
43530
|
settled = true;
|
|
43480
43531
|
clearTimeout(timer);
|
|
43481
|
-
|
|
43532
|
+
resolve8(parseCatFileBatch(Buffer.concat(chunks), paths.length));
|
|
43482
43533
|
};
|
|
43483
43534
|
const timer = setTimeout(() => {
|
|
43484
43535
|
child2.kill("SIGKILL");
|
|
@@ -43903,7 +43954,7 @@ function healthPollIntervalMs() {
|
|
|
43903
43954
|
return HEALTH_POLL_INTERVAL_MS;
|
|
43904
43955
|
}
|
|
43905
43956
|
function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
43906
|
-
return new Promise((
|
|
43957
|
+
return new Promise((resolve8, reject) => {
|
|
43907
43958
|
let settled = false;
|
|
43908
43959
|
const finish = (fn) => {
|
|
43909
43960
|
if (settled) return;
|
|
@@ -43913,7 +43964,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
|
43913
43964
|
child2.removeAllListeners("exit");
|
|
43914
43965
|
fn();
|
|
43915
43966
|
};
|
|
43916
|
-
const timer = setTimeout(() => finish(
|
|
43967
|
+
const timer = setTimeout(() => finish(resolve8), graceMs);
|
|
43917
43968
|
child2.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
|
|
43918
43969
|
child2.on("exit", (code, signal) => {
|
|
43919
43970
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -44106,10 +44157,10 @@ function pickStagePort(range, isFree) {
|
|
|
44106
44157
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
44107
44158
|
}
|
|
44108
44159
|
function isPortFree(port) {
|
|
44109
|
-
return new Promise((
|
|
44160
|
+
return new Promise((resolve8) => {
|
|
44110
44161
|
const srv = (0, import_node_net.createServer)();
|
|
44111
|
-
srv.once("error", () =>
|
|
44112
|
-
srv.once("listening", () => srv.close(() =>
|
|
44162
|
+
srv.once("error", () => resolve8(false));
|
|
44163
|
+
srv.once("listening", () => srv.close(() => resolve8(true)));
|
|
44113
44164
|
srv.listen(port, "127.0.0.1");
|
|
44114
44165
|
});
|
|
44115
44166
|
}
|
|
@@ -44387,7 +44438,7 @@ async function killTree(pid) {
|
|
|
44387
44438
|
} catch {
|
|
44388
44439
|
}
|
|
44389
44440
|
}
|
|
44390
|
-
await new Promise((
|
|
44441
|
+
await new Promise((resolve8) => setTimeout(resolve8, 500));
|
|
44391
44442
|
try {
|
|
44392
44443
|
process.kill(-pid, "SIGKILL");
|
|
44393
44444
|
} catch {
|
|
@@ -44460,7 +44511,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
44460
44511
|
} catch (e) {
|
|
44461
44512
|
last = e.message;
|
|
44462
44513
|
}
|
|
44463
|
-
await new Promise((
|
|
44514
|
+
await new Promise((resolve8) => setTimeout(resolve8, healthPollIntervalMs()));
|
|
44464
44515
|
}
|
|
44465
44516
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
44466
44517
|
}
|
|
@@ -44820,7 +44871,7 @@ function unlinkNodeModulesJunction(wtPath) {
|
|
|
44820
44871
|
}
|
|
44821
44872
|
}
|
|
44822
44873
|
function defaultSleep2(ms) {
|
|
44823
|
-
return new Promise((
|
|
44874
|
+
return new Promise((resolve8) => setTimeout(resolve8, ms));
|
|
44824
44875
|
}
|
|
44825
44876
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
44826
44877
|
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs42.rmdirSync, exists = import_node_fs42.existsSync } = options;
|
|
@@ -46570,7 +46621,7 @@ ${list}`);
|
|
|
46570
46621
|
// #5400: after grace, name "GitHub delivered zero runs" instead of burning the full budget as pending.
|
|
46571
46622
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr checks-wait", number, repo),
|
|
46572
46623
|
baseBranch,
|
|
46573
|
-
sleep: (ms) => new Promise((
|
|
46624
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
46574
46625
|
log: (message2) => console.warn(message2),
|
|
46575
46626
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
46576
46627
|
// Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
|
|
@@ -46725,7 +46776,7 @@ ${list}`);
|
|
|
46725
46776
|
// #5400: same zero-runs delivery probe as checks-wait — do not burn the land budget on silence.
|
|
46726
46777
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr land", prNumber, repo),
|
|
46727
46778
|
baseBranch: "development",
|
|
46728
|
-
sleep: (ms) => new Promise((
|
|
46779
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
46729
46780
|
log: (message2) => console.warn(message2),
|
|
46730
46781
|
// `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
|
|
46731
46782
|
// 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
|
|
@@ -46776,7 +46827,7 @@ ${list}`);
|
|
|
46776
46827
|
} else {
|
|
46777
46828
|
lastFailure = void 0;
|
|
46778
46829
|
}
|
|
46779
|
-
await new Promise((
|
|
46830
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_LAND_POLL_MS));
|
|
46780
46831
|
}
|
|
46781
46832
|
if (lastFailure) {
|
|
46782
46833
|
throw new Error(
|
|
@@ -46934,7 +46985,7 @@ ${list}`);
|
|
|
46934
46985
|
checksDiagnosticCommand: `gh pr checks ${number} --repo ${repo}`,
|
|
46935
46986
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr merge --wait", number, repo),
|
|
46936
46987
|
baseBranch,
|
|
46937
|
-
sleep: (ms) => new Promise((
|
|
46988
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
46938
46989
|
log: (message2) => console.warn(message2),
|
|
46939
46990
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
46940
46991
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -47003,7 +47054,7 @@ ${list}`);
|
|
|
47003
47054
|
}
|
|
47004
47055
|
if (!o.auto && mergeRejectedBaseBranchModified(message2)) {
|
|
47005
47056
|
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).`);
|
|
47006
|
-
await new Promise((
|
|
47057
|
+
await new Promise((resolve8) => setTimeout(resolve8, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
|
|
47007
47058
|
await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
47008
47059
|
const m2 = String(e2.message || "");
|
|
47009
47060
|
if (/already been merged/i.test(m2)) {
|
|
@@ -47264,7 +47315,7 @@ ${list}`);
|
|
|
47264
47315
|
console.warn(`ci checks-wait: zero-runs probe FAILED (${read.error}) \u2014 not treating as delivery failure this cycle`);
|
|
47265
47316
|
return null;
|
|
47266
47317
|
},
|
|
47267
|
-
sleep: (ms) => new Promise((
|
|
47318
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms)),
|
|
47268
47319
|
log: (message2) => console.warn(message2),
|
|
47269
47320
|
timeoutMs: timeoutMs ?? PR_CHECKS_TIMEOUT_MS,
|
|
47270
47321
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`ci checks-wait: ${state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -47629,7 +47680,7 @@ function hotfixFollowUpLegs(runs, foldPort, alignment, foldNote, deployNote) {
|
|
|
47629
47680
|
}
|
|
47630
47681
|
return legs;
|
|
47631
47682
|
}
|
|
47632
|
-
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((
|
|
47683
|
+
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))) {
|
|
47633
47684
|
for (let attempt = 0; ; attempt++) {
|
|
47634
47685
|
try {
|
|
47635
47686
|
return await cb(repo, true);
|
|
@@ -48358,18 +48409,63 @@ function rcRunCauseFromLog(log) {
|
|
|
48358
48409
|
function memberAnnounceSummaryFile(repo, summaryFile, memberSummaryFiles) {
|
|
48359
48410
|
return repo === SUITE_REPO ? summaryFile : memberSummaryFiles[repo];
|
|
48360
48411
|
}
|
|
48361
|
-
var suiteStateDir = (root) => (0,
|
|
48412
|
+
var suiteStateDir = (root) => (0, import_node_path45.join)(resolvePrimaryGitDir(root), "mmi-runtime", "suite");
|
|
48413
|
+
async function runSuiteMemberRcand(repo, previousRc, rcWasAbsent) {
|
|
48414
|
+
const deps = trainApplyDeps();
|
|
48415
|
+
deps.suiteCut = suiteCutContext(repo, SUITE_REPO);
|
|
48416
|
+
deps.announce = async () => ({ status: "skipped", note: "suite announcement belongs to MM-Work" });
|
|
48417
|
+
return rcandInCheckout(repo, deps, previousRc, rcWasAbsent);
|
|
48418
|
+
}
|
|
48419
|
+
async function rcandInCheckout(repo, deps, previousRc, rcWasAbsent) {
|
|
48420
|
+
if (repo !== SUITE_REPO) await prepareDevelopmentCheckout((args) => deps.run("git", args), repo);
|
|
48421
|
+
await deps.run("git", ["fetch", "origin", "--tags"]);
|
|
48422
|
+
let rcNow;
|
|
48423
|
+
try {
|
|
48424
|
+
rcNow = (await deps.run("git", ["rev-parse", "--verify", "origin/rc"])).trim();
|
|
48425
|
+
} catch (e) {
|
|
48426
|
+
if (!rcWasAbsent || !previousRc || repo === SUITE_REPO) throw e;
|
|
48427
|
+
const slug = repo.split("/")[1].toLowerCase();
|
|
48428
|
+
const meta = await fetchProjectBySlugChecked(slug, registryClientDeps(await loadConfig()));
|
|
48429
|
+
if (!meta.ok || meta.project?.releaseTrack !== "full" || meta.project.suite !== "mm-work")
|
|
48430
|
+
throw new Error(`${repo}: first rc branch requires releaseTrack full and suite=mm-work in META; no branch created`);
|
|
48431
|
+
const main = (await deps.run("git", ["rev-parse", "origin/main"])).trim();
|
|
48432
|
+
if (main.toLowerCase() !== previousRc.toLowerCase()) throw new Error(`${repo}: main moved since the cut snapshot; no rc branch created`);
|
|
48433
|
+
await deps.run("git", ["push", "origin", `${previousRc}:refs/heads/rc`]);
|
|
48434
|
+
await deps.run("git", ["fetch", "origin", "rc"]);
|
|
48435
|
+
rcNow = (await deps.run("git", ["rev-parse", "origin/rc"])).trim();
|
|
48436
|
+
}
|
|
48437
|
+
if (!(await deps.run("git", ["branch", "--list", "rc"])).trim()) {
|
|
48438
|
+
await deps.run("git", ["switch", "-c", "rc", "--track", "origin/rc"]);
|
|
48439
|
+
await deps.run("git", ["switch", "development"]);
|
|
48440
|
+
}
|
|
48441
|
+
if (previousRc && rcNow.toLowerCase() !== previousRc.toLowerCase()) {
|
|
48442
|
+
const tags = (await deps.run("git", ["tag", "--points-at", rcNow, "--list", "v*-rc.*"])).trim().split("\n").filter(Boolean);
|
|
48443
|
+
if (tags.length !== 1) throw new Error(`${repo}: rc moved since cut start without exactly one candidate tag; stop`);
|
|
48444
|
+
return { tag: tags[0], sha: rcNow };
|
|
48445
|
+
}
|
|
48446
|
+
try {
|
|
48447
|
+
const result = await runTrainApply("rcand", deps, { watch: true });
|
|
48448
|
+
if (result.deployStatus !== "success") throw new Error(`${repo}: rcand deploy not green (${result.deployStatus})`);
|
|
48449
|
+
return { tag: result.tag, sha: (await deps.run("git", ["rev-parse", result.tag])).trim() };
|
|
48450
|
+
} catch (e) {
|
|
48451
|
+
const existing = (await deps.run("git", ["tag", "--list", "v*-rc.*"])).trim();
|
|
48452
|
+
if (!existing || !/resume|partial|ledger|tag.*pushed/i.test(e.message)) throw e;
|
|
48453
|
+
const result = await runRcandResume(deps, { watch: true });
|
|
48454
|
+
if (result.deployStatus !== "success") throw new Error(`${repo}: resumed rcand deploy not green (${result.deployStatus})`);
|
|
48455
|
+
return { tag: result.tag, sha: result.tagSha };
|
|
48456
|
+
}
|
|
48457
|
+
}
|
|
48362
48458
|
function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr, memberSummaryFilesTr = {}) {
|
|
48363
|
-
const recordPath = (0,
|
|
48364
|
-
const base = (0,
|
|
48459
|
+
const recordPath = (0, import_node_path45.join)(suiteStateDir(root), "cut.json");
|
|
48460
|
+
const base = (0, import_node_path45.join)(suiteStateDir(root), "checkouts");
|
|
48365
48461
|
const checkout = async (repo) => {
|
|
48366
48462
|
if (repo === SUITE_REPO) return root;
|
|
48367
|
-
const target = (0,
|
|
48463
|
+
const target = (0, import_node_path45.join)(base, repo.split("/")[1]);
|
|
48368
48464
|
if (!(0, import_node_fs45.existsSync)(target)) {
|
|
48369
48465
|
(0, import_node_fs45.mkdirSync)(base, { recursive: true });
|
|
48370
48466
|
await exec("git", ["clone", "--quiet", "--branch", "development", `https://github.com/${repo}.git`, target], root, WATCH_TIMEOUT);
|
|
48371
48467
|
}
|
|
48372
|
-
if (!(0, import_node_fs45.existsSync)((0,
|
|
48468
|
+
if (!(0, import_node_fs45.existsSync)((0, import_node_path45.join)(target, ".git"))) throw new Error(`${repo}: suite checkout has no .git at ${target}; stop`);
|
|
48373
48469
|
const origin = await exec("git", ["remote", "get-url", "origin"], target);
|
|
48374
48470
|
if (!origin.toLowerCase().endsWith(`/${repo.toLowerCase()}.git`) && !origin.toLowerCase().endsWith(`/${repo.toLowerCase()}`))
|
|
48375
48471
|
throw new Error(`${repo}: suite checkout origin mismatch at ${target}; stop`);
|
|
@@ -48429,45 +48525,28 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
48429
48525
|
throw e;
|
|
48430
48526
|
}
|
|
48431
48527
|
};
|
|
48432
|
-
const runRcand = async (repo, previousRc, rcWasAbsent) =>
|
|
48433
|
-
if (repo
|
|
48434
|
-
|
|
48435
|
-
|
|
48436
|
-
|
|
48437
|
-
|
|
48438
|
-
|
|
48439
|
-
|
|
48440
|
-
|
|
48441
|
-
|
|
48442
|
-
|
|
48443
|
-
|
|
48444
|
-
|
|
48445
|
-
|
|
48446
|
-
|
|
48447
|
-
|
|
48448
|
-
|
|
48449
|
-
|
|
48450
|
-
|
|
48451
|
-
|
|
48452
|
-
|
|
48453
|
-
|
|
48454
|
-
if (previousRc && rcNow.toLowerCase() !== previousRc.toLowerCase()) {
|
|
48455
|
-
const tags = (await deps.run("git", ["tag", "--points-at", rcNow, "--list", "v*-rc.*"])).trim().split("\n").filter(Boolean);
|
|
48456
|
-
if (tags.length !== 1) throw new Error(`${repo}: rc moved since cut start without exactly one candidate tag; stop`);
|
|
48457
|
-
return { tag: tags[0], sha: rcNow };
|
|
48458
|
-
}
|
|
48459
|
-
try {
|
|
48460
|
-
const result = await runTrainApply("rcand", deps, { watch: true });
|
|
48461
|
-
if (result.deployStatus !== "success") throw new Error(`${repo}: rcand deploy not green (${result.deployStatus})`);
|
|
48462
|
-
return { tag: result.tag, sha: (await deps.run("git", ["rev-parse", result.tag])).trim() };
|
|
48463
|
-
} catch (e) {
|
|
48464
|
-
const existing = (await deps.run("git", ["tag", "--list", "v*-rc.*"])).trim();
|
|
48465
|
-
if (!existing || !/resume|partial|ledger|tag.*pushed/i.test(e.message)) throw e;
|
|
48466
|
-
const result = await runRcandResume(deps, { watch: true });
|
|
48467
|
-
if (result.deployStatus !== "success") throw new Error(`${repo}: resumed rcand deploy not green (${result.deployStatus})`);
|
|
48468
|
-
return { tag: result.tag, sha: result.tagSha };
|
|
48469
|
-
}
|
|
48470
|
-
});
|
|
48528
|
+
const runRcand = async (repo, previousRc, rcWasAbsent) => {
|
|
48529
|
+
if (repo === SUITE_REPO) return inTrain(repo, (deps) => rcandInCheckout(repo, deps, previousRc, rcWasAbsent));
|
|
48530
|
+
const target = await checkout(repo);
|
|
48531
|
+
return new Promise((resolveResult, reject) => {
|
|
48532
|
+
const child2 = (0, import_node_child_process18.fork)((0, import_node_path44.resolve)(process.argv[1]), ["devops", "suite", "member-rcand-internal", repo, previousRc ?? "", ...rcWasAbsent ? ["--rc-was-absent"] : []], {
|
|
48533
|
+
cwd: target,
|
|
48534
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"],
|
|
48535
|
+
execArgv: []
|
|
48536
|
+
});
|
|
48537
|
+
let result;
|
|
48538
|
+
let failure;
|
|
48539
|
+
child2.on("message", (message2) => {
|
|
48540
|
+
result = message2.result;
|
|
48541
|
+
failure = message2.error;
|
|
48542
|
+
});
|
|
48543
|
+
child2.on("error", reject);
|
|
48544
|
+
child2.on("exit", (code) => {
|
|
48545
|
+
if (code === 0 && result) resolveResult(result);
|
|
48546
|
+
else reject(new Error(failure ?? `${repo}: member rcand process exited ${code} without a result`));
|
|
48547
|
+
});
|
|
48548
|
+
});
|
|
48549
|
+
};
|
|
48471
48550
|
const release = async (repo, expected, rcSha, opts) => inTrain(repo, async (deps) => {
|
|
48472
48551
|
await prepareRcCheckout((args) => deps.run("git", args), repo);
|
|
48473
48552
|
const plainWords = {
|
|
@@ -48522,7 +48601,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
48522
48601
|
if (!reuse) await gh(["workflow", "run", "module-rc-publish.yml", "--repo", "mutmutco/MMI-Hub", ...fields.flatMap((f) => ["--field", f])]);
|
|
48523
48602
|
let candidate = reuse;
|
|
48524
48603
|
for (let i = 0; !candidate && i < 25; i++) {
|
|
48525
|
-
await new Promise((
|
|
48604
|
+
await new Promise((resolve8) => setTimeout(resolve8, 1e3));
|
|
48526
48605
|
candidate = (await rcRuns()).find((r) => r.displayTitle === title && Date.parse(r.createdAt) >= since);
|
|
48527
48606
|
}
|
|
48528
48607
|
if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
|
|
@@ -48677,7 +48756,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
48677
48756
|
for (let i = 0; ; i++) {
|
|
48678
48757
|
const pin = await openSuitePinPr({ root, run: (file, args, cwd) => exec(file, args, cwd, GIT_TIMEOUT_MS) }, { repo, tag, stage: "main" });
|
|
48679
48758
|
if (pin.status !== "pending" || i >= 40) return pin;
|
|
48680
|
-
await new Promise((
|
|
48759
|
+
await new Promise((resolve8) => setTimeout(resolve8, 3e4));
|
|
48681
48760
|
}
|
|
48682
48761
|
},
|
|
48683
48762
|
suiteMapping: async (leadTag) => {
|
|
@@ -48693,7 +48772,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
48693
48772
|
if (parseSuiteMapping(body)) return;
|
|
48694
48773
|
const dir = suiteStateDir(root);
|
|
48695
48774
|
(0, import_node_fs45.mkdirSync)(dir, { recursive: true });
|
|
48696
|
-
const file = (0,
|
|
48775
|
+
const file = (0, import_node_path45.join)(dir, `notes-${leadTag}.md`);
|
|
48697
48776
|
(0, import_node_fs45.writeFileSync)(file, `${body}
|
|
48698
48777
|
|
|
48699
48778
|
${suiteMappingBlock(leadTag, mapping)}`);
|
|
@@ -48759,11 +48838,23 @@ function suiteRcDataProdDeps() {
|
|
|
48759
48838
|
const rc = facts.stages.rc;
|
|
48760
48839
|
return rc?.present ? rc.substrate ?? "an unnamed substrate" : null;
|
|
48761
48840
|
},
|
|
48762
|
-
sleep: (ms) => new Promise((
|
|
48841
|
+
sleep: (ms) => new Promise((resolve8) => setTimeout(resolve8, ms))
|
|
48763
48842
|
};
|
|
48764
48843
|
}
|
|
48765
48844
|
function registerSuiteCommands(program3) {
|
|
48766
48845
|
const suite = program3.command("suite").description("MM-Work suite status, candidate cut and release (#7262/#7263)");
|
|
48846
|
+
suite.command("member-rcand-internal", { hidden: true }).argument("<repo>").argument("<previous-rc>").option("--rc-was-absent").action(async (repo, previousRc, o) => {
|
|
48847
|
+
if (!process.send) return fail("member rcand is an internal suite child operation");
|
|
48848
|
+
try {
|
|
48849
|
+
if (repo === SUITE_REPO || (await resolveRepo())?.toLowerCase() !== repo.toLowerCase())
|
|
48850
|
+
throw new Error(`${repo}: member rcand checkout mismatch; stop`);
|
|
48851
|
+
const result = await runSuiteMemberRcand(repo, previousRc || void 0, o.rcWasAbsent);
|
|
48852
|
+
process.send({ result }, () => process.disconnect());
|
|
48853
|
+
} catch (e) {
|
|
48854
|
+
process.exitCode = 1;
|
|
48855
|
+
process.send({ error: e.message }, () => process.disconnect());
|
|
48856
|
+
}
|
|
48857
|
+
});
|
|
48767
48858
|
const leadRoot = async () => {
|
|
48768
48859
|
const repo = await resolveRepo();
|
|
48769
48860
|
if (repo?.toLowerCase() !== SUITE_REPO.toLowerCase()) throw new Error(`suite commands run only from ${SUITE_REPO} (got ${repo ?? "unknown"})`);
|
|
@@ -49005,10 +49096,10 @@ function suiteStatusDeps(github, registry2) {
|
|
|
49005
49096
|
}
|
|
49006
49097
|
|
|
49007
49098
|
// src/packages-adopt-export.ts
|
|
49008
|
-
var
|
|
49099
|
+
var import_node_child_process19 = require("node:child_process");
|
|
49009
49100
|
var import_node_fs47 = require("node:fs");
|
|
49010
49101
|
var import_node_os22 = require("node:os");
|
|
49011
|
-
var
|
|
49102
|
+
var import_node_path46 = require("node:path");
|
|
49012
49103
|
var import_node_util6 = require("node:util");
|
|
49013
49104
|
init_cli_shared();
|
|
49014
49105
|
|
|
@@ -49628,25 +49719,25 @@ function expectedFromRun(run, opts) {
|
|
|
49628
49719
|
}
|
|
49629
49720
|
async function adoptExport(opts, deps) {
|
|
49630
49721
|
if (!/^sha256:[0-9a-f]{64}$/.test(opts.digest)) throw new Error("--digest must be sha256:<64 hex>, from the export run summary");
|
|
49631
|
-
const packagePath = (0,
|
|
49632
|
-
const lockPath = (0,
|
|
49722
|
+
const packagePath = (0, import_node_path46.join)(opts.dir, "package.json");
|
|
49723
|
+
const lockPath = (0, import_node_path46.join)(opts.dir, "package-lock.json");
|
|
49633
49724
|
const packageText = (0, import_node_fs47.readFileSync)(packagePath, "utf8");
|
|
49634
49725
|
const lockText = (0, import_node_fs47.readFileSync)(lockPath, "utf8");
|
|
49635
|
-
const work = (0, import_node_fs47.mkdtempSync)((0,
|
|
49726
|
+
const work = (0, import_node_fs47.mkdtempSync)((0, import_node_path46.join)((0, import_node_os22.tmpdir)(), "mmi-adopt-export-"));
|
|
49636
49727
|
try {
|
|
49637
49728
|
const run = JSON.parse((await deps.ghApi(`repos/${EXPORT_REPO}/actions/runs/${opts.run}`)).toString("utf8"));
|
|
49638
|
-
(0, import_node_fs47.writeFileSync)((0,
|
|
49639
|
-
(0, import_node_fs47.writeFileSync)((0,
|
|
49640
|
-
(0, import_node_fs47.writeFileSync)((0,
|
|
49641
|
-
const script = (0,
|
|
49729
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "expected.json"), JSON.stringify(expectedFromRun(run, opts)));
|
|
49730
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "provider.json"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}`));
|
|
49731
|
+
(0, import_node_fs47.writeFileSync)((0, import_node_path46.join)(work, "artifact.zip"), await deps.ghApi(`repos/${EXPORT_REPO}/actions/artifacts/${opts.artifact}/zip`));
|
|
49732
|
+
const script = (0, import_node_path46.join)(work, "package_export.py");
|
|
49642
49733
|
(0, import_node_fs47.writeFileSync)(script, package_export_default);
|
|
49643
49734
|
await deps.python(script, ["verify", work, opts.ds, opts.screens]);
|
|
49644
|
-
const verified = (0,
|
|
49645
|
-
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs47.readFileSync)((0,
|
|
49735
|
+
const verified = (0, import_node_path46.join)(work, "verified");
|
|
49736
|
+
const metadata = Object.fromEntries(PACKAGES.map(({ name, short: short3 }) => [name, JSON.parse((0, import_node_fs47.readFileSync)((0, import_node_path46.join)(verified, `${short3}.metadata.json`), "utf8"))]));
|
|
49646
49737
|
const plan = planAdoption(packageText, lockText, metadata);
|
|
49647
49738
|
for (const { name } of plan.result.adopted) {
|
|
49648
49739
|
const short3 = PACKAGES.find((p) => p.name === name).short;
|
|
49649
|
-
await deps.npm(["cache", "add", (0,
|
|
49740
|
+
await deps.npm(["cache", "add", (0, import_node_path46.join)(verified, `${short3}.tgz`)], opts.dir);
|
|
49650
49741
|
}
|
|
49651
49742
|
(0, import_node_fs47.writeFileSync)(packagePath, plan.packageText);
|
|
49652
49743
|
(0, import_node_fs47.writeFileSync)(lockPath, plan.lockText);
|
|
@@ -49662,7 +49753,7 @@ async function adoptExport(opts, deps) {
|
|
|
49662
49753
|
(0, import_node_fs47.rmSync)(work, { recursive: true, force: true });
|
|
49663
49754
|
}
|
|
49664
49755
|
}
|
|
49665
|
-
var execFileBuffer = (0, import_node_util6.promisify)(
|
|
49756
|
+
var execFileBuffer = (0, import_node_util6.promisify)(import_node_child_process19.execFile);
|
|
49666
49757
|
var MAX_OUTPUT = 200 * 1024 * 1024;
|
|
49667
49758
|
async function findPython() {
|
|
49668
49759
|
for (const bin of ["python3", "python"]) {
|
|
@@ -49788,7 +49879,7 @@ init_github_client();
|
|
|
49788
49879
|
init_cli_shared();
|
|
49789
49880
|
var import_node_fs49 = require("node:fs");
|
|
49790
49881
|
var import_node_os23 = require("node:os");
|
|
49791
|
-
var
|
|
49882
|
+
var import_node_path47 = require("node:path");
|
|
49792
49883
|
var GC_GH_TIMEOUT_MS3 = 2e4;
|
|
49793
49884
|
async function collectStatus() {
|
|
49794
49885
|
const repo = await resolveRepo();
|
|
@@ -49979,8 +50070,8 @@ async function collectOnboardStatus(opts = {}) {
|
|
|
49979
50070
|
}
|
|
49980
50071
|
const home = (0, import_node_os23.homedir)();
|
|
49981
50072
|
const plugin = onboardPluginGate({
|
|
49982
|
-
readKnown: () => readFileSyncSafe((0,
|
|
49983
|
-
readSettings: () => readFileSyncSafe((0,
|
|
50073
|
+
readKnown: () => readFileSyncSafe((0, import_node_path47.join)(home, ...KNOWN_MARKETPLACES_RELATIVE), import_node_fs49.readFileSync),
|
|
50074
|
+
readSettings: () => readFileSyncSafe((0, import_node_path47.join)(home, ".claude", "settings.json"), import_node_fs49.readFileSync)
|
|
49984
50075
|
});
|
|
49985
50076
|
return {
|
|
49986
50077
|
guide: "Read the MMI org-guide skill before project work; load only the relevant topic. Use mmi-help for a short introduction.",
|
|
@@ -53537,7 +53628,7 @@ function registerSessionReport(program3) {
|
|
|
53537
53628
|
|
|
53538
53629
|
// src/stage-commands.ts
|
|
53539
53630
|
var import_node_fs51 = require("node:fs");
|
|
53540
|
-
var
|
|
53631
|
+
var import_node_path48 = require("node:path");
|
|
53541
53632
|
init_cli_shared();
|
|
53542
53633
|
init_clean_exit();
|
|
53543
53634
|
|
|
@@ -53785,8 +53876,8 @@ function registerStageCommands(program3) {
|
|
|
53785
53876
|
const repo = Array.isArray(project2?.repos) && typeof project2.repos[0] === "string" ? project2.repos[0] : slug;
|
|
53786
53877
|
const resolution = decideStage({
|
|
53787
53878
|
registry: { deployModel: project2?.deployModel, portRange, error: read.ok ? void 0 : read.error },
|
|
53788
|
-
hasCompose: (0, import_node_fs51.existsSync)((0,
|
|
53789
|
-
hasEnvExample: (0, import_node_fs51.existsSync)((0,
|
|
53879
|
+
hasCompose: (0, import_node_fs51.existsSync)((0, import_node_path48.join)(process.cwd(), "docker-compose.yml")),
|
|
53880
|
+
hasEnvExample: (0, import_node_fs51.existsSync)((0, import_node_path48.join)(process.cwd(), ".env.example")),
|
|
53790
53881
|
repo
|
|
53791
53882
|
});
|
|
53792
53883
|
if (resolution.source === "derived" && resolution.derived) {
|
|
@@ -54081,7 +54172,7 @@ vercel preview (${preview.branch}): ${d.state}${d.url ? ` \u2014 ${d.url}` : ""}
|
|
|
54081
54172
|
var import_node_crypto17 = require("node:crypto");
|
|
54082
54173
|
var import_node_fs52 = require("node:fs");
|
|
54083
54174
|
var import_promises11 = require("node:fs/promises");
|
|
54084
|
-
var
|
|
54175
|
+
var import_node_path49 = require("node:path");
|
|
54085
54176
|
var ARTIFACT_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
54086
54177
|
var MAX_BYTES2 = 5 * 1024 * 1024 * 1024;
|
|
54087
54178
|
async function sha256File(path2) {
|
|
@@ -54091,7 +54182,7 @@ async function sha256File(path2) {
|
|
|
54091
54182
|
}
|
|
54092
54183
|
async function putTenantArtifact(repo, stage, inputPath, deps) {
|
|
54093
54184
|
if (!["dev", "rc", "main"].includes(stage)) throw new Error("tenant artifact put: <stage> must be dev, rc, or main");
|
|
54094
|
-
const path2 = (0,
|
|
54185
|
+
const path2 = (0, import_node_path49.resolve)(inputPath);
|
|
54095
54186
|
const info = await (0, import_promises11.stat)(path2);
|
|
54096
54187
|
if (!info.isFile()) throw new Error("tenant artifact put: input path must be a file");
|
|
54097
54188
|
if (!Number.isSafeInteger(info.size) || info.size < 1 || info.size > MAX_BYTES2) throw new Error(`tenant artifact put: file must be 1..${MAX_BYTES2} bytes`);
|
|
@@ -54281,7 +54372,7 @@ async function resolveWhoami(deps) {
|
|
|
54281
54372
|
}
|
|
54282
54373
|
|
|
54283
54374
|
// src/command-register-developer.ts
|
|
54284
|
-
var
|
|
54375
|
+
var import_node_path55 = require("node:path");
|
|
54285
54376
|
init_house_map();
|
|
54286
54377
|
init_hub_url();
|
|
54287
54378
|
|
|
@@ -54314,21 +54405,21 @@ async function executeWaveLand(plan, deps) {
|
|
|
54314
54405
|
}
|
|
54315
54406
|
|
|
54316
54407
|
// src/dist-drift.ts
|
|
54317
|
-
var
|
|
54408
|
+
var import_node_child_process20 = require("node:child_process");
|
|
54318
54409
|
var import_node_crypto19 = require("node:crypto");
|
|
54319
54410
|
var import_node_fs54 = require("node:fs");
|
|
54320
54411
|
var import_node_os24 = require("node:os");
|
|
54321
|
-
var
|
|
54412
|
+
var import_node_path51 = require("node:path");
|
|
54322
54413
|
|
|
54323
54414
|
// ../scripts/distribution-digest.mjs
|
|
54324
54415
|
var import_node_crypto18 = require("node:crypto");
|
|
54325
54416
|
var import_node_fs53 = require("node:fs");
|
|
54326
|
-
var
|
|
54417
|
+
var import_node_path50 = require("node:path");
|
|
54327
54418
|
var slash = (value) => value.replaceAll("\\", "/");
|
|
54328
54419
|
function repoPath(root, declaredPath, label) {
|
|
54329
|
-
const absoluteRoot = (0,
|
|
54330
|
-
const target = (0,
|
|
54331
|
-
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${
|
|
54420
|
+
const absoluteRoot = (0, import_node_path50.resolve)(root);
|
|
54421
|
+
const target = (0, import_node_path50.resolve)(root, declaredPath);
|
|
54422
|
+
if (target !== absoluteRoot && !target.startsWith(`${absoluteRoot}${import_node_path50.sep}`)) {
|
|
54332
54423
|
throw new Error(`${label} ${declaredPath} escapes the repository root`);
|
|
54333
54424
|
}
|
|
54334
54425
|
return target;
|
|
@@ -54443,7 +54534,7 @@ function walkFiles(root) {
|
|
|
54443
54534
|
const files = [];
|
|
54444
54535
|
const walk2 = (directory) => {
|
|
54445
54536
|
for (const entry of (0, import_node_fs54.readdirSync)(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
54446
|
-
const child2 = (0,
|
|
54537
|
+
const child2 = (0, import_node_path51.join)(directory, entry.name);
|
|
54447
54538
|
if (entry.isDirectory()) walk2(child2);
|
|
54448
54539
|
else files.push(child2);
|
|
54449
54540
|
}
|
|
@@ -54453,14 +54544,14 @@ function walkFiles(root) {
|
|
|
54453
54544
|
}
|
|
54454
54545
|
function bomPathFor(root) {
|
|
54455
54546
|
try {
|
|
54456
|
-
const registry2 = JSON.parse((0, import_node_fs54.readFileSync)((0,
|
|
54457
|
-
return (0,
|
|
54547
|
+
const registry2 = JSON.parse((0, import_node_fs54.readFileSync)((0, import_node_path51.join)(root, "surfaces.json"), "utf8"));
|
|
54548
|
+
return (0, import_node_path51.join)(root, registry2?.sharedAgentCore?.releaseMetadata?.bomPath ?? "distribution-bom.json");
|
|
54458
54549
|
} catch {
|
|
54459
|
-
return (0,
|
|
54550
|
+
return (0, import_node_path51.join)(root, "distribution-bom.json");
|
|
54460
54551
|
}
|
|
54461
54552
|
}
|
|
54462
54553
|
function rebuildTo(packageRoot, outDir) {
|
|
54463
|
-
(0,
|
|
54554
|
+
(0, import_node_child_process20.execFileSync)(process.execPath, ["build.mjs"], {
|
|
54464
54555
|
cwd: packageRoot,
|
|
54465
54556
|
env: { ...process.env, MMI_DIST_OUTDIR: outDir },
|
|
54466
54557
|
windowsHide: true,
|
|
@@ -54469,28 +54560,28 @@ function rebuildTo(packageRoot, outDir) {
|
|
|
54469
54560
|
});
|
|
54470
54561
|
}
|
|
54471
54562
|
function runDistStatus(root) {
|
|
54472
|
-
const stage = (0, import_node_fs54.mkdtempSync)((0,
|
|
54563
|
+
const stage = (0, import_node_fs54.mkdtempSync)((0, import_node_path51.join)((0, import_node_os24.tmpdir)(), "mmi-dist-drift-"));
|
|
54473
54564
|
let overlayCount = 0;
|
|
54474
54565
|
try {
|
|
54475
|
-
const cliOut = (0,
|
|
54476
|
-
const hubOut = (0,
|
|
54477
|
-
rebuildTo((0,
|
|
54478
|
-
rebuildTo((0,
|
|
54566
|
+
const cliOut = (0, import_node_path51.join)(stage, "cli-dist");
|
|
54567
|
+
const hubOut = (0, import_node_path51.join)(stage, "hub-dist");
|
|
54568
|
+
rebuildTo((0, import_node_path51.join)(root, "cli"), cliOut);
|
|
54569
|
+
rebuildTo((0, import_node_path51.join)(root, "updater"), hubOut);
|
|
54479
54570
|
const outDirFor = (packageDir) => packageDir === "cli" ? cliOut : hubOut;
|
|
54480
54571
|
const rebuilt = (path2) => {
|
|
54481
54572
|
const spec = DIST_ARTIFACTS.find((entry) => entry.path === path2);
|
|
54482
|
-
return spec ? readOrNull((0,
|
|
54573
|
+
return spec ? readOrNull((0, import_node_path51.join)(outDirFor(spec.packageDir), spec.output)) : null;
|
|
54483
54574
|
};
|
|
54484
|
-
const committed = (path2) => readOrNull((0,
|
|
54485
|
-
const tree = (path2) => readOrNull((0,
|
|
54486
|
-
const distRoot = (0,
|
|
54487
|
-
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0,
|
|
54575
|
+
const committed = (path2) => readOrNull((0, import_node_path51.join)(root, path2));
|
|
54576
|
+
const tree = (path2) => readOrNull((0, import_node_path51.join)(root, path2));
|
|
54577
|
+
const distRoot = (0, import_node_path51.join)(root, "cli", "dist");
|
|
54578
|
+
const distTree = () => walkFiles(distRoot).map((absolute) => `cli/dist/${(0, import_node_path51.relative)(distRoot, absolute).replaceAll("\\", "/")}`);
|
|
54488
54579
|
const bom = JSON.parse((0, import_node_fs54.readFileSync)(bomPathFor(root), "utf8"));
|
|
54489
54580
|
const digest = (entries) => {
|
|
54490
|
-
const overlay = (0,
|
|
54581
|
+
const overlay = (0, import_node_path51.join)(stage, `overlay-${overlayCount++}`);
|
|
54491
54582
|
for (const entry of entries) {
|
|
54492
|
-
const target = (0,
|
|
54493
|
-
(0, import_node_fs54.mkdirSync)((0,
|
|
54583
|
+
const target = (0, import_node_path51.join)(overlay, entry.path);
|
|
54584
|
+
(0, import_node_fs54.mkdirSync)((0, import_node_path51.dirname)(target), { recursive: true });
|
|
54494
54585
|
(0, import_node_fs54.writeFileSync)(target, entry.bytes);
|
|
54495
54586
|
}
|
|
54496
54587
|
return digestPackedFiles(overlay, entries.map((entry) => entry.path));
|
|
@@ -54502,7 +54593,7 @@ function runDistStatus(root) {
|
|
|
54502
54593
|
}
|
|
54503
54594
|
|
|
54504
54595
|
// src/doc-refs-live.ts
|
|
54505
|
-
var
|
|
54596
|
+
var import_node_path52 = require("node:path");
|
|
54506
54597
|
var concrete = (value) => !!value && !/[<>{}$]/.test(value);
|
|
54507
54598
|
var liveOption = (tokens2, flag) => {
|
|
54508
54599
|
const index = tokens2.indexOf(flag);
|
|
@@ -54575,7 +54666,7 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
54575
54666
|
const offline = runDocRefs(root, { ...deps, runbooksOnly: true });
|
|
54576
54667
|
const read = deps.readFile ?? readFileOrNull;
|
|
54577
54668
|
const docs = Object.fromEntries((deps.listDocs ?? defaultListDocs)(root).flatMap((doc) => {
|
|
54578
|
-
const body = read((0,
|
|
54669
|
+
const body = read((0, import_node_path52.join)(root, doc));
|
|
54579
54670
|
return body !== null && isRunbook(doc, body) && !deps.isIgnored?.([doc]).has(doc) ? [[doc, body]] : [];
|
|
54580
54671
|
}));
|
|
54581
54672
|
const remote = await checkRunbookLive(docs, live);
|
|
@@ -54584,9 +54675,9 @@ async function runLiveDocRefs(root, deps, live) {
|
|
|
54584
54675
|
}
|
|
54585
54676
|
|
|
54586
54677
|
// src/doc-refs-live-io.ts
|
|
54587
|
-
var
|
|
54678
|
+
var import_node_child_process21 = require("node:child_process");
|
|
54588
54679
|
var import_node_util7 = require("node:util");
|
|
54589
|
-
var exec2 = (0, import_node_util7.promisify)(
|
|
54680
|
+
var exec2 = (0, import_node_util7.promisify)(import_node_child_process21.execFile);
|
|
54590
54681
|
function createRunbookLiveDeps(cliBin) {
|
|
54591
54682
|
const json = async (args) => {
|
|
54592
54683
|
const { stdout } = await exec2(process.execPath, [cliBin, ...args, "--json"], { windowsHide: true, timeout: 3e4, maxBuffer: 4 * 1024 * 1024 });
|
|
@@ -54612,7 +54703,7 @@ function createRunbookLiveDeps(cliBin) {
|
|
|
54612
54703
|
};
|
|
54613
54704
|
const first = await probe().catch(() => 599);
|
|
54614
54705
|
if (first < 500) return first;
|
|
54615
|
-
await new Promise((
|
|
54706
|
+
await new Promise((resolve8) => setTimeout(resolve8, 2e4));
|
|
54616
54707
|
return probe();
|
|
54617
54708
|
}
|
|
54618
54709
|
};
|
|
@@ -54715,7 +54806,7 @@ init_hub_auth();
|
|
|
54715
54806
|
|
|
54716
54807
|
// src/schedules-lift-command.ts
|
|
54717
54808
|
var import_promises12 = require("node:fs/promises");
|
|
54718
|
-
var
|
|
54809
|
+
var import_node_path53 = require("node:path");
|
|
54719
54810
|
init_clean_exit();
|
|
54720
54811
|
init_cli_shared();
|
|
54721
54812
|
var DEFAULT_WORKFLOWS_DIR = ".github/workflows";
|
|
@@ -54744,7 +54835,7 @@ async function readWorkflowFiles(dir) {
|
|
|
54744
54835
|
const files = [];
|
|
54745
54836
|
for (const name of names.sort()) {
|
|
54746
54837
|
if (!/\.ya?ml$/.test(name)) continue;
|
|
54747
|
-
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0,
|
|
54838
|
+
files.push({ path: `.github/workflows/${name}`, text: await (0, import_promises12.readFile)((0, import_node_path53.join)(dir, name), "utf8") });
|
|
54748
54839
|
}
|
|
54749
54840
|
return files;
|
|
54750
54841
|
}
|
|
@@ -54826,9 +54917,9 @@ function registerSchedulesLiftCommand(program3, deps = {}) {
|
|
|
54826
54917
|
}
|
|
54827
54918
|
|
|
54828
54919
|
// src/spawn-policy-core.ts
|
|
54829
|
-
var
|
|
54920
|
+
var import_node_child_process22 = require("node:child_process");
|
|
54830
54921
|
var import_node_fs55 = require("node:fs");
|
|
54831
|
-
var
|
|
54922
|
+
var import_node_path54 = require("node:path");
|
|
54832
54923
|
var SPAWNERS = ["spawn", "spawnSync", "exec", "execSync", "execFile", "execFileSync"];
|
|
54833
54924
|
var CALL_SOURCE = String.raw`(^|[^.\w$])(${SPAWNERS.join("|")})\s*\(`;
|
|
54834
54925
|
var SOURCE_EXT = /\.(ts|mts|cts|js|mjs|cjs)$/;
|
|
@@ -54897,7 +54988,7 @@ function findViolationsInSource(raw) {
|
|
|
54897
54988
|
return found;
|
|
54898
54989
|
}
|
|
54899
54990
|
function policedFiles(root) {
|
|
54900
|
-
const r = (0,
|
|
54991
|
+
const r = (0, import_node_child_process22.spawnSync)("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
54901
54992
|
cwd: root,
|
|
54902
54993
|
encoding: "utf8",
|
|
54903
54994
|
windowsHide: true,
|
|
@@ -54914,7 +55005,7 @@ function runSpawnPolicy(root) {
|
|
|
54914
55005
|
for (const file of files) {
|
|
54915
55006
|
let raw;
|
|
54916
55007
|
try {
|
|
54917
|
-
raw = (0, import_node_fs55.readFileSync)((0,
|
|
55008
|
+
raw = (0, import_node_fs55.readFileSync)((0, import_node_path54.join)(root, file), "utf8");
|
|
54918
55009
|
} catch {
|
|
54919
55010
|
continue;
|
|
54920
55011
|
}
|
|
@@ -54934,7 +55025,7 @@ function runSpawnPolicy(root) {
|
|
|
54934
55025
|
function registerDeveloperCommands(program3) {
|
|
54935
55026
|
const rules = program3.command("rules").description("org-managed .gitignore delivery");
|
|
54936
55027
|
rules.command("gitignore").option("--write", "upsert the managed block into .gitignore (default: check only, non-zero exit on drift)").option("--json", "machine-readable output").description("verify (or --write) this repo's org-managed .gitignore block matches the SSOT").action((opts) => {
|
|
54937
|
-
const path2 = (0,
|
|
55028
|
+
const path2 = (0, import_node_path55.join)(process.cwd(), ".gitignore");
|
|
54938
55029
|
const current = (0, import_node_fs56.existsSync)(path2) ? (0, import_node_fs56.readFileSync)(path2, "utf8") : null;
|
|
54939
55030
|
const plan = planManagedGitignore(current);
|
|
54940
55031
|
const drift = [...plan.added.map((l) => `+${l}`), ...plan.removed.map((l) => `-${l}`)].join(", ") || "block normalize";
|
|
@@ -55215,7 +55306,7 @@ ${r.stderr ?? ""}`).catch(() => "");
|
|
|
55215
55306
|
var ENV_HEAL_LOCK_STALE_MS = 10 * 6e4;
|
|
55216
55307
|
var ENV_HEAL_LOCK_MAX_WAIT_MS = 2 * 6e4;
|
|
55217
55308
|
function envHealLockPath(home) {
|
|
55218
|
-
return (0,
|
|
55309
|
+
return (0, import_node_path56.join)(home, ".claude", "plugins", ".mmi-env-heal.lock");
|
|
55219
55310
|
}
|
|
55220
55311
|
async function withEnvHealLock(what, run) {
|
|
55221
55312
|
try {
|
|
@@ -55387,7 +55478,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
55387
55478
|
// has no generated routing index would
|
|
55388
55479
|
// get a permanent — demanding an artifact it never asked for.
|
|
55389
55480
|
docsIndexState: (root) => {
|
|
55390
|
-
if (!(0, import_node_fs57.existsSync)((0,
|
|
55481
|
+
if (!(0, import_node_fs57.existsSync)((0, import_node_path56.join)(root, DOCS_INDEX_PATH))) return void 0;
|
|
55391
55482
|
const real = createDocsIndexDeps(root);
|
|
55392
55483
|
let docs;
|
|
55393
55484
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -55396,7 +55487,7 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
55396
55487
|
},
|
|
55397
55488
|
// #4168: working-tree heal — write if drifted, then re-check. Commit remains the operator's step.
|
|
55398
55489
|
healDocsIndex: (root) => {
|
|
55399
|
-
if (!(0, import_node_fs57.existsSync)((0,
|
|
55490
|
+
if (!(0, import_node_fs57.existsSync)((0, import_node_path56.join)(root, DOCS_INDEX_PATH))) return { drift: false, docCount: 0 };
|
|
55400
55491
|
const real = createDocsIndexDeps(root);
|
|
55401
55492
|
let docs;
|
|
55402
55493
|
const listDocs = () => docs ??= real.listDocs();
|
|
@@ -55417,8 +55508,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
55417
55508
|
});
|
|
55418
55509
|
const raced = await Promise.race([
|
|
55419
55510
|
work.then((r) => ({ ...r, timedOut: false })),
|
|
55420
|
-
new Promise((
|
|
55421
|
-
ceiling = setTimeout(() =>
|
|
55511
|
+
new Promise((resolve8) => {
|
|
55512
|
+
ceiling = setTimeout(() => resolve8({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
|
|
55422
55513
|
})
|
|
55423
55514
|
]);
|
|
55424
55515
|
if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
|
|
@@ -55451,8 +55542,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
55451
55542
|
incomplete: nb.incomplete,
|
|
55452
55543
|
timedOut: false
|
|
55453
55544
|
})),
|
|
55454
|
-
new Promise((
|
|
55455
|
-
ceiling = setTimeout(() =>
|
|
55545
|
+
new Promise((resolve8) => {
|
|
55546
|
+
ceiling = setTimeout(() => resolve8({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
|
|
55456
55547
|
})
|
|
55457
55548
|
]);
|
|
55458
55549
|
if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
|
|
@@ -56504,16 +56595,16 @@ function ciAuditDeps2() {
|
|
|
56504
56595
|
// gate re-seed step is skipped gracefully rather than failing mid-run.
|
|
56505
56596
|
readSeedFile: (path2) => {
|
|
56506
56597
|
if (!root) return null;
|
|
56507
|
-
const fullPath = (0,
|
|
56598
|
+
const fullPath = (0, import_node_path56.join)(root, path2);
|
|
56508
56599
|
return (0, import_node_fs57.existsSync)(fullPath) ? (0, import_node_fs57.readFileSync)(fullPath, "utf8") : null;
|
|
56509
56600
|
}
|
|
56510
56601
|
};
|
|
56511
56602
|
}
|
|
56512
56603
|
function hubRoot2() {
|
|
56513
|
-
const fromPkg = (0,
|
|
56604
|
+
const fromPkg = (0, import_node_path56.join)(__dirname, "..", "..");
|
|
56514
56605
|
const marker = "skills/bootstrap/seeds/manifest.json";
|
|
56515
|
-
if ((0, import_node_fs57.existsSync)((0,
|
|
56516
|
-
if ((0, import_node_fs57.existsSync)((0,
|
|
56606
|
+
if ((0, import_node_fs57.existsSync)((0, import_node_path56.join)(fromPkg, marker))) return fromPkg;
|
|
56607
|
+
if ((0, import_node_fs57.existsSync)((0, import_node_path56.join)(process.cwd(), marker))) return process.cwd();
|
|
56517
56608
|
return null;
|
|
56518
56609
|
}
|
|
56519
56610
|
registerQueryCommands(program2);
|
|
@@ -56682,7 +56773,7 @@ function directoryBytes(path2) {
|
|
|
56682
56773
|
return 0;
|
|
56683
56774
|
}
|
|
56684
56775
|
for (const entry of entries) {
|
|
56685
|
-
const child2 = (0,
|
|
56776
|
+
const child2 = (0, import_node_path56.join)(path2, entry.name);
|
|
56686
56777
|
if (entry.isDirectory()) total += directoryBytes(child2);
|
|
56687
56778
|
else {
|
|
56688
56779
|
try {
|
|
@@ -56712,7 +56803,7 @@ function pluginCacheFsDeps(configRoot, dirBytes) {
|
|
|
56712
56803
|
dirBytes,
|
|
56713
56804
|
listStagingDirs: (root) => (0, import_node_fs57.readdirSync)(root, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => {
|
|
56714
56805
|
try {
|
|
56715
|
-
return { name: d.name, mtimeMs: newestMtimeMs((0,
|
|
56806
|
+
return { name: d.name, mtimeMs: newestMtimeMs((0, import_node_path56.join)(root, d.name), listDirEntries, (p) => (0, import_node_fs57.statSync)(p).mtimeMs) };
|
|
56716
56807
|
} catch {
|
|
56717
56808
|
return { name: d.name, mtimeMs: Date.now() };
|
|
56718
56809
|
}
|
|
@@ -56726,7 +56817,7 @@ function stagingApplyFsGuard(configRoot) {
|
|
|
56726
56817
|
return {
|
|
56727
56818
|
referencedPaths: () => readInstalledPluginRefs(configRoot),
|
|
56728
56819
|
mtimeMs: (name) => {
|
|
56729
|
-
const p = (0,
|
|
56820
|
+
const p = (0, import_node_path56.join)(stagingRoot, name);
|
|
56730
56821
|
if (!(0, import_node_fs57.existsSync)(p)) return null;
|
|
56731
56822
|
try {
|
|
56732
56823
|
return newestMtimeMs(p, listDirEntries, (q) => (0, import_node_fs57.statSync)(q).mtimeMs);
|
package/package.json
CHANGED