UNPKG

renovate

Version:

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

218 lines • 9.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sideCarImage = void 0; exports.prefetchDockerImage = prefetchDockerImage; exports.resetPrefetchedImages = resetPrefetchedImages; exports.getDockerTag = getDockerTag; exports.removeDockerContainer = removeDockerContainer; exports.removeDanglingContainers = removeDanglingContainers; exports.generateDockerCommand = generateDockerCommand; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); const global_1 = require("../../../config/global"); const error_messages_1 = require("../../../constants/error-messages"); const logger_1 = require("../../../logger"); const datasource_1 = require("../../../modules/datasource"); const allVersioning = tslib_1.__importStar(require("../../../modules/versioning")); const regex_1 = require("../../regex"); const uniq_1 = require("../../uniq"); const common_1 = require("../common"); const prefetchedImages = new Map(); const digestRegex = (0, regex_1.regEx)('Digest: (.*?)\n'); exports.sideCarImage = 'sidecar'; async function prefetchDockerImage(taggedImage) { if (prefetchedImages.has(taggedImage)) { logger_1.logger.debug(`Docker image is already prefetched: ${taggedImage}@${prefetchedImages.get(taggedImage)}`); } else { logger_1.logger.debug(`Fetching Docker image: ${taggedImage}`); const res = await (0, common_1.rawExec)(`docker pull ${taggedImage}`, { encoding: 'utf-8', }); const imageDigest = digestRegex.exec(res?.stdout)?.[1] ?? 'unknown'; logger_1.logger.debug(`Finished fetching Docker image ${taggedImage}@${imageDigest}`); prefetchedImages.set(taggedImage, imageDigest); } } function resetPrefetchedImages() { prefetchedImages.clear(); } function expandVolumeOption(x) { if (is_1.default.nonEmptyString(x)) { return [x, x]; } if (Array.isArray(x) && x.length === 2) { const [from, to] = x; if (is_1.default.nonEmptyString(from) && is_1.default.nonEmptyString(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) { const expanded = volumes.map(expandVolumeOption); const filtered = expanded.filter((vol) => vol !== null); const unique = (0, uniq_1.uniq)(filtered, volumesEql); return unique.map(([from, to]) => `-v "${from}":"${to}"`); } function prepareCommands(commands) { return commands.filter((command) => is_1.default.string(command)); } async function getDockerTag(packageName, constraint, versioning) { const versioningApi = allVersioning.get(versioning); if (!versioningApi.isValid(constraint)) { logger_1.logger.warn({ versioning, constraint }, `Invalid Docker image version constraint`); return 'latest'; } logger_1.logger.debug({ packageName, versioning, constraint }, `Found version constraint - checking for a compatible image to use`); const imageReleases = await (0, datasource_1.getPkgReleases)({ datasource: 'docker', packageName, versioning, }); if (imageReleases?.releases) { let versions = imageReleases.releases.map((release) => release.version); versions = versions.filter((version) => versioningApi.isVersion(version) && versioningApi.matches(version, constraint)); // Prefer stable versions over unstable, even if the range satisfies both types if (!versions.every((version) => versioningApi.isStable(version))) { logger_1.logger.debug('Filtering out unstable versions'); versions = versions.filter((version) => versioningApi.isStable(version)); } const version = versions .sort(versioningApi.sortVersions.bind(versioningApi)) .pop(); if (version) { logger_1.logger.debug({ packageName, versioning, constraint, version }, `Found compatible image version`); return version; } } else { logger_1.logger.error({ packageName }, `Docker exec: no releases found`); return 'latest'; } logger_1.logger.warn({ packageName, constraint, versioning }, 'Failed to find a tag satisfying constraint, using "latest" tag instead'); return 'latest'; } function getContainerName(image, prefix) { return `${prefix ?? 'renovate_'}${image}`.replace((0, regex_1.regEx)(/\//g), '_'); } function getContainerLabel(prefix) { return `${prefix ?? 'renovate_'}child`; } async function removeDockerContainer(image, prefix) { const containerName = getContainerName(image, prefix); let cmd = `docker ps --filter name=${containerName} -aq`; try { const res = await (0, common_1.rawExec)(cmd, { encoding: 'utf-8', }); const containerId = res?.stdout?.trim() || ''; if (containerId.length) { logger_1.logger.debug(`Removing container with ID: ${containerId}`); cmd = `docker rm -f ${containerId}`; await (0, common_1.rawExec)(cmd, { encoding: 'utf-8', }); } else { logger_1.logger.trace({ image, containerName }, 'No running containers to remove'); } } catch (err) { logger_1.logger.warn({ image, containerName, cmd, err }, 'Could not remove Docker container'); } } async function removeDanglingContainers() { if (global_1.GlobalConfig.get('binarySource') !== 'docker') { return; } try { const containerLabel = getContainerLabel(global_1.GlobalConfig.get('dockerChildPrefix')); logger_1.logger.debug(`Removing dangling child containers with label ${containerLabel}`); const res = await (0, common_1.rawExec)(`docker ps --filter label=${containerLabel} -aq`, { encoding: 'utf-8', }); if (res?.stdout?.trim().length) { const containerIds = res.stdout .trim() .split(regex_1.newlineRegex) .map((container) => container.trim()) .filter(Boolean); logger_1.logger.debug({ containerIds }, 'Removing dangling child containers'); await (0, common_1.rawExec)(`docker rm -f ${containerIds.join(' ')}`, { encoding: 'utf-8', }); } else { logger_1.logger.debug('No dangling containers to remove'); } } catch (err) { if (err.errno === 'ENOMEM') { throw new Error(error_messages_1.SYSTEM_INSUFFICIENT_MEMORY); } if (err.stderr?.includes('Cannot connect to the Docker daemon')) { logger_1.logger.info('No docker daemon found'); } else { logger_1.logger.warn({ err }, 'Error removing dangling containers'); } } } async function generateDockerCommand(commands, preCommands, options) { const { envVars, cwd } = options; let image = exports.sideCarImage; const volumes = options.volumes ?? []; const { localDir, cacheDir, containerbaseDir, dockerUser, dockerChildPrefix, dockerCliOptions, dockerSidecarImage, } = global_1.GlobalConfig.get(); const result = ['docker run --rm']; const containerName = getContainerName(image, 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_1.logger.debug('containerbaseDir is inside cacheDir'); } else { logger_1.logger.debug('containerbaseDir is separate from cacheDir'); volumeDirs.push(containerbaseDir); } } else { logger_1.logger.debug('containerbaseDir is missing'); } volumeDirs.push(...volumes); result.push(...prepareVolumes(volumeDirs)); if (envVars) { result.push(...(0, uniq_1.uniq)(envVars) .filter(is_1.default.string) .map((e) => `-e ${e}`)); } if (cwd) { result.push(`-w "${cwd}"`); } // TODO: #22198 image = dockerSidecarImage; // TODO: add constraint: const tag = getDockerTag(image, sideCarImageVersion, 'semver'); logger_1.logger.debug({ image /*, tagConstraint: sideCarImageVersion, tag */ }, 'Resolved tag constraint'); const taggedImage = image; // TODO: tag ? `${image}:${tag}` : `${image}`; await prefetchDockerImage(taggedImage); result.push(taggedImage); const bashCommand = [...prepareCommands(preCommands), ...commands].join(' && '); result.push(`bash -l -c "${bashCommand.replace((0, regex_1.regEx)(/"/g), '\\"')}"`); // lgtm [js/incomplete-sanitization] return result.join(' '); } //# sourceMappingURL=index.js.map