import childProcess, { ChildProcessWithoutNullStreams } from 'child_process'; import { CommandRunner, Logger, RunResult } from '@stackbit/types'; import { logProcess } from '@stackbit/utils'; type RunCommandOptions = { cwd?: string; shell?: boolean; env?: NodeJS.ProcessEnv; logger?: Logger; uid?: number }; export function runCommand(command: string, args?: string[], options?: RunCommandOptions) { if (options?.logger) { options.logger.info(`Running command '${command}${args ? ' ' : ''}${args?.join(' ')}'${options?.cwd ? ` in '${options.cwd}'` : ''} ...`); } const process = childProcess.spawn(command, args, { cwd: options?.cwd, shell: options?.shell, env: options?.env, ...(options?.uid ? { uid: options.uid } : {}) }); if (options?.logger) { logProcess(process, 'run', options.logger); } return process; } export function getCommandRunner(commandRunnerOptions: { env: NodeJS.ProcessEnv; uid?: number }): CommandRunner { return (command: string, args?: string[], options?: RunCommandOptions): Promise => { const process = runCommand(command, args, { ...options, uid: options?.uid ?? commandRunnerOptions.uid, env: { ...commandRunnerOptions.env, ...options?.env } }); return getProcessPromise(process); }; } function getProcessPromise(p: ChildProcessWithoutNullStreams): Promise<{ stdout: string; stderr: string; exitCode?: number; err?: Error; }> { return new Promise((resolve, reject) => { let stdout = ''; let stderr = ''; p.stdout.on('data', (out) => (stdout += out)); p.stderr.on('data', (out) => (stderr += out)); p.on('close', (exitCode) => { if (exitCode !== 0) { let msg = `Command '${p.spawnargs.join(' ')}' exited with code: ${exitCode},`; if (stderr) { msg += `, stderr: ${stderr}`; } if (stdout) { msg += `, stdout: ${stdout}`; } reject(new Error(msg)); } else { resolve({ stdout, stderr }); } }); p.on('error', (err) => { let msg = `Command '${p.spawnargs.join(' ')}' failed with error: ${err.message},`; if (stderr) { msg += `, stderr: ${stderr}`; } if (stdout) { msg += `, stdout: ${stdout}`; } reject(new Error(msg)); }); }); }