UNPKG

renovate

Version:

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

776 lines • 36.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.DockerDatasource = void 0; 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 external_host_error_1 = require("../../../types/errors/external-host-error"); const decorator_1 = require("../../../util/cache/package/decorator"); const env_1 = require("../../../util/env"); const http_1 = require("../../../util/http"); const memory_http_cache_provider_1 = require("../../../util/http/cache/memory-http-cache-provider"); const object_1 = require("../../../util/object"); const result_1 = require("../../../util/result"); const string_match_1 = require("../../../util/string-match"); const timestamp_1 = require("../../../util/timestamp"); const url_1 = require("../../../util/url"); const docker_1 = require("../../versioning/docker"); const datasource_1 = require("../datasource"); const util_1 = require("../util"); const common_1 = require("./common"); const dockerhub_cache_1 = require("./dockerhub-cache"); const ecr_1 = require("./ecr"); const schema_1 = require("./schema"); const defaultConfig = { commitMessageTopic: '{{{depName}}} Docker tag', commitMessageExtra: 'to {{#if isPinDigest}}{{{newDigestShort}}}{{else}}{{#if isMajor}}{{{prettyNewMajor}}}{{else}}{{{prettyNewVersion}}}{{/if}}{{/if}}', digest: { branchTopic: '{{{depNameSanitized}}}-{{{currentValue}}}', commitMessageExtra: 'to {{newDigestShort}}', commitMessageTopic: '{{{depName}}}{{#if currentValue}}:{{{currentValue}}}{{/if}} Docker digest', group: { commitMessageTopic: '{{{groupName}}}', commitMessageExtra: '', }, }, pin: { commitMessageExtra: '', groupName: 'Docker digests', group: { commitMessageTopic: '{{{groupName}}}', branchTopic: 'digests-pin', }, }, }; class DockerDatasource extends datasource_1.Datasource { static id = common_1.dockerDatasourceId; defaultVersioning = docker_1.id; defaultRegistryUrls = [common_1.DOCKER_HUB]; defaultConfig = defaultConfig; releaseTimestampSupport = true; releaseTimestampNote = 'The release timestamp is determined from the `tag_last_pushed` field in the results.'; sourceUrlSupport = 'package'; sourceUrlNote = 'The source URL is determined from the `org.opencontainers.image.source` and `org.label-schema.vcs-url` labels present in the metadata of the **latest stable** image found on the Docker registry.'; constructor() { super(DockerDatasource.id); } // TODO: debug why quay throws errors (#9612) async getManifestResponse(registryHost, dockerRepository, tag, mode = 'getText') { logger_1.logger.debug(`getManifestResponse(${registryHost}, ${dockerRepository}, ${tag}, ${mode})`); try { const headers = await (0, common_1.getAuthHeaders)(this.http, registryHost, dockerRepository); if (!headers) { logger_1.logger.warn('No docker auth found - returning'); return null; } headers.accept = [ 'application/vnd.docker.distribution.manifest.list.v2+json', 'application/vnd.docker.distribution.manifest.v2+json', 'application/vnd.oci.image.manifest.v1+json', 'application/vnd.oci.image.index.v1+json', ].join(', '); const url = `${registryHost}/v2/${dockerRepository}/manifests/${tag}`; const manifestResponse = await this.http[mode](url, { headers, noAuth: true, cacheProvider: memory_http_cache_provider_1.memCacheProvider, }); return manifestResponse; } catch (err) /* istanbul ignore next */ { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } if (err.statusCode === 401) { logger_1.logger.debug({ registryHost, dockerRepository }, 'Unauthorized docker lookup'); logger_1.logger.debug({ err }); return null; } if (err.statusCode === 404) { logger_1.logger.debug({ err, registryHost, dockerRepository, tag, }, 'Docker Manifest is unknown'); return null; } if (err.statusCode === 429 && (0, common_1.isDockerHost)(registryHost)) { throw new external_host_error_1.ExternalHostError(err); } if (err.statusCode >= 500 && err.statusCode < 600) { throw new external_host_error_1.ExternalHostError(err); } if (err.code === 'ETIMEDOUT') { logger_1.logger.debug({ registryHost }, 'Timeout when attempting to connect to docker registry'); logger_1.logger.debug({ err }); return null; } logger_1.logger.debug({ err, registryHost, dockerRepository, tag, }, 'Unknown Error looking up docker manifest'); return null; } } async getImageConfig(registryHost, dockerRepository, configDigest) { logger_1.logger.trace(`getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`); const headers = await (0, common_1.getAuthHeaders)(this.http, registryHost, dockerRepository); /* v8 ignore next 4 -- should never happen */ if (!headers) { logger_1.logger.warn('No docker auth found - returning'); return undefined; } const url = (0, url_1.joinUrlParts)(registryHost, 'v2', dockerRepository, 'blobs', configDigest); return await this.http.getJson(url, { headers, noAuth: true, }, schema_1.OciImageConfig); } async getHelmConfig(registryHost, dockerRepository, configDigest) { logger_1.logger.trace(`getImageConfig(${registryHost}, ${dockerRepository}, ${configDigest})`); const headers = await (0, common_1.getAuthHeaders)(this.http, registryHost, dockerRepository); /* v8 ignore next 4 -- should never happen */ if (!headers) { logger_1.logger.warn('No docker auth found - returning'); return undefined; } const url = (0, url_1.joinUrlParts)(registryHost, 'v2', dockerRepository, 'blobs', configDigest); return await this.http.getJson(url, { headers, noAuth: true, }, schema_1.OciHelmConfig); } async getConfigDigest(registry, dockerRepository, tag) { return ((await this.getManifest(registry, dockerRepository, tag))?.config ?.digest ?? null); } async getManifest(registry, dockerRepository, tag) { const manifestResponse = await this.getManifestResponse(registry, dockerRepository, tag); // If getting the manifest fails here, then abort // This means that the latest tag doesn't have a manifest, which shouldn't // be possible /* v8 ignore next 3 -- should never happen */ if (!manifestResponse) { return null; } // Softfail on invalid manifests const parsed = schema_1.ManifestJson.safeParse(manifestResponse.body); if (!parsed.success) { logger_1.logger.debug({ registry, dockerRepository, tag, body: manifestResponse.body, headers: manifestResponse.headers, err: parsed.error, }, 'Invalid manifest response'); return null; } const manifest = parsed.data; switch (manifest.mediaType) { case 'application/vnd.docker.distribution.manifest.v2+json': case 'application/vnd.oci.image.manifest.v1+json': return manifest; case 'application/vnd.docker.distribution.manifest.list.v2+json': case 'application/vnd.oci.image.index.v1+json': if (!manifest.manifests.length) { logger_1.logger.debug({ manifest }, 'Invalid manifest list with no manifests - returning'); return null; } logger_1.logger.trace({ registry, dockerRepository, tag }, 'Found manifest list, using first image'); return this.getManifest(registry, dockerRepository, manifest.manifests[0].digest); // istanbul ignore next: can't happen default: return null; } } async getImageArchitecture(registryHost, dockerRepository, currentDigest) { try { let manifestResponse; try { manifestResponse = await this.getManifestResponse(registryHost, dockerRepository, currentDigest, 'head'); } catch (_err) { const err = _err instanceof external_host_error_1.ExternalHostError ? _err.err : /* istanbul ignore next: can never happen */ _err; if (typeof err.statusCode === 'number' && err.statusCode >= 500 && err.statusCode < 600) { // querying the digest manifest for a non existent image leads to a 500 statusCode return null; } /* istanbul ignore next */ throw _err; } if (manifestResponse?.headers['content-type'] !== 'application/vnd.docker.distribution.manifest.v2+json' && manifestResponse?.headers['content-type'] !== 'application/vnd.oci.image.manifest.v1+json') { return null; } const configDigest = await this.getConfigDigest(registryHost, dockerRepository, currentDigest); if (!configDigest) { return null; } const configResponse = await this.getImageConfig(registryHost, dockerRepository, configDigest); // TODO: fix me, architecture is required in spec if (configResponse && ('config' in configResponse.body || 'architecture' in configResponse.body)) { const architecture = configResponse.body.architecture ?? null; logger_1.logger.debug(`Current digest ${currentDigest} relates to architecture ${architecture ?? 'null'}`); return architecture; } } catch (err) /* istanbul ignore next */ { if (err.statusCode !== 404 || err.message === error_messages_1.PAGE_NOT_FOUND_ERROR) { throw err; } logger_1.logger.debug({ registryHost, dockerRepository, currentDigest, err }, 'Unknown error getting image architecture'); } return undefined; } /* * docker.getLabels * * This function will: * - Return the labels for the requested image */ async getLabels(registryHost, dockerRepository, tag) { logger_1.logger.debug(`getLabels(${registryHost}, ${dockerRepository}, ${tag})`); // Skip Docker Hub image if RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP is set if ((0, env_1.getEnv)().RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP && registryHost === 'https://index.docker.io') { logger_1.logger.debug('Docker Hub image - skipping label lookup due to RENOVATE_X_DOCKER_HUB_DISABLE_LABEL_LOOKUP'); return {}; } // Docker Hub library images don't have labels we need if (registryHost === 'https://index.docker.io' && dockerRepository.startsWith('library/')) { logger_1.logger.debug('Docker Hub library image - skipping label lookup'); return {}; } try { let labels = {}; const manifest = await this.getManifest(registryHost, dockerRepository, tag); if (!manifest) { logger_1.logger.debug({ registryHost, dockerRepository, tag }, 'No manifest found'); return undefined; } if ('annotations' in manifest && manifest.annotations) { labels = manifest.annotations; } switch (manifest.config.mediaType) { case 'application/vnd.cncf.helm.config.v1+json': { if (labels[common_1.sourceLabel]) { // we already have the source url, so no need to pull the config return labels; } const configResponse = await this.getHelmConfig(registryHost, dockerRepository, manifest.config.digest); if (configResponse) { // Helm chart const url = (0, common_1.findHelmSourceUrl)(configResponse.body); if (url) { labels[common_1.sourceLabel] = url; } } break; } case 'application/vnd.oci.image.config.v1+json': case 'application/vnd.docker.container.image.v1+json': { if (labels[common_1.sourceLabel] && labels[common_1.gitRefLabel]) { // we already have the source url, so no need to pull the config return labels; } const configResponse = await this.getImageConfig(registryHost, dockerRepository, manifest.config.digest); /* v8 ignore next 3 -- should never happen */ if (!configResponse) { return labels; } const body = configResponse.body; if (body.config) { labels = { ...labels, ...body.config.Labels }; } else { logger_1.logger.debug({ headers: configResponse.headers, body }, `manifest blob response body missing the "config" property`); } break; } } if (labels) { logger_1.logger.debug({ labels, }, 'found labels in manifest'); } return labels; } catch (err) /* istanbul ignore next: should be tested in future */ { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } if (err.statusCode === 400 || err.statusCode === 401) { logger_1.logger.debug({ registryHost, dockerRepository, err }, 'Unauthorized docker lookup'); } else if (err.statusCode === 404) { logger_1.logger.warn({ err, registryHost, dockerRepository, tag, }, 'Config Manifest is unknown'); } else if (err.statusCode === 429 && (0, common_1.isDockerHost)(registryHost)) { logger_1.logger.warn({ err }, 'docker registry failure: too many requests'); } else if (err.statusCode >= 500 && err.statusCode < 600) { logger_1.logger.debug({ err, registryHost, dockerRepository, tag, }, 'docker registry failure: internal error'); } else if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID' || err.code === 'ETIMEDOUT') { logger_1.logger.debug({ registryHost, err }, 'Error connecting to docker registry'); } else if (registryHost === 'https://quay.io') { // istanbul ignore next logger_1.logger.debug('Ignoring quay.io errors until they fully support v2 schema'); } else { logger_1.logger.info({ registryHost, dockerRepository, tag, err }, 'Unknown error getting Docker labels'); } return {}; } } async getTagsQuayRegistry(registry, repository) { let tags = []; const limit = 100; const pageUrl = (page) => `${registry}/api/v1/repository/${repository}/tag/?limit=${limit}&page=${page}&onlyActiveTags=true`; let page = 1; let url = pageUrl(page); while (url && page <= 20) { // typescript issue :-/ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const res = (await this.http.getJsonUnchecked(url)); const pageTags = res.body.tags.map((tag) => tag.name); tags = tags.concat(pageTags); page += 1; url = res.body.has_additional ? pageUrl(page) : null; } return tags; } async getDockerApiTags(registryHost, dockerRepository) { let tags = []; // AWS ECR limits the maximum number of results to 1000 // See https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_DescribeRepositories.html#ECR-DescribeRepositories-request-maxResults // See https://docs.aws.amazon.com/AmazonECRPublic/latest/APIReference/API_DescribeRepositories.html#ecrpublic-DescribeRepositories-request-maxResults const limit = ecr_1.ecrRegex.test(registryHost) || ecr_1.ecrPublicRegex.test(registryHost) ? 1000 : 10000; let url = `${registryHost}/${dockerRepository}/tags/list?n=${limit}`; url = (0, url_1.ensurePathPrefix)(url, '/v2'); const headers = await (0, common_1.getAuthHeaders)(this.http, registryHost, dockerRepository, url); if (!headers) { logger_1.logger.debug('Failed to get authHeaders for getTags lookup'); return null; } let page = 0; const hostsNeedingAllPages = [ 'https://ghcr.io', // GHCR sorts from oldest to newest, so we need to get all pages 'https://quay.io', // Quay sorts from oldest to newest, so we need to get all pages ]; const pages = hostsNeedingAllPages.includes(registryHost) ? 1000 : global_1.GlobalConfig.get('dockerMaxPages', 20); logger_1.logger.trace({ registryHost, dockerRepository, pages }, 'docker.getTags'); let foundMaxResultsError = false; do { let res; try { res = await this.http.getJsonUnchecked(url, { headers, noAuth: true, }); } catch (err) { if (!foundMaxResultsError && err instanceof http_1.HttpError && (0, ecr_1.isECRMaxResultsError)(err)) { const maxResults = 1000; url = `${registryHost}/${dockerRepository}/tags/list?n=${maxResults}`; url = (0, url_1.ensurePathPrefix)(url, '/v2'); foundMaxResultsError = true; continue; } throw err; } tags = tags.concat(res.body.tags); const linkHeader = (0, url_1.parseLinkHeader)(res.headers.link); if ((0, util_1.isArtifactoryServer)(res)) { // Artifactory bug: next link comes back without virtual-repo prefix (RTFACT-18971) if (linkHeader?.next?.last) { // parse the current URL, strip any old "last" param, then set the new one const parsed = new URL(url); parsed.searchParams.delete('last'); parsed.searchParams.set('last', linkHeader.next.last); url = parsed.href; } else { url = null; } } else if (linkHeader?.next?.url) { // for the normal case we can still use URL to resolve relative-next url = new URL(linkHeader.next.url, url).href; } else { url = null; } page += 1; } while (url && page < pages); return tags; } async getTags(registryHost, dockerRepository) { try { const isQuay = registryHost === 'https://quay.io'; let tags; if (isQuay) { try { // Due to pagination and sorting limits on Quay Docker v2 API implementation we try the Quay v1 API first tags = await this.getTagsQuayRegistry(registryHost, dockerRepository); } catch (err) { // If we get a 401 Unauthorized error (v1 API requires separate auth for private images), fall back to Docker v2 API if (err.statusCode === 401) { logger_1.logger.debug({ registryHost, dockerRepository }, 'Quay v1 API unauthorized, falling back to Docker v2 API'); tags = await this.getDockerApiTags(registryHost, dockerRepository); } else { throw err; } } } else { tags = await this.getDockerApiTags(registryHost, dockerRepository); } return tags; } catch (_err) /* istanbul ignore next */ { const err = _err instanceof external_host_error_1.ExternalHostError ? _err.err : _err; if ((err.statusCode === 404 || err.message === error_messages_1.PAGE_NOT_FOUND_ERROR) && !dockerRepository.includes('/')) { logger_1.logger.debug(`Retrying Tags for ${registryHost}/${dockerRepository} using library/ prefix`); return this.getTags(registryHost, 'library/' + dockerRepository); } // JFrog Artifactory - Retry handling when resolving Docker Official Images // These follow the format of {{registryHost}}{{jFrogRepository}}/library/{{dockerRepository}} if ((err.statusCode === 404 || err.message === error_messages_1.PAGE_NOT_FOUND_ERROR) && (0, util_1.isArtifactoryServer)(err.response) && dockerRepository.split('/').length === 2) { logger_1.logger.debug(`JFrog Artifactory: Retrying Tags for ${registryHost}/${dockerRepository} using library/ path between JFrog virtual repository and image`); const dockerRepositoryParts = dockerRepository.split('/'); const jfrogRepository = dockerRepositoryParts[0]; const dockerImage = dockerRepositoryParts[1]; return this.getTags(registryHost, jfrogRepository + '/library/' + dockerImage); } if (err.statusCode === 429 && (0, common_1.isDockerHost)(registryHost)) { logger_1.logger.warn({ registryHost, dockerRepository, err }, 'docker registry failure: too many requests'); throw new external_host_error_1.ExternalHostError(err); } if (err.statusCode >= 500 && err.statusCode < 600) { logger_1.logger.warn({ registryHost, dockerRepository, err }, 'docker registry failure: internal error'); throw new external_host_error_1.ExternalHostError(err); } const errorCodes = ['ECONNRESET', 'ETIMEDOUT']; if (errorCodes.includes(err.code)) { logger_1.logger.warn({ registryHost, dockerRepository, err }, 'docker registry connection failure'); throw new external_host_error_1.ExternalHostError(err); } if ((0, common_1.isDockerHost)(registryHost)) { logger_1.logger.info({ err }, 'Docker Hub lookup failure'); } throw _err; } } /** * docker.getDigest * * The `newValue` supplied here should be a valid tag for the docker image. * * This function will: * - Look up a sha256 digest for a tag on its registry * - Return the digest as a string */ async getDigest({ registryUrl, lookupName, packageName, currentDigest }, newValue) { let registryHost; let dockerRepository; if (registryUrl && lookupName) { // Reuse the resolved values from getReleases() registryHost = registryUrl; dockerRepository = lookupName; } else { // Resolve values independently ({ registryHost, dockerRepository } = (0, common_1.getRegistryRepository)(packageName, registryUrl)); } logger_1.logger.debug( // TODO: types (#22198) `getDigest(${registryHost}, ${dockerRepository}, ${newValue})`); const newTag = is_1.default.nonEmptyString(newValue) ? newValue : 'latest'; let digest = null; try { let architecture = null; if (currentDigest && (0, string_match_1.isDockerDigest)(currentDigest)) { architecture = await this.getImageArchitecture(registryHost, dockerRepository, currentDigest); } let manifestResponse = null; if (!architecture) { manifestResponse = await this.getManifestResponse(registryHost, dockerRepository, newTag, 'head'); if (manifestResponse && (0, object_1.hasKey)('docker-content-digest', manifestResponse.headers)) { digest = manifestResponse.headers['docker-content-digest'] || null; } } if (is_1.default.string(architecture) || (manifestResponse && !(0, object_1.hasKey)('docker-content-digest', manifestResponse.headers))) { logger_1.logger.debug({ registryHost, dockerRepository }, 'Architecture-specific digest or missing docker-content-digest header - pulling full manifest'); manifestResponse = await this.getManifestResponse(registryHost, dockerRepository, newTag); if (architecture && manifestResponse) { const parsed = schema_1.ManifestJson.safeParse(manifestResponse.body); /* istanbul ignore else: hard to test */ if (parsed.success) { const manifestList = parsed.data; if (manifestList.mediaType === 'application/vnd.docker.distribution.manifest.list.v2+json' || manifestList.mediaType === 'application/vnd.oci.image.index.v1+json') { for (const manifest of manifestList.manifests) { if (manifest.platform?.architecture === architecture) { digest = manifest.digest; break; } } // TODO: return null if no matching architecture digest found // https://github.com/renovatebot/renovate/discussions/22639 } else if ((0, object_1.hasKey)('docker-content-digest', manifestResponse.headers)) { // TODO: return null if no matching architecture, requires to fetch the config manifest // https://github.com/renovatebot/renovate/discussions/22639 digest = manifestResponse.headers['docker-content-digest']; } } else { logger_1.logger.debug({ registryHost, dockerRepository, newTag, body: manifestResponse.body, headers: manifestResponse.headers, err: parsed.error, }, 'Failed to parse manifest response'); } } if (!digest) { logger_1.logger.debug({ registryHost, dockerRepository, newTag }, 'Extraction digest from manifest response body is deprecated'); digest = (0, common_1.extractDigestFromResponseBody)(manifestResponse); } } if (!manifestResponse && !dockerRepository.includes('/') && !packageName.includes('/')) { logger_1.logger.debug(`Retrying Digest for ${registryHost}/${dockerRepository} using library/ prefix`); return this.getDigest({ registryUrl, packageName: 'library/' + packageName, currentDigest, }, newValue); } if (manifestResponse) { // TODO: fix types (#22198) logger_1.logger.debug(`Got docker digest ${digest}`); } } catch (err) /* istanbul ignore next */ { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } logger_1.logger.debug({ err, packageName, newTag, }, 'Unknown Error looking up docker image digest'); } return digest; } async getDockerHubTags(dockerRepository) { let url = `https://hub.docker.com/v2/repositories/${dockerRepository}/tags?page_size=1000&ordering=last_updated`; const cache = await dockerhub_cache_1.DockerHubCache.init(dockerRepository); const maxPages = global_1.GlobalConfig.get('dockerMaxPages', 20); let page = 0, needNextPage = true; while (needNextPage && page < maxPages) { const { val, err } = await this.http .getJsonSafe(url, schema_1.DockerHubTagsPage) .unwrap(); if (err) { logger_1.logger.debug({ err }, `Docker: error fetching data from DockerHub`); return null; } page++; const { results, next, count } = val; needNextPage = cache.reconcile(results, count); if (!next) { break; } url = next; } await cache.save(); const items = cache.getItems(); return items.map(({ name: version, tag_last_pushed, digest: newDigest }) => { const release = { version }; const releaseTimestamp = (0, timestamp_1.asTimestamp)(tag_last_pushed); if (releaseTimestamp) { release.releaseTimestamp = releaseTimestamp; } if (newDigest) { release.newDigest = newDigest; } return release; }); } /** * docker.getReleases * * A docker image usually looks something like this: somehost.io/owner/repo:8.1.0-alpine * In the above: * - 'somehost.io' is the registry * - 'owner/repo' is the package name * - '8.1.0-alpine' is the tag * * This function will filter only tags that contain a semver version */ async getReleases({ packageName, registryUrl, }) { const { registryHost, dockerRepository } = (0, common_1.getRegistryRepository)(packageName, registryUrl); const getTags = () => result_1.Result.wrapNullable(this.getTags(registryHost, dockerRepository), 'tags-error').transform((tags) => tags.map((version) => ({ version }))); const getDockerHubTags = () => result_1.Result.wrapNullable(this.getDockerHubTags(dockerRepository), 'dockerhub-error').catch(getTags); const tagsResult = registryHost === 'https://index.docker.io' && !(0, env_1.getEnv)().RENOVATE_X_DOCKER_HUB_TAGS_DISABLE ? getDockerHubTags() : getTags(); const { val: releases, err } = await tagsResult.unwrap(); if (err instanceof Error) { throw err; } else if (err) { return null; } const ret = { registryUrl: registryHost, releases, }; if (dockerRepository !== packageName) { // This will be reused later if a getDigest() call is made ret.lookupName = dockerRepository; } const tags = releases.map((release) => release.version); const latestTag = tags.includes('latest') ? 'latest' : ((0, common_1.findLatestStable)(tags) ?? tags[tags.length - 1]); /* v8 ignore next 3 -- TODO: add test */ if (!latestTag) { return ret; } const labels = await this.getLabels(registryHost, dockerRepository, latestTag); if (labels) { if (is_1.default.nonEmptyString(labels[common_1.gitRefLabel])) { ret.gitRef = labels[common_1.gitRefLabel]; } for (const label of common_1.sourceLabels) { if (is_1.default.nonEmptyString(labels[label])) { ret.sourceUrl = labels[label]; break; } } if (is_1.default.nonEmptyString(labels[common_1.imageUrlLabel])) { ret.homepage = labels[common_1.imageUrlLabel]; } } return ret; } } exports.DockerDatasource = DockerDatasource; tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-imageconfig', key: (registryHost, dockerRepository, configDigest) => `${registryHost}:${dockerRepository}@${configDigest}`, ttlMinutes: 1440 * 28, }) ], DockerDatasource.prototype, "getImageConfig", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-imageconfig', key: (registryHost, dockerRepository, configDigest) => `${registryHost}:${dockerRepository}@${configDigest}`, ttlMinutes: 1440 * 28, }) ], DockerDatasource.prototype, "getHelmConfig", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-architecture', key: (registryHost, dockerRepository, currentDigest) => `${registryHost}:${dockerRepository}@${currentDigest}`, ttlMinutes: 1440 * 28, }) ], DockerDatasource.prototype, "getImageArchitecture", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-labels', key: (registryHost, dockerRepository, tag) => `${registryHost}:${dockerRepository}:${tag}`, ttlMinutes: 24 * 60, }) ], DockerDatasource.prototype, "getLabels", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-tags', key: (registryHost, dockerRepository) => `${registryHost}:${dockerRepository}`, }) ], DockerDatasource.prototype, "getTags", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-digest', key: ({ registryUrl, packageName, currentDigest }, newValue) => { const newTag = newValue ?? 'latest'; const { registryHost, dockerRepository } = (0, common_1.getRegistryRepository)(packageName, registryUrl); const digest = currentDigest ? `@${currentDigest}` : ''; return `${registryHost}:${dockerRepository}:${newTag}${digest}`; }, }) ], DockerDatasource.prototype, "getDigest", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-hub-tags', key: (dockerRepository) => `${dockerRepository}`, }) ], DockerDatasource.prototype, "getDockerHubTags", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: 'datasource-docker-releases-v2', key: ({ registryUrl, packageName }) => { const { registryHost, dockerRepository } = (0, common_1.getRegistryRepository)(packageName, registryUrl); return `${registryHost}:${dockerRepository}`; }, cacheable: ({ registryUrl, packageName }) => { const { registryHost } = (0, common_1.getRegistryRepository)(packageName, registryUrl); return registryHost === 'https://index.docker.io'; }, }) ], DockerDatasource.prototype, "getReleases", null); //# sourceMappingURL=index.js.map