UNPKG

renovate

Version:

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

223 lines • 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TerraformProviderDatasource = void 0; const tslib_1 = require("tslib"); // TODO: types (#22198) 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 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 hashicorpVersioning = tslib_1.__importStar(require("../../versioning/hashicorp")); const base_1 = require("../terraform-module/base"); const utils_1 = require("../terraform-module/utils"); class TerraformProviderDatasource extends base_1.TerraformDatasource { static id = 'terraform-provider'; static defaultRegistryUrls = [ 'https://registry.terraform.io', 'https://releases.hashicorp.com', ]; static repositoryRegex = (0, regex_1.regEx)(/^hashicorp\/(?<packageName>\S+)$/); constructor() { super(TerraformProviderDatasource.id); } defaultRegistryUrls = TerraformProviderDatasource.defaultRegistryUrls; defaultVersioning = hashicorpVersioning.id; registryStrategy = 'hunt'; releaseTimestampSupport = true; releaseTimestampNote = 'The release timestamp is only supported for the latest version, and is determined from the `published_at` field in the results.'; sourceUrlSupport = 'package'; sourceUrlNote = 'The source URL is determined from the the `source` field in the results.'; async getReleases({ packageName, registryUrl, }) { /* v8 ignore next 3 -- should never happen */ if (!registryUrl) { return null; } logger_1.logger.trace(`terraform-provider.getDependencies() packageName: ${packageName}`); if (registryUrl === this.defaultRegistryUrls[1]) { return await this.queryReleaseBackend(packageName, registryUrl); } const repository = TerraformProviderDatasource.getRepository({ packageName, }); const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryUrl); if (registryUrl === this.defaultRegistryUrls[0]) { return await this.queryRegistryExtendedApi(serviceDiscovery, registryUrl, repository); } return await this.queryRegistryVersions(serviceDiscovery, registryUrl, repository); } static getRepository({ packageName }) { return packageName.includes('/') ? packageName : `hashicorp/${packageName}`; } /** * this uses the api that terraform registry has in addition to the base api * this endpoint provides more information, such as release date * this api is undocumented. */ async queryRegistryExtendedApi(serviceDiscovery, registryUrl, repository) { const backendURL = (0, utils_1.createSDBackendURL)(registryUrl, 'providers.v1', serviceDiscovery, repository); const res = (await this.http.getJsonUnchecked(backendURL)).body; const dep = { releases: res.versions.map((version) => ({ version, })), }; if (res.source) { dep.sourceUrl = res.source; } // set published date for latest release const latestVersion = dep.releases.find((release) => res.version === release.version); if (latestVersion) { latestVersion.releaseTimestamp = (0, timestamp_1.asTimestamp)(res.published_at); } dep.homepage = `${registryUrl}/providers/${repository}`; return dep; } /** * this version uses the Provider Registry Protocol that all registries are required to implement * https://www.terraform.io/internals/provider-registry-protocol */ async queryRegistryVersions(serviceDiscovery, registryUrl, repository) { const backendURL = (0, utils_1.createSDBackendURL)(registryUrl, 'providers.v1', serviceDiscovery, `${repository}/versions`); const res = (await this.http.getJsonUnchecked(backendURL)).body; const dep = { releases: res.versions.map(({ version }) => ({ version, })), }; return dep; } async queryReleaseBackend(packageName, registryURL) { const hashicorpPackage = packageName.replace('hashicorp/', ''); const backendLookUpName = `terraform-provider-${hashicorpPackage}`; const backendURL = (0, url_1.joinUrlParts)(registryURL, backendLookUpName, `index.json`); const res = (await this.http.getJsonUnchecked(backendURL)).body; const dep = { releases: Object.keys(res.versions).map((version) => ({ version, })), sourceUrl: (0, url_1.joinUrlParts)('https://github.com/terraform-providers', backendLookUpName), }; return dep; } async getBuilds(registryURL, repository, version) { if (registryURL === TerraformProviderDatasource.defaultRegistryUrls[1]) { // check if registryURL === secondary backend const repositoryRegexResult = TerraformProviderDatasource.repositoryRegex.exec(repository)?.groups; if (!repositoryRegexResult) { // non hashicorp builds are not supported with releases.hashicorp.com return null; } const packageName = repositoryRegexResult.packageName; const backendLookUpName = `terraform-provider-${packageName}`; let versionReleaseBackend; try { versionReleaseBackend = await this.getReleaseBackendIndex(backendLookUpName, version); } catch (err) { if (err instanceof external_host_error_1.ExternalHostError) { throw err; } logger_1.logger.debug({ err, backendLookUpName, version }, `Failed to retrieve builds for ${backendLookUpName} ${version}`); // throw an error to disable caching throw new external_host_error_1.ExternalHostError(err); } return versionReleaseBackend.builds; } // check public or private Terraform registry const serviceDiscovery = await this.getTerraformServiceDiscoveryResult(registryURL); if (!serviceDiscovery) { // throw an error to disable caching throw new external_host_error_1.ExternalHostError(new Error(`Service discovery not found for ${registryURL}`)); } const backendURL = (0, utils_1.createSDBackendURL)(registryURL, 'providers.v1', serviceDiscovery, repository); const versionsResponse = (await this.http.getJsonUnchecked(`${backendURL}/versions`)).body; if (!versionsResponse.versions) { // throw an error to disable caching throw new external_host_error_1.ExternalHostError(new Error(`Failed to retrieve version list for ${backendURL}`)); } const builds = versionsResponse.versions.find((value) => value.version === version); if (!builds) { // should never happen, but just in case // throw an error to disable caching throw new external_host_error_1.ExternalHostError(new Error(`No builds found for ${repository}:${version} on ${registryURL}`)); } const result = await p.map(builds.platforms, async (platform) => { const buildURL = `${backendURL}/${version}/download/${platform.os}/${platform.arch}`; try { const res = (await this.http.getJsonUnchecked(buildURL)).body; const newBuild = { name: repository, url: res.download_url, version, ...res, }; return newBuild; } catch (err) { /* v8 ignore next 3 -- hard to test */ if (err instanceof external_host_error_1.ExternalHostError) { throw err; } logger_1.logger.debug({ err, url: buildURL }, 'Failed to retrieve build'); // throw an error to disable caching throw new external_host_error_1.ExternalHostError(err); } }, { concurrency: 4 }); return result; } async getZipHashes(zipHashUrl) { // The hashes are formatted as the result of sha256sum in plain text, each line: <hash>\t<filename> let rawHashData; try { rawHashData = (await this.http.getText(zipHashUrl)).body; } catch (err) { /* v8 ignore next 3 -- hard to test */ if (err instanceof external_host_error_1.ExternalHostError) { throw err; } logger_1.logger.debug({ err, zipHashUrl }, `Failed to retrieve zip hashes from ${zipHashUrl}`); return undefined; } return rawHashData .trimEnd() .split('\n') .map((line) => line.split(/\s/)[0]); } async getReleaseBackendIndex(backendLookUpName, version) { return (await this.http.getJsonUnchecked(`${TerraformProviderDatasource.defaultRegistryUrls[1]}/${backendLookUpName}/${version}/index.json`)).body; } } exports.TerraformProviderDatasource = TerraformProviderDatasource; tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: `datasource-${TerraformProviderDatasource.id}`, key: (getReleasesConfig) => { const url = getReleasesConfig.registryUrl; const repo = TerraformProviderDatasource.getRepository(getReleasesConfig); return `getReleases:${url}/${repo}`; }, }) ], TerraformProviderDatasource.prototype, "getReleases", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: `datasource-${TerraformProviderDatasource.id}`, key: (registryURL, repository, version) => `getBuilds:${registryURL}/${repository}/${version}`, }) ], TerraformProviderDatasource.prototype, "getBuilds", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: `datasource-${TerraformProviderDatasource.id}`, key: (zipHashUrl) => `getZipHashes:${zipHashUrl}`, }) ], TerraformProviderDatasource.prototype, "getZipHashes", null); tslib_1.__decorate([ (0, decorator_1.cache)({ namespace: `datasource-${TerraformProviderDatasource.id}`, key: (backendLookUpName, version) => `getReleaseBackendIndex:${backendLookUpName}/${version}`, }) ], TerraformProviderDatasource.prototype, "getReleaseBackendIndex", null); //# sourceMappingURL=index.js.map