UNPKG

renovate

Version:

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

402 lines • 16.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDatasourceList = exports.getDatasources = exports.isGetPkgReleasesConfig = void 0; exports.getRawPkgReleases = getRawPkgReleases; exports.applyDatasourceFilters = applyDatasourceFilters; exports.getPkgReleases = getPkgReleases; exports.supportsDigests = supportsDigests; exports.getDigest = getDigest; exports.getDefaultConfig = getDefaultConfig; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); const dequal_1 = require("dequal"); 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 array_1 = require("../../util/array"); const memCache = tslib_1.__importStar(require("../../util/cache/memory")); const packageCache = tslib_1.__importStar(require("../../util/cache/package")); const clone_1 = require("../../util/clone"); const filter_map_1 = require("../../util/filter-map"); const result_1 = require("../../util/result"); const stats_1 = require("../../util/stats"); const url_1 = require("../../util/url"); const versioning = tslib_1.__importStar(require("../versioning")); const api_1 = tslib_1.__importDefault(require("./api")); const common_1 = require("./common"); const metadata_1 = require("./metadata"); const npm_1 = require("./npm"); const npmrc_1 = require("./npm/npmrc"); tslib_1.__exportStar(require("./types"), exports); var common_2 = require("./common"); Object.defineProperty(exports, "isGetPkgReleasesConfig", { enumerable: true, get: function () { return common_2.isGetPkgReleasesConfig; } }); const getDatasources = () => api_1.default; exports.getDatasources = getDatasources; const getDatasourceList = () => Array.from(api_1.default.keys()); exports.getDatasourceList = getDatasourceList; // TODO: fix error Type function logError(datasource, packageName, err) { const { statusCode, code: errCode, url } = err; if (statusCode === 404) { logger_1.logger.debug({ datasource, packageName, url }, 'Datasource 404'); } else if (statusCode === 401 || statusCode === 403) { logger_1.logger.debug({ datasource, packageName, url }, 'Datasource unauthorized'); } else if (errCode) { logger_1.logger.debug({ datasource, packageName, url, errCode }, 'Datasource connection error'); } else { logger_1.logger.debug({ datasource, packageName, err }, 'Datasource unknown error'); } } async function getRegistryReleases(datasource, config, registryUrl) { const cacheNamespace = `datasource-releases-${datasource.id}`; const cacheKey = `${registryUrl}:${config.packageName}`; const cacheEnabled = !!datasource.caching; // tells if `isPrivate` flag is supported in datasource result const cacheForced = global_1.GlobalConfig.get('cachePrivatePackages', false); // tells if caching is forced via admin config if (cacheEnabled || cacheForced) { const cachedResult = await packageCache.get(cacheNamespace, cacheKey); if (cachedResult) { logger_1.logger.trace({ cacheKey }, 'Returning cached datasource response'); stats_1.DatasourceCacheStats.hit(datasource.id, registryUrl, config.packageName); return cachedResult; } stats_1.DatasourceCacheStats.miss(datasource.id, registryUrl, config.packageName); } const res = await datasource.getReleases({ ...config, registryUrl }); if (res?.releases.length) { res.registryUrl ??= registryUrl; } if (!res) { return null; } let cache = false; if (cacheForced) { cache = true; } else if (cacheEnabled && !res.isPrivate) { cache = true; } if (cache) { logger_1.logger.trace({ cacheKey }, 'Caching datasource response'); await packageCache.set(cacheNamespace, cacheKey, res, 15); stats_1.DatasourceCacheStats.set(datasource.id, registryUrl, config.packageName); } else { stats_1.DatasourceCacheStats.skip(datasource.id, registryUrl, config.packageName); } return res; } function firstRegistry(config, datasource, registryUrls) { if (registryUrls.length > 1) { logger_1.logger.warn({ datasource: datasource.id, packageName: config.packageName, registryUrls, }, 'Excess registryUrls found for datasource lookup - using first configured only'); } const registryUrl = registryUrls[0]; return getRegistryReleases(datasource, config, registryUrl); } async function huntRegistries(config, datasource, registryUrls) { let res = null; let caughtError; for (const registryUrl of registryUrls) { try { res = await getRegistryReleases(datasource, config, registryUrl); if (res) { break; } } catch (err) { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } // We'll always save the last-thrown error caughtError = err; logger_1.logger.trace({ err }, 'datasource hunt failure'); } } if (res) { return res; } if (caughtError) { throw caughtError; } return null; } async function mergeRegistries(config, datasource, registryUrls) { let combinedRes; let lastErr; let singleRegistry = true; const releaseVersioning = versioning.get(config.versioning); for (const registryUrl of registryUrls) { try { const res = await getRegistryReleases(datasource, config, registryUrl); if (!res) { continue; } if (!combinedRes) { // This is the first registry, so we can just use it and continue combinedRes = res; continue; } if (singleRegistry) { // This is the second registry // We need to move the registryUrl from the package level to the release level for (const release of (0, array_1.coerceArray)(combinedRes.releases)) { release.registryUrl ??= combinedRes.registryUrl; } singleRegistry = false; } const releases = (0, array_1.coerceArray)(res.releases); for (const release of releases) { // We have more than one registry, so we need to move the registryUrl // from the package level to the release level before merging release.registryUrl ??= res.registryUrl; } combinedRes.releases.push(...releases); // Merge the tags from the two results let tags = combinedRes.tags; if (tags) { if (res.tags) { // Both results had tags, so we need to compare them for (const tag of ['release', 'latest']) { const existingTag = combinedRes?.tags?.[tag]; const newTag = res.tags?.[tag]; if (is_1.default.string(newTag) && releaseVersioning.isVersion(newTag)) { if (is_1.default.string(existingTag) && releaseVersioning.isVersion(existingTag)) { // We need to compare them if (releaseVersioning.isGreaterThan(newTag, existingTag)) { // New tag is greater than the existing one tags[tag] = newTag; } } else { // Existing tag was not present or not a version // so we can just use the new one tags[tag] = newTag; } } } } } else { // Existing results had no tags, so we can just use the new ones tags = res.tags; } combinedRes = { ...res, ...combinedRes }; if (tags) { combinedRes.tags = tags; } // Remove the registryUrl from the package level when more than one registry delete combinedRes.registryUrl; } catch (err) { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } lastErr = err; logger_1.logger.trace({ err }, 'datasource merge failure'); } } if (!combinedRes) { if (lastErr) { throw lastErr; } return null; } const seenVersions = new Set(); combinedRes.releases = (0, filter_map_1.filterMap)(combinedRes.releases, (release) => { if (seenVersions.has(release.version)) { return null; } seenVersions.add(release.version); return release; }); return combinedRes; } function massageRegistryUrls(registryUrls) { return registryUrls.filter(Boolean).map(url_1.trimTrailingSlash); } function resolveRegistryUrls(datasource, defaultRegistryUrls, registryUrls, additionalRegistryUrls) { if (!datasource.customRegistrySupport) { if (is_1.default.nonEmptyArray(registryUrls) || is_1.default.nonEmptyArray(defaultRegistryUrls) || is_1.default.nonEmptyArray(additionalRegistryUrls)) { logger_1.logger.warn({ datasource: datasource.id, registryUrls, defaultRegistryUrls, additionalRegistryUrls, }, 'Custom registries are not allowed for this datasource and will be ignored'); } return is_1.default.function(datasource.defaultRegistryUrls) ? datasource.defaultRegistryUrls() : (datasource.defaultRegistryUrls ?? []); } const customUrls = registryUrls?.filter(Boolean); let resolvedUrls = []; if (is_1.default.nonEmptyArray(customUrls)) { resolvedUrls = [...customUrls]; } else if (is_1.default.nonEmptyArray(defaultRegistryUrls)) { resolvedUrls = [...defaultRegistryUrls]; resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []); } else if (is_1.default.function(datasource.defaultRegistryUrls)) { resolvedUrls = [...datasource.defaultRegistryUrls()]; resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []); } else if (is_1.default.nonEmptyArray(datasource.defaultRegistryUrls)) { resolvedUrls = [...datasource.defaultRegistryUrls]; resolvedUrls = resolvedUrls.concat(additionalRegistryUrls ?? []); } return massageRegistryUrls(resolvedUrls); } function applyReplacements(config) { if (config.replacementName && config.replacementVersion) { return { replacementName: config.replacementName, replacementVersion: config.replacementVersion, }; } return undefined; } async function fetchReleases(config) { const { datasource: datasourceName } = config; let { registryUrls } = config; // istanbul ignore if: need test if (!datasourceName || (0, common_1.getDatasourceFor)(datasourceName) === undefined) { logger_1.logger.warn({ datasource: datasourceName }, 'Unknown datasource'); return null; } if (datasourceName === 'npm') { if (is_1.default.string(config.npmrc)) { (0, npm_1.setNpmrc)(config.npmrc); } if (!is_1.default.nonEmptyArray(registryUrls)) { registryUrls = [(0, npmrc_1.resolveRegistryUrl)(config.packageName)]; } } const datasource = (0, common_1.getDatasourceFor)(datasourceName); // istanbul ignore if: needs test if (!datasource) { logger_1.logger.warn({ datasource: datasourceName }, 'Unknown datasource'); return null; } registryUrls = resolveRegistryUrls(datasource, config.defaultRegistryUrls, registryUrls, config.additionalRegistryUrls); let dep = null; const registryStrategy = config.registryStrategy ?? datasource.registryStrategy ?? 'hunt'; try { if (is_1.default.nonEmptyArray(registryUrls)) { if (registryStrategy === 'first') { dep = await firstRegistry(config, datasource, registryUrls); } else if (registryStrategy === 'hunt') { dep = await huntRegistries(config, datasource, registryUrls); } else if (registryStrategy === 'merge') { dep = await mergeRegistries(config, datasource, registryUrls); } } else { dep = await datasource.getReleases(config); } } catch (err) { if (err.message === error_messages_1.HOST_DISABLED || err.err?.message === error_messages_1.HOST_DISABLED) { return null; } if (err instanceof external_host_error_1.ExternalHostError) { throw err; } logError(datasource.id, config.packageName, err); } if (!dep || (0, dequal_1.dequal)(dep, { releases: [] })) { return null; } (0, metadata_1.addMetaData)(dep, datasourceName, config.packageName); dep = { ...dep, ...applyReplacements(config) }; return dep; } function fetchCachedReleases(config) { const { datasource, packageName, registryUrls } = config; const cacheKey = `datasource-mem:releases:${datasource}:${packageName}:${config.registryStrategy}:${String(registryUrls)}`; // By returning a Promise and reusing it, we should only fetch each package at most once const cachedResult = memCache.get(cacheKey); // istanbul ignore if if (cachedResult !== undefined) { return cachedResult; } const promisedRes = fetchReleases(config); memCache.set(cacheKey, promisedRes); return promisedRes; } function getRawPkgReleases(config) { if (!config.datasource) { logger_1.logger.warn('No datasource found'); return result_1.AsyncResult.err('no-datasource'); } const packageName = config.packageName; if (!packageName) { logger_1.logger.error({ config }, 'Datasource getReleases without packageName'); return result_1.AsyncResult.err('no-package-name'); } return result_1.Result.wrapNullable(fetchCachedReleases(config), 'no-result') .catch((e) => { if (e instanceof external_host_error_1.ExternalHostError) { e.hostType = config.datasource; e.packageName = packageName; } return result_1.Result.err(e); }) .transform(clone_1.clone); } function applyDatasourceFilters(releaseResult, config) { let res = releaseResult; res = (0, common_1.applyExtractVersion)(res, config.extractVersion); res = (0, common_1.applyVersionCompatibility)(res, config.versionCompatibility, config.currentCompatibility); res = (0, common_1.filterValidVersions)(res, config); res = (0, common_1.sortAndRemoveDuplicates)(res, config); res = (0, common_1.applyConstraintsFiltering)(res, config); return res; } async function getPkgReleases(config) { const { val = null, err } = await getRawPkgReleases(config) .transform((res) => applyDatasourceFilters(res, config)) .unwrap(); if (err instanceof Error) { throw err; } return val; } function supportsDigests(datasource) { const ds = !!datasource && (0, common_1.getDatasourceFor)(datasource); return !!ds && 'getDigest' in ds; } function getDigestConfig(datasource, config) { const { lookupName, currentValue, currentDigest } = config; const packageName = config.replacementName ?? config.packageName; // Prefer registryUrl from getReleases() lookup if it has been passed const registryUrl = config.registryUrl ?? resolveRegistryUrls(datasource, config.defaultRegistryUrls, config.registryUrls, config.additionalRegistryUrls)[0]; return { lookupName, packageName, registryUrl, currentValue, currentDigest }; } function getDigest(config, value) { const datasource = (0, common_1.getDatasourceFor)(config.datasource); // istanbul ignore if: need test if (!datasource || !('getDigest' in datasource)) { return Promise.resolve(null); } const digestConfig = getDigestConfig(datasource, config); return datasource.getDigest(digestConfig, value); } function getDefaultConfig(datasource) { const loadedDatasource = (0, common_1.getDatasourceFor)(datasource); return Promise.resolve(loadedDatasource?.defaultConfig ?? Object.create({})); } //# sourceMappingURL=index.js.map