@mutmutco/cli 4.5.160 → 4.5.162
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 +226 -100
- package/dist/runbook-sweep.cjs +30 -24
- 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((resolve7) => {
|
|
80
80
|
try {
|
|
81
|
-
stream.write("", () =>
|
|
81
|
+
stream.write("", () => resolve7());
|
|
82
82
|
} catch {
|
|
83
|
-
|
|
83
|
+
resolve7();
|
|
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((resolve7) => {
|
|
91
|
+
setTimeout(resolve7, 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((resolve7) => setImmediate(resolve7));
|
|
100
100
|
return void 0;
|
|
101
101
|
}
|
|
102
102
|
async function finishCliRun(watchdogMs = CLI_EXIT_WATCHDOG_MS) {
|
|
@@ -590,6 +590,11 @@ var init_github_client = __esm({
|
|
|
590
590
|
});
|
|
591
591
|
|
|
592
592
|
// src/gh-read-route.ts
|
|
593
|
+
function isAppFilingWrite(args) {
|
|
594
|
+
if (args.some((a) => VIEWER.test(a))) return false;
|
|
595
|
+
const [group, verb] = args;
|
|
596
|
+
return (group === "issue" || group === "label") && verb === "create";
|
|
597
|
+
}
|
|
593
598
|
function isViewerFreeGhRead(args) {
|
|
594
599
|
if (args.some((a) => VIEWER.test(a))) return false;
|
|
595
600
|
const [group, verb] = args;
|
|
@@ -692,7 +697,7 @@ function rateLimitedReceipt(opts) {
|
|
|
692
697
|
};
|
|
693
698
|
}
|
|
694
699
|
async function runWithRateLimitBackoff(operation, opts) {
|
|
695
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
700
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
696
701
|
const now = opts.now ?? Date.now;
|
|
697
702
|
const log = opts.log ?? ((message2) => console.warn(message2));
|
|
698
703
|
const capMs = opts.capMs ?? RATE_LIMIT_WAIT_CAP_MS;
|
|
@@ -782,7 +787,7 @@ async function fetchWithRetry(fetchImpl, url, init, opts = {}) {
|
|
|
782
787
|
const attempts = opts.attempts ?? 3;
|
|
783
788
|
const baseDelayMs = opts.baseDelayMs ?? 250;
|
|
784
789
|
const retryOn = opts.retryOn ?? ((res) => res.status >= 500);
|
|
785
|
-
const sleep2 = opts.sleep ?? ((ms) => new Promise((
|
|
790
|
+
const sleep2 = opts.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
786
791
|
let lastErr;
|
|
787
792
|
for (let i = 0; i < attempts; i++) {
|
|
788
793
|
const isLast = i === attempts - 1;
|
|
@@ -1109,8 +1114,8 @@ async function readStdin(opts = {}) {
|
|
|
1109
1114
|
})().catch(() => {
|
|
1110
1115
|
});
|
|
1111
1116
|
let timer;
|
|
1112
|
-
const timeout = new Promise((
|
|
1113
|
-
timer = setTimeout(
|
|
1117
|
+
const timeout = new Promise((resolve7) => {
|
|
1118
|
+
timer = setTimeout(resolve7, timeoutMs);
|
|
1114
1119
|
});
|
|
1115
1120
|
try {
|
|
1116
1121
|
await Promise.race([drain, timeout]);
|
|
@@ -1161,11 +1166,21 @@ __export(cli_shared_exports, {
|
|
|
1161
1166
|
readStdin: () => readStdin,
|
|
1162
1167
|
repoProject: () => repoProject,
|
|
1163
1168
|
setGhReadTokenProvider: () => setGhReadTokenProvider,
|
|
1169
|
+
setGhWriteTokenProvider: () => setGhWriteTokenProvider,
|
|
1164
1170
|
setInjectedStdin: () => setInjectedStdin
|
|
1165
1171
|
});
|
|
1166
1172
|
function setGhReadTokenProvider(provider) {
|
|
1167
1173
|
ghReadTokenProvider = provider;
|
|
1168
1174
|
}
|
|
1175
|
+
function setGhWriteTokenProvider(provider) {
|
|
1176
|
+
ghWriteTokenProvider = provider;
|
|
1177
|
+
}
|
|
1178
|
+
async function ghFilingAppEnv(file, args) {
|
|
1179
|
+
if (!ghWriteTokenProvider || !/(^|[\\/])gh(\.exe)?$/i.test(file) || process.env.MMI_ACTOR?.trim() === "personal") return void 0;
|
|
1180
|
+
if (!isAppFilingWrite(args)) return void 0;
|
|
1181
|
+
const token = await ghWriteTokenProvider().catch(() => void 0);
|
|
1182
|
+
return token ? { ...process.env, GH_TOKEN: token } : void 0;
|
|
1183
|
+
}
|
|
1169
1184
|
async function ghReadAppEnv(file, args) {
|
|
1170
1185
|
if (!ghReadTokenProvider || !/(^|[\\/])gh(\.exe)?$/i.test(file) || process.env.MMI_ACTOR?.trim() === "personal") return void 0;
|
|
1171
1186
|
if (!isViewerFreeGhRead(args)) return void 0;
|
|
@@ -1187,12 +1202,12 @@ function killProcessTree(pid) {
|
|
|
1187
1202
|
function execFileHard(file, args, options) {
|
|
1188
1203
|
const { timeout, step, ...rest } = options;
|
|
1189
1204
|
const started = Date.now();
|
|
1190
|
-
return new Promise((
|
|
1205
|
+
return new Promise((resolve7, reject) => {
|
|
1191
1206
|
const child2 = (0, import_node_child_process2.execFile)(file, args, { encoding: "utf8", windowsHide: true, ...rest, timeout: 0 }, (error, stdout, stderr) => {
|
|
1192
1207
|
clearTimeout(timer);
|
|
1193
1208
|
if (expired) return;
|
|
1194
1209
|
if (error) reject(error);
|
|
1195
|
-
else
|
|
1210
|
+
else resolve7({ stdout: String(stdout), stderr: String(stderr) });
|
|
1196
1211
|
});
|
|
1197
1212
|
let expired = false;
|
|
1198
1213
|
const timer = setTimeout(() => {
|
|
@@ -1340,7 +1355,7 @@ function printLine(value) {
|
|
|
1340
1355
|
(0, import_node_fs5.writeSync)(1, `${value}
|
|
1341
1356
|
`);
|
|
1342
1357
|
}
|
|
1343
|
-
var import_node_child_process2, import_node_fs5, import_node_util3, rawExecFileP, DEFAULT_EXEC_TIMEOUT_MS, execFileP, ghReadTokenProvider, GIT_TIMEOUT_MS, ExecDeadlineError, cachedGithubLogin, gitOut, consoleIo;
|
|
1358
|
+
var import_node_child_process2, import_node_fs5, import_node_util3, rawExecFileP, DEFAULT_EXEC_TIMEOUT_MS, execFileP, ghReadTokenProvider, ghWriteTokenProvider, GIT_TIMEOUT_MS, ExecDeadlineError, cachedGithubLogin, gitOut, consoleIo;
|
|
1344
1359
|
var init_cli_shared = __esm({
|
|
1345
1360
|
"src/cli-shared.ts"() {
|
|
1346
1361
|
"use strict";
|
|
@@ -1364,7 +1379,7 @@ var init_cli_shared = __esm({
|
|
|
1364
1379
|
// promisify(execFile)'s overloads widen to string|Buffer when options is spread in.
|
|
1365
1380
|
rawExecFileP(file, args, { encoding: "utf8", windowsHide: true, timeout: DEFAULT_EXEC_TIMEOUT_MS, killSignal: "SIGTERM", ...options, ...env ? { env } : {} })
|
|
1366
1381
|
);
|
|
1367
|
-
const appEnv = options.env ? void 0 : await ghReadAppEnv(file, args);
|
|
1382
|
+
const appEnv = options.env ? void 0 : await ghReadAppEnv(file, args) ?? await ghFilingAppEnv(file, args);
|
|
1368
1383
|
if (!appEnv) return run();
|
|
1369
1384
|
try {
|
|
1370
1385
|
return await run(appEnv);
|
|
@@ -6664,7 +6679,6 @@ var import_node_fs14 = require("node:fs");
|
|
|
6664
6679
|
var import_node_path11 = require("node:path");
|
|
6665
6680
|
var PLAN_ADVISORY_AGE_MS = 30 * 24 * 36e5;
|
|
6666
6681
|
var ROOT_SCRATCH_STALE_MS = 24 * 36e5;
|
|
6667
|
-
var SCRATCH_GC_THROTTLE_MS = 24 * 36e5;
|
|
6668
6682
|
function executeScratchGc(repoRoot2, opts, now = Date.now()) {
|
|
6669
6683
|
const snap = collectScratchSnapshot(repoRoot2);
|
|
6670
6684
|
const plan = planScratchGc(snap, now);
|
|
@@ -7239,7 +7253,9 @@ async function mintInstallationToken(deps) {
|
|
|
7239
7253
|
return mintScopedInstallationToken(deps, APP_POLL_PERMISSIONS);
|
|
7240
7254
|
}
|
|
7241
7255
|
var APP_WRITE_COMMANDS = /* @__PURE__ */ new Map([
|
|
7242
|
-
["issue comment", { metadata: "read", issues: "write", pull_requests: "write" }]
|
|
7256
|
+
["issue comment", { metadata: "read", issues: "write", pull_requests: "write" }],
|
|
7257
|
+
// #8401: the detached related-issue lookup (issue list/view reads + one marker comment) — no identity contract.
|
|
7258
|
+
["issue discover-related", { metadata: "read", issues: "write" }]
|
|
7243
7259
|
]);
|
|
7244
7260
|
async function mintCommandWriteToken(deps, commandPath3) {
|
|
7245
7261
|
const permissions = APP_WRITE_COMMANDS.get(commandPath3);
|
|
@@ -7251,6 +7267,14 @@ async function mintCommandWriteToken(deps, commandPath3) {
|
|
|
7251
7267
|
throw new AppActorError(`app actor: write token mint failed (${e?.constructor?.name ?? "Error"})`);
|
|
7252
7268
|
}
|
|
7253
7269
|
}
|
|
7270
|
+
async function mintFilingWriteToken(deps) {
|
|
7271
|
+
try {
|
|
7272
|
+
return await mintFromMember(deps, { metadata: "read", issues: "write" }, APP_VAULT_KEYS);
|
|
7273
|
+
} catch (e) {
|
|
7274
|
+
if (e instanceof AppActorError) throw e;
|
|
7275
|
+
throw new AppActorError(`app actor: filing token mint failed (${e?.constructor?.name ?? "Error"})`);
|
|
7276
|
+
}
|
|
7277
|
+
}
|
|
7254
7278
|
async function mintStatusWriteToken(deps) {
|
|
7255
7279
|
return mintScopedInstallationToken(deps, { metadata: "read", statuses: "write" });
|
|
7256
7280
|
}
|
|
@@ -8201,7 +8225,7 @@ async function readRestPrSnapshotWithRetry(prNumber, repo, gh2 = defaultGhApi, o
|
|
|
8201
8225
|
const retries = options?.retries ?? PR_SNAPSHOT_READ_RETRIES;
|
|
8202
8226
|
const untilMs = options?.retryTransientUntilMs;
|
|
8203
8227
|
const delayMs = options?.delayMs ?? (untilMs !== void 0 ? PR_CHECKS_POLL_MS : PR_SNAPSHOT_READ_DELAY_MS);
|
|
8204
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
8228
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
8205
8229
|
const now = options?.now ?? (() => Date.now());
|
|
8206
8230
|
let lastError = "no attempt completed";
|
|
8207
8231
|
let attempt = 0;
|
|
@@ -10181,15 +10205,15 @@ async function postJson(pathSuffix, payload, deps, method = "POST", opts = {}) {
|
|
|
10181
10205
|
return { ok: false, status: 0, body: null, error: e.message, unreachable: "transport" };
|
|
10182
10206
|
}
|
|
10183
10207
|
}
|
|
10184
|
-
async function edgeDnsForStage(slug, stage, deps, ensure = false) {
|
|
10208
|
+
async function edgeDnsForStage(slug, stage, deps, ensure = false, deletion) {
|
|
10185
10209
|
if (!deps.baseUrl) throw new Error("Hub API URL unavailable");
|
|
10186
10210
|
const token = await deps.token();
|
|
10187
10211
|
if (!token) throw new Error("Hub session token unavailable");
|
|
10188
10212
|
const path2 = `/projects/${encodeURIComponent(slug)}/edge-dns/${stage}`;
|
|
10189
10213
|
let body;
|
|
10190
|
-
if (ensure) {
|
|
10191
|
-
const write = await postJson(path2, {}, deps, "POST", { noRetry: true });
|
|
10192
|
-
if (!write.ok) throw new Error(`Hub edge dns ensure failed: ${write.error ?? `HTTP ${write.status}`}`);
|
|
10214
|
+
if (ensure || deletion) {
|
|
10215
|
+
const write = await postJson(path2, deletion ?? {}, deps, deletion ? "DELETE" : "POST", { noRetry: true });
|
|
10216
|
+
if (!write.ok) throw new Error(`Hub edge dns ${deletion ? "delete" : "ensure"} failed: ${write.error ?? `HTTP ${write.status}`}`);
|
|
10193
10217
|
body = write.body;
|
|
10194
10218
|
} else {
|
|
10195
10219
|
const res = await retriedFetch(
|
|
@@ -10203,6 +10227,20 @@ async function edgeDnsForStage(slug, stage, deps, ensure = false) {
|
|
|
10203
10227
|
if (typeof body?.required !== "boolean") throw new Error("Hub edge dns response invalid");
|
|
10204
10228
|
return body;
|
|
10205
10229
|
}
|
|
10230
|
+
async function boxPreviewsForHost(box, opts, deps) {
|
|
10231
|
+
const payload = {
|
|
10232
|
+
box,
|
|
10233
|
+
...opts.portStart !== void 0 ? { portStart: opts.portStart } : {},
|
|
10234
|
+
...opts.cap !== void 0 ? { cap: opts.cap } : {},
|
|
10235
|
+
apply: opts.apply === true
|
|
10236
|
+
};
|
|
10237
|
+
const write = await postJson("/projects/box-previews", payload, deps, "POST", { noRetry: opts.apply === true });
|
|
10238
|
+
if (!write.ok) {
|
|
10239
|
+
const detail = write.body?.error;
|
|
10240
|
+
throw new Error(`Hub box previews failed: ${detail ?? write.error ?? `HTTP ${write.status}`}`);
|
|
10241
|
+
}
|
|
10242
|
+
return write.body;
|
|
10243
|
+
}
|
|
10206
10244
|
async function edgeCertificateForStage(slug, stage, deps, ensure = false) {
|
|
10207
10245
|
if (!deps.baseUrl) throw new Error("Hub API URL unavailable");
|
|
10208
10246
|
const token = await deps.token();
|
|
@@ -10312,7 +10350,7 @@ init_github_client();
|
|
|
10312
10350
|
// src/project-model.ts
|
|
10313
10351
|
var PROJECT_TYPES = ["web-app", "hub-service", "content", "desktop-app", "desktop-game", "mobile-app", "non-deployable", "cli-tool", "worker"];
|
|
10314
10352
|
var DEPLOY_MODELS = ["hub-serverless", "serverless", "tenant-container", "solo-container", "static-cdn", "registry-publish", "content", "none", "vercel"];
|
|
10315
|
-
var RELEASE_TRACKS = ["full", "direct", "trunk"];
|
|
10353
|
+
var RELEASE_TRACKS = ["full", "direct", "trunk", "single"];
|
|
10316
10354
|
var REQUIRED_CHECK_BRANCHES = ["development", "rc", "main"];
|
|
10317
10355
|
var PROJECT_TYPE_SET = new Set(PROJECT_TYPES);
|
|
10318
10356
|
var DEPLOY_MODEL_SET = new Set(DEPLOY_MODELS);
|
|
@@ -10395,6 +10433,7 @@ function metaWithDeclaredClass(meta, declaredClass) {
|
|
|
10395
10433
|
}
|
|
10396
10434
|
function branchesForTrack(track) {
|
|
10397
10435
|
if (track === "trunk") return ["main"];
|
|
10436
|
+
if (track === "single") return ["development"];
|
|
10398
10437
|
if (track === "direct") return ["development", "main"];
|
|
10399
10438
|
return ["development", "rc", "main"];
|
|
10400
10439
|
}
|
|
@@ -10406,7 +10445,7 @@ function resolveRequiredCheckBranches(meta, repo) {
|
|
|
10406
10445
|
return branchesForTrack(resolveReleaseTrack(meta, void 0, repo));
|
|
10407
10446
|
}
|
|
10408
10447
|
function promotionBranchesForTrack(track) {
|
|
10409
|
-
if (track === "trunk") return [];
|
|
10448
|
+
if (track === "trunk" || track === "single") return [];
|
|
10410
10449
|
if (track === "direct") return ["main"];
|
|
10411
10450
|
return ["rc", "main"];
|
|
10412
10451
|
}
|
|
@@ -12062,7 +12101,7 @@ async function createPrViaRestFallback(args, swappedArgs, deps, knownPools) {
|
|
|
12062
12101
|
}
|
|
12063
12102
|
async function ghCreate(args, deps = {}) {
|
|
12064
12103
|
const exec3 = deps.exec ?? execFileP;
|
|
12065
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
12104
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
12066
12105
|
const now = deps.now ?? Date.now;
|
|
12067
12106
|
const read = deps.readFile ?? import_promises.readFile;
|
|
12068
12107
|
const restCreatePr = deps.restCreatePr ?? ((input) => defaultRestCreatePr(exec3, input));
|
|
@@ -12890,8 +12929,8 @@ var PRE_SPAWN_DRAIN_TIMEOUT_MS = 2e3;
|
|
|
12890
12929
|
async function drainHttpPoolBeforeSpawn() {
|
|
12891
12930
|
const drained = await Promise.race([
|
|
12892
12931
|
closeHttpPool().then(() => true),
|
|
12893
|
-
new Promise((
|
|
12894
|
-
setTimeout(() =>
|
|
12932
|
+
new Promise((resolve7) => {
|
|
12933
|
+
setTimeout(() => resolve7(false), PRE_SPAWN_DRAIN_TIMEOUT_MS).unref?.();
|
|
12895
12934
|
})
|
|
12896
12935
|
]);
|
|
12897
12936
|
if (!drained) destroyHttpPool();
|
|
@@ -15579,7 +15618,7 @@ async function setBoardItemPriority(client, cfg, itemId, priority) {
|
|
|
15579
15618
|
await updateItemSingleSelect(client, cfg.projectId, itemId, cfg.priorityFieldId, optionId);
|
|
15580
15619
|
return cliPriorityToFieldName(priority);
|
|
15581
15620
|
}
|
|
15582
|
-
var defaultRetrySleep = (ms) => new Promise((
|
|
15621
|
+
var defaultRetrySleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
15583
15622
|
async function resolveProjectItemIdWithRetry(client, cfg, selector, opts = {}) {
|
|
15584
15623
|
const attempts = Math.max(1, opts.attempts ?? 5);
|
|
15585
15624
|
const delayMs = opts.delayMs ?? 300;
|
|
@@ -16499,7 +16538,7 @@ function checkRunbookCoverage(docs, base, git3) {
|
|
|
16499
16538
|
const globs = lines2.slice(heading.start, markerEnd - 1).flatMap((text) => /<!--\s*runbook-covers:\s*(.*?)\s*-->/.exec(text)?.[1].split(",").map((s) => s.trim()) ?? []);
|
|
16500
16539
|
const affected = changed.filter((path2) => globs.some((glob) => matches(glob, path2)));
|
|
16501
16540
|
if (affected.length && !ranges.some((r) => r.count > 0 && r.start < end && r.start + r.count > heading.start)) {
|
|
16502
|
-
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title}: changed ${affected.join(", ")}; update this section or declare Runbook-Unaffected: <reason
|
|
16541
|
+
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title}: changed ${affected.join(", ")}; update this section or declare Runbook-Unaffected: <reason>`, section: { heading: heading.title, level: heading.level, startLine: heading.start, endLine: end - 1 }, sources: affected });
|
|
16503
16542
|
}
|
|
16504
16543
|
}
|
|
16505
16544
|
}
|
|
@@ -17182,7 +17221,7 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
|
|
|
17182
17221
|
GATE_EXTRA_STEPS_YAML: ""
|
|
17183
17222
|
};
|
|
17184
17223
|
const track = releaseTrack ?? (cls === "content" ? "trunk" : "full");
|
|
17185
|
-
const trackBranches = track === "trunk" ? ["main"] : track === "direct" ? ["development", "main"] : ["development", "rc", "main"];
|
|
17224
|
+
const trackBranches = track === "trunk" ? ["main"] : track === "single" ? ["development"] : track === "direct" ? ["development", "main"] : ["development", "rc", "main"];
|
|
17186
17225
|
const rulesetBranches = requiredCheckBranches?.length ? [...requiredCheckBranches] : trackBranches;
|
|
17187
17226
|
const rulesetRefs = JSON.stringify(rulesetBranches.map((branch) => `refs/heads/${branch}`));
|
|
17188
17227
|
const rulesetContexts = JSON.stringify((requiredChecks?.length ? requiredChecks : ["gate"]).map((context) => ({ context })));
|
|
@@ -17195,6 +17234,15 @@ function gateSeedVars(cls, releaseTrack, runtime = "node", requiredCheckBranches
|
|
|
17195
17234
|
GATE_RULESET_CONTEXTS_JSON: rulesetContexts
|
|
17196
17235
|
};
|
|
17197
17236
|
}
|
|
17237
|
+
if (track === "single") {
|
|
17238
|
+
return {
|
|
17239
|
+
...runtimeVars,
|
|
17240
|
+
GATE_PUSH_BRANCHES_YAML: "[development]",
|
|
17241
|
+
GATE_FULL_RUN_BRANCH: "development",
|
|
17242
|
+
GATE_RULESET_BRANCH_REFS_JSON: rulesetRefs,
|
|
17243
|
+
GATE_RULESET_CONTEXTS_JSON: rulesetContexts
|
|
17244
|
+
};
|
|
17245
|
+
}
|
|
17198
17246
|
if (track === "direct") {
|
|
17199
17247
|
return {
|
|
17200
17248
|
...runtimeVars,
|
|
@@ -17519,7 +17567,7 @@ function buildRegisterPayload(repo, cls, vars, options = {}) {
|
|
|
17519
17567
|
throw new Error(`deployModel must be one of: ${DEPLOY_MODELS.join(", ")}`);
|
|
17520
17568
|
}
|
|
17521
17569
|
if (options.releaseTrack && !isReleaseTrack(options.releaseTrack)) {
|
|
17522
|
-
throw new Error("releaseTrack must be full, direct, or
|
|
17570
|
+
throw new Error("releaseTrack must be full, direct, trunk, or single");
|
|
17523
17571
|
}
|
|
17524
17572
|
const shape = {
|
|
17525
17573
|
class: cls,
|
|
@@ -18647,7 +18695,7 @@ function classifyRow(row) {
|
|
|
18647
18695
|
} else if (row.onboarding.status === "unknown") {
|
|
18648
18696
|
reasons.add(`canonical onboarding link evidence unknown: ${row.onboarding.detail}`);
|
|
18649
18697
|
}
|
|
18650
|
-
const releaseRequired = row.track !== "trunk" && !row.classifications.includes("no-deploy-surface");
|
|
18698
|
+
const releaseRequired = row.track !== "trunk" && row.track !== "single" && !row.classifications.includes("no-deploy-surface");
|
|
18651
18699
|
const tagIsVersion = Boolean(row.release.version) && row.unknowns.some((u) => u.startsWith(NO_VERSION_LOCK_PREFIX));
|
|
18652
18700
|
for (const unknown of row.unknowns) {
|
|
18653
18701
|
if (!releaseRequired && unknown === NO_RELEASE_UNKNOWN) continue;
|
|
@@ -18909,10 +18957,10 @@ var rollout_plan_default = {
|
|
|
18909
18957
|
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)."
|
|
18910
18958
|
},
|
|
18911
18959
|
baseline: {
|
|
18912
|
-
version: "4.5.
|
|
18913
|
-
tag: "v4.5.
|
|
18914
|
-
commit: "
|
|
18915
|
-
npm: "@mutmutco/cli@4.5.
|
|
18960
|
+
version: "4.5.162",
|
|
18961
|
+
tag: "v4.5.162",
|
|
18962
|
+
commit: "5d12949c1714",
|
|
18963
|
+
npm: "@mutmutco/cli@4.5.162"
|
|
18916
18964
|
},
|
|
18917
18965
|
exitCriterion: "fleet-n-of-n",
|
|
18918
18966
|
hubOnlyShortcut: "forbidden",
|
|
@@ -18929,14 +18977,14 @@ var rollout_plan_default = {
|
|
|
18929
18977
|
repo: "mutmutco/mmi-hub",
|
|
18930
18978
|
role: "canary",
|
|
18931
18979
|
schedule: "train",
|
|
18932
|
-
v3Target: "v4.5.
|
|
18980
|
+
v3Target: "v4.5.162"
|
|
18933
18981
|
}
|
|
18934
18982
|
],
|
|
18935
18983
|
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.",
|
|
18936
18984
|
rollback: {
|
|
18937
18985
|
independent: true,
|
|
18938
|
-
mechanism: "npm dist-tag latest -> 4.5.
|
|
18939
|
-
v3Target: "v4.5.
|
|
18986
|
+
mechanism: "npm dist-tag latest -> 4.5.162 and redeploy the Hub Lambda from tag v4.5.162 (5d12949c1714); installed clients repair via `mmi-cli doctor`. No other cohort is touched.",
|
|
18987
|
+
v3Target: "v4.5.162 (@mutmutco/cli@4.5.162, tag commit 5d12949c1714 \u2014 last known-good release carrying the repo-index v4-only contract)"
|
|
18940
18988
|
}
|
|
18941
18989
|
},
|
|
18942
18990
|
{
|
|
@@ -20227,7 +20275,7 @@ function buildFleetTrackInventory(projects, releaseProbes, pluginProbes, generat
|
|
|
20227
20275
|
unknowns
|
|
20228
20276
|
};
|
|
20229
20277
|
});
|
|
20230
|
-
const tracks = { full: 0, direct: 0, trunk: 0 };
|
|
20278
|
+
const tracks = { full: 0, direct: 0, trunk: 0, single: 0 };
|
|
20231
20279
|
for (const row of repos) tracks[row.track] += 1;
|
|
20232
20280
|
return {
|
|
20233
20281
|
generatedAt,
|
|
@@ -20420,13 +20468,13 @@ var FLEET_LOCKSTEP_REF_INCLUDE = ["refs/heads/rc", "refs/heads/main"];
|
|
|
20420
20468
|
var STATUS_DESCRIPTION_MAX = 140;
|
|
20421
20469
|
function classifyFleetEnrollment(rows) {
|
|
20422
20470
|
return rows.map((row) => {
|
|
20423
|
-
if (row.track === "trunk") {
|
|
20471
|
+
if (row.track === "trunk" || row.track === "single") {
|
|
20424
20472
|
return {
|
|
20425
20473
|
repo: row.repo,
|
|
20426
20474
|
track: row.track,
|
|
20427
20475
|
deployModel: row.deployModel,
|
|
20428
20476
|
enrolled: false,
|
|
20429
|
-
reason: "excluded: trunk track has no release train \u2014 no rc/main surface exists to gate (declared shape, not an exemption from conformance: the fleet verdict still counts this repo)"
|
|
20477
|
+
reason: "excluded: trunk/single track has no release train \u2014 no rc/main surface exists to gate (declared shape, not an exemption from conformance: the fleet verdict still counts this repo)"
|
|
20430
20478
|
};
|
|
20431
20479
|
}
|
|
20432
20480
|
const noDeploy = row.classifications.includes("no-deploy-surface");
|
|
@@ -21023,7 +21071,7 @@ async function runMergeTreePreflight(deps, ours, theirs) {
|
|
|
21023
21071
|
async function predictMergeConflicts(deps, ours, theirs) {
|
|
21024
21072
|
return runMergeTreePreflight(deps, ours, theirs);
|
|
21025
21073
|
}
|
|
21026
|
-
async function mergeWithToleratedResolution(deps, sourceRef, label,
|
|
21074
|
+
async function mergeWithToleratedResolution(deps, sourceRef, label, resolve7, extraTolerated = []) {
|
|
21027
21075
|
try {
|
|
21028
21076
|
await deps.run("git", ["merge", sourceRef, "--no-edit"]);
|
|
21029
21077
|
return;
|
|
@@ -21037,7 +21085,7 @@ async function mergeWithToleratedResolution(deps, sourceRef, label, resolve8, ex
|
|
|
21037
21085
|
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)`
|
|
21038
21086
|
);
|
|
21039
21087
|
}
|
|
21040
|
-
await deps.run("git", ["checkout", `--${
|
|
21088
|
+
await deps.run("git", ["checkout", `--${resolve7}`, "--", ...unmerged]);
|
|
21041
21089
|
await deps.run("git", ["add", "--", ...unmerged]);
|
|
21042
21090
|
await deps.run("git", ["commit", "--no-edit"]);
|
|
21043
21091
|
}
|
|
@@ -21231,7 +21279,7 @@ var CORRELATE_SKEW_SLACK_MS = 1e4;
|
|
|
21231
21279
|
var CORRELATE_PAGE_LIMIT = 50;
|
|
21232
21280
|
var RUN_CONFIRM_ATTEMPTS = 3;
|
|
21233
21281
|
var RUN_CONFIRM_DELAY_MS = 1e3;
|
|
21234
|
-
var defaultSleep = (ms) => new Promise((
|
|
21282
|
+
var defaultSleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
21235
21283
|
function resolveSleep(deps) {
|
|
21236
21284
|
return deps.sleep ?? defaultSleep;
|
|
21237
21285
|
}
|
|
@@ -21535,7 +21583,7 @@ async function enqueueAlignmentAutoMerge(deps, ctx, prNumber, prUrl, openedNote)
|
|
|
21535
21583
|
}
|
|
21536
21584
|
return { ...base, note: manual };
|
|
21537
21585
|
}
|
|
21538
|
-
var ALIGNMENT_LAND_POLL_MS =
|
|
21586
|
+
var ALIGNMENT_LAND_POLL_MS = 6e4;
|
|
21539
21587
|
var ALIGNMENT_LAND_TIMEOUT_MS = 20 * 6e4;
|
|
21540
21588
|
async function awaitAlignmentLanded(deps, ctx, leg, base, opts = {}) {
|
|
21541
21589
|
if (leg.status !== "pr-pending" || !leg.autoMergeEnqueued || leg.prNumber === void 0) return leg;
|
|
@@ -23152,7 +23200,7 @@ async function runTenantReconcile(deps, options) {
|
|
|
23152
23200
|
};
|
|
23153
23201
|
}
|
|
23154
23202
|
function tenantControlWatches(action) {
|
|
23155
|
-
return action === "status" || action === "retire" || action === "verify-secrets" || action === "verify-broker" || action === "logs" || action === "run-task";
|
|
23203
|
+
return action === "status" || action === "retire" || action === "undo" || action === "verify-secrets" || action === "verify-broker" || action === "logs" || action === "run-task";
|
|
23156
23204
|
}
|
|
23157
23205
|
async function runTenantControl(deps, options) {
|
|
23158
23206
|
const { repo, stage, action } = options;
|
|
@@ -23799,8 +23847,6 @@ var import_node_path23 = require("node:path");
|
|
|
23799
23847
|
// src/jerv-cli-spawn.ts
|
|
23800
23848
|
var import_node_os13 = require("node:os");
|
|
23801
23849
|
var import_node_path22 = require("node:path");
|
|
23802
|
-
init_cli_shared();
|
|
23803
|
-
var JERV_CLI_ENTRY = (0, import_node_path22.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
23804
23850
|
function pathEnvEntries(pathEnv, platform2 = process.platform) {
|
|
23805
23851
|
if (platform2 !== "win32") {
|
|
23806
23852
|
return pathEnv.split(import_node_path22.delimiter).map((e) => e.trim()).filter(Boolean);
|
|
@@ -24434,7 +24480,7 @@ function lockTokenPid(token) {
|
|
|
24434
24480
|
const m = /^(\d+)-/.exec(token);
|
|
24435
24481
|
return m ? Number(m[1]) : void 0;
|
|
24436
24482
|
}
|
|
24437
|
-
var sleep = (ms) => new Promise((
|
|
24483
|
+
var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
24438
24484
|
var IMMEDIATE_RETRY_BUDGET = 3;
|
|
24439
24485
|
var FileLockBusyError = class extends Error {
|
|
24440
24486
|
lockPath;
|
|
@@ -25310,7 +25356,7 @@ async function defaultWatchRun(deps, runId, repo) {
|
|
|
25310
25356
|
}
|
|
25311
25357
|
let confirmError;
|
|
25312
25358
|
for (let attempt = 0; attempt < RUN_CONFIRM_ATTEMPTS2; attempt++) {
|
|
25313
|
-
if (attempt > 0) await new Promise((
|
|
25359
|
+
if (attempt > 0) await new Promise((resolve7) => setTimeout(resolve7, RUN_CONFIRM_DELAY_MS2));
|
|
25314
25360
|
try {
|
|
25315
25361
|
const parsed = JSON.parse(await deps.run("gh", ["run", "view", String(runId), "--repo", repo, "--json", "status,conclusion"]));
|
|
25316
25362
|
if (parsed.status === "completed") return parsed.conclusion === "success" ? "success" : "failure";
|
|
@@ -26352,11 +26398,11 @@ var defaultDeps = {
|
|
|
26352
26398
|
// #5813: the caller supplies that command line. `process.argv` is NOT it — the house shim splices
|
|
26353
26399
|
// the house root out in place, so re-reading it here re-ran the removed flat alias (`release …`
|
|
26354
26400
|
// instead of `devops release …`) and the converged child refused with exit 2.
|
|
26355
|
-
reexec: (argv) => new Promise((
|
|
26401
|
+
reexec: (argv) => new Promise((resolve7) => {
|
|
26356
26402
|
const args = [...argv];
|
|
26357
26403
|
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 });
|
|
26358
|
-
child2.on("exit", (code) =>
|
|
26359
|
-
child2.on("error", () =>
|
|
26404
|
+
child2.on("exit", (code) => resolve7(code ?? 1));
|
|
26405
|
+
child2.on("error", () => resolve7(1));
|
|
26360
26406
|
}),
|
|
26361
26407
|
// stderr only — stdout may be carrying --json output.
|
|
26362
26408
|
warn: (line) => console.error(line)
|
|
@@ -26464,7 +26510,7 @@ var defaultDeps2 = {
|
|
|
26464
26510
|
}
|
|
26465
26511
|
},
|
|
26466
26512
|
registryWhoami: probeRegistryWhoami,
|
|
26467
|
-
sleep: (ms) => new Promise((
|
|
26513
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
|
|
26468
26514
|
env: process.env
|
|
26469
26515
|
};
|
|
26470
26516
|
function message(e) {
|
|
@@ -27479,6 +27525,7 @@ async function preflight(deps, ctx, stage, meta, lane) {
|
|
|
27479
27525
|
if (model === "content") {
|
|
27480
27526
|
throw new Error(`${ctx.repo} is a content repo (deployModel=content) \u2014 the release train does not apply (trunk-based; PR to main)`);
|
|
27481
27527
|
}
|
|
27528
|
+
if (meta.releaseTrack === "single") throw new Error("single-track repos ship on merge; there is no release train");
|
|
27482
27529
|
if (model === "none") {
|
|
27483
27530
|
throw new Error(`${ctx.repo} is not Hub-deployed (deployModel=none) \u2014 the release train does not apply; use the project's own release path`);
|
|
27484
27531
|
}
|
|
@@ -29727,9 +29774,9 @@ function rulesetCoversReleaseBranches(payload, track) {
|
|
|
29727
29774
|
const conditions = payload.conditions;
|
|
29728
29775
|
const include = conditions?.ref_name?.include;
|
|
29729
29776
|
if (!Array.isArray(include)) {
|
|
29730
|
-
return track === "full" || track === "direct" || track === "trunk";
|
|
29777
|
+
return track === "full" || track === "direct" || track === "trunk" || track === "single";
|
|
29731
29778
|
}
|
|
29732
|
-
const releaseRefs = track === "full" ? ["refs/heads/rc", "refs/heads/main"] : ["refs/heads/main"];
|
|
29779
|
+
const releaseRefs = track === "full" ? ["refs/heads/rc", "refs/heads/main"] : track === "single" ? ["refs/heads/development"] : ["refs/heads/main"];
|
|
29733
29780
|
return include.some((entry) => typeof entry === "string" && (entry === "~DEFAULT_BRANCH" ? track === "trunk" : releaseRefs.some((ref) => refPatternCovers(entry, ref))));
|
|
29734
29781
|
}
|
|
29735
29782
|
function releaseBranchRefsCovered(conditions, track) {
|
|
@@ -32717,6 +32764,7 @@ function applyRemoteScript(items, date) {
|
|
|
32717
32764
|
|
|
32718
32765
|
// src/box-commands.ts
|
|
32719
32766
|
init_clean_exit();
|
|
32767
|
+
init_cli_shared();
|
|
32720
32768
|
|
|
32721
32769
|
// src/secrets-commands.ts
|
|
32722
32770
|
var import_node_fs31 = require("node:fs");
|
|
@@ -33592,6 +33640,14 @@ function buildSetDeployPatch(_slug, input) {
|
|
|
33592
33640
|
if (!DEPLOY_DOMAIN_RE.test(a)) throw new Error(`org project set-deploy: --alias must be a DNS hostname, got ${JSON.stringify(a)}`);
|
|
33593
33641
|
}
|
|
33594
33642
|
const uniqueAliases = [...new Set(aliases)];
|
|
33643
|
+
const redirectFrom = [...new Set((input.redirectFrom ?? []).map((a) => clean4(a)).filter((a) => a !== void 0))];
|
|
33644
|
+
for (const entry of redirectFrom) {
|
|
33645
|
+
const [host, target, extra] = entry.split("=");
|
|
33646
|
+
if (!host || !DEPLOY_DOMAIN_RE.test(host) || extra !== void 0 || target !== void 0 && !DEPLOY_DOMAIN_RE.test(target)) throw new Error(`org project set-deploy: invalid --redirect-from ${JSON.stringify(entry)}`);
|
|
33647
|
+
const serving = [domain, ...uniqueAliases].map((a) => a?.toLowerCase());
|
|
33648
|
+
if (serving.includes(host.toLowerCase())) throw new Error("org project set-deploy: --redirect-from must not equal the primary domain or an alias");
|
|
33649
|
+
if (target !== void 0 && domain !== void 0 && (input.aliases !== void 0 || input.clearAliases) && !serving.includes(target.toLowerCase())) throw new Error("org project set-deploy: --redirect-from target must be the primary domain or an alias");
|
|
33650
|
+
}
|
|
33595
33651
|
if (input.clearAliases && uniqueAliases.length) {
|
|
33596
33652
|
throw new Error("org project set-deploy: --clear-aliases cannot be combined with --alias (they contradict)");
|
|
33597
33653
|
}
|
|
@@ -33631,6 +33687,7 @@ function buildSetDeployPatch(_slug, input) {
|
|
|
33631
33687
|
// #2986: an explicit `[]` is what the route reads as "clear them"; omitting the key is what it reads as
|
|
33632
33688
|
// "keep them". Those are different instructions and they now have different flags.
|
|
33633
33689
|
...input.clearAliases ? { aliases: [] } : uniqueAliases.length ? { aliases: uniqueAliases } : {},
|
|
33690
|
+
...input.redirectFrom !== void 0 ? { redirectFrom } : {},
|
|
33634
33691
|
...input.noEnvFile !== void 0 ? { noEnvFile: input.noEnvFile } : {},
|
|
33635
33692
|
// #7353: an explicit `[]` is what the route reads as "clear them"; omitting the key is what it reads as
|
|
33636
33693
|
// "keep them" — same patch contract as aliases (#2986/#2984).
|
|
@@ -34393,6 +34450,48 @@ ${SSH_RECIPE_AGENT_NOTE}`);
|
|
|
34393
34450
|
await failGraceful(e.message);
|
|
34394
34451
|
}
|
|
34395
34452
|
});
|
|
34453
|
+
box.command("previews <name>").description("inspect or configure a box preview port range in BOX#<sshHost>/META (#8444; master-only, dry-run unless --apply)").option("--port-start <port>", "first port in the dedicated preview interval (>= 1024)", (v) => Number(v)).option("--cap <n>", "maximum concurrent PR previews on this box (1..20, default 3)", (v) => Number(v)).option("--apply", "write the preview port range to BOX#<sshHost>/META (default: dry-run)").option("--json", "machine-readable output").action(async (name, o) => {
|
|
34454
|
+
try {
|
|
34455
|
+
const { boxes, incomplete } = await fetchInventory();
|
|
34456
|
+
const result = lookupBox(name, boxes, incomplete);
|
|
34457
|
+
if (result.kind === "unknown") {
|
|
34458
|
+
warnIncomplete(result.reasons);
|
|
34459
|
+
await failGraceful(
|
|
34460
|
+
`runtime box: cannot confirm whether '${name}' exists \u2014 part of the inventory could not be read (see above). Refusing to answer "no such box" from a list I know is incomplete.`
|
|
34461
|
+
);
|
|
34462
|
+
return;
|
|
34463
|
+
}
|
|
34464
|
+
if (result.kind === "absent") {
|
|
34465
|
+
await failGraceful(`runtime box: no box named '${name}'. Known boxes: ${result.known.join(", ") || "(none)"}`);
|
|
34466
|
+
return;
|
|
34467
|
+
}
|
|
34468
|
+
const found = result.box;
|
|
34469
|
+
if (!found.address) {
|
|
34470
|
+
await failGraceful(`runtime box previews: box '${found.name}' has no public IPv4 address`);
|
|
34471
|
+
return;
|
|
34472
|
+
}
|
|
34473
|
+
const status = await boxPreviewsForHost(
|
|
34474
|
+
found.address,
|
|
34475
|
+
{ portStart: o.portStart, cap: o.cap, apply: o.apply === true },
|
|
34476
|
+
registryClientDeps(await loadConfig())
|
|
34477
|
+
);
|
|
34478
|
+
const fmt = (r) => r ? `${r.ports.start}..${r.ports.end} (portStart=${r.previewPortStart}, cap=${r.previewCap})` : "(unconfigured)";
|
|
34479
|
+
if (o.json) {
|
|
34480
|
+
console.log(JSON.stringify({ ...status, box: found.name, address: found.address, incomplete }, null, 2));
|
|
34481
|
+
} else {
|
|
34482
|
+
console.log(
|
|
34483
|
+
`${found.name} (${found.address}) \u2014 ${status.apply ? "applied" : "dry-run"}
|
|
34484
|
+
current: ${fmt(status.current)}
|
|
34485
|
+
planned: ${fmt(status.planned)}
|
|
34486
|
+
live reservations: ${status.activeReservations}`
|
|
34487
|
+
);
|
|
34488
|
+
warnIncomplete(incomplete);
|
|
34489
|
+
}
|
|
34490
|
+
if (incomplete.length) process.exitCode = 1;
|
|
34491
|
+
} catch (e) {
|
|
34492
|
+
await failGraceful(e.message);
|
|
34493
|
+
}
|
|
34494
|
+
});
|
|
34396
34495
|
box.command("control <name>").description("box-side control files (#7402): --plan (default, read-only) reports drift from the repo; --apply --only <src> installs with backups").option("--plan", "read-only drift report (the default)").option("--apply", "install the files named by --only on this box, backing up each previous copy").option("--only <src>", "with --apply: a manifest src to install (repeatable; approval is file by file)", (v, acc) => [...acc, v], []).option("--dest <path>", "with --apply: a destination of a multi-target --only source to install (repeatable, #7409)", (v, acc) => [...acc, v], []).option("--all-targets", "with --apply: install a multi-target --only source to every one of its destinations on this box").option("--ref <ref>", "git ref the repo content is read from", "origin/development").option("--json", "machine-readable output").action(async (name, o) => {
|
|
34397
34496
|
try {
|
|
34398
34497
|
if (o.apply && o.plan) throw new UsageError("box control: --plan and --apply are exclusive");
|
|
@@ -34905,6 +35004,10 @@ function classifyEnvironmentDrift(drift) {
|
|
|
34905
35004
|
}
|
|
34906
35005
|
var TRACK_ENVIRONMENT_EXCEPTIONS = {
|
|
34907
35006
|
direct: { rc: "direct release track has no rc stage" },
|
|
35007
|
+
single: {
|
|
35008
|
+
rc: "single release track has no rc stage",
|
|
35009
|
+
main: "single release track has no main branch; merges deploy the one stage"
|
|
35010
|
+
},
|
|
34908
35011
|
trunk: {
|
|
34909
35012
|
dev: "trunk release track has no dev stage",
|
|
34910
35013
|
rc: "trunk release track has no rc stage"
|
|
@@ -35073,6 +35176,7 @@ function branchProtectionPayload(current, owners, app = LOCKED_APP) {
|
|
|
35073
35176
|
}
|
|
35074
35177
|
function deployStagesForBootstrap(deployModel, track) {
|
|
35075
35178
|
if (deployModel !== "tenant-container" && deployModel !== "solo-container") return [];
|
|
35179
|
+
if (track === "single") return ["main"];
|
|
35076
35180
|
return branchesForTrack(track).map((branch) => branch === "development" ? "dev" : branch);
|
|
35077
35181
|
}
|
|
35078
35182
|
function bootstrapSetDeployCommand(repo, stage, deployModel) {
|
|
@@ -38311,7 +38415,7 @@ Each module must ship a live release that bind-mounts <deployPath>/data read-onl
|
|
|
38311
38415
|
let running = 0;
|
|
38312
38416
|
const waiting = [];
|
|
38313
38417
|
const results = await Promise.allSettled(record.members.map(async (member) => {
|
|
38314
|
-
if (running >= 4) await new Promise((
|
|
38418
|
+
if (running >= 4) await new Promise((resolve7) => waiting.push(resolve7));
|
|
38315
38419
|
else running++;
|
|
38316
38420
|
try {
|
|
38317
38421
|
if ((await deps.head(member.repo, "development"))?.toLowerCase() !== member.development.toLowerCase())
|
|
@@ -38982,7 +39086,7 @@ function clean3(out) {
|
|
|
38982
39086
|
return out.trim();
|
|
38983
39087
|
}
|
|
38984
39088
|
function sleeper(deps) {
|
|
38985
|
-
return deps.sleep ?? ((ms) => new Promise((
|
|
39089
|
+
return deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
38986
39090
|
}
|
|
38987
39091
|
function normalizeHotfixVersion(input) {
|
|
38988
39092
|
const m = /^v?(\d+\.\d+\.\d+)$/.exec(input.trim());
|
|
@@ -40656,7 +40760,7 @@ async function updatePrHeadFromBase(input) {
|
|
|
40656
40760
|
} catch (e) {
|
|
40657
40761
|
throw new Error(`GitHub could not update ${input.head} from ${input.base} (${e.message.trim()}) \u2014 ${remedy}`);
|
|
40658
40762
|
}
|
|
40659
|
-
const sleep2 = input.sleep ?? ((ms) => new Promise((
|
|
40763
|
+
const sleep2 = input.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
40660
40764
|
for (let attempt = 0; attempt < PR_HEAD_UPDATE_API_READ_RETRIES; attempt++) {
|
|
40661
40765
|
const to = await readHead2();
|
|
40662
40766
|
if (to && to !== from) return { from, to, base: input.base, via: "update-branch-api" };
|
|
@@ -40681,7 +40785,7 @@ async function mergeAutoWithTransientRetry(prNumber, repo, deps) {
|
|
|
40681
40785
|
if (first.mergeStatus !== "failed") return first;
|
|
40682
40786
|
const ready = await deps.probeMergeReady(prNumber, repo).catch(() => ({ open: false, mergeable: false, checksPassing: false }));
|
|
40683
40787
|
if (!ready.open || !ready.mergeable || !ready.checksPassing) return first;
|
|
40684
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
40788
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
40685
40789
|
await sleep2(PR_LAND_MERGE_RETRY_DELAY_MS);
|
|
40686
40790
|
const retried = await deps.mergeAuto(prNumber, repo);
|
|
40687
40791
|
if (retried.mergeStatus !== "failed") return retried;
|
|
@@ -40692,7 +40796,7 @@ var AUTO_MERGE_CONFIRM_DELAY_MS = 15e3;
|
|
|
40692
40796
|
async function confirmAutoMergeEnqueued(deps, options) {
|
|
40693
40797
|
const retries = options?.retries ?? AUTO_MERGE_CONFIRM_RETRIES;
|
|
40694
40798
|
const delayMs = options?.delayMs ?? AUTO_MERGE_CONFIRM_DELAY_MS;
|
|
40695
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
40799
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
40696
40800
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
40697
40801
|
if (await deps.readMerged().catch(() => false)) return "merged";
|
|
40698
40802
|
const stuck = await deps.readAutoMergeRequest().then((s) => s.trim()).catch(() => "");
|
|
@@ -40709,7 +40813,7 @@ async function confirmAutoMergeEnqueued(deps, options) {
|
|
|
40709
40813
|
async function readGhPrStateWithRetry(fetchState, options) {
|
|
40710
40814
|
const retries = options?.retries ?? PR_LAND_STATE_READ_RETRIES;
|
|
40711
40815
|
const delayMs = options?.delayMs ?? PR_LAND_STATE_READ_DELAY_MS;
|
|
40712
|
-
const sleep2 = options?.sleep ?? ((ms) => new Promise((
|
|
40816
|
+
const sleep2 = options?.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
40713
40817
|
let lastError = "empty state";
|
|
40714
40818
|
for (let attempt = 0; attempt < retries; attempt++) {
|
|
40715
40819
|
try {
|
|
@@ -40793,7 +40897,7 @@ async function runPrLand(prNumber, options, deps) {
|
|
|
40793
40897
|
return queueMerged(base, deps);
|
|
40794
40898
|
}
|
|
40795
40899
|
const deadline = (deps.now ?? (() => Date.now()))() + (options.queueTimeoutMs ?? PR_LAND_QUEUE_TIMEOUT_MS);
|
|
40796
|
-
await (deps.sleep ?? ((ms) => new Promise((
|
|
40900
|
+
await (deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms))))(deps.pollDelayMs?.(0) ?? PR_LAND_POLL_MS);
|
|
40797
40901
|
return pollPrMergeQueue(prNumber, options, deps, base, void 0, deadline);
|
|
40798
40902
|
}
|
|
40799
40903
|
if (merge.mergeStatus === "merged") {
|
|
@@ -40831,7 +40935,7 @@ function queueMerged(base, deps) {
|
|
|
40831
40935
|
}
|
|
40832
40936
|
async function pollPrMergeQueue(pr, options, deps, base, snapshot, deadlineMs) {
|
|
40833
40937
|
const now = deps.now ?? (() => Date.now());
|
|
40834
|
-
const sleep2 = deps.sleep ?? ((ms) => new Promise((
|
|
40938
|
+
const sleep2 = deps.sleep ?? ((ms) => new Promise((resolve7) => setTimeout(resolve7, ms)));
|
|
40835
40939
|
const timeoutMs = options.queueTimeoutMs ?? PR_LAND_QUEUE_TIMEOUT_MS;
|
|
40836
40940
|
const deadline = deadlineMs ?? now() + timeoutMs;
|
|
40837
40941
|
let attempt = 0;
|
|
@@ -41974,7 +42078,7 @@ async function composeOverrideBodyFile(prNumber, repoArgs, gh2, opts) {
|
|
|
41974
42078
|
async function ghAdminTrueMergeWhenClean(prNumber, repo, opts = {}) {
|
|
41975
42079
|
const io = opts.io ?? defaultGhMergeAutoIo;
|
|
41976
42080
|
const sleep2 = opts.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
41977
|
-
const pollMs = opts.pollMs ??
|
|
42081
|
+
const pollMs = opts.pollMs ?? 6e4;
|
|
41978
42082
|
const deadline = Date.now() + (opts.timeoutMs ?? 20 * 6e4);
|
|
41979
42083
|
for (; ; ) {
|
|
41980
42084
|
const raw = await io.gh(["pr", "view", prNumber, "--repo", repo, "--json", "state,mergeStateStatus"], GC_GH_TIMEOUT_MS).catch(() => "");
|
|
@@ -42191,6 +42295,7 @@ var readApi = async (args) => {
|
|
|
42191
42295
|
const env = await ghReadEnv();
|
|
42192
42296
|
return (await execFileP("gh", ["api", ...args], {
|
|
42193
42297
|
timeout: 2e4,
|
|
42298
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
42194
42299
|
...env ? { env } : {}
|
|
42195
42300
|
})).stdout;
|
|
42196
42301
|
};
|
|
@@ -42220,7 +42325,7 @@ async function readPrMergeQueue(pr, repo, gh2 = readApi) {
|
|
|
42220
42325
|
return result.pullRequest;
|
|
42221
42326
|
}
|
|
42222
42327
|
async function findPrMergeGroupRun(pr, repo, base, gh2 = readApi) {
|
|
42223
|
-
const pages = JSON.parse(await gh2([`repos/${repo}/actions/runs?event=merge_group&per_page=100
|
|
42328
|
+
const pages = [JSON.parse(await gh2([`repos/${repo}/actions/runs?event=merge_group&per_page=100&exclude_pull_requests=true`]))];
|
|
42224
42329
|
const prefix = `gh-readonly-queue/${base}/pr-${pr}-`;
|
|
42225
42330
|
const run = pages.flatMap((page) => page.workflow_runs).filter((run2) => run2.head_branch?.startsWith(prefix)).sort((a, b) => b.created_at.localeCompare(a.created_at) || b.id - a.id)[0];
|
|
42226
42331
|
return run ? { id: run.id, url: run.html_url, conclusion: run.conclusion } : void 0;
|
|
@@ -44000,7 +44105,7 @@ var import_promises7 = require("node:fs/promises");
|
|
|
44000
44105
|
init_cli_shared();
|
|
44001
44106
|
init_error_codes();
|
|
44002
44107
|
var GC_GH_TIMEOUT_MS2 = 2e4;
|
|
44003
|
-
var CHECKS_WATCH_POLL_MS =
|
|
44108
|
+
var CHECKS_WATCH_POLL_MS = 6e4;
|
|
44004
44109
|
var CHECKS_WATCH_TIMEOUT_MS = 10 * 6e4;
|
|
44005
44110
|
function prCommentMarker(markerId) {
|
|
44006
44111
|
return `<!-- mmi-pr-marker:${markerId} -->`;
|
|
@@ -44539,7 +44644,7 @@ async function checkPrReview(number, repo, head, deps = {
|
|
|
44539
44644
|
}
|
|
44540
44645
|
}
|
|
44541
44646
|
function computePrPatchId(number, repo) {
|
|
44542
|
-
return new Promise((
|
|
44647
|
+
return new Promise((resolve7, reject) => {
|
|
44543
44648
|
const gh2 = (0, import_node_child_process14.spawn)("gh", ["pr", "diff", number, "--repo", repo], { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
44544
44649
|
const git3 = (0, import_node_child_process14.spawn)("git", ["patch-id", "--stable"], { windowsHide: true, stdio: ["pipe", "pipe", "pipe"] });
|
|
44545
44650
|
let out = "";
|
|
@@ -44577,7 +44682,7 @@ function computePrPatchId(number, repo) {
|
|
|
44577
44682
|
if (gitCode !== 0) return reject(new Error(`git patch-id --stable exited ${gitCode}: ${gitErr.trim()}`));
|
|
44578
44683
|
const id = out.trim().split(/\s+/)[0];
|
|
44579
44684
|
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"})`));
|
|
44580
|
-
|
|
44685
|
+
resolve7(id);
|
|
44581
44686
|
};
|
|
44582
44687
|
gh2.on("close", (code) => {
|
|
44583
44688
|
ghCode = code;
|
|
@@ -44628,7 +44733,7 @@ init_cli_shared();
|
|
|
44628
44733
|
var GIT_TIMEOUT_MS2 = 15e3;
|
|
44629
44734
|
function catFileBatch(root, ref, paths) {
|
|
44630
44735
|
if (paths.length === 0) return Promise.resolve([]);
|
|
44631
|
-
return new Promise((
|
|
44736
|
+
return new Promise((resolve7) => {
|
|
44632
44737
|
const chunks = [];
|
|
44633
44738
|
let settled = false;
|
|
44634
44739
|
const child2 = (0, import_node_child_process15.spawn)("git", ["-C", root, "cat-file", "--batch", "--buffer"], { windowsHide: true });
|
|
@@ -44636,7 +44741,7 @@ function catFileBatch(root, ref, paths) {
|
|
|
44636
44741
|
if (settled) return;
|
|
44637
44742
|
settled = true;
|
|
44638
44743
|
clearTimeout(timer);
|
|
44639
|
-
|
|
44744
|
+
resolve7(parseCatFileBatch(Buffer.concat(chunks), paths.length));
|
|
44640
44745
|
};
|
|
44641
44746
|
const timer = setTimeout(() => {
|
|
44642
44747
|
child2.kill("SIGKILL");
|
|
@@ -45061,7 +45166,7 @@ function healthPollIntervalMs() {
|
|
|
45061
45166
|
return HEALTH_POLL_INTERVAL_MS;
|
|
45062
45167
|
}
|
|
45063
45168
|
function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
45064
|
-
return new Promise((
|
|
45169
|
+
return new Promise((resolve7, reject) => {
|
|
45065
45170
|
let settled = false;
|
|
45066
45171
|
const finish = (fn) => {
|
|
45067
45172
|
if (settled) return;
|
|
@@ -45071,7 +45176,7 @@ function waitForProcessStability(child2, graceMs = earlyExitGraceMs()) {
|
|
|
45071
45176
|
child2.removeAllListeners("exit");
|
|
45072
45177
|
fn();
|
|
45073
45178
|
};
|
|
45074
|
-
const timer = setTimeout(() => finish(
|
|
45179
|
+
const timer = setTimeout(() => finish(resolve7), graceMs);
|
|
45075
45180
|
child2.on("error", (err) => finish(() => reject(new Error(`stage process failed to start: ${err.message}`))));
|
|
45076
45181
|
child2.on("exit", (code, signal) => {
|
|
45077
45182
|
const detail = code != null ? `code ${code}` : signal ? `signal ${signal}` : "unknown reason";
|
|
@@ -45264,10 +45369,10 @@ function pickStagePort(range, isFree) {
|
|
|
45264
45369
|
throw new Error(`no free stage port in range ${start}-${end} \u2014 every port is in use`);
|
|
45265
45370
|
}
|
|
45266
45371
|
function isPortFree(port) {
|
|
45267
|
-
return new Promise((
|
|
45372
|
+
return new Promise((resolve7) => {
|
|
45268
45373
|
const srv = (0, import_node_net.createServer)();
|
|
45269
|
-
srv.once("error", () =>
|
|
45270
|
-
srv.once("listening", () => srv.close(() =>
|
|
45374
|
+
srv.once("error", () => resolve7(false));
|
|
45375
|
+
srv.once("listening", () => srv.close(() => resolve7(true)));
|
|
45271
45376
|
srv.listen(port, "127.0.0.1");
|
|
45272
45377
|
});
|
|
45273
45378
|
}
|
|
@@ -45545,7 +45650,7 @@ async function killTree(pid) {
|
|
|
45545
45650
|
} catch {
|
|
45546
45651
|
}
|
|
45547
45652
|
}
|
|
45548
|
-
await new Promise((
|
|
45653
|
+
await new Promise((resolve7) => setTimeout(resolve7, 500));
|
|
45549
45654
|
try {
|
|
45550
45655
|
process.kill(-pid, "SIGKILL");
|
|
45551
45656
|
} catch {
|
|
@@ -45618,7 +45723,7 @@ async function waitForHealth(url, timeoutMs, anyStatus = false) {
|
|
|
45618
45723
|
} catch (e) {
|
|
45619
45724
|
last = e.message;
|
|
45620
45725
|
}
|
|
45621
|
-
await new Promise((
|
|
45726
|
+
await new Promise((resolve7) => setTimeout(resolve7, healthPollIntervalMs()));
|
|
45622
45727
|
}
|
|
45623
45728
|
throw new Error(`stage health check timed out for ${url}${last ? ` (${last})` : ""}`);
|
|
45624
45729
|
}
|
|
@@ -45805,7 +45910,7 @@ function unlinkNodeModulesJunction(wtPath) {
|
|
|
45805
45910
|
}
|
|
45806
45911
|
}
|
|
45807
45912
|
function defaultSleep2(ms) {
|
|
45808
|
-
return new Promise((
|
|
45913
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
45809
45914
|
}
|
|
45810
45915
|
async function removeResidueDirectory(wtPath, options = {}) {
|
|
45811
45916
|
const { sleep: sleep2 = defaultSleep2, removeEmptyDir = import_node_fs44.rmdirSync, exists = import_node_fs44.existsSync } = options;
|
|
@@ -47555,7 +47660,7 @@ ${list}`);
|
|
|
47555
47660
|
// #5400: after grace, name "GitHub delivered zero runs" instead of burning the full budget as pending.
|
|
47556
47661
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr checks-wait", number, repo),
|
|
47557
47662
|
baseBranch,
|
|
47558
|
-
sleep: (ms) => new Promise((
|
|
47663
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
|
|
47559
47664
|
log: (message2) => console.warn(message2),
|
|
47560
47665
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
47561
47666
|
// Liveness on stderr, one line per poll. A silent bounded wait is indistinguishable from a hang, and
|
|
@@ -47725,7 +47830,7 @@ ${list}`);
|
|
|
47725
47830
|
// #5400: same zero-runs delivery probe as checks-wait — do not burn the land budget on silence.
|
|
47726
47831
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr land", prNumber, repo),
|
|
47727
47832
|
baseBranch: "development",
|
|
47728
|
-
sleep: (ms) => new Promise((
|
|
47833
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
|
|
47729
47834
|
log: (message2) => console.warn(message2),
|
|
47730
47835
|
// `pr land` inherits the same (raised) checks budget, so it needs the same liveness — otherwise the
|
|
47731
47836
|
// 30m wait is SILENT and reads exactly like the hang #2940 was filed about, only three times longer.
|
|
@@ -47777,7 +47882,7 @@ ${list}`);
|
|
|
47777
47882
|
} else {
|
|
47778
47883
|
lastFailure = void 0;
|
|
47779
47884
|
}
|
|
47780
|
-
await new Promise((
|
|
47885
|
+
await new Promise((resolve7) => setTimeout(resolve7, PR_LAND_POLL_MS));
|
|
47781
47886
|
}
|
|
47782
47887
|
if (lastFailure) {
|
|
47783
47888
|
throw new Error(
|
|
@@ -47968,7 +48073,7 @@ ${list}`);
|
|
|
47968
48073
|
checksDiagnosticCommand: `gh pr checks ${number} --repo ${repo}`,
|
|
47969
48074
|
pollHeadWorkflowRunCount: () => waitLoopHeadWorkflowRunCount("pr merge --wait", number, repo),
|
|
47970
48075
|
baseBranch,
|
|
47971
|
-
sleep: (ms) => new Promise((
|
|
48076
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
|
|
47972
48077
|
log: (message2) => console.warn(message2),
|
|
47973
48078
|
timeoutMs: Math.max(1, budgetMs - (Date.now() - waitStarted)),
|
|
47974
48079
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`pr merge: --wait checks \u2014 ${state}, ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -48058,7 +48163,7 @@ ${list}`);
|
|
|
48058
48163
|
}
|
|
48059
48164
|
if (!o.auto && mergeRejectedBaseBranchModified(message2)) {
|
|
48060
48165
|
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).`);
|
|
48061
|
-
await new Promise((
|
|
48166
|
+
await new Promise((resolve7) => setTimeout(resolve7, PR_MERGE_BASE_RACE_RETRY_DELAY_MS));
|
|
48062
48167
|
await execFileP("gh", buildPrMergeArgs({ number, repoArgs, method, auto: false, deleteBranch: false, bodyFile }), { timeout: GH_MUTATION_TIMEOUT_MS }).catch((e2) => {
|
|
48063
48168
|
const m2 = String(e2.message || "");
|
|
48064
48169
|
if (/already been merged/i.test(m2)) {
|
|
@@ -48320,7 +48425,7 @@ ${list}`);
|
|
|
48320
48425
|
console.warn(`ci checks-wait: zero-runs probe FAILED (${read.error}) \u2014 not treating as delivery failure this cycle`);
|
|
48321
48426
|
return null;
|
|
48322
48427
|
},
|
|
48323
|
-
sleep: (ms) => new Promise((
|
|
48428
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms)),
|
|
48324
48429
|
log: (message2) => console.warn(message2),
|
|
48325
48430
|
timeoutMs: timeoutMs ?? PR_CHECKS_TIMEOUT_MS,
|
|
48326
48431
|
progress: ({ state, elapsedMs, remainingMs }) => console.warn(`ci checks-wait: ${state} \u2014 ${Math.round(elapsedMs / 1e3)}s elapsed, ${Math.round(remainingMs / 6e4)}m left`)
|
|
@@ -48697,7 +48802,7 @@ function hotfixFollowUpLegs(runs, foldPort, alignment, foldNote, deployNote) {
|
|
|
48697
48802
|
}
|
|
48698
48803
|
return legs;
|
|
48699
48804
|
}
|
|
48700
|
-
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((
|
|
48805
|
+
async function runProjectInfoSyncLeg(cb, repo, sleep2 = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))) {
|
|
48701
48806
|
for (let attempt = 0; ; attempt++) {
|
|
48702
48807
|
try {
|
|
48703
48808
|
return await cb(repo, true);
|
|
@@ -49622,7 +49727,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
49622
49727
|
if (!reuse) await gh(["workflow", "run", "module-rc-publish.yml", "--repo", "mutmutco/MMI-Hub", ...fields.flatMap((f) => ["--field", f])]);
|
|
49623
49728
|
let candidate = reuse;
|
|
49624
49729
|
for (let i = 0; !candidate && i < 25; i++) {
|
|
49625
|
-
await new Promise((
|
|
49730
|
+
await new Promise((resolve7) => setTimeout(resolve7, 1e3));
|
|
49626
49731
|
candidate = (await rcRuns()).find((r) => r.displayTitle === title && Date.parse(r.createdAt) >= since);
|
|
49627
49732
|
}
|
|
49628
49733
|
if (!candidate) throw new Error(`workflow dispatch outcome ambiguous for "${title}"; inspect Actions before retrying`);
|
|
@@ -49778,7 +49883,7 @@ function suiteTrainIo(root, summaryFile, memberSummaryFiles = {}, summaryFileTr,
|
|
|
49778
49883
|
for (let i = 0; ; i++) {
|
|
49779
49884
|
const pin = await openSuitePinPr({ root, run: (file, args, cwd) => exec(file, args, cwd, GIT_TIMEOUT_MS) }, { repo, tag, stage: "main" });
|
|
49780
49885
|
if (pin.status !== "pending" || i >= 40) return pin;
|
|
49781
|
-
await new Promise((
|
|
49886
|
+
await new Promise((resolve7) => setTimeout(resolve7, 3e4));
|
|
49782
49887
|
}
|
|
49783
49888
|
},
|
|
49784
49889
|
suiteMapping: async (leadTag) => {
|
|
@@ -49860,7 +49965,7 @@ function suiteRcDataProdDeps() {
|
|
|
49860
49965
|
const rc = facts.stages.rc;
|
|
49861
49966
|
return rc?.present ? rc.substrate ?? "an unnamed substrate" : null;
|
|
49862
49967
|
},
|
|
49863
|
-
sleep: (ms) => new Promise((
|
|
49968
|
+
sleep: (ms) => new Promise((resolve7) => setTimeout(resolve7, ms))
|
|
49864
49969
|
};
|
|
49865
49970
|
}
|
|
49866
49971
|
function registerSuiteCommands(program3) {
|
|
@@ -52648,6 +52753,12 @@ async function runDoctorClean(opts, io, deps) {
|
|
|
52648
52753
|
init_cli_shared();
|
|
52649
52754
|
init_error_codes();
|
|
52650
52755
|
init_house_map();
|
|
52756
|
+
var PR_PREFLIGHT_STEPS = [
|
|
52757
|
+
{ label: "Review the docs and runbook sections affected by the code" },
|
|
52758
|
+
{ label: "Update affected instructions, or record a precise Runbook-Unaffected explanation when they genuinely remain unchanged" },
|
|
52759
|
+
{ label: "Commit code and documentation first (coverage compares committed base...HEAD)", command: "git commit" },
|
|
52760
|
+
{ label: "Run the repository's existing fast local preflight (check:local where declared, otherwise its documented equivalent)", command: "npm run check:local" }
|
|
52761
|
+
];
|
|
52651
52762
|
var LOOP_PLAYBOOKS = {
|
|
52652
52763
|
agent: {
|
|
52653
52764
|
title: "Agent",
|
|
@@ -52659,7 +52770,8 @@ var LOOP_PLAYBOOKS = {
|
|
|
52659
52770
|
{ label: "Claim board work (board mutation only \u2014 never `oracle issue claim` or the flat `board claim`; ground unknown write routes via `commands` / `explain` first)", command: "mmi-cli oracle board claim <ref>" },
|
|
52660
52771
|
{ label: "Prepare the local workspace through the host surface" },
|
|
52661
52772
|
{ label: "Before broad docs rewrites, follow docs/Guides/doc-ceremony-cut.md" },
|
|
52662
|
-
{ label: "Apply the repository test policy,
|
|
52773
|
+
{ label: "Apply the repository test policy and run only its permitted focused checks, without a broad local build or full suite", command: "mmi-cli tests policy --base origin/development" },
|
|
52774
|
+
...PR_PREFLIGHT_STEPS,
|
|
52663
52775
|
{ label: "Publish the branch", command: "git push origin <branch>:<branch>" },
|
|
52664
52776
|
{ label: "Open the development-base PR", command: 'mmi-cli devops pr create --title "<title>" --body-file .jerv/PR_BODY.md --base development' },
|
|
52665
52777
|
{ label: "Land to development (waits for checks itself)", command: "mmi-cli devops pr land <PR-number>" },
|
|
@@ -52681,6 +52793,7 @@ var LOOP_PLAYBOOKS = {
|
|
|
52681
52793
|
"ship-pr": {
|
|
52682
52794
|
title: "Ship PR",
|
|
52683
52795
|
steps: [
|
|
52796
|
+
...PR_PREFLIGHT_STEPS,
|
|
52684
52797
|
{ label: "Publish the branch", command: "git push origin <branch>:<branch>" },
|
|
52685
52798
|
{ label: "Open the development-base PR", command: 'mmi-cli devops pr create --title "<title>" --body-file .jerv/PR_BODY.md --base development' },
|
|
52686
52799
|
{ label: "Land the PR (merge to development \u2014 waits for checks itself)", command: "mmi-cli devops pr land <PR-number>" }
|
|
@@ -55839,7 +55952,7 @@ function createRunbookLiveDeps(cliBin) {
|
|
|
55839
55952
|
};
|
|
55840
55953
|
const first = await probe().catch(() => 599);
|
|
55841
55954
|
if (first < 500) return first;
|
|
55842
|
-
await new Promise((
|
|
55955
|
+
await new Promise((resolve7) => setTimeout(resolve7, 2e4));
|
|
55843
55956
|
return probe();
|
|
55844
55957
|
}
|
|
55845
55958
|
};
|
|
@@ -55912,11 +56025,13 @@ function registerEdgeCommands(program3) {
|
|
|
55912
56025
|
process.exitCode = 1;
|
|
55913
56026
|
}
|
|
55914
56027
|
});
|
|
55915
|
-
edge.command("dns <action> <owner/repo> <stage>").description("read (`status`)
|
|
55916
|
-
if (
|
|
56028
|
+
edge.command("dns <action> <owner/repo> <stage>").description("read (`status`), create missing (`ensure`) or guarded delete (`delete`, dry-run unless --apply) stage DNS; writes are master-only").option("--host <host>", "declared stage host to delete").option("--apply", "apply the guarded DNS deletion").action(async (action, repo, stage, options) => {
|
|
56029
|
+
if (!["status", "ensure", "delete"].includes(action)) return fail("runtime edge dns: <action> must be status, ensure or delete");
|
|
56030
|
+
if (action === "delete" && !options.host) return fail("runtime edge dns delete: --host is required");
|
|
56031
|
+
if (action !== "delete" && (options.host || options.apply)) return fail("runtime edge dns: --host and --apply are only for delete");
|
|
55917
56032
|
if (stage !== "dev" && stage !== "rc" && stage !== "main") return fail("runtime edge dns: <stage> must be dev, rc, or main");
|
|
55918
56033
|
try {
|
|
55919
|
-
const result = await edgeDnsForStage(slugOf(repo), stage, registryClientDeps(await loadConfig()), action === "ensure");
|
|
56034
|
+
const result = await edgeDnsForStage(slugOf(repo), stage, registryClientDeps(await loadConfig()), action === "ensure", action === "delete" ? { host: options.host, apply: options.apply === true } : void 0);
|
|
55920
56035
|
console.log(JSON.stringify(result, null, 2));
|
|
55921
56036
|
if (result.required && result.complete === false) process.exitCode = 1;
|
|
55922
56037
|
} catch (e) {
|
|
@@ -56656,8 +56771,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
56656
56771
|
});
|
|
56657
56772
|
const raced = await Promise.race([
|
|
56658
56773
|
work.then((r) => ({ ...r, timedOut: false })),
|
|
56659
|
-
new Promise((
|
|
56660
|
-
ceiling = setTimeout(() =>
|
|
56774
|
+
new Promise((resolve7) => {
|
|
56775
|
+
ceiling = setTimeout(() => resolve7({ timedOut: true, scanned: 0, findings: 0, fixed: 0, failed: 0 }), BOARD_DOCTOR_TIMEOUT_MS);
|
|
56661
56776
|
})
|
|
56662
56777
|
]);
|
|
56663
56778
|
if (raced.timedOut) return { scanned: 0, findings: 0, fixed: 0, failed: 0, timedOut: true };
|
|
@@ -56690,8 +56805,8 @@ function mmiDoctorDeps(opts = {}) {
|
|
|
56690
56805
|
incomplete: nb.incomplete,
|
|
56691
56806
|
timedOut: false
|
|
56692
56807
|
})),
|
|
56693
|
-
new Promise((
|
|
56694
|
-
ceiling = setTimeout(() =>
|
|
56808
|
+
new Promise((resolve7) => {
|
|
56809
|
+
ceiling = setTimeout(() => resolve7({ driftLines: [], incomplete: [], timedOut: true }), SCHEDULES_DRIFT_TIMEOUT_MS);
|
|
56695
56810
|
})
|
|
56696
56811
|
]);
|
|
56697
56812
|
if (!raced.timedOut && raced.incomplete.length === 0) writeSchedulesDriftCache(cachePath, raced.driftLines);
|
|
@@ -56971,7 +57086,6 @@ function envelopeAwareWriteErr(str2) {
|
|
|
56971
57086
|
}
|
|
56972
57087
|
writeHumanParseError(str2);
|
|
56973
57088
|
}
|
|
56974
|
-
var INVOKED_ARGV3 = process.argv.slice(2);
|
|
56975
57089
|
var program2 = new Command();
|
|
56976
57090
|
program2.name("mmi-cli").description("MMI Future Hub CLI \u2014 the org control plane for agentic coding.").version(resolveClientVersion()).configureOutput({ writeErr: envelopeAwareWriteErr }).showHelpAfterError(PARSE_HINT_SENTINEL);
|
|
56977
57091
|
program2.addHelpText(
|
|
@@ -56996,6 +57110,8 @@ function appActorDeps() {
|
|
|
56996
57110
|
}
|
|
56997
57111
|
setPollTokenProvider(async () => activeAppToken() ?? pollTokenOrUndefined(appActorDeps()));
|
|
56998
57112
|
setGhReadTokenProvider(pollToken);
|
|
57113
|
+
var filingToken;
|
|
57114
|
+
setGhWriteTokenProvider(() => filingToken ??= mintFilingWriteToken(appActorDeps()).then((t) => t.token, () => void 0));
|
|
56999
57115
|
setStatusWriteTokenProvider(() => mintStatusWriteToken(appActorDeps()));
|
|
57000
57116
|
var registerPreAction = Reflect.get(program2, ["h", "ook"].join(""));
|
|
57001
57117
|
registerPreAction.call(program2, "preAction", async (_thisCommand, actionCommand) => {
|
|
@@ -57073,6 +57189,15 @@ Dry run: pass --confirm-data-loss ${repo}/${stage} to decommission.`);
|
|
|
57073
57189
|
return failGraceful(`runtime tenant control: ${e.message}`);
|
|
57074
57190
|
}
|
|
57075
57191
|
});
|
|
57192
|
+
tenant.command("undo <owner/repo> <stage>").description("switch a tenant stage back to its previous release (warm standby: seconds; else recreated from the retained release, no rebuild); watches by default").option("--json", "machine-readable output").action(async (repo, stage, o) => {
|
|
57193
|
+
try {
|
|
57194
|
+
const result = await runTenantControl(trainApplyDeps(), { repo, stage, action: "undo", watch: true });
|
|
57195
|
+
printLine(o.json ? JSON.stringify(result, null, 2) : renderTenantControl(result));
|
|
57196
|
+
if (result.conclusion === "failure") return failGraceful(`runtime tenant undo ${stage}: ${result.category ?? "failed"} \u2014 ${result.note}`);
|
|
57197
|
+
} catch (e) {
|
|
57198
|
+
return failGraceful(`runtime tenant undo: ${e.message}`);
|
|
57199
|
+
}
|
|
57200
|
+
});
|
|
57076
57201
|
var tenantArtifact = tenant.command("artifact").description("private stage-bound inputs for declared tenant tasks");
|
|
57077
57202
|
tenantArtifact.command("put <owner/repo> <stage> <path>").description("upload a private file through a short-lived Hub grant; prints only an opaque artifact id").option("--json", "machine-readable output").action(async (repo, stage, path2, o) => {
|
|
57078
57203
|
try {
|
|
@@ -57515,7 +57640,7 @@ fullTrack.command("readiness <owner/repo>").description("aggregate branch topolo
|
|
|
57515
57640
|
console.log(JSON.stringify(report, null, 2));
|
|
57516
57641
|
if (!report.rcand.canApply) process.exitCode = 1;
|
|
57517
57642
|
});
|
|
57518
|
-
project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row \u2014 project-admin may set only --no-env-file/--compose-profiles on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row \u2014 `mmi-cli devops runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--clear-aliases", "remove EVERY serving alias from the row (#2986) \u2014 omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) \u2014 own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--compose-profiles <profiles>", "comma-separated compose profiles to enable for this stage (e.g. huddles,slack-bridge); omit to leave unchanged").option("--clear-compose-profiles", "remove EVERY compose profile from the row \u2014 omitting --compose-profiles preserves them, so clearing needs saying out loud").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
57643
|
+
project.command("set-deploy [owner/repo]").description("patch a tenant DEPLOY row \u2014 project-admin may set only --no-env-file/--compose-profiles on their own existing dev/rc row; master may seed/change all coords and main; defaults to the current repo").addOption(new Option("--stage <stage>", "dev | rc | main").makeOptionMandatory().choices(["dev", "rc", "main"])).option("--ssh-host <host>", "the box address the deploy ssh-es into; omit to keep the stored value (required only for a NEW hetzner-ssh row \u2014 `mmi-cli devops runtime box list` finds it)").option("--ssh-user <user>", "ssh user; omit to leave the row unchanged (default root on a new row)").option("--port <port>", "loopback port the container binds / Caddy upstream (1..65535); omit to leave the row unchanged").addOption(new Option("--substrate <substrate>", "hetzner-ssh | s3-cloudfront; omit to leave the row unchanged").choices([...DEPLOY_SUBSTRATES])).option("--deploy-role-arn <arn>", "s3-cloudfront: the scoped OIDC deployer role tenant-deploy.yml assumes for this stage").option("--app-bucket <bucket>", "s3-cloudfront: the S3 bucket the built dist/ is synced into").option("--distribution-id <id>", "s3-cloudfront: the CloudFront distribution invalidated after the sync").option("--distribution-domain <domain>", "s3-cloudfront: the host the post-deploy security-header proof is measured against").option("--deploy-path <path>", "on-box per-stage release root; omit to leave the row unchanged (default /opt/mmi/<slug>/<stage> on a new row)").option("--service <name>", "systemd/compose service name; omit to leave the row unchanged (default the slug on a new row)").option("--domain <domain>", "canonical serving host; omit to leave the row unchanged").option("--alias <domain...>", "extra serving hostname the box Caddy answers (repeatable); omit to leave the stored aliases unchanged").option("--redirect-from <host>", "permanent redirect host or host=target (primary or alias); repeatable", (host, hosts) => [...hosts, host], []).option("--clear-aliases", "remove EVERY serving alias from the row (#2986) \u2014 omitting --alias preserves them, so clearing needs saying out loud").option("--no-env-file <bool>", "set the DEPLOY# fileless flag (true|false) \u2014 own-repo project-admin dev/rc; master main; true = env passthrough with no .env symlink").option("--compose-profiles <profiles>", "comma-separated compose profiles to enable for this stage (e.g. huddles,slack-bridge); omit to leave unchanged").option("--clear-compose-profiles", "remove EVERY compose profile from the row \u2014 omitting --compose-profiles preserves them, so clearing needs saying out loud").option("--force", "explicit recovery override: skip fileless-compose verification only after independent proof").option("--json", "machine-readable output").action(async (repoOrSlug, o) => {
|
|
57519
57644
|
const cfg = await loadConfig();
|
|
57520
57645
|
let target;
|
|
57521
57646
|
try {
|
|
@@ -57570,6 +57695,7 @@ ${filelessTransitionGuide(target, o.stage)}`
|
|
|
57570
57695
|
distributionId: o.distributionId,
|
|
57571
57696
|
distributionDomain: o.distributionDomain,
|
|
57572
57697
|
aliases: o.alias,
|
|
57698
|
+
redirectFrom: o.redirectFrom?.length ? o.redirectFrom : void 0,
|
|
57573
57699
|
clearAliases: o.clearAliases,
|
|
57574
57700
|
noEnvFile,
|
|
57575
57701
|
composeProfiles,
|
package/dist/runbook-sweep.cjs
CHANGED
|
@@ -191,6 +191,11 @@ var init_error_codes = __esm({
|
|
|
191
191
|
});
|
|
192
192
|
|
|
193
193
|
// src/gh-read-route.ts
|
|
194
|
+
function isAppFilingWrite(args) {
|
|
195
|
+
if (args.some((a) => VIEWER.test(a))) return false;
|
|
196
|
+
const [group, verb] = args;
|
|
197
|
+
return (group === "issue" || group === "label") && verb === "create";
|
|
198
|
+
}
|
|
194
199
|
function isViewerFreeGhRead(args) {
|
|
195
200
|
if (args.some((a) => VIEWER.test(a))) return false;
|
|
196
201
|
const [group, verb] = args;
|
|
@@ -285,6 +290,12 @@ var init_stdin_inject = __esm({
|
|
|
285
290
|
});
|
|
286
291
|
|
|
287
292
|
// src/cli-shared.ts
|
|
293
|
+
async function ghFilingAppEnv(file, args) {
|
|
294
|
+
if (!ghWriteTokenProvider || !/(^|[\\/])gh(\.exe)?$/i.test(file) || process.env.MMI_ACTOR?.trim() === "personal") return void 0;
|
|
295
|
+
if (!isAppFilingWrite(args)) return void 0;
|
|
296
|
+
const token = await ghWriteTokenProvider().catch(() => void 0);
|
|
297
|
+
return token ? { ...process.env, GH_TOKEN: token } : void 0;
|
|
298
|
+
}
|
|
288
299
|
async function ghReadAppEnv(file, args) {
|
|
289
300
|
if (!ghReadTokenProvider || !/(^|[\\/])gh(\.exe)?$/i.test(file) || process.env.MMI_ACTOR?.trim() === "personal") return void 0;
|
|
290
301
|
if (!isViewerFreeGhRead(args)) return void 0;
|
|
@@ -325,7 +336,7 @@ function execFileHard(file, args, options) {
|
|
|
325
336
|
timer.unref?.();
|
|
326
337
|
});
|
|
327
338
|
}
|
|
328
|
-
var import_node_child_process3, import_node_util2, rawExecFileP, DEFAULT_EXEC_TIMEOUT_MS, execFileP, ghReadTokenProvider, ExecDeadlineError;
|
|
339
|
+
var import_node_child_process3, import_node_util2, rawExecFileP, DEFAULT_EXEC_TIMEOUT_MS, execFileP, ghReadTokenProvider, ghWriteTokenProvider, ExecDeadlineError;
|
|
329
340
|
var init_cli_shared = __esm({
|
|
330
341
|
"src/cli-shared.ts"() {
|
|
331
342
|
"use strict";
|
|
@@ -348,7 +359,7 @@ var init_cli_shared = __esm({
|
|
|
348
359
|
// promisify(execFile)'s overloads widen to string|Buffer when options is spread in.
|
|
349
360
|
rawExecFileP(file, args, { encoding: "utf8", windowsHide: true, timeout: DEFAULT_EXEC_TIMEOUT_MS, killSignal: "SIGTERM", ...options, ...env ? { env } : {} })
|
|
350
361
|
);
|
|
351
|
-
const appEnv = options.env ? void 0 : await ghReadAppEnv(file, args);
|
|
362
|
+
const appEnv = options.env ? void 0 : await ghReadAppEnv(file, args) ?? await ghFilingAppEnv(file, args);
|
|
352
363
|
if (!appEnv) return run();
|
|
353
364
|
try {
|
|
354
365
|
return await run(appEnv);
|
|
@@ -775,7 +786,7 @@ function checkRunbookCoverage(docs, base, git) {
|
|
|
775
786
|
const globs = lines.slice(heading.start, markerEnd - 1).flatMap((text) => /<!--\s*runbook-covers:\s*(.*?)\s*-->/.exec(text)?.[1].split(",").map((s) => s.trim()) ?? []);
|
|
776
787
|
const affected = changed.filter((path2) => globs.some((glob) => matches(glob, path2)));
|
|
777
788
|
if (affected.length && !ranges.some((r) => r.count > 0 && r.start < end && r.start + r.count > heading.start)) {
|
|
778
|
-
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title}: changed ${affected.join(", ")}; update this section or declare Runbook-Unaffected: <reason
|
|
789
|
+
findings.push({ kind: "runbook-section-stale", doc, line: heading.start, detail: `${heading.title}: changed ${affected.join(", ")}; update this section or declare Runbook-Unaffected: <reason>`, section: { heading: heading.title, level: heading.level, startLine: heading.start, endLine: end - 1 }, sources: affected });
|
|
779
790
|
}
|
|
780
791
|
}
|
|
781
792
|
}
|
|
@@ -1084,7 +1095,7 @@ var TEST_RUNNER_AT_START = new RegExp(
|
|
|
1084
1095
|
// src/project-model.ts
|
|
1085
1096
|
var PROJECT_TYPES = ["web-app", "hub-service", "content", "desktop-app", "desktop-game", "mobile-app", "non-deployable", "cli-tool", "worker"];
|
|
1086
1097
|
var DEPLOY_MODELS = ["hub-serverless", "serverless", "tenant-container", "solo-container", "static-cdn", "registry-publish", "content", "none", "vercel"];
|
|
1087
|
-
var RELEASE_TRACKS = ["full", "direct", "trunk"];
|
|
1098
|
+
var RELEASE_TRACKS = ["full", "direct", "trunk", "single"];
|
|
1088
1099
|
var REQUIRED_CHECK_BRANCHES = ["development", "rc", "main"];
|
|
1089
1100
|
var PROJECT_TYPE_SET = new Set(PROJECT_TYPES);
|
|
1090
1101
|
var DEPLOY_MODEL_SET = new Set(DEPLOY_MODELS);
|
|
@@ -4849,7 +4860,7 @@ async function discoverShaWorkflowRuns(deps, repo, headSha, seenRunIds) {
|
|
|
4849
4860
|
var import_node_child_process7 = require("node:child_process");
|
|
4850
4861
|
var import_node_fs7 = require("node:fs");
|
|
4851
4862
|
var import_node_os = require("node:os");
|
|
4852
|
-
var
|
|
4863
|
+
var import_node_path8 = require("node:path");
|
|
4853
4864
|
|
|
4854
4865
|
// src/doctor-io.ts
|
|
4855
4866
|
var import_node_fs5 = require("node:fs");
|
|
@@ -4942,23 +4953,18 @@ function writeGitignore(content) {
|
|
|
4942
4953
|
// src/doctor-fleet-drift.ts
|
|
4943
4954
|
var JOURNAL_TAIL_BYTES = 256 * 1024;
|
|
4944
4955
|
|
|
4945
|
-
// src/jerv-cli-spawn.ts
|
|
4946
|
-
var import_node_path6 = require("node:path");
|
|
4947
|
-
init_cli_shared();
|
|
4948
|
-
var JERV_CLI_ENTRY = (0, import_node_path6.join)("node_modules", "@jervaise", "jerv-cli", "dist", "index.cjs");
|
|
4949
|
-
|
|
4950
4956
|
// src/claude-binary-doctor.ts
|
|
4951
4957
|
var PACKAGE = "@anthropic-ai/claude-code";
|
|
4952
4958
|
var CLAUDE_BINARY_INSTALL_COMMAND = `npm install -g ${PACKAGE}`;
|
|
4953
4959
|
|
|
4954
4960
|
// src/release-ledger.ts
|
|
4955
4961
|
var import_node_fs6 = require("node:fs");
|
|
4956
|
-
var
|
|
4962
|
+
var import_node_path7 = require("node:path");
|
|
4957
4963
|
var import_node_crypto = require("node:crypto");
|
|
4958
4964
|
|
|
4959
4965
|
// src/file-lock.ts
|
|
4960
4966
|
var import_promises = require("node:fs/promises");
|
|
4961
|
-
var
|
|
4967
|
+
var import_node_path6 = require("node:path");
|
|
4962
4968
|
function pidIsDead(pid) {
|
|
4963
4969
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
4964
4970
|
if (pid === process.pid) return false;
|
|
@@ -5073,7 +5079,7 @@ async function releaseFileLock(lockPath, guard) {
|
|
|
5073
5079
|
}
|
|
5074
5080
|
async function withFencedFileLock(lockPath, opts, fn) {
|
|
5075
5081
|
const resolved = resolveFileLockOpts(opts);
|
|
5076
|
-
await (0, import_promises.mkdir)((0,
|
|
5082
|
+
await (0, import_promises.mkdir)((0, import_node_path6.dirname)(lockPath), { recursive: true }).catch(() => void 0);
|
|
5077
5083
|
const guard = await acquireFileLock(lockPath, resolved, Date.now() + resolved.maxWaitMs);
|
|
5078
5084
|
try {
|
|
5079
5085
|
return await fn(() => fileLockHeldBy(lockPath, guard.token));
|
|
@@ -5228,26 +5234,26 @@ function resolvePrimaryGitDir(cwd) {
|
|
|
5228
5234
|
}
|
|
5229
5235
|
let common;
|
|
5230
5236
|
try {
|
|
5231
|
-
common = (0, import_node_fs6.readFileSync)((0,
|
|
5237
|
+
common = (0, import_node_fs6.readFileSync)((0, import_node_path7.join)(gitDir, "commondir"), "utf8").trim();
|
|
5232
5238
|
} catch (e) {
|
|
5233
5239
|
if (e.code !== "ENOENT") {
|
|
5234
5240
|
throw new ReleaseLedgerError("unreadable", `release ledger: the worktree gitdir's commondir pointer could not be READ (${e instanceof Error ? e.message : String(e)}) \u2014 a failed read is unverified, not primary (#5987/#4713)`);
|
|
5235
5241
|
}
|
|
5236
5242
|
}
|
|
5237
5243
|
if (!common) return gitDir;
|
|
5238
|
-
const commonDir = (0,
|
|
5244
|
+
const commonDir = (0, import_node_path7.isAbsolute)(common) ? common : (0, import_node_path7.resolve)(gitDir, common);
|
|
5239
5245
|
try {
|
|
5240
|
-
(0, import_node_fs6.readFileSync)((0,
|
|
5246
|
+
(0, import_node_fs6.readFileSync)((0, import_node_path7.join)(commonDir, "HEAD"), "utf8");
|
|
5241
5247
|
} catch (e) {
|
|
5242
5248
|
throw new ReleaseLedgerError("no-checkout", `release ledger: the worktree's commondir pointer names ${commonDir}, which has no readable HEAD (${e instanceof Error ? e.message : String(e)}) \u2014 the primary checkout may have been removed; refusing to write the ledger into a doomed location`);
|
|
5243
5249
|
}
|
|
5244
5250
|
return commonDir;
|
|
5245
5251
|
}
|
|
5246
5252
|
function releaseLedgerPath(cwd) {
|
|
5247
|
-
return (0,
|
|
5253
|
+
return (0, import_node_path7.join)(resolvePrimaryGitDir(cwd), "mmi-runtime", "release", "phases.json");
|
|
5248
5254
|
}
|
|
5249
5255
|
function atomicWriteJson(path2, ledger) {
|
|
5250
|
-
(0, import_node_fs6.mkdirSync)((0,
|
|
5256
|
+
(0, import_node_fs6.mkdirSync)((0, import_node_path7.dirname)(path2), { recursive: true });
|
|
5251
5257
|
const tmp = `${path2}.tmp-${process.pid}-${(0, import_node_crypto.randomBytes)(4).toString("hex")}`;
|
|
5252
5258
|
(0, import_node_fs6.writeFileSync)(tmp, `${JSON.stringify(ledger, null, 2)}
|
|
5253
5259
|
`, "utf8");
|
|
@@ -5263,16 +5269,16 @@ async function withLedgerLock(path2, fn, opts = {}) {
|
|
|
5263
5269
|
}));
|
|
5264
5270
|
}
|
|
5265
5271
|
function checkoutRootFrom(cwd) {
|
|
5266
|
-
let dir = (0,
|
|
5272
|
+
let dir = (0, import_node_path7.resolve)(cwd);
|
|
5267
5273
|
for (; ; ) {
|
|
5268
5274
|
if (resolveGitDir(dir)) return dir;
|
|
5269
|
-
const parent = (0,
|
|
5275
|
+
const parent = (0, import_node_path7.dirname)(dir);
|
|
5270
5276
|
if (parent === dir) throw new ReleaseLedgerError("no-checkout", `train writer lock: no git checkout found above ${cwd}`);
|
|
5271
5277
|
dir = parent;
|
|
5272
5278
|
}
|
|
5273
5279
|
}
|
|
5274
5280
|
function trainWriterLockPath(cwd) {
|
|
5275
|
-
return (0,
|
|
5281
|
+
return (0, import_node_path7.join)((0, import_node_path7.dirname)(releaseLedgerPath(checkoutRootFrom(cwd))), "train.lock");
|
|
5276
5282
|
}
|
|
5277
5283
|
var TRAIN_WRITER_STALE_MS = 12 * 60 * 6e4;
|
|
5278
5284
|
function describeTrainWriterToken(token) {
|
|
@@ -5663,8 +5669,8 @@ function readOriginWorkflowsDefault(root, ref) {
|
|
|
5663
5669
|
}
|
|
5664
5670
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
5665
5671
|
async function probeRegistryWhoami(train) {
|
|
5666
|
-
const dir = (0, import_node_fs7.mkdtempSync)((0,
|
|
5667
|
-
const rc = (0,
|
|
5672
|
+
const dir = (0, import_node_fs7.mkdtempSync)((0, import_node_path8.join)((0, import_node_os.tmpdir)(), "mmi-npmrc-"));
|
|
5673
|
+
const rc = (0, import_node_path8.join)(dir, "npmrc");
|
|
5668
5674
|
try {
|
|
5669
5675
|
(0, import_node_fs7.writeFileSync)(rc, "//registry.npmjs.org/:_authToken=${NPM_TOKEN}\n");
|
|
5670
5676
|
await train.runSelf(["secrets", "use", "npm/NPM_TOKEN", "--slug", "_org", "--", "npm", "whoami", "--registry", NPM_REGISTRY, "--userconfig", rc]);
|
|
@@ -5701,7 +5707,7 @@ var defaultDeps2 = {
|
|
|
5701
5707
|
trainWriterHolder: liveTrainWriterHolder,
|
|
5702
5708
|
readSurfacesRaw: (cwd) => {
|
|
5703
5709
|
try {
|
|
5704
|
-
return (0, import_node_fs7.readFileSync)((0,
|
|
5710
|
+
return (0, import_node_fs7.readFileSync)((0, import_node_path8.join)(cwd, "surfaces.json"), "utf8");
|
|
5705
5711
|
} catch {
|
|
5706
5712
|
return void 0;
|
|
5707
5713
|
}
|
package/package.json
CHANGED