Skip to content

Commit ca70015

Browse files
committed
fix(scripts): prebuild gateway cpu bench
1 parent 4798264 commit ca70015

3 files changed

Lines changed: 134 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
88

99
### Fixes
1010

11+
- Scripts: build CLI startup artifacts before the gateway CPU startup bench so fresh Linux runners do not report false `n/a` readiness and process metrics.
1112
- Scripts: remove stale Knip unused-file allowlist entries so the dead-code gate fails only on current findings.
1213
- Tests: normalize bundled plugin lifecycle probe paths and state-root lookup so native Windows release sweeps accept valid packaged plugin installs.
1314
- Config: keep benign legacy metadata write anomalies out of default doctor and config command output while preserving explicit anomaly logging for diagnostics.

scripts/check-gateway-cpu-scenarios.mjs

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
#!/usr/bin/env node
22

3-
import { spawnSync } from "node:child_process";
3+
import { spawnSync as defaultSpawnSync } from "node:child_process";
44
import fs from "node:fs";
55
import path from "node:path";
66
import process from "node:process";
7+
import { pathToFileURL } from "node:url";
78
import { collectGatewayCpuObservations } from "./lib/plugin-gateway-gauntlet.mjs";
89
import { createPnpmRunnerSpawnSpec } from "./pnpm-runner.mjs";
910

@@ -137,9 +138,10 @@ function readJsonIfExists(filePath) {
137138
return JSON.parse(fs.readFileSync(filePath, "utf8"));
138139
}
139140

