mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 10:30:44 +00:00
perf: parallelize local check gate
This commit is contained in:
@@ -1,57 +1,3 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { performance } from "node:perf_hooks";
|
||||
import { main } from "./check.mjs";
|
||||
|
||||
const includeArchitecture = process.argv.includes("--include-architecture");
|
||||
|
||||
const stages = [
|
||||
{ name: "conflict markers", args: ["check:no-conflict-markers"] },
|
||||
{ name: "tool display", args: ["tool-display:check"] },
|
||||
{ name: "host env policy", args: ["check:host-env-policy:swift"] },
|
||||
{ name: "typecheck", args: ["tsgo:all"] },
|
||||
{ name: "lint", args: ["lint"] },
|
||||
{ name: "webhook body guard", args: ["lint:webhook:no-low-level-body-read"] },
|
||||
{ name: "pairing store guard", args: ["lint:auth:no-pairing-store-group"] },
|
||||
{ name: "pairing account guard", args: ["lint:auth:pairing-account-scope"] },
|
||||
{ name: "runtime import cycles", args: ["check:import-cycles"] },
|
||||
];
|
||||
|
||||
if (includeArchitecture) {
|
||||
stages.push({ name: "architecture import cycles", args: ["check:madge-import-cycles"] });
|
||||
}
|
||||
|
||||
const timings = [];
|
||||
let exitCode = 0;
|
||||
|
||||
for (const { name, args } of stages) {
|
||||
const startedAt = performance.now();
|
||||
console.error(`\n[check:timed] ${name}`);
|
||||
const result = spawnSync("pnpm", args, {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
const durationMs = performance.now() - startedAt;
|
||||
timings.push({ name, durationMs, status: result.status ?? 1 });
|
||||
|
||||
if (result.error) {
|
||||
throw result.error;
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
exitCode = result.status ?? 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.error("\n[check:timed] summary");
|
||||
for (const timing of timings) {
|
||||
const status = timing.status === 0 ? "ok" : `failed:${timing.status}`;
|
||||
console.error(`${formatMs(timing.durationMs).padStart(8)} ${status.padEnd(9)} ${timing.name}`);
|
||||
}
|
||||
|
||||
process.exitCode = exitCode;
|
||||
|
||||
function formatMs(durationMs) {
|
||||
if (durationMs < 1000) {
|
||||
return `${Math.round(durationMs)}ms`;
|
||||
}
|
||||
return `${(durationMs / 1000).toFixed(2)}s`;
|
||||
}
|
||||
await main([...process.argv.slice(2), "--timed"]);
|
||||
|
||||
125
scripts/check.mjs
Normal file
125
scripts/check.mjs
Normal file
@@ -0,0 +1,125 @@
|
||||
import { spawn } from "node:child_process";
|
||||
import { performance } from "node:perf_hooks";
|
||||
|
||||
export async function main(argv = process.argv.slice(2)) {
|
||||
const timed = argv.includes("--timed");
|
||||
const includeArchitecture = argv.includes("--include-architecture");
|
||||
|
||||
const tailChecks = [
|
||||
{ name: "webhook body guard", args: ["lint:webhook:no-low-level-body-read"] },
|
||||
{ name: "pairing store guard", args: ["lint:auth:no-pairing-store-group"] },
|
||||
{ name: "pairing account guard", args: ["lint:auth:pairing-account-scope"] },
|
||||
includeArchitecture
|
||||
? { name: "architecture import cycles", args: ["check:architecture"] }
|
||||
: { name: "runtime import cycles", args: ["check:import-cycles"] },
|
||||
];
|
||||
|
||||
const stages = [
|
||||
{
|
||||
name: "preflight guards",
|
||||
parallel: false,
|
||||
commands: [
|
||||
{ name: "conflict markers", args: ["check:no-conflict-markers"] },
|
||||
{ name: "tool display", args: ["tool-display:check"] },
|
||||
{ name: "host env policy", args: ["check:host-env-policy:swift"] },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "typecheck",
|
||||
parallel: false,
|
||||
commands: [{ name: "typecheck", args: ["tsgo:all"] }],
|
||||
},
|
||||
{
|
||||
name: "lint",
|
||||
parallel: false,
|
||||
commands: [{ name: "lint", args: ["lint"] }],
|
||||
},
|
||||
{
|
||||
name: "policy guards",
|
||||
parallel: true,
|
||||
commands: tailChecks,
|
||||
},
|
||||
];
|
||||
|
||||
const timings = [];
|
||||
let exitCode = 0;
|
||||
|
||||
for (const stage of stages) {
|
||||
console.error(`\n[check] ${stage.name}`);
|
||||
const results = stage.parallel
|
||||
? await Promise.all(stage.commands.map((command) => runCommand(command)))
|
||||
: await runSerial(stage.commands);
|
||||
|
||||
timings.push(...results);
|
||||
const failed = results.find((result) => result.status !== 0);
|
||||
if (failed) {
|
||||
exitCode = failed.status;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timed || exitCode !== 0) {
|
||||
printSummary(timings);
|
||||
}
|
||||
|
||||
process.exitCode = exitCode;
|
||||
}
|
||||
|
||||
async function runSerial(commands) {
|
||||
const results = [];
|
||||
for (const command of commands) {
|
||||
const result = await runCommand(command);
|
||||
results.push(result);
|
||||
if (result.status !== 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
async function runCommand(command) {
|
||||
const startedAt = performance.now();
|
||||
const child = spawn("pnpm", command.args, {
|
||||
stdio: "inherit",
|
||||
shell: process.platform === "win32",
|
||||
});
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
child.once("error", (error) => {
|
||||
console.error(error);
|
||||
resolve({
|
||||
name: command.name,
|
||||
durationMs: performance.now() - startedAt,
|
||||
status: 1,
|
||||
});
|
||||
});
|
||||
child.once("close", (status) => {
|
||||
resolve({
|
||||
name: command.name,
|
||||
durationMs: performance.now() - startedAt,
|
||||
status: status ?? 1,
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function printSummary(timings) {
|
||||
console.error("\n[check] summary");
|
||||
for (const timing of timings) {
|
||||
const status = timing.status === 0 ? "ok" : `failed:${timing.status}`;
|
||||
console.error(
|
||||
`${formatMs(timing.durationMs).padStart(8)} ${status.padEnd(9)} ${timing.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function formatMs(durationMs) {
|
||||
if (durationMs < 1000) {
|
||||
return `${Math.round(durationMs)}ms`;
|
||||
}
|
||||
return `${(durationMs / 1000).toFixed(2)}s`;
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
await main();
|
||||
}
|
||||
62
scripts/run-oxlint-shards.mjs
Normal file
62
scripts/run-oxlint-shards.mjs
Normal file
@@ -0,0 +1,62 @@
|
||||
import { spawn, spawnSync } from "node:child_process";
|
||||
import path from "node:path";
|
||||
|
||||
const extraArgs = process.argv.slice(2);
|
||||
const runner = path.resolve("scripts", "run-oxlint.mjs");
|
||||
|
||||
const prepareResult = spawnSync(
|
||||
process.execPath,
|
||||
[path.resolve("scripts", "prepare-extension-package-boundary-artifacts.mjs")],
|
||||
{
|
||||
stdio: "inherit",
|
||||
env: process.env,
|
||||
},
|
||||
);
|
||||
|
||||
if (prepareResult.error) {
|
||||
throw prepareResult.error;
|
||||
}
|
||||
if ((prepareResult.status ?? 1) !== 0) {
|
||||
process.exit(prepareResult.status ?? 1);
|
||||
}
|
||||
|
||||
const shards = [
|
||||
{
|
||||
name: "core",
|
||||
args: ["--tsconfig", "tsconfig.oxlint.core.json", "src", "ui", "packages"],
|
||||
},
|
||||
{
|
||||
name: "extensions",
|
||||
args: ["--tsconfig", "tsconfig.oxlint.extensions.json", "extensions"],
|
||||
},
|
||||
{
|
||||
name: "scripts",
|
||||
args: ["--tsconfig", "tsconfig.oxlint.scripts.json", "scripts"],
|
||||
},
|
||||
];
|
||||
|
||||
const results = await Promise.all(shards.map((shard) => runShard(shard)));
|
||||
process.exitCode = results.find((status) => status !== 0) ?? 0;
|
||||
|
||||
async function runShard(shard) {
|
||||
console.error(`[oxlint:${shard.name}] starting`);
|
||||
const child = spawn(process.execPath, [runner, ...shard.args, ...extraArgs], {
|
||||
stdio: "inherit",
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_OXLINT_SKIP_LOCK: "1",
|
||||
OPENCLAW_OXLINT_SKIP_PREPARE: "1",
|
||||
},
|
||||
});
|
||||
|
||||
return await new Promise((resolve) => {
|
||||
child.once("error", (error) => {
|
||||
console.error(error);
|
||||
resolve(1);
|
||||
});
|
||||
child.once("close", (status) => {
|
||||
console.error(`[oxlint:${shard.name}] finished`);
|
||||
resolve(status ?? 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -54,19 +54,25 @@ function prepareExtensionPackageBoundaryArtifacts(env) {
|
||||
|
||||
export function main(argv = process.argv.slice(2), runtimeEnv = process.env) {
|
||||
const { args: finalArgs, env } = applyLocalOxlintPolicy(argv, runtimeEnv);
|
||||
const releaseLock = shouldAcquireLocalHeavyCheckLockForOxlint(finalArgs, {
|
||||
cwd: process.cwd(),
|
||||
env,
|
||||
})
|
||||
? acquireLocalHeavyCheckLockSync({
|
||||
cwd: process.cwd(),
|
||||
env,
|
||||
toolName: "oxlint",
|
||||
})
|
||||
: () => {};
|
||||
const releaseLock =
|
||||
env.OPENCLAW_OXLINT_SKIP_LOCK === "1"
|
||||
? () => {}
|
||||
: shouldAcquireLocalHeavyCheckLockForOxlint(finalArgs, {
|
||||
cwd: process.cwd(),
|
||||
env,
|
||||
})
|
||||
? acquireLocalHeavyCheckLockSync({
|
||||
cwd: process.cwd(),
|
||||
env,
|
||||
toolName: "oxlint",
|
||||
})
|
||||
: () => {};
|
||||
|
||||
try {
|
||||
if (shouldPrepareExtensionPackageBoundaryArtifacts(finalArgs)) {
|
||||
if (
|
||||
env.OPENCLAW_OXLINT_SKIP_PREPARE !== "1" &&
|
||||
shouldPrepareExtensionPackageBoundaryArtifacts(finalArgs)
|
||||
) {
|
||||
prepareExtensionPackageBoundaryArtifacts(env);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user