Skip to content

Commit

Permalink
fix(merge-confidence): fix initialization (#29300)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gabriel-Ladzaretti committed May 28, 2024
1 parent 49a95d3 commit 3bcd779
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 108 deletions.
2 changes: 0 additions & 2 deletions lib/config/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ export class GlobalConfig {
'httpCacheTtlDays',
'autodiscoverRepoSort',
'autodiscoverRepoOrder',
'mergeConfidenceEndpoint',
'mergeConfidenceDatasources',
'userAgent',
];

Expand Down
2 changes: 2 additions & 0 deletions lib/config/options/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ const options: RenovateOptions[] = [
name: 'mergeConfidenceEndpoint',
description:
'If set, Renovate will query this API for Merge Confidence data.',
stage: 'global',
type: 'string',
default: 'https://developer.mend.io/',
advancedUse: true,
Expand All @@ -82,6 +83,7 @@ const options: RenovateOptions[] = [
name: 'mergeConfidenceDatasources',
description:
'If set, Renovate will query the merge-confidence JSON API only for datasources that are part of this list.',
stage: 'global',
allowedValues: supportedDatasources,
default: supportedDatasources,
type: 'array',
Expand Down
4 changes: 2 additions & 2 deletions lib/config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export interface GlobalOnlyConfig {
globalExtends?: string[];
logFile?: string;
logFileLevel?: LogLevel;
mergeConfidenceDatasources?: string[];
mergeConfidenceEndpoint?: string;
platform?: PlatformId;
prCommitsPerRunLimit?: number;
privateKeyPath?: string;
Expand Down Expand Up @@ -155,8 +157,6 @@ export interface RepoGlobalConfig {
githubTokenWarn?: boolean;
includeMirrors?: boolean;
localDir?: string;
mergeConfidenceEndpoint?: string;
mergeConfidenceDatasources?: string[];
migratePresets?: Record<string, string>;
platform?: PlatformId;
presetCachePersistence?: boolean;
Expand Down
102 changes: 30 additions & 72 deletions lib/util/merge-confidence/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as httpMock from '../../../test/http-mock';
import { GlobalConfig } from '../../config/global';
import { EXTERNAL_HOST_ERROR } from '../../constants/error-messages';
import { logger } from '../../logger';
import type { HostRule } from '../../types';
Expand All @@ -10,23 +9,14 @@ import {
initConfig,
initMergeConfidence,
isActiveConfidenceLevel,
parseSupportedDatasourceString,
resetConfig,
satisfiesConfidenceLevel,
} from '.';

describe('util/merge-confidence/index', () => {
const apiBaseUrl = 'https://www.baseurl.com/';
const defaultApiBaseUrl = 'https://developer.mend.io/';
const supportedDatasources = [
'go',
'maven',
'npm',
'nuget',
'packagist',
'pypi',
'rubygems',
];
const userSupportedDatasources = ['go'];

describe('isActiveConfidenceLevel()', () => {
it('returns false if null', () => {
Expand Down Expand Up @@ -68,9 +58,8 @@ describe('util/merge-confidence/index', () => {

beforeEach(() => {
hostRules.add(hostRule);
initConfig();
initConfig({ mergeConfidenceEndpoint: apiBaseUrl });
memCache.reset();
GlobalConfig.set({ mergeConfidenceEndpoint: apiBaseUrl });
});

afterEach(() => {
Expand Down Expand Up @@ -311,14 +300,13 @@ describe('util/merge-confidence/index', () => {
resetConfig();
});

it('using default base url if none is set', async () => {
GlobalConfig.reset();
it('using default base url and supported datasources if either is set', async () => {
httpMock
.scope(defaultApiBaseUrl)
.get(`/api/mc/availability`)
.reply(200);

await expect(initMergeConfidence()).toResolve();
await expect(initMergeConfidence({})).toResolve();
expect(logger.debug).toHaveBeenCalledWith(
{
supportedDatasources: [
Expand All @@ -336,13 +324,14 @@ describe('util/merge-confidence/index', () => {
});

it('warns and then resolves if base url is invalid', async () => {
GlobalConfig.set({ mergeConfidenceEndpoint: 'invalid-url.com' });
httpMock
.scope(defaultApiBaseUrl)
.get(`/api/mc/availability`)
.reply(200);

await expect(initMergeConfidence()).toResolve();
await expect(
initMergeConfidence({ mergeConfidenceEndpoint: 'invalid-url.com' }),
).toResolve();
expect(logger.warn).toHaveBeenCalledWith(
expect.anything(),
'invalid merge confidence API base URL found in environment variables - using default value instead',
Expand All @@ -353,27 +342,33 @@ describe('util/merge-confidence/index', () => {
);
});

it('uses a custom base url containing path', async () => {
it('uses custom supported datasources and a base URL containing a path', async () => {
const renovateApi = 'https://domain.com/proxy/renovate-api';
GlobalConfig.set({ mergeConfidenceEndpoint: renovateApi });
httpMock.scope(renovateApi).get(`/api/mc/availability`).reply(200);

await expect(initMergeConfidence()).toResolve();
await expect(
initMergeConfidence({
mergeConfidenceEndpoint: renovateApi,
mergeConfidenceDatasources: userSupportedDatasources,
}),
).toResolve();

expect(logger.trace).toHaveBeenCalledWith(
expect.anything(),
'using merge confidence API base found in environment variables',
);
expect(logger.debug).toHaveBeenCalledWith(
expect.anything(),
{
supportedDatasources: ['go'],
},
'merge confidence API - successfully authenticated',
);
});

it('resolves if no token', async () => {
hostRules.clear();

await expect(initMergeConfidence()).toResolve();
await expect(initMergeConfidence({})).toResolve();
expect(logger.trace).toHaveBeenCalledWith(
'merge confidence API usage is disabled',
);
Expand All @@ -382,7 +377,9 @@ describe('util/merge-confidence/index', () => {
it('resolves when token is valid', async () => {
httpMock.scope(apiBaseUrl).get(`/api/mc/availability`).reply(200);

await expect(initMergeConfidence()).toResolve();
await expect(
initMergeConfidence({ mergeConfidenceEndpoint: apiBaseUrl }),
).toResolve();
expect(logger.debug).toHaveBeenCalledWith(
expect.anything(),
'merge confidence API - successfully authenticated',
Expand All @@ -392,9 +389,9 @@ describe('util/merge-confidence/index', () => {
it('throws on 403-Forbidden from mc API', async () => {
httpMock.scope(apiBaseUrl).get(`/api/mc/availability`).reply(403);

await expect(initMergeConfidence()).rejects.toThrow(
EXTERNAL_HOST_ERROR,
);
await expect(
initMergeConfidence({ mergeConfidenceEndpoint: apiBaseUrl }),
).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(logger.error).toHaveBeenCalledWith(
expect.anything(),
'merge confidence API token rejected - aborting run',
Expand All @@ -404,9 +401,9 @@ describe('util/merge-confidence/index', () => {
it('throws on 5xx host errors from mc API', async () => {
httpMock.scope(apiBaseUrl).get(`/api/mc/availability`).reply(503);

await expect(initMergeConfidence()).rejects.toThrow(
EXTERNAL_HOST_ERROR,
);
await expect(
initMergeConfidence({ mergeConfidenceEndpoint: apiBaseUrl }),
).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(logger.error).toHaveBeenCalledWith(
expect.anything(),
'merge confidence API failure: 5xx - aborting run',
Expand All @@ -419,53 +416,14 @@ describe('util/merge-confidence/index', () => {
.get(`/api/mc/availability`)
.replyWithError({ code: 'ECONNRESET' });

await expect(initMergeConfidence()).rejects.toThrow(
EXTERNAL_HOST_ERROR,
);
await expect(
initMergeConfidence({ mergeConfidenceEndpoint: apiBaseUrl }),
).rejects.toThrow(EXTERNAL_HOST_ERROR);
expect(logger.error).toHaveBeenCalledWith(
expect.anything(),
'merge confidence API request failed - aborting run',
);
});

describe('parseSupportedDatasourceList()', () => {
afterEach(() => {
GlobalConfig.reset();
});

it.each([
{
name: 'it should do return default value when the input is undefined',
datasources: undefined,
expected: supportedDatasources,
},
{
name: 'it should successfully parse the given datasource list',
datasources: ['go', 'npm'],
expected: ['go', 'npm'],
},
{
name: 'it should gracefully handle invalid JSON',
datasources: `{`,
expected: supportedDatasources,
},
{
name: 'it should discard non-array JSON input',
datasources: `{}`,
expected: supportedDatasources,
},
{
name: 'it should discard non-string array JSON input',
datasources: `[1,2]`,
expected: supportedDatasources,
},
])(`$name`, ({ datasources, expected }) => {
GlobalConfig.set({
mergeConfidenceDatasources: datasources,
});
expect(parseSupportedDatasourceString()).toStrictEqual(expected);
});
});
});
});
});
42 changes: 13 additions & 29 deletions lib/util/merge-confidence/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import is from '@sindresorhus/is';
import { GlobalConfig } from '../../config/global';
import { supportedDatasources as presetSupportedDatasources } from '../../config/presets/internal/merge-confidence';
import type { UpdateType } from '../../config/types';
import type { AllConfig, UpdateType } from '../../config/types';
import { logger } from '../../logger';
import { ExternalHostError } from '../../types/errors/external-host-error';
import * as packageCache from '../cache/package';
Expand All @@ -25,33 +24,21 @@ export const confidenceLevels: Record<MergeConfidence, number> = {
'very high': 2,
};

export function initConfig(): void {
apiBaseUrl = getApiBaseUrl();
export function initConfig({
mergeConfidenceEndpoint,
mergeConfidenceDatasources,
}: AllConfig): void {
apiBaseUrl = getApiBaseUrl(mergeConfidenceEndpoint);
token = getApiToken();
supportedDatasources = parseSupportedDatasourceString();

supportedDatasources =
mergeConfidenceDatasources ?? presetSupportedDatasources;

if (!is.nullOrUndefined(token)) {
logger.debug(`Merge confidence token found for ${apiBaseUrl}`);
}
}

export function parseSupportedDatasourceString(): string[] {
const supportedDatasources = GlobalConfig.get(
'mergeConfidenceDatasources',
presetSupportedDatasources,
);

if (!is.array(supportedDatasources, is.string)) {
logger.warn(
{ supportedDatasources },
`Expected a string array but got ${typeof supportedDatasources} - using default value instead`,
);
return presetSupportedDatasources;
}

return supportedDatasources;
}

export function resetConfig(): void {
token = undefined;
apiBaseUrl = undefined;
Expand Down Expand Up @@ -205,8 +192,8 @@ async function queryApi(
* authenticate with the API. If either the base URL or token is not defined, it will immediately return
* without making a request.
*/
export async function initMergeConfidence(): Promise<void> {
initConfig();
export async function initMergeConfidence(config: AllConfig): Promise<void> {
initConfig(config);

if (is.nullOrUndefined(apiBaseUrl) || is.nullOrUndefined(token)) {
logger.trace('merge confidence API usage is disabled');
Expand All @@ -227,12 +214,9 @@ export async function initMergeConfidence(): Promise<void> {
return;
}

function getApiBaseUrl(): string {
function getApiBaseUrl(mergeConfidenceEndpoint: string | undefined): string {
const defaultBaseUrl = 'https://developer.mend.io/';
const baseFromEnv = GlobalConfig.get(
'mergeConfidenceEndpoint',
defaultBaseUrl,
);
const baseFromEnv = mergeConfidenceEndpoint ?? defaultBaseUrl;

try {
const parsedBaseUrl = new URL(baseFromEnv).toString();
Expand Down
2 changes: 1 addition & 1 deletion lib/workers/global/initialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export async function globalInitialize(
setEmojiConfig(config);
setGlobalHostRules(config);
configureThirdPartyLibraries(config);
await initMergeConfidence();
await initMergeConfidence(config);
return config;
}

Expand Down
10 changes: 8 additions & 2 deletions lib/workers/repository/process/lookup/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { Fixtures } from '../../../../../test/fixtures';
import * as httpMock from '../../../../../test/http-mock';
import { partial } from '../../../../../test/util';
import { getConfig } from '../../../../config/defaults';
import { supportedDatasources as presetSupportedDatasources } from '../../../../config/presets/internal/merge-confidence';
import type { AllConfig } from '../../../../config/types';
import { CONFIG_VALIDATION } from '../../../../constants/error-messages';
import { CustomDatasource } from '../../../../modules/datasource/custom';
import { DockerDatasource } from '../../../../modules/datasource/docker';
Expand Down Expand Up @@ -4545,6 +4547,10 @@ describe('workers/repository/process/lookup/index', () => {

describe('handles merge confidence', () => {
const defaultApiBaseUrl = 'https://developer.mend.io/';
const mcConfig: AllConfig = {
mergeConfidenceEndpoint: defaultApiBaseUrl,
mergeConfidenceDatasources: presetSupportedDatasources,
};
const getMergeConfidenceSpy = jest.spyOn(
McApi,
'getMergeConfidenceLevel',
Expand All @@ -4556,7 +4562,7 @@ describe('workers/repository/process/lookup/index', () => {

beforeEach(() => {
hostRules.add(hostRule);
initConfig();
initConfig(mcConfig);
memCache.reset();
});

Expand Down Expand Up @@ -4637,7 +4643,7 @@ describe('workers/repository/process/lookup/index', () => {
config.packageName = 'webpack';
config.datasource = datasource;
hostRules.clear(); // reset merge confidence
initConfig();
initConfig(mcConfig);
httpMock
.scope('https://registry.npmjs.org')
.get('/webpack')
Expand Down

0 comments on commit 3bcd779

Please sign in to comment.