mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 01:31:08 +00:00
perf(plugins): report slow boundary compiles
This commit is contained in:
@@ -23,6 +23,7 @@ const prepareBoundaryArtifactsBin = resolve(
|
||||
);
|
||||
const extensionPackageBoundaryBaseConfig = "../tsconfig.package-boundary.base.json";
|
||||
const FAILURE_OUTPUT_TAIL_LINES = 40;
|
||||
const SLOW_COMPILE_SUMMARY_LIMIT = 10;
|
||||
const COMPILE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".json"]);
|
||||
const ROOTDIR_BOUNDARY_CANARY_IMPORT_PATH =
|
||||
"../../src/plugins/contracts/rootdir-boundary-canary.ts";
|
||||
@@ -123,6 +124,23 @@ export function formatSkippedCompileProgress(params = {}) {
|
||||
return `skipped ${skippedCount} fresh plugin compiles\n`;
|
||||
}
|
||||
|
||||
export function formatSlowCompileSummary(params = {}) {
|
||||
const compileTimings = Array.isArray(params.compileTimings) ? params.compileTimings : [];
|
||||
if (compileTimings.length === 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const limit =
|
||||
Number.isInteger(params.limit) && params.limit > 0 ? params.limit : SLOW_COMPILE_SUMMARY_LIMIT;
|
||||
const lines = ["slowest plugin compiles:"];
|
||||
for (const timing of [...compileTimings]
|
||||
.toSorted((left, right) => right.elapsedMs - left.elapsedMs)
|
||||
.slice(0, limit)) {
|
||||
lines.push(`- ${timing.extensionId}: ${timing.elapsedMs}ms`);
|
||||
}
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
export function formatStepFailure(label, params = {}) {
|
||||
const stdoutSection = summarizeOutputSection("stdout", params.stdout ?? "");
|
||||
const stderrSection = summarizeOutputSection("stderr", params.stderr ?? "");
|
||||
@@ -411,7 +429,7 @@ export function runNodeStepAsync(label, args, timeoutMs, params = {}) {
|
||||
clearTimeout(timer);
|
||||
settled = true;
|
||||
if (code === 0) {
|
||||
resolvePromise({ stdout, stderr });
|
||||
resolvePromise({ stdout, stderr, elapsedMs: Date.now() - startedAt });
|
||||
return;
|
||||
}
|
||||
const error = attachStepFailureMetadata(
|
||||
@@ -454,13 +472,13 @@ export async function runNodeStepsWithConcurrency(steps, concurrency) {
|
||||
}
|
||||
const step = steps[index];
|
||||
step.onStart?.();
|
||||
await runNodeStepAsync(step.label, step.args, step.timeoutMs, {
|
||||
const result = await runNodeStepAsync(step.label, step.args, step.timeoutMs, {
|
||||
abortController,
|
||||
onFailure(error) {
|
||||
firstFailure ??= error;
|
||||
},
|
||||
});
|
||||
step.onSuccess?.();
|
||||
step.onSuccess?.(result);
|
||||
}
|
||||
});
|
||||
await Promise.allSettled(workers);
|
||||
@@ -573,6 +591,7 @@ async function runCompileCheck(extensionIds) {
|
||||
process.stdout.write(`compile concurrency ${concurrency}\n`);
|
||||
const compileStartedAt = Date.now();
|
||||
let skippedCompileCount = 0;
|
||||
const compileTimings = [];
|
||||
const steps = extensionIds
|
||||
.map((extensionId, index) => {
|
||||
const tsBuildInfoPath = resolveBoundaryTsBuildInfoPath(extensionId);
|
||||
@@ -602,8 +621,12 @@ async function runCompileCheck(extensionIds) {
|
||||
onStart() {
|
||||
process.stdout.write(`[${index + 1}/${extensionIds.length}] ${extensionId}\n`);
|
||||
},
|
||||
onSuccess() {
|
||||
onSuccess(result) {
|
||||
writeStampFile(resolveBoundaryTsStampPath(extensionId));
|
||||
compileTimings.push({
|
||||
extensionId,
|
||||
elapsedMs: result.elapsedMs,
|
||||
});
|
||||
},
|
||||
args: [
|
||||
tscBin,
|
||||
@@ -634,6 +657,7 @@ async function runCompileCheck(extensionIds) {
|
||||
compileCount: steps.length,
|
||||
skippedCompileCount,
|
||||
compileElapsedMs: Date.now() - compileStartedAt,
|
||||
compileTimings,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -709,12 +733,13 @@ export async function main(argv = process.argv.slice(2)) {
|
||||
let compileCount = 0;
|
||||
let skippedCompileCount = 0;
|
||||
let compileElapsedMs;
|
||||
let compileTimings = [];
|
||||
let canaryElapsedMs;
|
||||
|
||||
try {
|
||||
cleanupCanaryArtifactsForExtensions(cleanupExtensionIds);
|
||||
if (mode === "all" || mode === "compile") {
|
||||
({ prepElapsedMs, compileCount, skippedCompileCount, compileElapsedMs } =
|
||||
({ prepElapsedMs, compileCount, skippedCompileCount, compileElapsedMs, compileTimings } =
|
||||
await runCompileCheck(optInExtensionIds));
|
||||
}
|
||||
if (shouldRunCanary) {
|
||||
@@ -732,6 +757,11 @@ export async function main(argv = process.argv.slice(2)) {
|
||||
elapsedMs: Date.now() - startedAt,
|
||||
}),
|
||||
);
|
||||
process.stdout.write(
|
||||
formatSlowCompileSummary({
|
||||
compileTimings,
|
||||
}),
|
||||
);
|
||||
} finally {
|
||||
releaseBoundaryLock?.();
|
||||
teardownCanaryCleanup?.();
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
acquireBoundaryCheckLock,
|
||||
cleanupCanaryArtifactsForExtensions,
|
||||
formatBoundaryCheckSuccessSummary,
|
||||
formatSlowCompileSummary,
|
||||
formatSkippedCompileProgress,
|
||||
formatStepFailure,
|
||||
installCanaryArtifactCleanup,
|
||||
@@ -206,6 +207,19 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
).toBe("skipped 97 fresh plugin compiles\n");
|
||||
});
|
||||
|
||||
it("formats the slowest plugin compiles in descending order", () => {
|
||||
expect(
|
||||
formatSlowCompileSummary({
|
||||
compileTimings: [
|
||||
{ extensionId: "quick", elapsedMs: 40 },
|
||||
{ extensionId: "slow", elapsedMs: 900 },
|
||||
{ extensionId: "medium", elapsedMs: 250 },
|
||||
],
|
||||
limit: 2,
|
||||
}),
|
||||
).toBe(["slowest plugin compiles:", "- slow: 900ms", "- medium: 250ms", ""].join("\n"));
|
||||
});
|
||||
|
||||
it("treats a plugin compile as fresh only when its outputs are newer than plugin and shared sdk inputs", () => {
|
||||
const { rootDir, extensionRoot } = createTempExtensionRoot();
|
||||
const extensionSourcePath = path.join(extensionRoot, "index.ts");
|
||||
@@ -331,4 +345,25 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
|
||||
expect(Date.now() - startedAt).toBeLessThan(2_000);
|
||||
});
|
||||
|
||||
it("passes successful step timing metadata to onSuccess handlers", async () => {
|
||||
const elapsedTimes: number[] = [];
|
||||
|
||||
await runNodeStepsWithConcurrency(
|
||||
[
|
||||
{
|
||||
label: "demo-step",
|
||||
args: ["--eval", "setTimeout(() => process.exit(0), 10)"],
|
||||
timeoutMs: 5_000,
|
||||
onSuccess(result: { elapsedMs: number }) {
|
||||
elapsedTimes.push(result.elapsedMs);
|
||||
},
|
||||
},
|
||||
],
|
||||
1,
|
||||
);
|
||||
|
||||
expect(elapsedTimes).toHaveLength(1);
|
||||
expect(elapsedTimes[0]).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user