Skip to content

Commit

Permalink
fix: append the static repo config to the repo config (renovatebot#34313
Browse files Browse the repository at this point in the history
)
  • Loading branch information
Gabriel-Ladzaretti authored Feb 21, 2025
1 parent d972478 commit 718e197
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 72 deletions.
46 changes: 0 additions & 46 deletions lib/workers/repository/init/config.spec.ts

This file was deleted.

22 changes: 1 addition & 21 deletions lib/workers/repository/init/config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
import is from '@sindresorhus/is';
import { mergeChildConfig } from '../../../config';
import type { AllConfig, RenovateConfig } from '../../../config/types';
import { parseAndValidateOrExit } from '../../global/config/parse/env';
import type { RenovateConfig } from '../../../config/types';
import { checkOnboardingBranch } from '../onboarding/branch';
import { mergeInheritedConfig } from './inherited';
import { mergeRenovateConfig } from './merge';
Expand All @@ -13,24 +10,7 @@ export async function getRepoConfig(
let config = { ...config_ };
config.baseBranch = config.defaultBranch;
config = await mergeInheritedConfig(config);
config = await mergeStaticRepoEnvConfig(config, process.env);
config = await checkOnboardingBranch(config);
config = await mergeRenovateConfig(config);
return config;
}

export async function mergeStaticRepoEnvConfig(
config: AllConfig,
env: NodeJS.ProcessEnv,
): Promise<AllConfig> {
const repoEnvConfig = await parseAndValidateOrExit(
env,
'RENOVATE_STATIC_REPO_CONFIG',
);

if (!is.nonEmptyObject(repoEnvConfig)) {
return config;
}

return mergeChildConfig(config, repoEnvConfig);
}
191 changes: 190 additions & 1 deletion lib/workers/repository/init/merge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as decrypt from '../../../config/decrypt';
import { getConfig } from '../../../config/defaults';
import * as _migrateAndValidate from '../../../config/migrate-validate';
import * as _migrate from '../../../config/migration';
import type { AllConfig } from '../../../config/types';
import * as memCache from '../../../util/cache/memory';
import * as repoCache from '../../../util/cache/repository';
import { initRepoCache } from '../../../util/cache/repository/init';
Expand All @@ -21,6 +22,7 @@ import {
checkForRepoConfigError,
detectRepoFileConfig,
mergeRenovateConfig,
mergeStaticRepoEnvConfig,
setNpmTokenInNpmrc,
} from './merge';

Expand All @@ -45,10 +47,13 @@ jest.mock('../../../config/migration');
jest.mock('../../../config/migrate-validate');

