Skip to content

Commit 2a35991

Browse files
committed
fix: use Node.js child_process for spawn/exec compatibility
Replace Bun.spawn/spawnSync with Node.js child_process equivalents. Bun's --target node flag doesn't transform Bun-specific APIs to Node.js equivalents, causing runtime errors when running compiled JS with Node.js. This fixes the "Docker is not running" false positive in CI where Bun.spawnSync was undefined when running with Node.js.
1 parent 3b4241c commit 2a35991

1 file changed

Lines changed: 41 additions & 54 deletions

File tree

cli/bin/postgres-ai.ts

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import * as pkce from "../lib/pkce";
1616
import * as authServer from "../lib/auth-server";
1717
import { maskSecret } from "../lib/util";
1818
import { createInterface } from "readline";
19+
import * as childProcess from "child_process";
1920

2021
// Singleton readline interface for stdin prompts
2122
let rl: ReturnType<typeof createInterface> | null = null;
@@ -32,84 +33,70 @@ function closeReadline() {
3233
}
3334
}
3435

35-
// Helper functions for spawning processes using Bun APIs
36+
// Helper functions for spawning processes - use Node.js child_process for compatibility
3637
async function execPromise(command: string): Promise<{ stdout: string; stderr: string }> {
37-
const proc = Bun.spawn(["sh", "-c", command], {
38-
stdout: "pipe",
39-
stderr: "pipe",
38+
return new Promise((resolve, reject) => {
39+
childProcess.exec(command, (error, stdout, stderr) => {
40+
if (error) {
41+
const err = error as Error & { code: number };
42+
err.code = error.code ?? 1;
43+
reject(err);
44+
} else {
45+
resolve({ stdout, stderr });
46+
}
47+
});
4048
});
41-
const stdout = await new Response(proc.stdout).text();
42-
const stderr = await new Response(proc.stderr).text();
43-
const exitCode = await proc.exited;
44-
if (exitCode !== 0) {
45-
const error = new Error(`Command failed: ${command}\n${stderr}`) as Error & { code: number };
46-
error.code = exitCode;
47-
throw error;
48-
}
49-
return { stdout, stderr };
5049
}
5150

5251
async function execFilePromise(file: string, args: string[]): Promise<{ stdout: string; stderr: string }> {
53-
const proc = Bun.spawn([file, ...args], {
54-
stdout: "pipe",
55-
stderr: "pipe",
52+
return new Promise((resolve, reject) => {
53+
childProcess.execFile(file, args, (error, stdout, stderr) => {
54+
if (error) {
55+
const err = error as Error & { code: number };
56+
err.code = error.code ?? 1;
57+
reject(err);
58+
} else {
59+
resolve({ stdout, stderr });
60+
}
61+
});
5662
});
57-
const stdout = await new Response(proc.stdout).text();
58-
const stderr = await new Response(proc.stderr).text();
59-
const exitCode = await proc.exited;
60-
if (exitCode !== 0) {
61-
const error = new Error(`Command failed: ${file} ${args.join(" ")}\n${stderr}`) as Error & { code: number };
62-
error.code = exitCode;
63-
throw error;
64-
}
65-
return { stdout, stderr };
6663
}
6764

6865
function spawnSync(cmd: string, args: string[], options?: { stdio?: "pipe" | "ignore" | "inherit"; encoding?: string; env?: Record<string, string | undefined>; cwd?: string }): { status: number | null; stdout: string; stderr: string } {
69-
const proc = Bun.spawnSync([cmd, ...args], {
70-
stdout: options?.stdio === "ignore" ? "ignore" : "pipe",
71-
stderr: options?.stdio === "ignore" ? "ignore" : "pipe",
72-
env: options?.env as Record<string, string>,
66+
const result = childProcess.spawnSync(cmd, args, {
67+
stdio: options?.stdio === "inherit" ? "inherit" : "pipe",
68+
env: options?.env as NodeJS.ProcessEnv,
7369
cwd: options?.cwd,
70+
encoding: "utf8",
7471
});
7572
return {
76-
status: proc.exitCode,
77-
stdout: proc.stdout ? new TextDecoder().decode(proc.stdout) : "",
78-
stderr: proc.stderr ? new TextDecoder().decode(proc.stderr) : "",
73+
status: result.status,
74+
stdout: typeof result.stdout === "string" ? result.stdout : "",
75+
stderr: typeof result.stderr === "string" ? result.stderr : "",
7976
};
8077
}
8178

82-
function spawn(cmd: string, args: string[], options?: { stdio?: "pipe" | "ignore" | "inherit"; env?: Record<string, string | undefined>; cwd?: string; detached?: boolean }): { on: (event: string, cb: (code: number | null, signal?: string) => void) => void; unref: () => void } {
83-
const proc = Bun.spawn([cmd, ...args], {
84-
stdout: options?.stdio === "inherit" ? "inherit" : options?.stdio === "ignore" ? "ignore" : "pipe",
85-
stderr: options?.stdio === "inherit" ? "inherit" : options?.stdio === "ignore" ? "ignore" : "pipe",
86-
stdin: options?.stdio === "inherit" ? "inherit" : "ignore",
87-
env: options?.env as Record<string, string>,
79+
function spawn(cmd: string, args: string[], options?: { stdio?: "pipe" | "ignore" | "inherit"; env?: Record<string, string | undefined>; cwd?: string; detached?: boolean }): { on: (event: string, cb: (code: number | null, signal?: string) => void) => void; unref: () => void; pid?: number } {
80+
const proc = childProcess.spawn(cmd, args, {
81+
stdio: options?.stdio ?? "pipe",
82+
env: options?.env as NodeJS.ProcessEnv,
8883
cwd: options?.cwd,
89-
});
90-
91-
const handlers: Record<string, ((code: number | null, signal?: string) => void)[]> = {};
92-
93-
proc.exited.then((code) => {
94-
handlers["close"]?.forEach((cb) => cb(code));
95-
handlers["exit"]?.forEach((cb) => cb(code));
96-
}).catch((err) => {
97-
if (handlers["error"]?.length) {
98-
handlers["error"].forEach((cb) => cb(null, String(err)));
99-
} else {
100-
console.error(`Spawn error for ${cmd}:`, err);
101-
}
84+
detached: options?.detached,
10285
});
10386

10487
return {
10588
on(event: string, cb: (code: number | null, signal?: string) => void) {
106-
if (!handlers[event]) handlers[event] = [];
107-
handlers[event].push(cb);
89+
if (event === "close" || event === "exit") {
90+
proc.on(event, (code, signal) => cb(code, signal ?? undefined));
91+
} else if (event === "error") {
92+
proc.on("error", (err) => cb(null, String(err)));
93+
}
10894
return this;
10995
},
11096
unref() {
111-
// Bun handles this automatically for detached processes
97+
proc.unref();
11298
},
99+
pid: proc.pid,
113100
};
114101
}
115102

0 commit comments

Comments
 (0)