204 lines
5 KiB
JavaScript
204 lines
5 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { spawnSync } from "node:child_process";
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { homedir, tmpdir } from "node:os";
|
|
import { dirname, join } from "node:path";
|
|
|
|
function getManagedRtkPath() {
|
|
return join(
|
|
homedir(),
|
|
".sf",
|
|
"agent",
|
|
"bin",
|
|
process.platform === "win32" ? "rtk.exe" : "rtk",
|
|
);
|
|
}
|
|
|
|
function run(command, args, options = {}) {
|
|
const result = spawnSync(command, args, {
|
|
encoding: "utf-8",
|
|
stdio: ["ignore", "pipe", "pipe"],
|
|
...options,
|
|
});
|
|
if (result.error) throw result.error;
|
|
return result;
|
|
}
|
|
|
|
function ensureOk(result, label) {
|
|
if (result.status !== 0) {
|
|
throw new Error(
|
|
`${label} failed: ${result.stderr || result.stdout || `exit ${result.status}`}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function createFixture(projectDir) {
|
|
mkdirSync(join(projectDir, "src", "components"), { recursive: true });
|
|
|
|
writeFileSync(
|
|
join(projectDir, "package.json"),
|
|
JSON.stringify(
|
|
{
|
|
name: "sf-rtk-benchmark",
|
|
version: "1.0.0",
|
|
scripts: {
|
|
test: "node test.js",
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
|
|
const testLines = [];
|
|
for (let i = 0; i < 120; i += 1) {
|
|
const group = i % 6;
|
|
testLines.push(
|
|
`console.log('FAIL src/components/file${group}.test.ts:${i + 1}: expected value ${i}')`,
|
|
);
|
|
}
|
|
testLines.push("process.exit(1)");
|
|
writeFileSync(join(projectDir, "test.js"), `${testLines.join("\n")}\n`);
|
|
|
|
for (let i = 1; i <= 80; i += 1) {
|
|
writeFileSync(
|
|
join(projectDir, "src", "components", `file${i}.ts`),
|
|
`export function component_${i}() {\n return "value_${i}";\n}\n`,
|
|
);
|
|
}
|
|
|
|
ensureOk(run("git", ["init", "-q"], { cwd: projectDir }), "git init");
|
|
ensureOk(
|
|
run("git", ["config", "user.email", "benchmark@example.com"], {
|
|
cwd: projectDir,
|
|
}),
|
|
"git config email",
|
|
);
|
|
ensureOk(
|
|
run("git", ["config", "user.name", "Benchmark"], { cwd: projectDir }),
|
|
"git config name",
|
|
);
|
|
ensureOk(run("git", ["add", "."], { cwd: projectDir }), "git add");
|
|
ensureOk(
|
|
run("git", ["commit", "-qm", "init"], { cwd: projectDir }),
|
|
"git commit",
|
|
);
|
|
|
|
for (let i = 1; i <= 25; i += 1) {
|
|
writeFileSync(
|
|
join(projectDir, "src", "components", `file${i}.ts`),
|
|
`export function component_${i}() {\n return "value_${i}";\n}\n// change ${i}\n`,
|
|
);
|
|
}
|
|
|
|
for (let i = 81; i <= 100; i += 1) {
|
|
writeFileSync(
|
|
join(projectDir, "src", "components", `file${i}.ts`),
|
|
`export const new_${i} = ${i}\n`,
|
|
);
|
|
}
|
|
}
|
|
|
|
function renderMarkdown({ summary, history, binaryPath }) {
|
|
const timestamp = new Date().toISOString();
|
|
return [
|
|
"# RTK benchmark evidence",
|
|
"",
|
|
`- Generated: ${timestamp}`,
|
|
`- RTK binary: \`${binaryPath}\``,
|
|
`- Telemetry: disabled via \`RTK_TELEMETRY_DISABLED=1\``,
|
|
`- Fixture: synthetic git + find + ls + npm test workload`,
|
|
"",
|
|
"## Aggregate savings",
|
|
"",
|
|
"| Commands | Input tokens | Output tokens | Saved tokens | Savings | Avg command time |",
|
|
"| --- | ---: | ---: | ---: | ---: | ---: |",
|
|
`| ${summary.total_commands} | ${summary.total_input} | ${summary.total_output} | ${summary.total_saved} | ${summary.avg_savings_pct.toFixed(1)}% | ${summary.avg_time_ms} ms |`,
|
|
"",
|
|
"## Command breakdown",
|
|
"",
|
|
"```text",
|
|
history.trim(),
|
|
"```",
|
|
"",
|
|
"## Commands exercised",
|
|
"",
|
|
"- `git status`",
|
|
"- `git diff`",
|
|
"- `find src -type f`",
|
|
"- `ls -R src`",
|
|
"- `npm run test`",
|
|
"",
|
|
].join("\n");
|
|
}
|
|
|
|
function main() {
|
|
const outputIndex = process.argv.indexOf("--output");
|
|
const outputPath = outputIndex !== -1 ? process.argv[outputIndex + 1] : null;
|
|
const binaryPath =
|
|
process.env.SF_RTK_PATH || process.env.SF_RTK_PATH || getManagedRtkPath();
|
|
|
|
if (!binaryPath) {
|
|
throw new Error("RTK binary path not resolved");
|
|
}
|
|
|
|
const workspace = mkdtempSync(join(tmpdir(), "sf-rtk-benchmark-"));
|
|
const homeDir = join(workspace, "home");
|
|
const projectDir = join(workspace, "project");
|
|
mkdirSync(homeDir, { recursive: true });
|
|
mkdirSync(projectDir, { recursive: true });
|
|
|
|
try {
|
|
createFixture(projectDir);
|
|
|
|
const env = {
|
|
...process.env,
|
|
HOME: homeDir,
|
|
RTK_TELEMETRY_DISABLED: "1",
|
|
};
|
|
|
|
const commands = [
|
|
["git", "status"],
|
|
["git", "diff"],
|
|
["find", "src", "-type", "f"],
|
|
["ls", "-R", "src"],
|
|
["npm", "run", "test"],
|
|
];
|
|
|
|
for (const command of commands) {
|
|
run(binaryPath, command, { cwd: projectDir, env });
|
|
}
|
|
|
|
const summaryJson = run(binaryPath, ["gain", "--all", "--format", "json"], {
|
|
cwd: projectDir,
|
|
env,
|
|
});
|
|
ensureOk(summaryJson, "rtk gain --all --format json");
|
|
const historyText = run(binaryPath, ["gain", "--history"], {
|
|
cwd: projectDir,
|
|
env,
|
|
});
|
|
ensureOk(historyText, "rtk gain --history");
|
|
|
|
const parsed = JSON.parse(summaryJson.stdout);
|
|
const markdown = renderMarkdown({
|
|
summary: parsed.summary,
|
|
history: historyText.stdout,
|
|
binaryPath,
|
|
});
|
|
|
|
if (outputPath) {
|
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
writeFileSync(outputPath, markdown, "utf-8");
|
|
console.log(outputPath);
|
|
return;
|
|
}
|
|
|
|
console.log(markdown);
|
|
} finally {
|
|
rmSync(workspace, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
main();
|