mirror of
https://github.com/redwoodjs/local-ci.git
synced 2026-09-19 07:54:29 +08:00
106 lines
3.4 KiB
JavaScript
Executable File
106 lines
3.4 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
import { spawnSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { performance } from "node:perf_hooks";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
const rustBin = path.join(root, "target", "release", "agent-ci");
|
|
const tsCli = ["node", "packages/cli/dist/native-launcher.js"];
|
|
|
|
function runRequired(command, args) {
|
|
const result = spawnSync(command, args, { cwd: root, stdio: "inherit", env: process.env });
|
|
if (result.status !== 0) {
|
|
process.exit(result.status ?? 1);
|
|
}
|
|
}
|
|
|
|
function timed(command, args, env = {}) {
|
|
const start = performance.now();
|
|
const result = spawnSync(command, args, {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
env: { ...process.env, ...env },
|
|
});
|
|
const durationMs = performance.now() - start;
|
|
return { durationMs, status: result.status ?? 1, stdout: result.stdout, stderr: result.stderr };
|
|
}
|
|
|
|
function bench(label, command, args, options = {}) {
|
|
const iterations = options.iterations ?? 5;
|
|
const expectedStatuses = new Set(options.expectedStatuses ?? [0]);
|
|
const times = [];
|
|
for (let i = 0; i < iterations; i += 1) {
|
|
const result = timed(command, args, options.env);
|
|
if (!expectedStatuses.has(result.status)) {
|
|
throw new Error(
|
|
`${label}: unexpected exit ${result.status}\n${result.stdout}\n${result.stderr}`,
|
|
);
|
|
}
|
|
times.push(result.durationMs);
|
|
}
|
|
times.sort((a, b) => a - b);
|
|
const avg = times.reduce((sum, value) => sum + value, 0) / times.length;
|
|
return { label, min: times[0], avg, max: times.at(-1) };
|
|
}
|
|
|
|
runRequired("pnpm", ["--filter", "@redwoodjs/agent-ci", "build"]);
|
|
runRequired("cargo", ["build", "--release", "-p", "agent-ci"]);
|
|
|
|
const rows = [
|
|
bench("startup: TypeScript --help", tsCli[0], [...tsCli.slice(1), "--help"], {
|
|
env: { AGENT_CI_FORCE_TYPESCRIPT: "1" },
|
|
}),
|
|
bench("startup: Rust --help", rustBin, ["--help"]),
|
|
bench("workflow parse: TypeScript diff parser", "node", ["scripts/diff-parse.ts"], {
|
|
iterations: 3,
|
|
}),
|
|
bench(
|
|
"workflow parse: Rust smoke discovery",
|
|
rustBin,
|
|
["run", "--workflow", ".github/workflows/smoke-expressions.yml", "--quiet", "--no-matrix"],
|
|
{
|
|
iterations: 3,
|
|
expectedStatuses: [0, 1, 2],
|
|
},
|
|
),
|
|
bench(
|
|
"job orchestration: Rust matrix discovery",
|
|
rustBin,
|
|
["run", "--workflow", ".github/workflows/smoke-matrix.yml", "--quiet"],
|
|
{
|
|
iterations: 3,
|
|
expectedStatuses: [0, 1, 2],
|
|
},
|
|
),
|
|
];
|
|
|
|
function formatMs(value) {
|
|
return `${value.toFixed(1)} ms`;
|
|
}
|
|
|
|
const lines = [
|
|
"# Rust rewrite performance snapshot",
|
|
"",
|
|
`Generated by \`pnpm rust:perf\` on ${new Date().toISOString()}.`,
|
|
"",
|
|
"| Benchmark | min | avg | max |",
|
|
"| --- | ---: | ---: | ---: |",
|
|
...rows.map(
|
|
(row) =>
|
|
`| ${row.label} | ${formatMs(row.min)} | ${formatMs(row.avg)} | ${formatMs(row.max)} |`,
|
|
),
|
|
"",
|
|
"Notes:",
|
|
"",
|
|
"- Rust workflow execution parity is validated separately; workflow benchmarks time the release binary path and accept any known Agent CI exit status.",
|
|
"- The TypeScript workflow-parse comparison uses the existing differential parser script to avoid launching Docker jobs.",
|
|
"- Re-run before switching defaults so release notes include current numbers from the final release build.",
|
|
"",
|
|
];
|
|
|
|
const outPath = path.join(root, ".docs/rust-performance.md");
|
|
fs.writeFileSync(outPath, `${lines.join("\n")}`);
|
|
console.log(lines.join("\n"));
|