ci: fix additional guard failures

This commit is contained in:
Peter Steinberger
2026-04-10 19:20:45 +01:00
parent e7db987ce6
commit 925a499d84
6 changed files with 207 additions and 42 deletions

View File

@@ -12,6 +12,8 @@ import { resolveBuildRequirement } from "./run-node.mjs";
const DEFAULTS = {
outputDir: path.join(process.cwd(), ".local", "gateway-watch-regression"),
windowMs: 10_000,
readyTimeoutMs: 20_000,
readySettleMs: 500,
sigkillGraceMs: 10_000,
cpuWarnMs: 1_000,
cpuFailMs: 8_000,
@@ -51,6 +53,12 @@ function parseArgs(argv) {
case "--window-ms":
options.windowMs = Number(readValue());
break;
case "--ready-timeout-ms":
options.readyTimeoutMs = Number(readValue());
break;
case "--ready-settle-ms":
options.readySettleMs = Number(readValue());
break;
case "--sigkill-grace-ms":
options.sigkillGraceMs = Number(readValue());
break;
@@ -229,6 +237,90 @@ function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function parsePsCpuTimeMs(timeText) {
const [maybeDays, clockText] = timeText.includes("-") ? timeText.split("-", 2) : ["0", timeText];
const days = Number(maybeDays);
const parts = clockText.split(":");
if (!Number.isFinite(days) || parts.length < 2 || parts.length > 3) {
return null;
}
const seconds = Number(parts.at(-1));
const minutes = Number(parts.at(-2));
const hours = parts.length === 3 ? Number(parts[0]) : 0;
if (![seconds, minutes, hours].every(Number.isFinite)) {
return null;
}
return Math.round(((days * 24 + hours) * 60 * 60 + minutes * 60 + seconds) * 1000);
}
function readProcessTreeCpuMs(rootPid) {
if (!Number.isInteger(rootPid) || rootPid <= 0) {
return null;
}
const result = spawnSync("ps", ["-eo", "pid=,ppid=,time="], {
cwd: process.cwd(),
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
});
if (result.status !== 0) {
return null;
}
const rows = [];
for (const line of result.stdout.split("\n")) {
const match = line.trim().match(/^(\d+)\s+(\d+)\s+(\S+)$/);
if (!match) {
continue;
}
const pid = Number(match[1]);
const ppid = Number(match[2]);
const cpuMs = parsePsCpuTimeMs(match[3]);
if (!Number.isInteger(pid) || !Number.isInteger(ppid) || cpuMs == null) {
continue;
}
rows.push({ pid, ppid, cpuMs });
}
const childrenByParent = new Map();
const cpuByPid = new Map();
for (const row of rows) {
cpuByPid.set(row.pid, row.cpuMs);
const children = childrenByParent.get(row.ppid) ?? [];
children.push(row.pid);
childrenByParent.set(row.ppid, children);
}
if (!cpuByPid.has(rootPid)) {
return null;
}
let totalCpuMs = 0;
const seen = new Set();
const stack = [rootPid];
while (stack.length > 0) {
const pid = stack.pop();
if (!pid || seen.has(pid)) {
continue;
}
seen.add(pid);
totalCpuMs += cpuByPid.get(pid) ?? 0;
for (const childPid of childrenByParent.get(pid) ?? []) {
stack.push(childPid);
}
}
return totalCpuMs;
}
async function waitForGatewayReady(readText, timeoutMs) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (/\[gateway\] ready \(/.test(readText())) {
return true;
}
await sleep(100);
}
return false;
}
async function allocateLoopbackPort() {
return await new Promise((resolve, reject) => {
const server = net.createServer();
@@ -355,7 +447,16 @@ async function runTimedWatch(options, outputDir) {
await sleep(100);
}
const readyBeforeWindow = await waitForGatewayReady(
() => `${stdout}\n${stderr}`,
options.readyTimeoutMs,
);
if (readyBeforeWindow && options.readySettleMs > 0) {
await sleep(options.readySettleMs);
}
const idleCpuStartMs = watchPid ? readProcessTreeCpuMs(watchPid) : null;
await sleep(options.windowMs);
const idleCpuEndMs = watchPid ? readProcessTreeCpuMs(watchPid) : null;
if (watchPid) {
try {
@@ -390,6 +491,11 @@ async function runTimedWatch(options, outputDir) {
return {
exit,
timing,
readyBeforeWindow,
idleCpuMs:
idleCpuStartMs == null || idleCpuEndMs == null
? null
: Math.max(0, idleCpuEndMs - idleCpuStartMs),
stdoutPath,
stderrPath,
timeFilePath,
@@ -503,7 +609,10 @@ async function main() {
const distRuntimeAddedPaths = diff.added.filter((entry) =>
entry.startsWith("dist-runtime/"),
).length;
const cpuMs = Math.round((watchResult.timing.userSeconds + watchResult.timing.sysSeconds) * 1000);
const totalCpuMs = Math.round(
(watchResult.timing.userSeconds + watchResult.timing.sysSeconds) * 1000,
);
const cpuMs = watchResult.idleCpuMs ?? totalCpuMs;
const watchTriggeredBuild =
fs
.readFileSync(watchResult.stderrPath, "utf8")
@@ -519,6 +628,8 @@ async function main() {
watchTriggeredBuild,
watchBuildReason,
cpuMs,
totalCpuMs,
readyBeforeWindow: watchResult.readyBeforeWindow,
cpuWarnMs: options.cpuWarnMs,
cpuFailMs: options.cpuFailMs,
distRuntimeFileGrowth,