From c1f359c2761a4da98d5e51af91eaf219bcafa897 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 25 Apr 2026 02:45:29 -0700 Subject: [PATCH] fix(test): reuse heavy-check lock in boundary prep --- package.json | 2 +- ...e-extension-package-boundary-artifacts.mjs | 33 ++++++++----- scripts/run-tsgo.mjs | 4 +- ...tension-package-boundary-artifacts.test.ts | 47 +++++++++++++++++++ 4 files changed, 73 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 73e968568cb..3fafa7673ce 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/scripts/prepare-extension-package-boundary-artifacts.mjs b/scripts/prepare-extension-package-boundary-artifacts.mjs index 8f62cf329d4..4e87448c6b7 100644 --- a/scripts/prepare-extension-package-boundary-artifacts.mjs +++ b/scripts/prepare-extension-package-boundary-artifacts.mjs @@ -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); diff --git a/scripts/run-tsgo.mjs b/scripts/run-tsgo.mjs index 2f8c7fe741f..5bbcbdaa599 100644 --- a/scripts/run-tsgo.mjs +++ b/scripts/run-tsgo.mjs @@ -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(), diff --git a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts index 95e5e12aa86..d7ba87fa11d 100644 --- a/test/scripts/prepare-extension-package-boundary-artifacts.test.ts +++ b/test/scripts/prepare-extension-package-boundary-artifacts.test.ts @@ -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);