UNPKG

renovate

Version:

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

138 lines (137 loc) 5.32 kB
import "../../../constants/error-messages.js"; import { newlineRegex, regEx } from "../../../util/regex.js"; import { logger } from "../../../logger/index.js"; import { ExternalHostError } from "../../../types/errors/external-host-error.js"; import { withCache } from "../../../util/cache/package/with-cache.js"; import { Datasource } from "../datasource.js"; import { GithubHttp } from "../../../util/http/github.js"; import { massageGithubUrl } from "../metadata.js"; import crypto from "node:crypto"; //#region lib/modules/datasource/pod/index.ts function shardParts(packageName) { return crypto.createHash("md5").update(packageName).digest("hex").slice(0, 3).split(""); } const githubRegex = 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("/"); const packageNamePath = useSpecs ? `Specs/${packageName}` : packageName; const shardPath = useSpecs ? `Specs/${shard}/${packageName}` : `${shard}/${packageName}`; return `${prefix}/${account}/${repo}/contents/${useShard ? shardPath : packageNamePath}`; } function handleError(packageName, err) { const errorData = { packageName, err }; const statusCode = err.response?.statusCode ?? 0; if (statusCode === 429 || statusCode >= 500 && statusCode < 600) { logger.warn({ packageName, err }, `CocoaPods registry failure`); throw new ExternalHostError(err); } if (statusCode === 401) logger.debug(errorData, "Authorization error"); else if (statusCode === 404) logger.debug(errorData, "Package lookup error"); else if (err.message === "host-disabled") logger.trace(errorData, "Host disabled"); else 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"; } return false; } function releasesCDNUrl(packageName, registryUrl) { return `${registryUrl}/all_pods_versions_${shardParts(packageName).join("_")}.txt`; } var PodDatasource = class PodDatasource extends Datasource { static id = "pod"; defaultRegistryUrls = ["https://cdn.cocoapods.org"]; registryStrategy = "hunt"; githubHttp; constructor() { super(PodDatasource.id); this.githubHttp = new 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) return { releases: resp.map(({ name }) => ({ version: name })) }; 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"); 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(newlineRegex); for (const line of lines) { const [name, ...versions] = line.split("/"); if (name === packageName.replace(regEx(/\/.*$/), "")) return { releases: versions.map((version) => ({ version })) }; } } return null; } async _getReleases({ packageName, registryUrl }) { /* v8 ignore next 3 -- should never happen */ if (!registryUrl) return null; const podName = packageName.replace(regEx(/\/.*$/), ""); let baseUrl = registryUrl.replace(regEx(/\/+$/), ""); if (isDefaultRepo(baseUrl)) [baseUrl] = this.defaultRegistryUrls; let result = null; const match = githubRegex.exec(baseUrl); if (match?.groups && !baseUrl.includes("/api/pods/")) { baseUrl = 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; } getReleases(config) { return withCache({ ttlMinutes: 30, namespace: `datasource-${PodDatasource.id}`, key: `${config.registryUrl}:${config.packageName}`, fallback: true }, () => this._getReleases(config)); } }; //#endregion export { PodDatasource }; //# sourceMappingURL=index.js.map