UNPKG

renovate

Version:

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

174 lines • 7.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PodDatasource = void 0; const tslib_1 = require("tslib"); const node_crypto_1 = tslib_1.__importDefault(require("node:crypto")); 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 github_1 = require("../../../util/http/github"); const regex_1 = require("../../../util/regex"); const datasource_1 = require("../datasource"); const metadata_1 = require("../metadata"); function shardParts(packageName) { return node_crypto_1.default .createHash('md5') .update(packageName) .digest('hex') .slice(0, 3) .split(''); } const githubRegex = (0, regex_1.regEx)(/(?<hostURL>^https:\/\/[a-zA-Z0-9-.]+)\/(?<account>[^/]+)\/(?<repo>[^/]+?)(?:\.git|\/.*)?$/); function releasesGithubUrl(packageName, opts) { const { hostURL, account, repo, useShard, useSpecs } = opts; const prefix = hostURL && hostURL !== 'https://github.com' ? `${hostURL}/api/v3/repos` : 'https://api.github.com/repos'; const shard = shardParts(packageName).join('/'); // `Specs` in the pods repo URL is a new requirement for legacy support also allow pod repo URL without `Specs` const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName; const shardPath = useSpecs ? `Specs/${shard}/${packageName}` : `${shard}/${packageName}`; const suffix = useShard ? shardPath : packageNamePath; return `${prefix}/${account}/${repo}/contents/${suffix}`; } function handleError(packageName, err) { const errorData = { packageName, err }; const statusCode = err.response?.statusCode ?? 0; if (statusCode === 429 || (statusCode >= 500 && statusCode < 600)) { logger_1.logger.warn({ packageName, err }, `CocoaPods registry failure`); throw new external_host_error_1.ExternalHostError(err); } if (statusCode === 401) { logger_1.logger.debug(errorData, 'Authorization error'); } else if (statusCode === 404) { logger_1.logger.debug(errorData, 'Package lookup error'); } else if (err.message === error_messages_1.HOST_DISABLED) { logger_1.logger.trace(errorData, 'Host disabled'); } else { logger_1.logger.warn(errorData, 'CocoaPods lookup failure: Unknown error'); } } function isDefaultRepo(url) { const match = githubRegex.exec(url); if (match?.groups) { const { account, repo } = match.groups; return (account.toLowerCase() === 'cocoapods' && repo.toLowerCase() === 'specs'); // https://github.com/CocoaPods/Specs.git } return false; } function releasesCDNUrl(packageName, registryUrl) { const shard = shardParts(packageName).join('_'); return `${registryUrl}/all_pods_versions_${shard}.txt`; } class PodDatasource extends datasource_1.Datasource { static id = 'pod'; defaultRegistryUrls = ['https://cdn.cocoapods.org']; registryStrategy = 'hunt'; githubHttp; constructor() { super(PodDatasource.id); this.githubHttp = new github_1.GithubHttp(PodDatasource.id); } async requestCDN(url, packageName) { try { const resp = await this.http.getText(url); if (resp?.body) { return resp.body; } } catch (err) { handleError(packageName, err); } return null; } async requestGithub(url, packageName) { try { const resp = await this.githubHttp.getJsonUnchecked(url); if (resp?.body) { return resp.body; } } catch (err) { handleError(packageName, err); } return null; } async getReleasesFromGithub(packageName, opts, useShard = true, useSpecs = true, urlFormatOptions = 'withShardWithSpec') { const url = releasesGithubUrl(packageName, { ...opts, useShard, useSpecs }); const resp = await this.requestGithub(url, packageName); if (resp) { const releases = resp.map(({ name }) => ({ version: name })); return { releases }; } // support different url formats switch (urlFormatOptions) { case 'withShardWithSpec': return this.getReleasesFromGithub(packageName, opts, true, false, 'withShardWithoutSpec'); case 'withShardWithoutSpec': return this.getReleasesFromGithub(packageName, opts, false, true, 'withSpecsWithoutShard'); case 'withSpecsWithoutShard': return this.getReleasesFromGithub(packageName, opts, false, false, 'withoutSpecsWithoutShard'); case 'withoutSpecsWithoutShard': default: return null; } } async getReleasesFromCDN(packageName, registryUrl) { const url = releasesCDNUrl(packageName, registryUrl); const resp = await this.requestCDN(url, packageName); if (resp) { const lines = resp.split(regex_1.newlineRegex); for (const line of lines) { const [name, ...versions] = line.split('/'); if (name === packageName.replace((0, regex_1.regEx)(/\/.*$/), '')) { const releases = versions.map((version) => ({ version })); return { releases }; } } } return null; } async getReleases({ packageName, registryUrl, }) { /* v8 ignore next 3 -- should never happen */ if (!registryUrl) { return null; } const podName = packageName.replace((0, regex_1.regEx)(/\/.*$/), ''); let baseUrl = registryUrl.replace((0, regex_1.regEx)(/\/+$/), ''); // In order to not abuse github API limits, query CDN instead if (isDefaultRepo(baseUrl)) { [baseUrl] = this.defaultRegistryUrls; } let result = null; const match = githubRegex.exec(baseUrl); // We would ideally have a reliable way to differentiate between // a CDN URL and a Github URL, but we'll start with detecting Artifactory if (match?.groups && !baseUrl.includes('/api/pods/')) { baseUrl = (0, metadata_1.massageGithubUrl)(baseUrl); const { hostURL, account, repo } = match.groups; const opts = { hostURL, account, repo }; result = await this.getReleasesFromGithub(podName, opts); } else { result = await this.getReleasesFromCDN(podName, baseUrl); } return result; } } exports.PodDatasource = PodDatasource; tslib_1.__decorate([ (0, decorator_1.cache)({ ttlMinutes: 30, namespace: `datasource-${PodDatasource.id}`, key: ({ packageName, registryUrl }) => // TODO: types (#22198) `${registryUrl}:${packageName}`, }) ], PodDatasource.prototype, "getReleases", null); //# sourceMappingURL=index.js.map