2020-01-15 07:14:44 +00:00
|
|
|
import { hrtime } from 'process';
|
2019-07-23 12:39:15 +00:00
|
|
|
import { promisify } from 'util';
|
2019-12-23 14:59:57 +00:00
|
|
|
import {
|
|
|
|
exec as cpExec,
|
|
|
|
ExecOptions as ChildProcessExecOptions,
|
|
|
|
} from 'child_process';
|
2020-01-12 18:47:39 +00:00
|
|
|
import { dockerCmd, DockerOptions, setDockerConfig } from './docker';
|
2020-01-10 14:18:41 +00:00
|
|
|
import { getChildProcessEnv } from './env';
|
2020-01-15 07:14:44 +00:00
|
|
|
import { logger } from '../../logger';
|
2019-07-23 12:39:15 +00:00
|
|
|
|
2020-01-12 18:47:39 +00:00
|
|
|
let localDir;
|
|
|
|
|
|
|
|
export function setExecConfig(config): void {
|
|
|
|
localDir = config.localDir;
|
|
|
|
setDockerConfig(config);
|
|
|
|
}
|
|
|
|
|
2019-12-23 14:59:57 +00:00
|
|
|
const pExec: (
|
|
|
|
cmd: string,
|
|
|
|
opts: ChildProcessExecOptions & { encoding: string }
|
|
|
|
) => Promise<ExecResult> = promisify(cpExec);
|
|
|
|
|
2020-01-14 11:12:03 +00:00
|
|
|
type ExtraEnv<T = unknown> = Record<string, T>;
|
|
|
|
|
2019-12-23 14:59:57 +00:00
|
|
|
export interface ExecOptions extends ChildProcessExecOptions {
|
2020-01-14 11:12:03 +00:00
|
|
|
extraEnv?: ExtraEnv | null | undefined;
|
|
|
|
docker?: DockerOptions | null | undefined;
|
2019-12-23 14:59:57 +00:00
|
|
|
}
|
2019-07-23 12:39:15 +00:00
|
|
|
|
|
|
|
export interface ExecResult {
|
|
|
|
stdout: string;
|
|
|
|
stderr: string;
|
|
|
|
}
|
|
|
|
|
2020-01-14 11:12:03 +00:00
|
|
|
function createChildEnv(
|
|
|
|
env: NodeJS.ProcessEnv,
|
|
|
|
extraEnv: ExtraEnv
|
|
|
|
): ExtraEnv<string> {
|
|
|
|
const extraEnvKeys = Object.keys(extraEnv || {});
|
|
|
|
|
|
|
|
const childEnv =
|
|
|
|
env || extraEnv
|
|
|
|
? {
|
|
|
|
...extraEnv,
|
|
|
|
...getChildProcessEnv(extraEnvKeys),
|
|
|
|
...env,
|
|
|
|
}
|
|
|
|
: getChildProcessEnv();
|
|
|
|
|
|
|
|
const result: ExtraEnv<string> = {};
|
|
|
|
Object.entries(childEnv).forEach(([key, val]) => {
|
|
|
|
if (val === null) return;
|
|
|
|
if (val === undefined) return;
|
|
|
|
result[key] = val.toString();
|
|
|
|
});
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
function dockerEnvVars(
|
|
|
|
extraEnv: ExtraEnv,
|
|
|
|
childEnv: ExtraEnv<string>
|
|
|
|
): string[] {
|
|
|
|
const extraEnvKeys = Object.keys(extraEnv || {});
|
|
|
|
return extraEnvKeys.filter(key => typeof childEnv[key] !== 'undefined');
|
|
|
|
}
|
|
|
|
|
2020-01-15 07:14:44 +00:00
|
|
|
export async function exec(
|
2020-01-14 11:12:03 +00:00
|
|
|
cmd: string | string[],
|
|
|
|
opts: ExecOptions = {}
|
|
|
|
): Promise<ExecResult> {
|
|
|
|
const { env, extraEnv, docker } = opts;
|
2020-01-12 18:47:39 +00:00
|
|
|
const cwd = opts.cwd || localDir;
|
2020-01-14 11:12:03 +00:00
|
|
|
const childEnv = createChildEnv(env, extraEnv);
|
|
|
|
|
|
|
|
const execOptions: ExecOptions = { ...opts };
|
|
|
|
delete execOptions.extraEnv;
|
|
|
|
delete execOptions.docker;
|
2020-01-10 14:18:41 +00:00
|
|
|
|
2020-01-14 11:12:03 +00:00
|
|
|
const pExecOptions: ChildProcessExecOptions & { encoding: string } = {
|
2020-01-10 14:18:41 +00:00
|
|
|
encoding: 'utf-8',
|
2020-01-14 11:12:03 +00:00
|
|
|
...execOptions,
|
|
|
|
env: childEnv,
|
2020-01-12 18:47:39 +00:00
|
|
|
cwd,
|
2020-01-10 14:18:41 +00:00
|
|
|
};
|
2020-01-14 11:12:03 +00:00
|
|
|
|
2020-01-16 21:05:50 +00:00
|
|
|
let commands = typeof cmd === 'string' ? [cmd] : cmd;
|
2020-01-10 14:18:41 +00:00
|
|
|
if (docker) {
|
|
|
|
const dockerOptions = {
|
|
|
|
...docker,
|
|
|
|
cwd,
|
2020-01-14 11:12:03 +00:00
|
|
|
envVars: dockerEnvVars(extraEnv, childEnv),
|
2020-01-10 14:18:41 +00:00
|
|
|
};
|
2019-12-23 14:59:57 +00:00
|
|
|
|
2020-01-16 21:05:50 +00:00
|
|
|
let singleCommand = commands.join(' && ');
|
|
|
|
singleCommand = `bash -l -c "${singleCommand.replace(/"/g, '\\"')}"`;
|
|
|
|
singleCommand = dockerCmd(singleCommand, dockerOptions);
|
|
|
|
commands = [singleCommand];
|
|
|
|
}
|
|
|
|
|
|
|
|
let res: ExecResult | null = null;
|
|
|
|
for (const pExecCommand of commands) {
|
|
|
|
const startTime = hrtime();
|
|
|
|
res = await pExec(pExecCommand, pExecOptions);
|
|
|
|
const duration = hrtime(startTime);
|
|
|
|
const seconds = Math.round(duration[0] + duration[1] / 1e9);
|
|
|
|
if (res) {
|
|
|
|
logger.debug(
|
|
|
|
{ cmd, seconds, stdout: res.stdout, stderr: res.stderr },
|
|
|
|
'exec completed'
|
|
|
|
);
|
|
|
|
}
|
2019-12-23 14:59:57 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 07:14:44 +00:00
|
|
|
return res;
|
2019-07-23 12:39:15 +00:00
|
|
|
}
|