describe('workers/repository/init/merge', () => {
afterEach(() => {
jest.restoreAllMocks();
});

describe('detectRepoFileConfig()', () => {
beforeEach(async () => {
await initRepoCache({ repoFingerprint: '0123456789abcdef' });
jest.restoreAllMocks();
});

it('returns config if not found', async () => {
Expand Down Expand Up @@ -469,4 +474,188 @@ describe('workers/repository/init/merge', () => {
expect(config).toMatchObject({ npmrc: 'something_auth=token\n' });
});
});

describe('static repository config', () => {
const repoStaticConfigKey = 'RENOVATE_STATIC_REPO_CONFIG';

beforeEach(() => {
migrate.migrateConfig.mockImplementation((c) => ({
isMigrated: true,
migratedConfig: c,
}));
migrateAndValidate.migrateAndValidate.mockImplementationOnce((_, c) => {
return Promise.resolve({
...c,
warnings: [],
errors: [],
});
});
});

describe('mergeStaticRepoEnvConfig()', () => {
interface MergeRepoEnvTestCase {
name: string;
currentConfig: AllConfig;
env: NodeJS.ProcessEnv;
want: AllConfig;
}

const testCases: MergeRepoEnvTestCase[] = [
{
name: 'it does nothing',
env: {},
currentConfig: { repositories: ['some/repo'] },
want: { repositories: ['some/repo'] },
},
{
name: 'it merges env with the current config',
env: { [repoStaticConfigKey]: '{"dependencyDashboard":true}' },
currentConfig: { repositories: ['some/repo'] },
want: {
dependencyDashboard: true,
repositories: ['some/repo'],
},
},
{
name: 'it ignores env with other renovate specific configuration options',
env: { RENOVATE_CONFIG: '{"dependencyDashboard":true}' },
currentConfig: { repositories: ['some/repo'] },
want: { repositories: ['some/repo'] },
},
];

it.each(testCases)(
'$name',
async ({ env, currentConfig, want }: MergeRepoEnvTestCase) => {
const got = await mergeStaticRepoEnvConfig(currentConfig, env);

expect(got).toEqual(want);
},
);
});

describe('mergeRenovateConfig() with a static repository config', () => {
beforeEach(() => {
delete process.env[repoStaticConfigKey];

scm.getFileList.mockResolvedValueOnce(['renovate.json']);
});

interface MergeRepoFileAndEnvConfigTestCase {
name: string;
currentConfig: AllConfig;
repoFileConfig: AllConfig;
staticConfig: AllConfig;
wantConfig: AllConfig;
}

it.each<MergeRepoFileAndEnvConfigTestCase>([
{
name: 'it does nothing',
currentConfig: {},
repoFileConfig: {},
staticConfig: {},
wantConfig: {
renovateJsonPresent: true,
warnings: [],
},
},
{
name: 'it should resolve and use the repo file config when the static config is not set',
currentConfig: {},
repoFileConfig: {
extends: ['group:socketio'],
},
staticConfig: {},
wantConfig: {
description: ['Group socket.io packages.'],
packageRules: [
{
groupName: 'socket.io packages',
matchPackageNames: ['socket.io**'],
},
],
renovateJsonPresent: true,
warnings: [],
},
},
{
name: 'it should resolve and use the static config when no repo file present',
currentConfig: {},
repoFileConfig: {},
staticConfig: { extends: ['group:socketio'] },
wantConfig: {
description: ['Group socket.io packages.'],
packageRules: [
{
groupName: 'socket.io packages',
matchPackageNames: ['socket.io**'],
},
],
renovateJsonPresent: true,
warnings: [],
},
},
{
name: 'it should merge a static repo config into the repo config by appending it',
currentConfig: {},
repoFileConfig: {
extends: ['group:socketio'],
packageRules: [
{
matchConfidence: ['high', 'very high'],
groupName: 'high merge confidence',
},
],
},
staticConfig: {
dependencyDashboard: true,
packageRules: [
{
groupName: 'my-custom-socketio-override',
matchPackageNames: ['socket.io**'],
},
],
},
wantConfig: {
dependencyDashboard: true,
description: ['Group socket.io packages.'],
packageRules: [
{
groupName: 'socket.io packages',
matchPackageNames: ['socket.io**'],
},
{
groupName: 'high merge confidence',
matchConfidence: ['high', 'very high'],
},
{
groupName: 'my-custom-socketio-override',
matchPackageNames: ['socket.io**'],
},
],
renovateJsonPresent: true,
warnings: [],
},
},
])(
'$name',
async ({
staticConfig,
repoFileConfig,
currentConfig,
wantConfig,
}: MergeRepoFileAndEnvConfigTestCase) => {
fs.readLocalFile.mockResolvedValueOnce(
JSON.stringify(repoFileConfig),
);
process.env[repoStaticConfigKey] = JSON.stringify(staticConfig);

const got = await mergeRenovateConfig(currentConfig);

expect(got).toStrictEqual(wantConfig);
},
);
});
});
});
29 changes: 25 additions & 4 deletions lib/workers/repository/init/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { migrateConfig } from '../../../config/migration';
import { parseFileConfig } from '../../../config/parse';
import * as presets from '../../../config/presets';
import { applySecretsToConfig } from '../../../config/secrets';
import type { RenovateConfig } from '../../../config/types';
import type { AllConfig, RenovateConfig } from '../../../config/types';
import {
CONFIG_VALIDATION,
REPOSITORY_CHANGED,
Expand All @@ -25,6 +25,7 @@ import * as queue from '../../../util/http/queue';
import * as throttle from '../../../util/http/throttle';
import { maskToken } from '../../../util/mask';
import { regEx } from '../../../util/regex';
import { parseAndValidateOrExit } from '../../global/config/parse/env';
import { getOnboardingConfig } from '../onboarding/branch/config';
import { getDefaultConfigFileName } from '../onboarding/branch/create';
import {
Expand Down Expand Up @@ -187,15 +188,19 @@ export async function mergeRenovateConfig(
};
}
const configFileParsed = repoConfig?.configFileParsed || {};
const configFileAndEnv = await mergeStaticRepoEnvConfig(
configFileParsed,
process.env,
);
if (is.nonEmptyArray(returnConfig.extends)) {
configFileParsed.extends = [
configFileAndEnv.extends = [
...returnConfig.extends,
...(configFileParsed.extends || []),
...(configFileAndEnv.extends ?? []),
];
delete returnConfig.extends;
}
checkForRepoConfigError(repoConfig);
const migratedConfig = await migrateAndValidate(config, configFileParsed);
const migratedConfig = await migrateAndValidate(config, configFileAndEnv);
if (migratedConfig.errors?.length) {
const error = new Error(CONFIG_VALIDATION);
error.validationSource = repoConfig.configFileName;
Expand Down Expand Up @@ -312,3 +317,19 @@ export function setNpmTokenInNpmrc(config: RenovateConfig): void {

delete config.npmToken;
}

export async function mergeStaticRepoEnvConfig(
config: AllConfig,
env: NodeJS.ProcessEnv,
): Promise<AllConfig> {
const repoEnvConfig = await parseAndValidateOrExit(
env,
'RENOVATE_STATIC_REPO_CONFIG',
);

if (!is.nonEmptyObject(repoEnvConfig)) {
return config;
}

return mergeChildConfig(config, repoEnvConfig);
}

0 comments on commit 718e197

Please sign in to comment.