diff --git a/docs/usage/merge-confidence.md b/docs/usage/merge-confidence.md index 76e361aa616b1c..01a24c4633abbb 100644 --- a/docs/usage/merge-confidence.md +++ b/docs/usage/merge-confidence.md @@ -29,6 +29,7 @@ Renovate will show Merge Confidence badges for these languages: | Language | Datasource | | ---------- | ----------- | +| Golang | `go` | | JavaScript | `npm` | | Java | `maven` | | Python | `pypi` | diff --git a/docs/usage/self-hosted-experimental.md b/docs/usage/self-hosted-experimental.md index 45d7c8fffc7003..01a2993e414873 100644 --- a/docs/usage/self-hosted-experimental.md +++ b/docs/usage/self-hosted-experimental.md @@ -126,6 +126,11 @@ Skip initializing `RE2` for regular expressions and instead use Node-native `Reg If set, Renovate will query this API for Merge Confidence data. This feature is in private beta. +## `RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES` + +If set, Renovate will query the merge-confidence JSON API only for datasources that are part of this list. +The expected value for this environment variable is a JSON array of strings. + ## `RENOVATE_X_PLATFORM_VERSION` If set, Renovate will use this string as GitLab server version instead of checking via the GitLab API. diff --git a/lib/config/presets/internal/merge-confidence.ts b/lib/config/presets/internal/merge-confidence.ts index abde81e4398f2a..f2e900c8bf621b 100644 --- a/lib/config/presets/internal/merge-confidence.ts +++ b/lib/config/presets/internal/merge-confidence.ts @@ -1,19 +1,21 @@ import type { Preset } from '../types'; +export const supportedDatasources = [ + 'go', + 'maven', + 'npm', + 'nuget', + 'packagist', + 'pypi', + 'rubygems', +]; + export const presets: Record = { 'all-badges': { description: 'Show all Merge Confidence badges for pull requests.', packageRules: [ { - matchDatasources: [ - 'maven', - 'npm', - 'nuget', - 'packagist', - 'pypi', - 'rubygems', - 'go', - ], + matchDatasources: supportedDatasources, matchUpdateTypes: ['patch', 'minor', 'major'], prBodyColumns: [ 'Package', @@ -31,15 +33,7 @@ export const presets: Record = { 'Show only the Age and Confidence Merge Confidence badges for pull requests.', packageRules: [ { - matchDatasources: [ - 'maven', - 'npm', - 'nuget', - 'packagist', - 'pypi', - 'rubygems', - 'go', - ], + matchDatasources: supportedDatasources, matchUpdateTypes: ['patch', 'minor', 'major'], prBodyColumns: ['Package', 'Change', 'Age', 'Confidence'], }, diff --git a/lib/util/merge-confidence/index.spec.ts b/lib/util/merge-confidence/index.spec.ts index a347fd0170f306..6c9e379abe23cc 100644 --- a/lib/util/merge-confidence/index.spec.ts +++ b/lib/util/merge-confidence/index.spec.ts @@ -9,6 +9,7 @@ import { initConfig, initMergeConfidence, isActiveConfidenceLevel, + parseSupportedDatasourceString, resetConfig, satisfiesConfidenceLevel, } from '.'; @@ -296,8 +297,11 @@ describe('util/merge-confidence/index', () => { }); describe('initMergeConfidence()', () => { - it('using default base url if none is set', async () => { + beforeEach(() => { resetConfig(); + }); + + it('using default base url if none is set', async () => { delete process.env.RENOVATE_X_MERGE_CONFIDENCE_API_BASE_URL; httpMock .scope(defaultApiBaseUrl) @@ -309,12 +313,22 @@ describe('util/merge-confidence/index', () => { 'using default merge confidence API base URL', ); expect(logger.debug).toHaveBeenCalledWith( + { + supportedDatasources: [ + 'go', + 'maven', + 'npm', + 'nuget', + 'packagist', + 'pypi', + 'rubygems', + ], + }, 'merge confidence API - successfully authenticated', ); }); it('warns and then resolves if base url is invalid', async () => { - resetConfig(); process.env.RENOVATE_X_MERGE_CONFIDENCE_API_BASE_URL = 'invalid-url.com'; httpMock @@ -328,12 +342,12 @@ describe('util/merge-confidence/index', () => { 'invalid merge confidence API base URL found in environment variables - using default value instead', ); expect(logger.debug).toHaveBeenCalledWith( + expect.anything(), 'merge confidence API - successfully authenticated', ); }); it('resolves if no token', async () => { - resetConfig(); hostRules.clear(); await expect(initMergeConfidence()).toResolve(); @@ -347,6 +361,7 @@ describe('util/merge-confidence/index', () => { await expect(initMergeConfidence()).toResolve(); expect(logger.debug).toHaveBeenCalledWith( + expect.anything(), 'merge confidence API - successfully authenticated', ); }); @@ -389,6 +404,57 @@ describe('util/merge-confidence/index', () => { 'merge confidence API request failed - aborting run', ); }); + + describe('parseSupportedDatasourceList()', () => { + type ParseSupportedDatasourceTestCase = { + name: string; + datasourceListString: string | undefined; + expected: string[] | undefined; + }; + + afterEach(() => { + delete process.env.RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES; + }); + + it.each([ + { + name: 'it should do nothing when the input is undefined', + datasourceListString: undefined, + expected: undefined, + }, + { + name: 'it should successfully parse the given datasource list', + datasourceListString: `["go","npm"]`, + expected: ['go', 'npm'], + }, + { + name: 'it should gracefully handle invalid json', + datasourceListString: `{`, + expected: undefined, + }, + { + name: 'it should discard non-array JSON input', + datasourceListString: `{}`, + expected: undefined, + }, + { + name: 'it should discard non-string array JSON input', + datasourceListString: `[1,2]`, + expected: undefined, + }, + ])( + `$name`, + ({ + datasourceListString, + expected, + }: ParseSupportedDatasourceTestCase) => { + process.env.RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES = + datasourceListString; + + expect(parseSupportedDatasourceString()).toStrictEqual(expected); + }, + ); + }); }); }); }); diff --git a/lib/util/merge-confidence/index.ts b/lib/util/merge-confidence/index.ts index e903d32cab0f4b..95386836a04a36 100644 --- a/lib/util/merge-confidence/index.ts +++ b/lib/util/merge-confidence/index.ts @@ -1,8 +1,10 @@ import is from '@sindresorhus/is'; +import { supportedDatasources as presetSupportedDatasources } from '../../config/presets/internal/merge-confidence'; import type { UpdateType } from '../../config/types'; import { logger } from '../../logger'; import { ExternalHostError } from '../../types/errors/external-host-error'; import * as packageCache from '../cache/package'; +import { parseJson } from '../common'; import * as hostRules from '../host-rules'; import { Http } from '../http'; import { MERGE_CONFIDENCE } from './common'; @@ -12,8 +14,7 @@ const hostType = 'merge-confidence'; const http = new Http(hostType); let token: string | undefined; let apiBaseUrl: string | undefined; - -const supportedDatasources = ['npm', 'maven', 'pypi']; +let supportedDatasources: string[] = []; export const confidenceLevels: Record = { low: -1, @@ -25,14 +26,47 @@ export const confidenceLevels: Record = { export function initConfig(): void { apiBaseUrl = getApiBaseUrl(); token = getApiToken(); + supportedDatasources = + parseSupportedDatasourceString() ?? presetSupportedDatasources; + if (!is.nullOrUndefined(token)) { logger.debug(`Merge confidence token found for ${apiBaseUrl}`); } } +export function parseSupportedDatasourceString(): string[] | undefined { + const supportedDatasourceString = + process.env.RENOVATE_X_MERGE_CONFIDENCE_SUPPORTED_DATASOURCES; + + if (!is.string(supportedDatasourceString)) { + return undefined; + } + + let parsedDatasourceList: unknown; + try { + parsedDatasourceList = parseJson(supportedDatasourceString, '.json5'); + } catch (err) { + logger.error( + { supportedDatasourceString, err }, + 'Failed to parse supported datasources list; Invalid JSON format', + ); + } + + if (!is.array(parsedDatasourceList, is.string)) { + logger.warn( + { parsedDatasourceList }, + `Expected a string array but got ${typeof parsedDatasourceList}`, + ); + return undefined; + } + + return parsedDatasourceList; +} + export function resetConfig(): void { token = undefined; apiBaseUrl = undefined; + supportedDatasources = []; } export function isMergeConfidence(value: string): value is MergeConfidence { @@ -190,7 +224,10 @@ export async function initMergeConfidence(): Promise { apiErrorHandler(err); } - logger.debug('merge confidence API - successfully authenticated'); + logger.debug( + { supportedDatasources }, + 'merge confidence API - successfully authenticated', + ); return; }