"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.exec = exec;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const upath_1 = tslib_1.__importDefault(require("upath"));
const global_1 = require("../../config/global");
const error_messages_1 = require("../../constants/error-messages");
const logger_1 = require("../../logger");
const common_1 = require("./common");
const containerbase_1 = require("./containerbase");
const docker_1 = require("./docker");
const hermit_1 = require("./hermit");
const utils_1 = require("./utils");
function dockerEnvVars(extraEnv, childEnv) {
    const extraEnvKeys = Object.keys(extraEnv);
    return extraEnvKeys.filter((key) => is_1.default.nonEmptyString(childEnv[key]));
}
function getCwd({ cwd, cwdFile }) {
    const defaultCwd = global_1.GlobalConfig.get('localDir');
    const paramCwd = cwdFile
        ? upath_1.default.join(defaultCwd, upath_1.default.dirname(cwdFile))
        : cwd;
    return paramCwd ?? defaultCwd;
}
function getRawExecOptions(opts) {
    const defaultExecutionTimeout = global_1.GlobalConfig.get('executionTimeout');
    const childEnv = (0, utils_1.getChildEnv)(opts);
    const cwd = getCwd(opts);
    const rawExecOptions = {
        cwd,
        encoding: 'utf-8',
        env: childEnv,
        maxBuffer: opts.maxBuffer,
        timeout: opts.timeout,
    };
    // Set default timeout config.executionTimeout if specified; othrwise to 15 minutes
    if (!rawExecOptions.timeout) {
        if (defaultExecutionTimeout) {
            rawExecOptions.timeout = defaultExecutionTimeout * 60 * 1000;
        }
        else {
            rawExecOptions.timeout = 15 * 60 * 1000;
        }
    }
    // Set default max buffer size to 10MB
    rawExecOptions.maxBuffer = rawExecOptions.maxBuffer ?? 10 * 1024 * 1024;
    if (opts.ignoreStdout) {
        rawExecOptions.stdio = ['pipe', 'ignore', 'pipe'];
    }
    return rawExecOptions;
}
function isDocker(docker) {
    return global_1.GlobalConfig.get('binarySource') === 'docker' && !!docker;
}
async function prepareRawExec(cmd, opts) {
    const { docker } = opts;
    const preCommands = opts.preCommands ?? [];
    const { customEnvVariables, containerbaseDir, binarySource } = global_1.GlobalConfig.get();
    if (binarySource === 'docker' || binarySource === 'install') {
        logger_1.logger.debug(`Setting CONTAINERBASE_CACHE_DIR to ${containerbaseDir}`);
        opts.env ??= {};
        opts.env.CONTAINERBASE_CACHE_DIR = containerbaseDir;
    }
    const rawOptions = getRawExecOptions(opts);
    let rawCommands = typeof cmd === 'string' ? [cmd] : cmd;
    if (isDocker(docker)) {
        logger_1.logger.debug({ image: docker_1.sideCarImage }, 'Using docker to execute');
        const extraEnv = {
            ...opts.extraEnv,
            ...customEnvVariables,
            ...opts.userConfiguredEnv,
        };
        const childEnv = (0, utils_1.getChildEnv)(opts);
        const envVars = [
            ...dockerEnvVars(extraEnv, childEnv),
            'CONTAINERBASE_CACHE_DIR',
        ];
        const cwd = getCwd(opts);
        const dockerOptions = { ...docker, cwd, envVars };
        const dockerCommand = await (0, docker_1.generateDockerCommand)(rawCommands, [
            ...(await (0, containerbase_1.generateInstallCommands)(opts.toolConstraints)),
            ...preCommands,
        ], dockerOptions);
        rawCommands = [dockerCommand];
    }
    else if ((0, containerbase_1.isDynamicInstall)(opts.toolConstraints)) {
        logger_1.logger.debug('Using containerbase dynamic installs');
        rawCommands = [
            ...(await (0, containerbase_1.generateInstallCommands)(opts.toolConstraints)),
            ...preCommands,
            ...rawCommands,
        ];
    }
    else if ((0, hermit_1.isHermit)()) {
        const hermitEnvVars = await (0, hermit_1.getHermitEnvs)(rawOptions);
        logger_1.logger.debug({ hermitEnvVars }, 'merging hermit environment variables into the execution options');
        rawOptions.env = {
            ...rawOptions.env,
            ...hermitEnvVars,
        };
    }
    return { rawCommands, rawOptions };
}
async function exec(cmd, opts = {}) {
    const { docker } = opts;
    const dockerChildPrefix = global_1.GlobalConfig.get('dockerChildPrefix', 'renovate_');
    const { rawCommands, rawOptions } = await prepareRawExec(cmd, opts);
    const useDocker = isDocker(docker);
    let res = { stdout: '', stderr: '' };
    for (const rawCmd of rawCommands) {
        const startTime = Date.now();
        if (useDocker) {
            await (0, docker_1.removeDockerContainer)(docker_1.sideCarImage, dockerChildPrefix);
        }
        logger_1.logger.debug({ command: rawCmd }, 'Executing command');
        logger_1.logger.trace({ commandOptions: rawOptions }, 'Command options');
        try {
            res = await (0, common_1.rawExec)(rawCmd, rawOptions);
        }
        catch (err) {
            const durationMs = Math.round(Date.now() - startTime);
            logger_1.logger.debug({ err, durationMs }, 'rawExec err');
            if (useDocker) {
                await (0, docker_1.removeDockerContainer)(docker_1.sideCarImage, dockerChildPrefix).catch((removeErr) => {
                    const message = err.message;
                    throw new Error(`Error: "${removeErr.message}" - Original Error: "${message}"`);
                });
            }
            if (err.signal === `SIGTERM`) {
                logger_1.logger.debug({ err }, 'exec interrupted by SIGTERM - run needs to be aborted');
                throw new Error(error_messages_1.TEMPORARY_ERROR);
            }
            throw err;
        }
        const durationMs = Math.round(Date.now() - startTime);
        logger_1.logger.debug({
            durationMs,
            stdout: res.stdout,
            stderr: res.stderr,
        }, 'exec completed');
    }
    return res;
}
//# sourceMappingURL=index.js.map