| 224 | } |
| 225 | |
| 226 | async function runCommand( |
| 227 | cmd: string, |
| 228 | args: readonly string[], |
| 229 | cwd: string, |
| 230 | options: RunCommandOptions = {}, |
| 231 | ): Promise<RunResult> { |
| 232 | return new Promise<RunResult>((resolve) => { |
| 233 | const child = spawn(cmd, args, { |
| 234 | cwd, |
| 235 | stdio: ['ignore', 'pipe', 'pipe'], |
| 236 | env: options.env ? { ...process.env, ...options.env } : process.env, |
| 237 | windowsHide: true, |
| 238 | }); |
| 239 | let stdout = ''; |
| 240 | let stderr = ''; |
| 241 | let settled = false; |
| 242 | let timer: ReturnType<typeof setTimeout> | undefined; |
| 243 | const finish = (result: RunResult) => { |
| 244 | if (settled) return; |
| 245 | settled = true; |
| 246 | if (timer !== undefined) clearTimeout(timer); |
| 247 | resolve(result); |
| 248 | }; |
| 249 | if (options.timeoutMs !== undefined) { |
| 250 | timer = setTimeout(() => { |
| 251 | killChild(child); |
| 252 | finish({ exitCode: -1, stdout, stderr }); |
| 253 | }, options.timeoutMs); |
| 254 | timer.unref?.(); |
| 255 | } |
| 256 | child.stdout.setEncoding('utf-8'); |
| 257 | child.stderr.setEncoding('utf-8'); |
| 258 | child.stdout.on('data', (c: string) => { |
| 259 | stdout += c; |
| 260 | }); |
| 261 | child.stderr.on('data', (c: string) => { |
| 262 | stderr += c; |
| 263 | }); |
| 264 | child.once('error', () => { |
| 265 | finish({ exitCode: -1, stdout, stderr }); |
| 266 | }); |
| 267 | child.once('close', (code) => { |
| 268 | finish({ exitCode: code ?? -1, stdout, stderr }); |
| 269 | }); |
| 270 | }); |
| 271 | } |
| 272 | |
| 273 | function killChild(child: ChildProcess): void { |
| 274 | // On Windows, `ChildProcess.kill()` only signals the direct child (e.g. the |