renovate
Version:
Automated dependency updates. Flexible so you don't need to be.
216 lines • 8.54 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.confidenceLevels = void 0;
exports.initConfig = initConfig;
exports.resetConfig = resetConfig;
exports.isMergeConfidence = isMergeConfidence;
exports.isActiveConfidenceLevel = isActiveConfidenceLevel;
exports.satisfiesConfidenceLevel = satisfiesConfidenceLevel;
exports.getMergeConfidenceLevel = getMergeConfidenceLevel;
exports.initMergeConfidence = initMergeConfidence;
exports.getApiToken = getApiToken;
const tslib_1 = require("tslib");
const is_1 = tslib_1.__importDefault(require("@sindresorhus/is"));
const merge_confidence_1 = require("../../config/presets/internal/merge-confidence");
const logger_1 = require("../../logger");
const external_host_error_1 = require("../../types/errors/external-host-error");
const packageCache = tslib_1.__importStar(require("../cache/package"));
const hostRules = tslib_1.__importStar(require("../host-rules"));
const http_1 = require("../http");
const memory_http_cache_provider_1 = require("../http/cache/memory-http-cache-provider");
const regex_1 = require("../regex");
const url_1 = require("../url");
const common_1 = require("./common");
const hostType = 'merge-confidence';
const http = new http_1.Http(hostType);
let token;
let apiBaseUrl;
let supportedDatasources = [];
exports.confidenceLevels = {
low: -1,
neutral: 0,
high: 1,
'very high': 2,
};
function initConfig({ mergeConfidenceEndpoint, mergeConfidenceDatasources, }) {
apiBaseUrl = getApiBaseUrl(mergeConfidenceEndpoint);
token = getApiToken();
supportedDatasources =
mergeConfidenceDatasources ?? merge_confidence_1.supportedDatasources;
if (!is_1.default.nullOrUndefined(token)) {
logger_1.logger.debug(`Merge confidence token found for ${apiBaseUrl}`);
}
}
function resetConfig() {
token = undefined;
apiBaseUrl = undefined;
supportedDatasources = [];
}
function isMergeConfidence(value) {
return common_1.MERGE_CONFIDENCE.includes(value);
}
function isActiveConfidenceLevel(confidence) {
return isMergeConfidence(confidence) && confidence !== 'low';
}
function satisfiesConfidenceLevel(confidence, minimumConfidence) {
return exports.confidenceLevels[confidence] >= exports.confidenceLevels[minimumConfidence];
}
const updateTypeConfidenceMapping = {
pin: 'high',
digest: 'neutral',
pinDigest: 'high',
bump: 'neutral',
lockFileMaintenance: 'neutral',
lockfileUpdate: 'neutral',
rollback: 'neutral',
replacement: 'neutral',
major: null,
minor: null,
patch: null,
};
/**
* Retrieves the merge confidence of a package update if the merge confidence API is enabled. Otherwise, undefined is returned.
*
* @param datasource
* @param packageName
* @param currentVersion
* @param newVersion
* @param updateType
*
* @returns The merge confidence level for the given package release.
* @throws {ExternalHostError} If a request has been made and an error occurs during the request, such as a timeout, connection reset, authentication failure, or internal server error.
*/
async function getMergeConfidenceLevel(datasource, packageName, currentVersion, newVersion, updateType) {
if (is_1.default.nullOrUndefined(apiBaseUrl) || is_1.default.nullOrUndefined(token)) {
return undefined;
}
if (!supportedDatasources.includes(datasource)) {
return undefined;
}
if (!(currentVersion && newVersion && updateType)) {
return 'neutral';
}
const mappedConfidence = updateTypeConfidenceMapping[updateType];
if (mappedConfidence) {
return mappedConfidence;
}
return await queryApi(datasource, packageName, currentVersion, newVersion);
}
/**
* Queries the Merge Confidence API with the given package release information.
*
* @param datasource
* @param packageName
* @param currentVersion
* @param newVersion
*
* @returns The merge confidence level for the given package release.
* @throws {ExternalHostError} if a timeout or connection reset error, authentication failure, or internal server error occurs during the request.
*
* @remarks
* Results are cached for 60 minutes to reduce the number of API calls.
*/
async function queryApi(datasource, packageName, currentVersion, newVersion) {
// istanbul ignore if: defensive, already been validated before calling this function
if (is_1.default.nullOrUndefined(apiBaseUrl) || is_1.default.nullOrUndefined(token)) {
return 'neutral';
}
const escapedPackageName = packageName.replace((0, regex_1.regEx)(/\//g), '%2f');
const url = (0, url_1.joinUrlParts)(apiBaseUrl, 'api/mc/json', datasource, escapedPackageName, currentVersion, newVersion);
const cacheKey = `${token}:${url}`;
const cachedResult = await packageCache.get(hostType, cacheKey);
// istanbul ignore if
if (cachedResult) {
logger_1.logger.debug({
datasource,
packageName,
currentVersion,
newVersion,
cachedResult,
}, 'using merge confidence cached result');
return cachedResult;
}
let confidence = 'neutral';
try {
const res = (await http.getJsonUnchecked(url, {
cacheProvider: memory_http_cache_provider_1.memCacheProvider,
})).body;
if (isMergeConfidence(res.confidence)) {
confidence = res.confidence;
}
}
catch (err) {
apiErrorHandler(err);
}
await packageCache.set(hostType, cacheKey, confidence, 60);
return confidence;
}
/**
* Checks the health of the Merge Confidence API by attempting to authenticate with it.
*
* @returns Resolves when the API health check is completed successfully.
*
* @throws {ExternalHostError} if a timeout, connection reset error, authentication failure, or internal server error occurs during the request.
*
* @remarks
* This function first checks that the API base URL and an authentication bearer token are defined before attempting to
* authenticate with the API. If either the base URL or token is not defined, it will immediately return
* without making a request.
*/
async function initMergeConfidence(config) {
initConfig(config);
if (is_1.default.nullOrUndefined(apiBaseUrl) || is_1.default.nullOrUndefined(token)) {
logger_1.logger.trace('merge confidence API usage is disabled');
return;
}
const url = (0, url_1.joinUrlParts)(apiBaseUrl, 'api/mc/availability');
try {
await http.get(url);
}
catch (err) {
apiErrorHandler(err);
}
logger_1.logger.debug({ supportedDatasources }, 'merge confidence API - successfully authenticated');
return;
}
function getApiBaseUrl(mergeConfidenceEndpoint) {
const defaultBaseUrl = 'https://developer.mend.io/';
const baseFromEnv = mergeConfidenceEndpoint ?? defaultBaseUrl;
try {
const parsedBaseUrl = new URL(baseFromEnv).toString();
logger_1.logger.trace({ baseUrl: parsedBaseUrl }, 'using merge confidence API base found in environment variables');
return (0, url_1.ensureTrailingSlash)(parsedBaseUrl);
}
catch (err) {
logger_1.logger.warn({ err, baseFromEnv }, 'invalid merge confidence API base URL found in environment variables - using default value instead');
return defaultBaseUrl;
}
}
function getApiToken() {
return hostRules.find({
url: apiBaseUrl,
hostType,
})?.token;
}
/**
* Handles errors returned by the Merge Confidence API.
*
* @param err - The error object returned by the API.
* @throws {ExternalHostError} if a timeout or connection reset error, authentication failure, or internal server error occurs during the request.
*/
function apiErrorHandler(err) {
if (err.code === 'ETIMEDOUT' || err.code === 'ECONNRESET') {
logger_1.logger.error({ err }, 'merge confidence API request failed - aborting run');
throw new external_host_error_1.ExternalHostError(err, hostType);
}
if (err.statusCode === 403) {
logger_1.logger.error({ err }, 'merge confidence API token rejected - aborting run');
throw new external_host_error_1.ExternalHostError(err, hostType);
}
if (err.statusCode >= 500 && err.statusCode < 600) {
logger_1.logger.error({ err }, 'merge confidence API failure: 5xx - aborting run');
throw new external_host_error_1.ExternalHostError(err, hostType);
}
logger_1.logger.warn({ err }, 'error fetching merge confidence data');
}
//# sourceMappingURL=index.js.map