UNPKG

renovate

Version:

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

233 lines • 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.GoProxyDatasource = void 0; exports.pseudoVersionToRelease = pseudoVersionToRelease; const tslib_1 = require("tslib"); const is_1 = tslib_1.__importDefault(require("@sindresorhus/is")); 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 filter_map_1 = require("../../../util/filter-map"); const http_1 = require("../../../util/http"); const p = tslib_1.__importStar(require("../../../util/promises")); const regex_1 = require("../../../util/regex"); const timestamp_1 = require("../../../util/timestamp"); const url_1 = require("../../../util/url"); const go_mod_directive_1 = tslib_1.__importDefault(require("../../versioning/go-mod-directive")); const datasource_1 = require("../datasource"); const base_1 = require("./base"); const common_1 = require("./common"); const goproxy_parser_1 = require("./goproxy-parser"); const releases_direct_1 = require("./releases-direct"); const modRegex = (0, regex_1.regEx)(/^(?<baseMod>.*?)(?:[./]v(?<majorVersion>\d+))?$/); /** * @see https://go.dev/ref/mod#pseudo-versions */ const pseudoVersionRegex = (0, regex_1.regEx)(/v\d+\.\d+\.\d+-(?:\w+\.)?(?:0\.)?(?<timestamp>\d{14})-(?<digest>[a-f0-9]{12})/i); function pseudoVersionToRelease(pseudoVersion) { const match = pseudoVersion.match(pseudoVersionRegex)?.groups; if (!match) { return null; } const { digest: newDigest, timestamp } = match; const releaseTimestamp = (0, timestamp_1.asTimestamp)(timestamp); return { version: pseudoVersion, newDigest, releaseTimestamp, }; } class GoProxyDatasource extends datasource_1.Datasource { static id = 'go-proxy'; constructor() { super(GoProxyDatasource.id); } direct = new releases_direct_1.GoDirectDatasource(); async getReleases(config) { const { packageName } = config; logger_1.logger.trace(`goproxy.getReleases(${packageName})`); const goproxy = (0, env_1.getEnv)().GOPROXY ?? 'https://proxy.golang.org,direct'; if (goproxy === 'direct') { return this.direct.getReleases(config); } const proxyList = (0, goproxy_parser_1.parseGoproxy)(goproxy); const noproxy = (0, goproxy_parser_1.parseNoproxy)(); let result = null; if (noproxy?.test(packageName)) { logger_1.logger.debug(`Fetching ${packageName} via GONOPROXY match`); result = await this.direct.getReleases(config); return result; } for (const { url, fallback } of proxyList) { try { if (url === 'off') { break; } else if (url === 'direct') { result = await this.direct.getReleases(config); break; } const res = await this.getVersionsWithInfo(url, packageName); if (res.releases.length) { result = res; break; } } catch (err) { const potentialHttpError = err instanceof external_host_error_1.ExternalHostError ? err.err : err; const statusCode = potentialHttpError?.response?.statusCode; const canFallback = fallback === '|' ? true : statusCode === 404 || statusCode === 410; const msg = canFallback ? 'Goproxy error: trying next URL provided with GOPROXY' : 'Goproxy error: skipping other URLs provided with GOPROXY'; logger_1.logger.debug({ err }, msg); if (!canFallback) { break; } } } if (result && !result.sourceUrl) { try { const datasource = await base_1.BaseGoDatasource.getDatasource(packageName); const sourceUrl = (0, common_1.getSourceUrl)(datasource); if (sourceUrl) { result.sourceUrl = sourceUrl; } } catch (err) { logger_1.logger.trace({ err }, `Can't get datasource for ${packageName}`); } } return result; } /** * Avoid ambiguity when serving from case-insensitive file systems. * * @see https://golang.org/ref/mod#goproxy-protocol */ encodeCase(input) { return input.replace((0, regex_1.regEx)(/([A-Z])/g), (x) => `!${x.toLowerCase()}`); } async listVersions(baseUrl, packageName) { const url = (0, url_1.joinUrlParts)(baseUrl, this.encodeCase(packageName), '@v', 'list'); const { body } = await this.http.getText(url); return (0, filter_map_1.filterMap)(body.split(regex_1.newlineRegex), (str) => { if (!is_1.default.nonEmptyStringAndNotWhitespace(str)) { return null; } const [version, timestamp] = str.trim().split((0, regex_1.regEx)(/\s+/)); const release = pseudoVersionToRelease(version) ?? { version }; const releaseTimestamp = (0, timestamp_1.asTimestamp)(timestamp); if (releaseTimestamp) { release.releaseTimestamp = releaseTimestamp; } return release; }); } async versionInfo(baseUrl, packageName, version) { const url = (0, url_1.joinUrlParts)(baseUrl, this.encodeCase(packageName), '@v', `${version}.info`); const res = await this.http.getJsonUnchecked(url); const result = { version: res.body.Version, }; const releaseTimestamp = (0, timestamp_1.asTimestamp)(res.body.Time); if (releaseTimestamp) { result.releaseTimestamp = releaseTimestamp; } return result; } async getLatestVersion(baseUrl, packageName) { try { const url = (0, url_1.joinUrlParts)(baseUrl, this.encodeCase(packageName), '@latest'); const res = await this.http.getJsonUnchecked(url); return res.body.Version; } catch (err) { logger_1.logger.trace({ err }, 'Failed to get latest version'); return null; } } async getVersionsWithInfo(baseUrl, packageName) { const isGopkgin = packageName.startsWith('gopkg.in/'); const majorSuffixSeparator = isGopkgin ? '.' : '/'; const modParts = packageName.match(modRegex)?.groups; const baseMod = modParts?.baseMod ?? /* istanbul ignore next */ packageName; const packageMajor = parseInt(modParts?.majorVersion ?? '0'); const result = { releases: [] }; for (let major = packageMajor;; major += 1) { let pkg = `${baseMod}${majorSuffixSeparator}v${major}`; if (!isGopkgin && major < 2) { pkg = baseMod; major += 1; // v0 and v1 are the same module } let releases = []; try { const res = await this.listVersions(baseUrl, pkg); // Artifactory returns all versions in any major (past and future), // so starting from v2, we filter them in order to avoid the infinite loop const filteredReleases = res.filter(({ version }) => { if (major < 2) { return true; } return (version.split((0, regex_1.regEx)(/[^\d]+/)).find(is_1.default.truthy) === major.toString()); }); releases = await p.map(filteredReleases, async (versionInfo) => { const { version, newDigest, releaseTimestamp } = versionInfo; if (releaseTimestamp) { return { version, newDigest, releaseTimestamp }; } try { return await this.versionInfo(baseUrl, pkg, version); } catch (err) { logger_1.logger.trace({ err }, `Can't obtain data from ${baseUrl}`); return { version }; } }); result.releases.push(...releases); } catch (err) { const potentialHttpError = err instanceof external_host_error_1.ExternalHostError ? err.err : err; if (potentialHttpError instanceof http_1.HttpError && potentialHttpError.response?.statusCode === 404 && major !== packageMajor) { break; } throw err; } const latestVersion = await this.getLatestVersion(baseUrl, pkg); if (latestVersion) { result.tags ??= {}; result.tags.latest ??= latestVersion; if (go_mod_directive_1.default.isGreaterThan(latestVersion, result.tags.latest)) { result.tags.latest = latestVersion; } if (!result.releases.length) { const releaseFromLatest = pseudoVersionToRelease(latestVersion); if (releaseFromLatest) { result.releases.push(releaseFromLatest); } } } if (!releases.length) { break; } } return result; } static getCacheKey({ packageName }) { const goproxy = (0, env_1.getEnv)().GOPROXY; const noproxy = (0, goproxy_parser_1.parseNoproxy)(); // TODO: types (#22198) return `${packageName}@@${goproxy}@@${noproxy?.toString()}`; } } exports.GoProxyDatasource = GoProxyDatasource; tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: `datasource-${GoProxyDatasource.id}`, key: (config) => GoProxyDatasource.getCacheKey(config), }) ], GoProxyDatasource.prototype, "getReleases", null); //# sourceMappingURL=releases-goproxy.js.map