140-
function runStep(name, command, args, options = {}) {
141+
function runStep(name, command, args, options = {}, params = {}) {
141142
console.error(`[gateway-cpu] start ${name}`);
142-
const result = spawnSync(command, args, {
143+
const spawn = params.spawnSync ?? defaultSpawnSync;
144+
const result = spawn(command, args, {
143145
cwd: process.cwd(),
144146
env: process.env,
145147
stdio: "inherit",
@@ -167,8 +169,7 @@ function toRepoRelativePath(absolutePath) {
167169
return relativePath;
168170
}
169171

170-
async function main() {
171-
const options = parseArgs(process.argv.slice(2));
172+
async function runGatewayCpuScenarios(options, params = {}) {
172173
fs.mkdirSync(options.outputDir, { recursive: true });
173174

174175
const startupOutput = path.join(options.outputDir, "gateway-startup-bench.json");
@@ -177,19 +178,35 @@ async function main() {
177178
const steps = [];
178179

179180
if (!options.skipStartup) {
181+
const startupBuild = runStep(
182+
"startup build",
183+
process.execPath,
184+
["scripts/ensure-cli-startup-build.mjs"],
185+
{},
186+
params,
187+
);
188+
steps.push(startupBuild);
180189
steps.push(
181-
runStep("startup bench", process.execPath, [
182-
"--import",
183-
"tsx",
184-
"scripts/bench-gateway-startup.ts",
185-
"--runs",
186-
String(options.runs),
187-
"--warmup",
188-
String(options.warmup),
189-
"--output",
190-
startupOutput,
191-
...options.startupCases.flatMap((id) => ["--case", id]),
192-
]),
190+
startupBuild.status === 0
191+
? runStep(
192+
"startup bench",
193+
process.execPath,
194+
[
195+
"--import",
196+
"tsx",
197+
"scripts/bench-gateway-startup.ts",
198+
"--runs",
199+
String(options.runs),
200+
"--warmup",
201+
String(options.warmup),
202+
"--output",
203+
startupOutput,
204+
...options.startupCases.flatMap((id) => ["--case", id]),
205+
],
206+
{},
207+
params,
208+
)
209+
: { name: "startup bench", signal: null, status: 1 },
193210
);
194211
}
195212

@@ -207,7 +224,7 @@ async function main() {
207224
...options.qaScenarios.flatMap((id) => ["--scenario", id]),
208225
]);
209226
steps.push(
210-
runStep("qa suite", qaCommand.command, qaCommand.args, qaCommand.options),
227+
runStep("qa suite", qaCommand.command, qaCommand.args, qaCommand.options, params),
211228
);
212229
}
213230

@@ -239,14 +256,30 @@ async function main() {
239256
};
240257
const summaryPath = path.join(options.outputDir, "summary.json");
241258
fs.writeFileSync(summaryPath, `${JSON.stringify(summary, null, 2)}\n`);
242-
console.log(JSON.stringify(summary, null, 2));
259+
if (!params.silent) {
260+
console.log(JSON.stringify(summary, null, 2));
261+
}
262+
263+
const exitCode = steps.some((step) => step.status !== 0) ? 1 : 0;
264+
return { exitCode, summary };
265+
}
243266

244-
if (steps.some((step) => step.status !== 0)) {
267+
async function main(params = {}) {
268+
const options = parseArgs(params.argv ?? process.argv.slice(2));
269+
const result = await runGatewayCpuScenarios(options, params);
270+
if (result.exitCode !== 0) {
245271
process.exitCode = 1;
246272
}
247273
}
248274

249-
main().catch((error) => {
250-
console.error(error instanceof Error ? error.stack : String(error));
251-
process.exitCode = 1;
252-
});
275+
export const testing = {
276+
parseArgs,
277+
runGatewayCpuScenarios,
278+
};
279+
280+
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
281+
main().catch((error) => {
282+
console.error(error instanceof Error ? error.stack : String(error));
283+
process.exitCode = 1;
284+
});
285+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
2+
import path from "node:path";
3+
import { afterEach, describe, expect, it } from "vitest";
4+
import { testing } from "../../scripts/check-gateway-cpu-scenarios.mjs";
5+
6+
const tempRoots: string[] = [];
7+
8+
function makeTempRoot(): string {
9+
const artifactRoot = path.join(process.cwd(), ".artifacts");
10+
mkdirSync(artifactRoot, { recursive: true });
11+
const root = mkdtempSync(path.join(artifactRoot, "gateway-cpu-test-"));
12+
tempRoots.push(root);
13+
return root;
14+
}
15+
16+
afterEach(() => {
17+
for (const root of tempRoots.splice(0)) {
18+
rmSync(root, { recursive: true, force: true });
19+
}
20+
});
21+
22+
describe("gateway CPU scenario guard", () => {
23+
it("prepares CLI startup artifacts before running the startup bench", async () => {
24+
const outputDir = makeTempRoot();
25+
const calls: Array<{ command: string; args: string[] }> = [];
26+
const options = testing.parseArgs([
27+
"--output-dir",
28+
outputDir,
29+
"--runs",
30+
"1",
31+
"--warmup",
32+
"0",
33+
"--skip-qa",
34+
]);
35+
36+
const result = await testing.runGatewayCpuScenarios(options, {
37+
silent: true,
38+
spawnSync: (command: string, args: string[]) => {
39+
calls.push({ command, args });
40+
return { status: 0 };
41+
},
42+
});
43+
44+
expect(result.exitCode).toBe(0);
45+
expect(calls.map((call) => call.args[0])).toEqual([
46+
"scripts/ensure-cli-startup-build.mjs",
47+
"--import",
48+
]);
49+
expect(calls[1]?.args).toContain("scripts/bench-gateway-startup.ts");
50+
});
51+
52+
it("does not run the startup bench when the startup build fails", async () => {
53+
const outputDir = makeTempRoot();
54+
const calls: string[][] = [];
55+
const options = testing.parseArgs([
56+
"--output-dir",
57+
outputDir,
58+
"--skip-qa",
59+
]);
60+
61+
const result = await testing.runGatewayCpuScenarios(options, {
62+
silent: true,
63+
spawnSync: (_command: string, args: string[]) => {
64+
calls.push(args);
65+
return { status: 1 };
66+
},
67+
});
68+
69+
expect(result.exitCode).toBe(1);
70+
expect(calls).toEqual([["scripts/ensure-cli-startup-build.mjs"]]);
71+
expect(result.summary.steps).toEqual([
72+
{ name: "startup build", signal: null, status: 1 },
73+
{ name: "startup bench", signal: null, status: 1 },
74+
]);
75+
});
76+
});

0 commit comments

Comments
 (0)