git-executor.ts•2.64 kB
/**
 * @fileoverview Centralized Git CLI command executor with cross-runtime support
 * @module services/git/providers/cli/utils/git-executor
 *
 * This module provides the core execution engine for all git operations,
 * with support for both Bun and Node.js runtimes. When running via bunx
 * (Node.js), it uses child_process.spawn. When running in native Bun,
 * it uses Bun.spawn for better performance.
 *
 * Key features:
 * - Cross-runtime compatibility (Bun and Node.js)
 * - Configurable timeouts (60s default)
 * - Buffer size limits (10MB max)
 * - Automatic argument validation
 * - Standardized environment setup
 * - Comprehensive error mapping
 */
import { mapGitError } from './error-mapper.js';
import { buildGitEnv, validateGitArgs } from './command-builder.js';
import { spawnGitCommand } from './runtime-adapter.js';
/** Maximum execution time for git commands (60 seconds) */
const GIT_COMMAND_TIMEOUT_MS = 60000;
/**
 * Executes a git command with cross-runtime support.
 *
 * This function automatically detects the runtime (Bun vs Node.js) and uses
 * the appropriate process spawning method:
 * - In Bun runtime: Uses Bun.spawn for optimal performance
 * - In Node.js runtime (bunx): Uses child_process.spawn for compatibility
 *
 * Features:
 * - Cross-runtime compatibility
 * - Streaming I/O for efficient handling of large outputs
 * - Robust timeout handling
 * - Automatic security validation
 *
 * @param args - Git command arguments (e.g., ['status', '--porcelain'])
 * @param cwd - The working directory to execute the command in
 * @returns A promise that resolves with the stdout and stderr of the command
 * @throws {McpError} If the command fails, times out, or produces an error
 *
 * @example
 * ```typescript
 * const result = await executeGitCommand(
 *   ['status', '--porcelain=v2', '-b'],
 *   '/path/to/repo'
 * );
 * console.log(result.stdout); // Git status output
 * ```
 */
export async function executeGitCommand(
  args: string[],
  cwd: string,
): Promise<{ stdout: string; stderr: string }> {
  try {
    // Validate arguments for security before execution
    validateGitArgs(args);
    // Use runtime adapter to spawn the process
    // This works in both Bun and Node.js runtimes
    const result = await spawnGitCommand(
      args,
      cwd,
      buildGitEnv(process.env as Record<string, string>),
      GIT_COMMAND_TIMEOUT_MS,
    );
    return result;
  } catch (error) {
    // mapGitError will transform the raw error into a structured McpError
    // with appropriate error codes and user-friendly messages
    throw mapGitError(error, args[0] || 'unknown');
  }
}