fix(test): reuse heavy-check lock in boundary prep

This commit is contained in:
Vincent Koc
2026-04-25 02:45:29 -07:00
parent 678d2c327c
commit c1f359c276
4 changed files with 73 additions and 13 deletions

View File

@@ -1299,7 +1299,7 @@
"build": "node scripts/build-all.mjs",
"build:ci-artifacts": "node scripts/build-all.mjs ciArtifacts",
"build:docker": "node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && node --import tsx scripts/canvas-a2ui-copy.ts && node --import tsx scripts/copy-hook-metadata.ts && node --import tsx scripts/copy-export-html-templates.ts && node --import tsx scripts/write-build-info.ts && node --experimental-strip-types scripts/write-cli-startup-metadata.ts && node --import tsx scripts/write-cli-compat.ts",
"build:plugin-sdk:dts": "tsgo -p tsconfig.plugin-sdk.dts.json",
"build:plugin-sdk:dts": "node scripts/run-tsgo.mjs -p tsconfig.plugin-sdk.dts.json --declaration true",
"build:plugin-sdk:strict-smoke": "pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts",
"build:strict-smoke": "pnpm canvas:a2ui:bundle && node scripts/tsdown-build.mjs && node scripts/runtime-postbuild.mjs && node scripts/build-stamp.mjs && pnpm build:plugin-sdk:dts && node --import tsx scripts/write-plugin-sdk-entry-dts.ts && node scripts/check-plugin-sdk-exports.mjs",
"canon:check": "node scripts/canon.mjs check",

View File

