mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
test(ci): skip unrelated Bun launcher builds (#122333)
Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
committed by
GitHub
parent
b3cb8f7ab2
commit
25f7fc0ec5
@@ -1842,7 +1842,7 @@ jobs:
|
||||
fi
|
||||
;;
|
||||
bun-launcher)
|
||||
OPENCLAW_TEST_BUN_LAUNCHER=1 pnpm test test/openclaw-launcher.e2e.test.ts
|
||||
OPENCLAW_E2E_SKIP_BUILD=1 OPENCLAW_TEST_BUN_LAUNCHER=1 pnpm test test/openclaw-launcher.e2e.test.ts
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported checks-fast task: $TASK" >&2
|
||||
|
||||
@@ -4,7 +4,7 @@ import { once } from "node:events";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { build } from "esbuild";
|
||||
import { build as esbuild } from "esbuild";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { cleanupTempDirs, makeTempDir } from "./helpers/temp-dir.js";
|
||||
|
||||
@@ -20,7 +20,7 @@ async function makeLauncherFixture(fixtureRoots: string[]): Promise<string> {
|
||||
|
||||
async function addCompiledMjsEntryFixture(fixtureRoot: string): Promise<void> {
|
||||
const sourceRoot = path.resolve(process.cwd(), "src");
|
||||
await build({
|
||||
await esbuild({
|
||||
bundle: true,
|
||||
entryPoints: [path.join(sourceRoot, "entry.ts")],
|
||||
format: "esm",
|
||||
@@ -41,26 +41,6 @@ async function addCompiledMjsEntryFixture(fixtureRoot: string): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
async function makeLauncherProbeFixture(
|
||||
fixtureRoots: string[],
|
||||
probeSource: string,
|
||||
): Promise<string> {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
const launcherPath = path.join(fixtureRoot, "openclaw.mjs");
|
||||
const launcher = await fs.readFile(launcherPath, "utf8");
|
||||
const bootstrapStart = "\nif (!waitingForCompileCacheRespawn) {";
|
||||
const bootstrapIndex = launcher.indexOf(bootstrapStart);
|
||||
if (bootstrapIndex < 0) {
|
||||
throw new Error("openclaw launcher bootstrap block was not found");
|
||||
}
|
||||
await fs.writeFile(
|
||||
launcherPath,
|
||||
`${launcher.slice(0, bootstrapIndex)}\n${probeSource}\n`,
|
||||
"utf8",
|
||||
);
|
||||
return fixtureRoot;
|
||||
}
|
||||
|
||||
async function addSourceTreeMarker(fixtureRoot: string): Promise<void> {
|
||||
await fs.mkdir(path.join(fixtureRoot, "src"), { recursive: true });
|
||||
await fs.writeFile(path.join(fixtureRoot, "src", "entry.ts"), "export {};\n", "utf8");
|
||||
@@ -83,26 +63,6 @@ async function addCompileCacheProbe(fixtureRoot: string): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
async function addLauncherRuntimeMock(
|
||||
fixtureRoot: string,
|
||||
params: { nodeVersion: string; platform: NodeJS.Platform },
|
||||
): Promise<string> {
|
||||
const mockPath = path.join(fixtureRoot, "mock-launcher-runtime.mjs");
|
||||
await fs.writeFile(
|
||||
mockPath,
|
||||
[
|
||||
"Object.defineProperty(process, 'platform', {",
|
||||
` value: ${JSON.stringify(params.platform)},`,
|
||||
"});",
|
||||
"Object.defineProperty(process.versions, 'node', {",
|
||||
` value: ${JSON.stringify(params.nodeVersion)},`,
|
||||
"});",
|
||||
].join("\n"),
|
||||
"utf8",
|
||||
);
|
||||
return mockPath;
|
||||
}
|
||||
|
||||
async function waitForJsonFile<T>(filePath: string, timeoutMs: number): Promise<T> {
|
||||
const deadline = Date.now() + timeoutMs;
|
||||
let lastError: unknown;
|
||||
@@ -188,10 +148,6 @@ describe("openclaw launcher", () => {
|
||||
});
|
||||
|
||||
it("keeps the bootstrap Node range aligned with the package engine", async () => {
|
||||
const packageJsonRaw = await fs.readFile(path.resolve(process.cwd(), "package.json"), "utf8");
|
||||
const packageJson = JSON.parse(packageJsonRaw) as { engines?: { node?: string } };
|
||||
expect(packageJson.engines?.node).toBe(">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0");
|
||||
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
await fs.writeFile(
|
||||
path.join(fixtureRoot, "dist", "entry.js"),
|
||||
@@ -291,36 +247,6 @@ describe("openclaw launcher", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("runs the CLI under Bun when the runtime provides node:sqlite", async () => {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
await fs.writeFile(
|
||||
path.join(fixtureRoot, "dist", "entry.js"),
|
||||
'process.stdout.write("bun-runtime-entry\\n");\n',
|
||||
"utf8",
|
||||
);
|
||||
const mockRuntime = path.join(fixtureRoot, "mock-bun-sqlite-runtime.mjs");
|
||||
await fs.writeFile(
|
||||
mockRuntime,
|
||||
// Simulates Bun >=1.4 (Rust rewrite): bun-branded runtime with node:sqlite
|
||||
// available; Node's own getBuiltinModule answers the launcher probe.
|
||||
"Object.defineProperty(process.versions, 'bun', { value: '1.4.0' });",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--import", pathToFileURL(mockRuntime).href, path.join(fixtureRoot, "openclaw.mjs")],
|
||||
{
|
||||
cwd: fixtureRoot,
|
||||
env: launcherEnv(),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toContain("bun-runtime-entry");
|
||||
});
|
||||
|
||||
it("surfaces transitive entry import failures instead of masking them as missing dist", async () => {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
await fs.writeFile(
|
||||
@@ -372,57 +298,6 @@ describe("openclaw launcher", () => {
|
||||
expect(result.stderr).toContain("--profile requires a value");
|
||||
});
|
||||
|
||||
it("treats Bun direct optional import misses as direct launcher misses", async () => {
|
||||
const fixtureRoot = await makeLauncherProbeFixture(
|
||||
fixtureRoots,
|
||||
[
|
||||
"const result = {",
|
||||
" direct: isDirectModuleNotFoundError(",
|
||||
" { message: `Cannot find module './dist/warning-filter.js' from '${fileURLToPath(import.meta.url)}'` },",
|
||||
" './dist/warning-filter.js',",
|
||||
" ),",
|
||||
" directWithCode: isDirectModuleNotFoundError(",
|
||||
" { code: 'ERR_MODULE_NOT_FOUND', message: `Cannot find module './dist/warning-filter.js' from '${fileURLToPath(import.meta.url)}'` },",
|
||||
" './dist/warning-filter.js',",
|
||||
" ),",
|
||||
" transitive: isDirectModuleNotFoundError(",
|
||||
" { message: \"Cannot find module './nested.js' from '/pkg/openclaw/dist/entry.js'\" },",
|
||||
" './dist/entry.js',",
|
||||
" ),",
|
||||
" sameSpecifierTransitive: isDirectModuleNotFoundError(",
|
||||
" { message: \"Cannot find module './dist/entry.js' from '/pkg/openclaw/dist/entry.js'\" },",
|
||||
" './dist/entry.js',",
|
||||
" ),",
|
||||
" nonModuleUrl: isDirectModuleNotFoundError(",
|
||||
" { message: 'boom', url: new URL('./dist/warning-filter.js', import.meta.url).href },",
|
||||
" './dist/warning-filter.js',",
|
||||
" ),",
|
||||
" nonModulePath: isDirectModuleNotFoundError(",
|
||||
" { message: `Cannot find module '${fileURLToPath(new URL('./dist/warning-filter.js', import.meta.url))}'` },",
|
||||
" './dist/warning-filter.js',",
|
||||
" ),",
|
||||
"};",
|
||||
"process.stdout.write(`${JSON.stringify(result)}\\n`);",
|
||||
].join("\n"),
|
||||
);
|
||||
|
||||
const result = spawnSync(process.execPath, [path.join(fixtureRoot, "openclaw.mjs")], {
|
||||
cwd: fixtureRoot,
|
||||
env: launcherEnv(),
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(JSON.parse(result.stdout)).toEqual({
|
||||
direct: true,
|
||||
directWithCode: true,
|
||||
nonModulePath: false,
|
||||
nonModuleUrl: false,
|
||||
sameSpecifierTransitive: false,
|
||||
transitive: false,
|
||||
});
|
||||
});
|
||||
|
||||
it.runIf(process.env.OPENCLAW_TEST_BUN_LAUNCHER === "1" && hasBunRuntime())(
|
||||
"gates the real Bun runtime on node:sqlite availability",
|
||||
async () => {
|
||||
@@ -796,21 +671,6 @@ describe("openclaw launcher", () => {
|
||||
expect(result.stderr).toContain("github:openclaw/openclaw#<ref>");
|
||||
});
|
||||
|
||||
it("keeps compile cache off for source-checkout launchers", async () => {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
await addSourceTreeMarker(fixtureRoot);
|
||||
await addCompileCacheProbe(fixtureRoot);
|
||||
|
||||
const result = spawnSync(process.execPath, [path.join(fixtureRoot, "openclaw.mjs")], {
|
||||
cwd: fixtureRoot,
|
||||
env: launcherEnv(),
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toBe("cache:disabled;respawn:0");
|
||||
});
|
||||
|
||||
it("respawns source-checkout launchers without inherited NODE_COMPILE_CACHE", async () => {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
await addGitMarker(fixtureRoot);
|
||||
@@ -1101,38 +961,6 @@ describe("openclaw launcher", () => {
|
||||
expect(result.stdout).not.toContain(path.join(runCwd, "openclaw"));
|
||||
});
|
||||
|
||||
it("keeps compile cache enabled for unaffected packaged launcher runtimes", async () => {
|
||||
const cases: Array<{ nodeVersion: string; platform: NodeJS.Platform }> = [
|
||||
{ nodeVersion: "24.15.0", platform: "win32" },
|
||||
{ nodeVersion: "22.22.3", platform: "linux" },
|
||||
{ nodeVersion: "25.9.0", platform: "darwin" },
|
||||
];
|
||||
|
||||
for (const runtime of cases) {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
const tmpRoot = makeTempDir(fixtureRoots, "openclaw-launcher-tmp-");
|
||||
const mockRuntime = await addLauncherRuntimeMock(fixtureRoot, runtime);
|
||||
await addCompileCacheProbe(fixtureRoot);
|
||||
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
["--import", pathToFileURL(mockRuntime).href, path.join(fixtureRoot, "openclaw.mjs")],
|
||||
{
|
||||
cwd: fixtureRoot,
|
||||
env: launcherEnv({
|
||||
TMP: tmpRoot,
|
||||
TEMP: tmpRoot,
|
||||
TMPDIR: tmpRoot,
|
||||
}),
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
expect(result.stdout).toBe("cache:enabled;respawn:0");
|
||||
}
|
||||
});
|
||||
|
||||
it("enables compile cache for packaged launchers", async () => {
|
||||
const fixtureRoot = await makeLauncherFixture(fixtureRoots);
|
||||
const tmpRoot = makeTempDir(fixtureRoots, "openclaw-launcher-tmp-");
|
||||
|
||||
@@ -5020,6 +5020,10 @@ printf '%s\n' "\${CURL_SUCCESS_IP:-203.0.113.7}"
|
||||
expect(checksFastRun.run).toContain("max-lines-ratchet)");
|
||||
expect(checksFastRun.run).toContain("coercion-helpers)");
|
||||
expect(checksFastRun.run).toContain("pnpm check:coercion-helpers");
|
||||
expect(checksFastRun.run).toContain("bun-launcher)");
|
||||
expect(checksFastRun.run).toContain(
|
||||
"OPENCLAW_E2E_SKIP_BUILD=1 OPENCLAW_TEST_BUN_LAUNCHER=1 pnpm test test/openclaw-launcher.e2e.test.ts",
|
||||
);
|
||||
expect(checksFastRun.run).toContain('has_package_script "check:max-lines-ratchet"');
|
||||
expect(checksFastRun.env.RATCHET_PR_HEAD_SHA).toBe(
|
||||
"${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }}",
|
||||
|
||||
@@ -61,13 +61,16 @@ describe("vitest E2E global setup", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("uses an exact-run prebuilt dist without rebuilding it", async () => {
|
||||
const runCommand = vi.fn<SetupCommandRunner>();
|
||||
it.each(["OPENCLAW_E2E_SKIP_BUILD", "OPENCLAW_E2E_USE_PREBUILT_DIST"] as const)(
|
||||
"skips rebuilding when %s is set",
|
||||
async (envName) => {
|
||||
const runCommand = vi.fn<SetupCommandRunner>();
|
||||
|
||||
await runE2eGlobalSetup(runCommand, { OPENCLAW_E2E_USE_PREBUILT_DIST: "1" });
|
||||
await runE2eGlobalSetup(runCommand, { [envName]: "1" });
|
||||
|
||||
expect(runCommand).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(runCommand).not.toHaveBeenCalled();
|
||||
},
|
||||
);
|
||||
|
||||
posixIt("forwards output and SIGTERM through the runner process group", async () => {
|
||||
const fixtureDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-e2e-setup-group-"));
|
||||
|
||||
@@ -30,9 +30,9 @@ export async function runE2eGlobalSetup(
|
||||
runCommand: SetupCommandRunner = runE2eSetupCommand,
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
): Promise<void> {
|
||||
// Exact-run CI artifact consumers already have the complete built surface.
|
||||
// Rebuilding here would discard that provenance and duplicate the slow step.
|
||||
if (env.OPENCLAW_E2E_USE_PREBUILT_DIST === "1") {
|
||||
// Some focused suites bring their own fixtures, while exact-run artifact consumers already
|
||||
// have the complete built surface. In both cases rebuilding here would duplicate slow work.
|
||||
if (env.OPENCLAW_E2E_SKIP_BUILD === "1" || env.OPENCLAW_E2E_USE_PREBUILT_DIST === "1") {
|
||||
return;
|
||||
}
|
||||
const commands = [
|
||||
@@ -49,8 +49,8 @@ export async function runE2eGlobalSetup(
|
||||
env,
|
||||
},
|
||||
];
|
||||
for (const { args, env } of commands) {
|
||||
const status = await runCommand(args, env);
|
||||
for (const { args, env: commandEnv } of commands) {
|
||||
const status = await runCommand(args, commandEnv);
|
||||
if (status !== 0) {
|
||||
throw new Error(`E2E setup command failed with exit code ${status}: ${args.join(" ")}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user