mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-23 06:10:02 +08:00
- Enforce oxlint and oxfmt for owned JavaScript and TypeScript with independent CI checks. - Preserve compiler import boundaries and protected fixtures while cleaning up lint findings. - Standardize source formatting and validate import, fixture, and embedded-source protections.
136 lines
4.0 KiB
JavaScript
136 lines
4.0 KiB
JavaScript
/* Advisory single-flight lock for FULL suite runs (`vitest run` with no file
|
|
* filters and no -t). Concurrent full suites — typically parallel agents —
|
|
* oversubscribe the CPU and produce a known flake class (vitest worker RPC
|
|
* timeouts, event-loop timing failures), so later runs queue behind the first
|
|
* with a visible waiting message instead of thrashing.
|
|
*
|
|
* Deliberately simple and advisory: a pidfile in the OS temp dir, no daemon.
|
|
* Stale locks (dead pid, unreadable record) are stolen; a waiter gives up
|
|
* after 45 minutes and proceeds anyway. Filtered/watch runs never wait.
|
|
* Opt out entirely with SCRIPTC_NO_LOCK=1. */
|
|
import { readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
/* The lock is PER FLAVOR: the plain and sanitized lanes read the same
|
|
* committed tree through separate binary/oracle cache directories, so one
|
|
* of each may run concurrently by design (the merge-gate pattern splits
|
|
* workers between them via SCRIPTC_TEST_WORKERS). The lock's job is only
|
|
* to stop two runs of the SAME flavor from thrashing each other's caches
|
|
* and the CPU accidentally. */
|
|
const FLAVOR = process.env.SCRIPTC_SAN === "1" ? "san" : "plain";
|
|
const LOCK_PATH = join(tmpdir(), `scriptc-full-suite-${FLAVOR}.lock`);
|
|
const MAX_WAIT_MS = 45 * 60 * 1000;
|
|
const POLL_MS = 2000;
|
|
|
|
// Flags whose VALUE arrives as a separate argv token — those values must not
|
|
// count as file filters. Misclassification only ever skips the lock (fails
|
|
// open to today's behavior), never blocks a filtered run.
|
|
const VALUE_FLAGS = new Set([
|
|
"-t",
|
|
"--testNamePattern",
|
|
"--reporter",
|
|
"--outputFile",
|
|
"--config",
|
|
"--root",
|
|
"--dir",
|
|
"--project",
|
|
"--pool",
|
|
"--maxWorkers",
|
|
"--minWorkers",
|
|
"--exclude",
|
|
"--shard",
|
|
"--retry",
|
|
"--bail",
|
|
"--testTimeout",
|
|
"--hookTimeout",
|
|
"--environment",
|
|
"--outputFile.json",
|
|
]);
|
|
|
|
function isFullSuiteRun() {
|
|
const argv = process.argv.slice(2);
|
|
if (argv[0] !== "run") return false; // watch/list/bench sessions never hold the lock
|
|
for (let i = 1; i < argv.length; i++) {
|
|
const a = argv[i];
|
|
if (a === "-t" || a.startsWith("--testNamePattern")) return false;
|
|
if (a.startsWith("-")) {
|
|
if (VALUE_FLAGS.has(a)) i++; // skip the flag's value token
|
|
continue;
|
|
}
|
|
return false; // a positional file filter
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function holderPid() {
|
|
try {
|
|
const rec = JSON.parse(readFileSync(LOCK_PATH, "utf8"));
|
|
return typeof rec.pid === "number" ? rec.pid : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function pidAlive(pid) {
|
|
try {
|
|
process.kill(pid, 0);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
|
|
export default async function setup() {
|
|
if (process.env.SCRIPTC_NO_LOCK === "1" || !isFullSuiteRun()) return () => {};
|
|
|
|
const started = Date.now();
|
|
let waitingSince = 0;
|
|
let acquired = false;
|
|
while (Date.now() - started < MAX_WAIT_MS) {
|
|
try {
|
|
writeFileSync(
|
|
LOCK_PATH,
|
|
JSON.stringify({
|
|
pid: process.pid,
|
|
startedAt: new Date().toISOString(),
|
|
cwd: process.cwd(),
|
|
}),
|
|
{ flag: "wx" },
|
|
);
|
|
acquired = true;
|
|
break;
|
|
} catch {
|
|
const pid = holderPid();
|
|
if (pid === null || !pidAlive(pid)) {
|
|
try {
|
|
rmSync(LOCK_PATH, { force: true });
|
|
} catch {
|
|
/* racing steal — retry */
|
|
}
|
|
continue;
|
|
}
|
|
if (Date.now() - waitingSince > 30_000) {
|
|
console.log(
|
|
`[scriptc] full-suite lock held by pid ${pid} (${LOCK_PATH}) — waiting; SCRIPTC_NO_LOCK=1 skips`,
|
|
);
|
|
waitingSince = Date.now();
|
|
}
|
|
await sleep(POLL_MS);
|
|
}
|
|
}
|
|
if (!acquired)
|
|
console.warn("[scriptc] full-suite lock wait exceeded 45 minutes — proceeding without it");
|
|
|
|
return () => {
|
|
if (!acquired) return;
|
|
try {
|
|
if (holderPid() === process.pid) rmSync(LOCK_PATH, { force: true });
|
|
} catch {
|
|
/* best-effort release; a dead-pid steal cleans up otherwise */
|
|
}
|
|
};
|
|
}
|