(cwd: string, args: string[])
| 72 | } |
| 73 | |
| 74 | function runGitAsync(cwd: string, args: string[]): Promise<GitResult> { |
| 75 | return new Promise((resolveResult) => { |
| 76 | const child = spawn("git", ["--no-optional-locks", "-C", cwd, ...args], { |
| 77 | stdio: ["ignore", "pipe", "pipe"], |
| 78 | }); |
| 79 | let stdout = ""; |
| 80 | let stderr = ""; |
| 81 | let stdoutBytes = 0; |
| 82 | let stderrBytes = 0; |
| 83 | let settled = false; |
| 84 | let timeout: ReturnType<typeof setTimeout> | null = null; |
| 85 | |
| 86 | const finish = (result: GitResult) => { |
| 87 | if (settled) return; |
| 88 | settled = true; |
| 89 | if (timeout) clearTimeout(timeout); |
| 90 | resolveResult(result); |
| 91 | }; |
| 92 | |
| 93 | const timeoutMs = getGitTimeoutMs(); |
| 94 | timeout = setTimeout(() => { |
| 95 | child.kill("SIGKILL"); |
| 96 | finish({ ok: false, error: `git timed out after ${timeoutMs}ms` }); |
| 97 | }, timeoutMs); |
| 98 | |
| 99 | child.stdout.setEncoding("utf8"); |
| 100 | child.stderr.setEncoding("utf8"); |
| 101 | child.stdout.on("data", (chunk: string) => { |
| 102 | stdoutBytes += Buffer.byteLength(chunk); |
| 103 | if (stdoutBytes > GIT_MAX_BUFFER) { |
| 104 | child.kill(); |
| 105 | finish({ ok: false, error: `git stdout exceeded ${GIT_MAX_BUFFER} bytes` }); |
| 106 | return; |
| 107 | } |
| 108 | stdout += chunk; |
| 109 | }); |
| 110 | child.stderr.on("data", (chunk: string) => { |
| 111 | stderrBytes += Buffer.byteLength(chunk); |
| 112 | if (stderrBytes <= GIT_MAX_BUFFER) stderr += chunk; |
| 113 | }); |
| 114 | child.on("error", (error) => finish({ ok: false, error: error.message })); |
| 115 | child.on("close", (status) => { |
| 116 | if (status === 0) { |
| 117 | finish({ ok: true, stdout }); |
| 118 | return; |
| 119 | } |
| 120 | const message = stderr.trim() || `git exited with status ${status ?? "unknown"}`; |
| 121 | finish({ ok: false, error: message }); |
| 122 | }); |
| 123 | }); |
| 124 | } |
| 125 | |
| 126 | function resolveGitPath(cwd: string, value: string): string { |
| 127 | return isAbsolute(value) ? value : resolve(cwd, value); |
no test coverage detected