mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-03-17 22:13:08 +00:00
71 lines
2.5 KiB
TypeScript
71 lines
2.5 KiB
TypeScript
/**
|
|
* Git command execution utilities
|
|
*/
|
|
|
|
import { spawnProcess } from '@automaker/platform';
|
|
|
|
/**
|
|
* Execute git command with array arguments to prevent command injection.
|
|
* Uses spawnProcess from @automaker/platform for secure, cross-platform execution.
|
|
*
|
|
* @param args - Array of git command arguments (e.g., ['worktree', 'add', path])
|
|
* @param cwd - Working directory to execute the command in
|
|
* @param env - Optional additional environment variables to pass to the git process.
|
|
* These are merged on top of the current process environment. Pass
|
|
* `{ LC_ALL: 'C' }` to force git to emit English output regardless of the
|
|
* system locale so that text-based output parsing remains reliable.
|
|
* @param abortController - Optional AbortController to cancel the git process.
|
|
* When the controller is aborted the underlying process is sent SIGTERM and
|
|
* the returned promise rejects with an Error whose message is 'Process aborted'.
|
|
* @returns Promise resolving to stdout output
|
|
* @throws Error with stderr/stdout message if command fails. The thrown error
|
|
* also has `stdout` and `stderr` string properties for structured access.
|
|
*
|
|
* @example
|
|
* ```typescript
|
|
* // Safe: no injection possible
|
|
* await execGitCommand(['branch', '-D', branchName], projectPath);
|
|
*
|
|
* // Force English output for reliable text parsing:
|
|
* await execGitCommand(['rebase', '--', 'main'], worktreePath, { LC_ALL: 'C' });
|
|
*
|
|
* // With a process-level timeout:
|
|
* const controller = new AbortController();
|
|
* const timerId = setTimeout(() => controller.abort(), 30_000);
|
|
* try {
|
|
* await execGitCommand(['fetch', '--all', '--quiet'], cwd, undefined, controller);
|
|
* } finally {
|
|
* clearTimeout(timerId);
|
|
* }
|
|
*
|
|
* // Instead of unsafe:
|
|
* // await execAsync(`git branch -D ${branchName}`, { cwd });
|
|
* ```
|
|
*/
|
|
export async function execGitCommand(
|
|
args: string[],
|
|
cwd: string,
|
|
env?: Record<string, string>,
|
|
abortController?: AbortController
|
|
): Promise<string> {
|
|
const result = await spawnProcess({
|
|
command: 'git',
|
|
args,
|
|
cwd,
|
|
...(env !== undefined ? { env } : {}),
|
|
...(abortController !== undefined ? { abortController } : {}),
|
|
});
|
|
|
|
// spawnProcess returns { stdout, stderr, exitCode }
|
|
if (result.exitCode === 0) {
|
|
return result.stdout;
|
|
} else {
|
|
const errorMessage =
|
|
result.stderr || result.stdout || `Git command failed with code ${result.exitCode}`;
|
|
throw Object.assign(new Error(errorMessage), {
|
|
stdout: result.stdout,
|
|
stderr: result.stderr,
|
|
});
|
|
}
|
|
}
|