@@ -1,14 +1,10 @@
import { spawn } from "node:child_process";
import fs from "node:fs";
import { createRequire } from "node:module";
import path, { resolve } from "node:path";
import { isLocalCheckEnabled } from "./lib/local-heavy-check-runtime.mjs";
const require = createRequire(import.meta.url);
const repoRoot = resolve(import.meta.dirname, "..");
const tsgoBin = path.join(
path.dirname(require.resolve("@typescript/native-preview/package.json")),
"bin/tsgo.js",
);
const runTsgoScript = path.join(repoRoot, "scripts/run-tsgo.mjs");
const TYPE_INPUT_EXTENSIONS = new Set([".ts", ".tsx", ".d.ts", ".js", ".mjs", ".json"]);
const VALID_MODES = new Set(["all", "package-boundary"]);
@@ -167,7 +163,7 @@ export function runNodeStep(label, args, timeoutMs, params = {}) {
return new Promise((resolvePromise, rejectPromise) => {
const child = spawn(process.execPath, args, {
cwd: repoRoot,
env: process.env,
env: params.env ? { ...process.env, ...params.env } : process.env,
signal: abortController?.signal,
stdio: ["ignore", "pipe", "pipe"],
});
@@ -231,7 +227,9 @@ export function runNodeStep(label, args, timeoutMs, params = {}) {
export async function runNodeStepsInParallel(steps) {
const abortController = new AbortController();
const results = await Promise.allSettled(
steps.map((step) => runNodeStep(step.label, step.args, step.timeoutMs, { abortController })),
steps.map((step) =>
runNodeStep(step.label, step.args, step.timeoutMs, { abortController, env: step.env }),
),
);
const firstFailure = results.find((result) => result.status === "rejected");
if (firstFailure) {
@@ -239,6 +237,17 @@ export async function runNodeStepsInParallel(steps) {
}
}
export async function runNodeSteps(steps, env = process.env) {
if (!isLocalCheckEnabled(env)) {
await runNodeStepsInParallel(steps);
return;
}
for (const step of steps) {
await runNodeStep(step.label, step.args, step.timeoutMs, { env: step.env });
}
}
export async function main(argv = process.argv.slice(2)) {
try {
const mode = parseMode(argv);
@@ -272,7 +281,8 @@ export async function main(argv = process.argv.slice(2)) {
});
pendingSteps.push({
label: "plugin-sdk boundary dts",
args: [tsgoBin, "-p", "tsconfig.plugin-sdk.dts.json"],
args: [runTsgoScript, "-p", "tsconfig.plugin-sdk.dts.json", "--declaration", "true"],
env: { OPENCLAW_TSGO_HEAVY_CHECK_LOCK_HELD: "1" },
timeoutMs: 300_000,
stampPath: ROOT_DTS_STAMP,
});
@@ -287,7 +297,8 @@ export async function main(argv = process.argv.slice(2)) {
});
pendingSteps.push({
label: "plugin-sdk package boundary dts",
args: [tsgoBin, "-p", "packages/plugin-sdk/tsconfig.json"],
args: [runTsgoScript, "-p", "packages/plugin-sdk/tsconfig.json", "--declaration", "true"],
env: { OPENCLAW_TSGO_HEAVY_CHECK_LOCK_HELD: "1" },
timeoutMs: 300_000,
stampPath: PACKAGE_DTS_STAMP,
});
@@ -296,7 +307,7 @@ export async function main(argv = process.argv.slice(2)) {
}
if (pendingSteps.length > 0) {
await runNodeStepsInParallel(pendingSteps);
await runNodeSteps(pendingSteps);
for (const step of pendingSteps) {
if (step.stampPath) {
writeStampFile(step.stampPath);

View File

@@ -21,7 +21,9 @@ if (tsBuildInfoFile) {
}
const sparseGuardError = getSparseTsgoGuardError(finalArgs, { cwd: process.cwd() });
const releaseLock =
sparseGuardError || !shouldAcquireLocalHeavyCheckLockForTsgo(finalArgs, env)
sparseGuardError ||
env.OPENCLAW_TSGO_HEAVY_CHECK_LOCK_HELD === "1" ||
!shouldAcquireLocalHeavyCheckLockForTsgo(finalArgs, env)
? () => {}
: acquireLocalHeavyCheckLockSync({
cwd: process.cwd(),

View File

@@ -6,6 +6,7 @@ import {
createPrefixedOutputWriter,
isArtifactSetFresh,
parseMode,
runNodeSteps,
runNodeStepsInParallel,
} from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
@@ -57,6 +58,52 @@ describe("prepare-extension-package-boundary-artifacts", () => {
expect(Date.now() - startedAt).toBeLessThan(abortBudgetMs);
}, 45_000);
it("runs boundary prep steps serially for local checks", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-serial-"));
tempRoots.add(rootDir);
const logPath = path.join(rootDir, "steps.log");
const appendScript = (label: string) =>
`const fs=require("node:fs");` +
`const log=${JSON.stringify(logPath)};` +
`fs.appendFileSync(log, ${JSON.stringify(`${label}-start\n`)});` +
`setTimeout(()=>{fs.appendFileSync(log, ${JSON.stringify(`${label}-end\n`)});}, 50);`;
await runNodeSteps(
[
{ label: "first", args: ["--eval", appendScript("first")], timeoutMs: 5_000 },
{ label: "second", args: ["--eval", appendScript("second")], timeoutMs: 5_000 },
],
{ OPENCLAW_LOCAL_CHECK: "1" },
);
expect(fs.readFileSync(logPath, "utf8").trim().split("\n")).toEqual([
"first-start",
"first-end",
"second-start",
"second-end",
]);
});
it("passes step-specific environment overrides to child steps", async () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-env-"));
tempRoots.add(rootDir);
const outputPath = path.join(rootDir, "env.txt");
const writeEnvScript =
`const fs=require("node:fs");` +
`fs.writeFileSync(${JSON.stringify(outputPath)}, process.env.OPENCLAW_TEST_ENV || "", "utf8");`;
await runNodeStepsInParallel([
{
label: "env-step",
args: ["--eval", writeEnvScript],
env: { OPENCLAW_TEST_ENV: "passed" },
timeoutMs: 5_000,
},
]);
expect(fs.readFileSync(outputPath, "utf8")).toBe("passed");
});
it("treats artifacts as fresh only when outputs are newer than inputs", () => {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-boundary-prep-"));
tempRoots.add(rootDir);