Skip to content

Commit 5a1dd03

Browse files
committed
fix(pi): replace synchronous spawnSync with async spawn in git runtime
Pi's reviewRuntime.runGit used spawnSync, which blocked the Node.js event loop during git operations. The ls-remote call from #609 could freeze the HTTP server for up to 5 seconds on slow networks. Now mirrors the Bun server's async pattern: spawn with piped stdio, kill timer for timeouts, stream-based stdout/stderr collection. For provenance purposes, this commit was AI assisted.
1 parent 364cd87 commit 5a1dd03

1 file changed

Lines changed: 31 additions & 11 deletions

File tree

apps/pi-extension/server/serverReview.ts

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { execSync, spawnSync } from "node:child_process";
1+
import { execSync, spawn } from "node:child_process";
22
import { readFileSync, existsSync } from "node:fs";
33
import { createServer } from "node:http";
44
import os from "node:os";
@@ -105,20 +105,40 @@ export interface ReviewServerResult {
105105
}
106106

107107
export const reviewRuntime: ReviewGitRuntime = {
108-
async runGit(
108+
runGit(
109109
args: string[],
110110
options?: { cwd?: string; timeoutMs?: number },
111111
): Promise<GitCommandResult> {
112-
const result = spawnSync("git", args, {
113-
cwd: options?.cwd,
114-
encoding: "utf-8",
115-
...(options?.timeoutMs ? { timeout: options.timeoutMs } : {}),
112+
return new Promise((resolve) => {
113+
const proc = spawn("git", args, {
114+
cwd: options?.cwd,
115+
stdio: ["ignore", "pipe", "pipe"],
116+
});
117+
118+
let timer: ReturnType<typeof setTimeout> | undefined;
119+
if (options?.timeoutMs) {
120+
timer = setTimeout(() => proc.kill(), options.timeoutMs);
121+
}
122+
123+
const stdoutChunks: Buffer[] = [];
124+
const stderrChunks: Buffer[] = [];
125+
proc.stdout!.on("data", (chunk: Buffer) => stdoutChunks.push(chunk));
126+
proc.stderr!.on("data", (chunk: Buffer) => stderrChunks.push(chunk));
127+
128+
proc.on("close", (code) => {
129+
if (timer) clearTimeout(timer);
130+
resolve({
131+
stdout: Buffer.concat(stdoutChunks).toString("utf-8"),
132+
stderr: Buffer.concat(stderrChunks).toString("utf-8"),
133+
exitCode: code ?? 1,
134+
});
135+
});
136+
137+
proc.on("error", () => {
138+
if (timer) clearTimeout(timer);
139+
resolve({ stdout: "", stderr: "git not found", exitCode: 1 });
140+
});
116141
});
117-
return {
118-
stdout: result.stdout ?? "",
119-
stderr: result.stderr ?? "",
120-
exitCode: result.status ?? (result.error ? 1 : 0),
121-
};
122142
},
123143

124144
async readTextFile(path: string): Promise<string | null> {

0 commit comments

Comments
 (0)