mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 04:31:10 +00:00
perf(plugins): stabilize warm boundary compile skips
This commit is contained in:
@@ -106,6 +106,20 @@ export function formatBoundaryCheckSuccessSummary(params = {}) {
|
||||
return `${lines.join("\n")}\n`;
|
||||
}
|
||||
|
||||
export function formatSkippedCompileProgress(params = {}) {
|
||||
const skippedCount = params.skippedCount ?? 0;
|
||||
const totalCount = params.totalCount ?? 0;
|
||||
if (!Number.isInteger(skippedCount) || skippedCount <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const staleCount = Math.max(0, totalCount - skippedCount);
|
||||
if (staleCount > 0) {
|
||||
return `skipped ${skippedCount} fresh plugin compiles before running ${staleCount} stale plugin checks\n`;
|
||||
}
|
||||
return `skipped ${skippedCount} fresh plugin compiles\n`;
|
||||
}
|
||||
|
||||
export function formatStepFailure(label, params = {}) {
|
||||
const stdoutSection = summarizeOutputSection("stdout", params.stdout ?? "");
|
||||
const stderrSection = summarizeOutputSection("stderr", params.stderr ?? "");
|
||||
@@ -226,11 +240,16 @@ export function isBoundaryCompileFresh(extensionId, params = {}) {
|
||||
collectNewestMtime(resolve(rootDir, "packages/plugin-sdk/dist")),
|
||||
);
|
||||
const oldestOutputMtimeMs = collectOldestMtime([
|
||||
resolve(rootDir, "extensions", extensionId, "dist", ".boundary-tsc.tsbuildinfo"),
|
||||
resolveBoundaryTsStampPath(extensionId, rootDir),
|
||||
]);
|
||||
return oldestOutputMtimeMs !== null && oldestOutputMtimeMs >= newestInputMtimeMs;
|
||||
}
|
||||
|
||||
function writeStampFile(filePath) {
|
||||
mkdirSync(dirname(filePath), { recursive: true });
|
||||
writeFileSync(filePath, `${new Date().toISOString()}\n`, "utf8");
|
||||
}
|
||||
|
||||
function runNodeStep(label, args, timeoutMs) {
|
||||
const startedAt = Date.now();
|
||||
const result = spawnSync(process.execPath, args, {
|
||||
@@ -429,6 +448,7 @@ export async function runNodeStepsWithConcurrency(steps, concurrency) {
|
||||
firstFailure ??= error;
|
||||
},
|
||||
});
|
||||
step.onSuccess?.();
|
||||
}
|
||||
});
|
||||
await Promise.allSettled(workers);
|
||||
@@ -474,6 +494,10 @@ function resolveBoundaryTsBuildInfoPath(extensionId) {
|
||||
return resolve(repoRoot, "extensions", extensionId, "dist", ".boundary-tsc.tsbuildinfo");
|
||||
}
|
||||
|
||||
function resolveBoundaryTsStampPath(extensionId, rootDir = repoRoot) {
|
||||
return resolve(rootDir, "extensions", extensionId, "dist", ".boundary-tsc.stamp");
|
||||
}
|
||||
|
||||
export function resolveBoundaryCheckLockPath(rootDir = repoRoot) {
|
||||
return resolve(rootDir, "dist", ".extension-package-boundary.lock");
|
||||
}
|
||||
@@ -525,6 +549,7 @@ async function runCompileCheck(extensionIds) {
|
||||
runNodeStep("plugin-sdk boundary prep", [prepareBoundaryArtifactsBin], 420_000);
|
||||
const prepElapsedMs = Date.now() - prepStartedAt;
|
||||
const concurrency = resolveCompileConcurrency();
|
||||
const verboseFreshLogs = process.env.OPENCLAW_EXTENSION_BOUNDARY_VERBOSE_FRESH === "1";
|
||||
process.stdout.write(`compile concurrency ${concurrency}\n`);
|
||||
const compileStartedAt = Date.now();
|
||||
let skippedCompileCount = 0;
|
||||
@@ -534,9 +559,11 @@ async function runCompileCheck(extensionIds) {
|
||||
mkdirSync(dirname(tsBuildInfoPath), { recursive: true });
|
||||
if (isBoundaryCompileFresh(extensionId)) {
|
||||
skippedCompileCount += 1;
|
||||
process.stdout.write(
|
||||
`[${index + 1}/${extensionIds.length}] ${extensionId} (fresh; skipping)\n`,
|
||||
);
|
||||
if (verboseFreshLogs) {
|
||||
process.stdout.write(
|
||||
`[${index + 1}/${extensionIds.length}] ${extensionId} (fresh; skipping)\n`,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
@@ -544,6 +571,9 @@ async function runCompileCheck(extensionIds) {
|
||||
onStart() {
|
||||
process.stdout.write(`[${index + 1}/${extensionIds.length}] ${extensionId}\n`);
|
||||
},
|
||||
onSuccess() {
|
||||
writeStampFile(resolveBoundaryTsStampPath(extensionId));
|
||||
},
|
||||
args: [
|
||||
tscBin,
|
||||
"-p",
|
||||
@@ -557,6 +587,14 @@ async function runCompileCheck(extensionIds) {
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
if (!verboseFreshLogs && skippedCompileCount > 0) {
|
||||
process.stdout.write(
|
||||
formatSkippedCompileProgress({
|
||||
skippedCount: skippedCompileCount,
|
||||
totalCount: extensionIds.length,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (steps.length > 0) {
|
||||
await runNodeStepsWithConcurrency(steps, concurrency);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
acquireBoundaryCheckLock,
|
||||
cleanupCanaryArtifactsForExtensions,
|
||||
formatBoundaryCheckSuccessSummary,
|
||||
formatSkippedCompileProgress,
|
||||
formatStepFailure,
|
||||
installCanaryArtifactCleanup,
|
||||
isBoundaryCompileFresh,
|
||||
@@ -189,11 +190,27 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("formats skipped compile progress concisely", () => {
|
||||
expect(
|
||||
formatSkippedCompileProgress({
|
||||
skippedCount: 13,
|
||||
totalCount: 97,
|
||||
}),
|
||||
).toBe("skipped 13 fresh plugin compiles before running 84 stale plugin checks\n");
|
||||
|
||||
expect(
|
||||
formatSkippedCompileProgress({
|
||||
skippedCount: 97,
|
||||
totalCount: 97,
|
||||
}),
|
||||
).toBe("skipped 97 fresh plugin compiles\n");
|
||||
});
|
||||
|
||||
it("treats a plugin compile as fresh only when its outputs are newer than plugin and sdk inputs", () => {
|
||||
const { rootDir, extensionRoot } = createTempExtensionRoot();
|
||||
const extensionSourcePath = path.join(extensionRoot, "index.ts");
|
||||
const extensionTsconfigPath = path.join(extensionRoot, "tsconfig.json");
|
||||
const buildInfoPath = path.join(extensionRoot, "dist", ".boundary-tsc.tsbuildinfo");
|
||||
const stampPath = path.join(extensionRoot, "dist", ".boundary-tsc.stamp");
|
||||
const rootSdkBuildInfoPath = path.join(rootDir, "dist", "plugin-sdk", ".tsbuildinfo");
|
||||
const packageSdkBuildInfoPath = path.join(
|
||||
rootDir,
|
||||
@@ -210,7 +227,7 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
);
|
||||
|
||||
fs.mkdirSync(path.dirname(extensionSourcePath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(buildInfoPath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(stampPath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(rootSdkBuildInfoPath), { recursive: true });
|
||||
fs.mkdirSync(path.dirname(packageSdkBuildInfoPath), { recursive: true });
|
||||
|
||||
@@ -220,7 +237,7 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
'{ "extends": "../tsconfig.package-boundary.base.json" }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(buildInfoPath, "ok\n", "utf8");
|
||||
fs.writeFileSync(stampPath, "ok\n", "utf8");
|
||||
fs.writeFileSync(rootSdkBuildInfoPath, "ok\n", "utf8");
|
||||
fs.writeFileSync(packageSdkBuildInfoPath, "ok\n", "utf8");
|
||||
fs.writeFileSync(entryShimStampPath, "ok\n", "utf8");
|
||||
@@ -230,7 +247,7 @@ describe("check-extension-package-tsc-boundary", () => {
|
||||
fs.utimesSync(rootSdkBuildInfoPath, new Date(2_000), new Date(2_000));
|
||||
fs.utimesSync(packageSdkBuildInfoPath, new Date(2_000), new Date(2_000));
|
||||
fs.utimesSync(entryShimStampPath, new Date(2_000), new Date(2_000));
|
||||
fs.utimesSync(buildInfoPath, new Date(3_000), new Date(3_000));
|
||||
fs.utimesSync(stampPath, new Date(3_000), new Date(3_000));
|
||||
|
||||
expect(isBoundaryCompileFresh("demo", { rootDir })).toBe(true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user