renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
223 lines • 10.8 kB
JavaScript
;
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