diff --git a/apps/vscode/scripts/test-standalone-core-api-server.ts b/apps/vscode/scripts/test-standalone-core-api-server.ts index 09b2bec945..0854c3d404 100644 --- a/apps/vscode/scripts/test-standalone-core-api-server.ts +++ b/apps/vscode/scripts/test-standalone-core-api-server.ts @@ -83,8 +83,8 @@ async function main(): Promise { const clineTestWorkspace = mkdtempSync(path.join(os.tmpdir(), "cline-test-workspace-")) console.log("Starting HostBridge test server...") - const hostbridge: ChildProcess = spawn("npx", ["tsx", path.join(__dirname, "test-hostbridge-server.ts")], { - stdio: "pipe", + const hostbridge: ChildProcess = spawn("bun", [path.join(__dirname, "test-hostbridge-server.ts")], { + stdio: "inherit", env: { ...process.env, TEST_HOSTBRIDGE_WORKSPACE_DIR: clineTestWorkspace, @@ -118,11 +118,13 @@ async function main(): Promise { const workosFetchMockPath = path.join(projectRoot, "scripts", "testing-platform-workos-fetch-mock.cjs") const baseArgs = ["--enable-source-maps", "--require", workosFetchMockPath, path.join(distDir, "cline-core.js")] - const spawnArgs = USE_C8 ? ["c8", "--report-dir", covDir, "node", ...baseArgs] : ["node", ...baseArgs] + const c8Bin = path.join(projectRoot, "node_modules", ".bin", process.platform === "win32" ? "c8.cmd" : "c8") + const spawnCommand = USE_C8 ? c8Bin : "node" + const spawnArgs = USE_C8 ? ["--report-dir", covDir, "node", ...baseArgs] : baseArgs console.log(`Starting Cline Core Service... (useC8=${USE_C8})`) - const coreService: ChildProcess = spawn("npx", spawnArgs, { + const coreService: ChildProcess = spawn(spawnCommand, spawnArgs, { cwd: projectRoot, env: { ...process.env, diff --git a/apps/vscode/scripts/testing-platform-orchestrator.ts b/apps/vscode/scripts/testing-platform-orchestrator.ts index a92df0a3ad..10a0ac4193 100644 --- a/apps/vscode/scripts/testing-platform-orchestrator.ts +++ b/apps/vscode/scripts/testing-platform-orchestrator.ts @@ -29,7 +29,7 @@ import kill from "tree-kill" let showServerLogs = false let fix = false let coverage = false -const WAIT_SERVER_DEFAULT_TIMEOUT = 15000 +const WAIT_SERVER_DEFAULT_TIMEOUT = 60000 const usedPorts = new Set() /** @@ -94,8 +94,10 @@ async function startServer(): Promise<{ server: ChildProcess; grpcPort: string } const grpcPort = (await getAvailablePort()).toString() const hostbridgePort = (await getAvailablePort()).toString() - const server = spawn("npx", ["tsx", "scripts/test-standalone-core-api-server.ts"], { - stdio: showServerLogs ? "inherit" : "pipe", + const server = spawn("bun", ["scripts/test-standalone-core-api-server.ts"], { + // When logs are hidden, ignore stdio instead of piping it without a reader: + // an unread pipe can fill and stall server startup/shutdown in CI. + stdio: showServerLogs ? "inherit" : "ignore", env: { ...process.env, PROTOBUS_PORT: grpcPort, @@ -104,23 +106,44 @@ async function startServer(): Promise<{ server: ChildProcess; grpcPort: string } }, }) - // Wait for either the server to become ready or fail on spawn error - await Promise.race([ - waitForPort(Number(grpcPort), "127.0.0.1", WAIT_SERVER_DEFAULT_TIMEOUT), - new Promise((_, reject) => server.once("error", reject)), - ]) + try { + // Wait for either the server to become ready or fail on spawn error. + await Promise.race([ + waitForPort(Number(grpcPort), "127.0.0.1", WAIT_SERVER_DEFAULT_TIMEOUT), + new Promise((_, reject) => server.once("error", reject)), + new Promise((_, reject) => + server.once("exit", (code, signal) => reject(new Error(`Server exited before ready: ${code ?? signal}`))), + ), + ]) + } catch (error) { + await stopServer(server) + throw error + } return { server, grpcPort } } function stopServer(server: ChildProcess): Promise { return new Promise((resolve) => { - if (!server.pid) return resolve() + if (!server.pid || server.exitCode !== null || server.signalCode !== null) return resolve() + let settled = false + const finish = () => { + if (!settled) { + settled = true + resolve() + } + } + + server.once("exit", finish) kill(server.pid, "SIGINT", (err) => { if (err) console.warn("Failed to kill server process:", err) - server.once("exit", () => resolve()) }) + setTimeout(() => { + if (!settled && server.pid) { + kill(server.pid, "SIGKILL", finish) + } + }, 5000).unref() }) }