import { TEMPORARY_ERROR } from "../../constants/error-messages.js";
import { getCustomEnv, getUserEnv } from "../env.js";
import { GlobalConfig } from "../../config/global.js";
import { logger } from "../../logger/index.js";
import { getChildEnv } from "./utils.js";
import { rawExec } from "./common.js";
import { generateInstallCommands, isDynamicInstall } from "./containerbase.js";
import { generateDockerCommand, removeDockerContainer } from "./docker/index.js";
import { getHermitEnvs, isHermit } from "./hermit.js";
import { isNonEmptyString } from "@sindresorhus/is";
import upath from "upath";

//#region lib/util/exec/index.ts
function dockerEnvVars(extraEnv, childEnv) {
	return Object.keys(extraEnv).filter((key) => isNonEmptyString(childEnv[key]));
}
function getCwd({ cwd, cwdFile }) {
	const defaultCwd = GlobalConfig.get("localDir");
	return (cwdFile ? upath.join(defaultCwd, upath.dirname(cwdFile)) : cwd) ?? defaultCwd;
}
function getRawExecOptions(opts) {
	const defaultExecutionTimeout = GlobalConfig.get("executionTimeout");
	const childEnv = getChildEnv(opts);
	const cwd = getCwd(opts);
	let timeout = opts.timeout;
	if (!timeout) if (defaultExecutionTimeout) timeout = defaultExecutionTimeout * 60 * 1e3;
	else timeout = 900 * 1e3;
	return {
		cwd,
		env: childEnv,
		maxBuffer: opts.maxBuffer ?? 10 * 1024 * 1024,
		timeout,
		...opts.shell !== void 0 && { shell: opts.shell },
		stdin: "pipe",
		stdout: opts.ignoreStdout ? "ignore" : "pipe",
		stderr: "pipe"
	};
}
function isDocker(docker) {
	return GlobalConfig.get("binarySource") === "docker" && !!docker;
}
async function prepareRawExec(cmd, opts, sideCarImage) {
	const { docker } = opts;
	const preCommands = opts.preCommands ?? [];
	const customEnvVariables = getCustomEnv();
	const userConfiguredEnv = getUserEnv();
	const { containerbaseDir, binarySource } = GlobalConfig.get();
	if (binarySource === "docker" || binarySource === "install") {
		logger.debug(`Setting CONTAINERBASE_CACHE_DIR to ${containerbaseDir}`);
		opts.env ??= {};
		opts.env.CONTAINERBASE_CACHE_DIR = containerbaseDir;
	}
	let rawOptions = getRawExecOptions(opts);
	let rawCommands = typeof cmd === "string" ? [cmd] : cmd;
	if (isDocker(docker)) {
		logger.debug({ image: sideCarImage }, "Using docker to execute");
		const envVars = [...dockerEnvVars({
			...opts.extraEnv,
			...customEnvVariables,
			...userConfiguredEnv
		}, getChildEnv(opts)), "CONTAINERBASE_CACHE_DIR"];
		const cwd = getCwd(opts);
		const dockerOptions = {
			...docker,
			cwd,
			envVars
		};
		rawCommands = [await generateDockerCommand(rawCommands, [...await generateInstallCommands(opts.toolConstraints), ...preCommands], dockerOptions, sideCarImage)];
	} else if (isDynamicInstall(opts.toolConstraints)) {
		logger.debug("Using containerbase dynamic installs");
		rawCommands = [
			...await generateInstallCommands(opts.toolConstraints),
			...preCommands,
			...rawCommands
		];
	} else if (isHermit()) {
		const hermitEnvVars = await getHermitEnvs(rawOptions);
		logger.debug({ hermitEnvVars }, "merging hermit environment variables into the execution options");
		rawOptions = {
			...rawOptions,
			env: {
				...rawOptions.env,
				...hermitEnvVars
			}
		};
	}
	if (GlobalConfig.get("binarySource") === "global" && opts.toolConstraints?.length) logger.once.debug({ toolConstraints: opts.toolConstraints }, "Ignoring tool contraints because of `binarySource=global`");
	return {
		rawCommands,
		rawOptions
	};
}
async function exec(cmd, opts = {}) {
	const { docker } = opts;
	const dockerChildPrefix = GlobalConfig.get("dockerChildPrefix", "renovate_");
	const sideCarImage = GlobalConfig.get("dockerSidecarImage");
	const { rawCommands, rawOptions } = await prepareRawExec(cmd, opts, sideCarImage);
	const useDocker = isDocker(docker);
	let res = {
		stdout: "",
		stderr: ""
	};
	for (const rawCmd of rawCommands) {
		const startTime = Date.now();
		if (useDocker) await removeDockerContainer(sideCarImage, dockerChildPrefix);
		logger.debug({ command: rawCmd }, "Executing command");
		logger.trace({ commandOptions: rawOptions }, "Command options");
		try {
			res = await rawExec(rawCmd, rawOptions);
		} catch (err) {
			const durationMs = Math.round(Date.now() - startTime);
			logger.debug({
				err,
				durationMs
			}, "rawExec err");
			if (useDocker) await removeDockerContainer(sideCarImage, dockerChildPrefix).catch((removeErr) => {
				const message = err.message;
				throw new Error(`Error: "${removeErr.message}" - Original Error: "${message}"`);
			});
			if (err.signal === `SIGTERM`) {
				logger.debug({ err }, "exec interrupted by SIGTERM - run needs to be aborted");
				throw new Error(TEMPORARY_ERROR);
			}
			throw err;
		}
		const durationMs = Math.round(Date.now() - startTime);
		logger.debug({
			durationMs,
			stdout: res.stdout,
			stderr: res.stderr
		}, "exec completed");
	}
	return res;
}
function getToolSettingsOptions(repoConfig) {
	let defaults = GlobalConfig.get("toolSettings");
	defaults ??= {
		jvmMaxMemory: 512,
		jvmMemory: 512
	};
	const options = {};
	options.jvmMaxMemory = defaults?.jvmMaxMemory ?? 512;
	options.jvmMemory = defaults?.jvmMemory ?? options.jvmMaxMemory;
	if (repoConfig !== void 0) {
		if (repoConfig.jvmMaxMemory) {
			if (repoConfig.jvmMaxMemory > options.jvmMaxMemory) logger.once.debug(`A higher jvmMaxMemory (${repoConfig.jvmMaxMemory}) than the global configuration (${options.jvmMaxMemory}) is not permitted for Java VM invocations. Using global configuration instead`);
			options.jvmMaxMemory = Math.min(options.jvmMaxMemory, repoConfig.jvmMaxMemory);
		}
		if (repoConfig.jvmMemory) options.jvmMemory = repoConfig.jvmMemory;
	}
	options.jvmMaxMemory = Math.floor(options.jvmMaxMemory);
	options.jvmMemory = Math.floor(options.jvmMemory);
	options.jvmMemory = Math.min(options.jvmMemory, options.jvmMaxMemory);
	if (options.jvmMaxMemory < 512 || options.jvmMemory < 512) {
		options.jvmMaxMemory = Math.max(options.jvmMaxMemory, 512);
		logger.once.debug("Overriding low memory settings for Java VM invocations to a minimum of 512M");
		options.jvmMemory = Math.max(options.jvmMemory, 512);
	}
	return options;
}

//#endregion
export { exec, getToolSettingsOptions };
//# sourceMappingURL=index.js.map