UNPKG

renovate

Version:

Automated dependency updates. Flexible so you don't need to be.

126 lines (125 loc) • 5.78 kB
import { SYSTEM_INSUFFICIENT_MEMORY } from "../../../constants/error-messages.js"; import { newlineRegex, regEx } from "../../regex.js"; import { GlobalConfig } from "../../../config/global.js"; import { logger } from "../../../logger/index.js"; import { isCommandWithOptions } from "../utils.js"; import { rawExec } from "../common.js"; import { uniq } from "../../uniq.js"; import { isNonEmptyString, isString } from "@sindresorhus/is"; import { join } from "shlex"; //#region lib/util/exec/docker/index.ts const prefetchedImages = /* @__PURE__ */ new Map(); const digestRegex = regEx("Digest: (.*?)\n"); const sideCarName = "sidecar"; async function prefetchDockerImage(taggedImage) { if (prefetchedImages.has(taggedImage)) logger.debug(`Docker image is already prefetched: ${taggedImage}@${prefetchedImages.get(taggedImage)}`); else { logger.debug(`Fetching Docker image: ${taggedImage}`); const res = await rawExec(`docker pull ${taggedImage}`, {}); const imageDigest = digestRegex.exec(res?.stdout)?.[1] ?? "unknown"; logger.debug(`Finished fetching Docker image ${taggedImage}@${imageDigest}`); prefetchedImages.set(taggedImage, imageDigest); } } function expandVolumeOption(x) { if (isNonEmptyString(x)) return [x, x]; if (Array.isArray(x) && x.length === 2) { const [from, to] = x; // v8 ignore else -- TODO: add test #40625 if (isNonEmptyString(from) && isNonEmptyString(to)) return [from, to]; } return null; } function volumesEql(x, y) { const [xFrom, xTo] = x; const [yFrom, yTo] = y; return xFrom === yFrom && xTo === yTo; } function prepareVolumes(volumes) { return uniq(volumes.map(expandVolumeOption).filter((vol) => vol !== null), volumesEql).map(([from, to]) => `-v "${from}":"${to}"`); } function getContainerName(name, prefix) { return `${prefix ?? "renovate_"}${name}`.replace(regEx(/\//g), "_"); } function getContainerLabel(prefix) { return `${prefix ?? "renovate_"}child`; } async function removeDockerContainer(image, prefix) { const containerName = getContainerName(sideCarName, prefix); let cmd = `docker ps --filter name=${containerName} -aq`; try { const containerId = (await rawExec(cmd, {}))?.stdout?.trim() || ""; if (containerId.length) { logger.debug(`Removing container with ID: ${containerId}`); cmd = `docker rm -f ${containerId}`; await rawExec(cmd, {}); } else logger.trace({ image, containerName }, "No running containers to remove"); } catch (err) { logger.warn({ image, containerName, cmd, err }, "Could not remove Docker container"); } } async function removeDanglingContainers() { if (GlobalConfig.get("binarySource") !== "docker") return; try { const containerLabel = getContainerLabel(GlobalConfig.get("dockerChildPrefix")); logger.debug(`Removing dangling child containers with label ${containerLabel}`); const res = await rawExec(`docker ps --filter label=${containerLabel} -aq`, {}); if (res?.stdout?.trim().length) { const containerIds = res.stdout.trim().split(newlineRegex).map((container) => container.trim()).filter(Boolean); logger.debug({ containerIds }, "Removing dangling child containers"); await rawExec(`docker rm -f ${containerIds.join(" ")}`, {}); } else logger.debug("No dangling containers to remove"); } catch (err) { if (err.errno === "ENOMEM") throw new Error(SYSTEM_INSUFFICIENT_MEMORY); if (err.stderr?.includes("Cannot connect to the Docker daemon")) logger.info("No docker daemon found"); else logger.warn({ err }, "Error removing dangling containers"); } } async function generateDockerCommand(commands, preCommands, options, sideCarImage) { const { envVars, cwd } = options; const volumes = options.volumes ?? []; const { localDir, cacheDir, containerbaseDir, dockerUser, dockerChildPrefix, dockerCliOptions } = GlobalConfig.get(); const result = ["docker run --rm"]; const containerName = getContainerName(sideCarName, dockerChildPrefix); const containerLabel = getContainerLabel(dockerChildPrefix); result.push(`--name=${containerName}`); result.push(`--label=${containerLabel}`); if (dockerUser) result.push(`--user=${dockerUser}`); if (dockerCliOptions) result.push(dockerCliOptions); const volumeDirs = [localDir, cacheDir]; if (containerbaseDir) if (cacheDir && containerbaseDir.startsWith(cacheDir)) logger.debug("containerbaseDir is inside cacheDir"); else { logger.debug("containerbaseDir is separate from cacheDir"); volumeDirs.push(containerbaseDir); } else logger.debug("containerbaseDir is missing"); volumeDirs.push(...volumes); result.push(...prepareVolumes(volumeDirs)); // v8 ignore else -- TODO: add test #40625 if (envVars) result.push(...uniq(envVars).filter(isString).map((e) => `-e ${e}`)); if (cwd) result.push(`-w "${cwd}"`); logger.debug({ image: sideCarImage }, "Resolved tag constraint"); await prefetchDockerImage(sideCarImage); result.push(sideCarImage); const bashCommandParts = []; for (const preCommand of preCommands) if (isCommandWithOptions(preCommand) && isString(join(preCommand.command))) if (preCommand.ignoreFailure) bashCommandParts.push(`${join(preCommand.command)} || true`); else bashCommandParts.push(join(preCommand.command)); else if (isString(preCommand)) bashCommandParts.push(preCommand); for (const command of commands) if (isCommandWithOptions(command)) if (command.ignoreFailure) bashCommandParts.push(`${join(command.command)} || true`); else bashCommandParts.push(join(command.command)); else bashCommandParts.push(command); const bashCommand = bashCommandParts.join(" && "); result.push(`bash -l -c "${bashCommand.replace(regEx(/"/g), "\\\"")}"`); return result.join(" "); } //#endregion export { generateDockerCommand, removeDanglingContainers, removeDockerContainer }; //# sourceMappingURL=index.js.map