Skip to content
Snippets Groups Projects
Unverified Commit eb074924 authored by RahulGautamSingh's avatar RahulGautamSingh Committed by GitHub
Browse files

fix(config/inherit): resolve presets (#31642)

parent d094afeb
No related branches found
No related tags found
No related merge requests found
...@@ -159,6 +159,19 @@ Inherited config may use all Repository config settings, and any Global config o ...@@ -159,6 +159,19 @@ Inherited config may use all Repository config settings, and any Global config o
For information on how the Mend Renovate App supports Inherited config, see the dedicated "Mend Renovate App Config" section toward the end of this page. For information on how the Mend Renovate App supports Inherited config, see the dedicated "Mend Renovate App Config" section toward the end of this page.
#### Presets handling
If the inherited config contains `extends` presets, then Renovate will:
1. Resolve the presets
1. Add the resolved preset config to the beginning of the inherited config
1. Merge the presets on top of the global config
##### You can not ignore presets from inherited config
You can _not_ use `ignorePresets` in your repository config to ignore presets _within_ inherited config.
This is because inherited config is resolved _before_ the repository config.
### Repository config ### Repository config
Repository config is the config loaded from a config file in the repository. Repository config is the config loaded from a config file in the repository.
......
import { platform } from '../../../../test/util'; import { mocked, platform } from '../../../../test/util';
import * as presets_ from '../../../config/presets';
import type { RenovateConfig } from '../../../config/types'; import type { RenovateConfig } from '../../../config/types';
import * as validation from '../../../config/validation';
import { import {
CONFIG_INHERIT_NOT_FOUND, CONFIG_INHERIT_NOT_FOUND,
CONFIG_INHERIT_PARSE_ERROR, CONFIG_INHERIT_PARSE_ERROR,
...@@ -8,6 +10,10 @@ import { ...@@ -8,6 +10,10 @@ import {
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import { mergeInheritedConfig } from './inherited'; import { mergeInheritedConfig } from './inherited';
jest.mock('../../../config/presets');
const presets = mocked(presets_);
describe('workers/repository/init/inherited', () => { describe('workers/repository/init/inherited', () => {
let config: RenovateConfig; let config: RenovateConfig;
...@@ -84,4 +90,132 @@ describe('workers/repository/init/inherited', () => { ...@@ -84,4 +90,132 @@ describe('workers/repository/init/inherited', () => {
expect(res.onboarding).toBeFalse(); expect(res.onboarding).toBeFalse();
expect(logger.warn).not.toHaveBeenCalled(); expect(logger.warn).not.toHaveBeenCalled();
}); });
it('should resolve presets found in inherited config', async () => {
platform.getRawFile.mockResolvedValue(
'{"onboarding":false,"labels":["test"],"extends":[":automergeAll"]}',
);
presets.resolveConfigPresets.mockResolvedValue({
onboarding: false,
labels: ['test'],
automerge: true,
});
const res = await mergeInheritedConfig(config);
expect(res.labels).toEqual(['test']);
expect(res.onboarding).toBeFalse();
expect(logger.warn).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledWith(
'Resolving presets found in inherited config',
);
});
it('should warn if presets fails validation with warnings', async () => {
platform.getRawFile.mockResolvedValue(
'{"onboarding":false,"labels":["test"],"extends":[":automergeAll"]}',
);
jest
.spyOn(validation, 'validateConfig')
.mockResolvedValueOnce({
warnings: [],
errors: [],
})
.mockResolvedValueOnce({
warnings: [
{
message: 'some warning',
topic: 'Configuration Error',
},
],
errors: [],
});
presets.resolveConfigPresets.mockResolvedValue({
onboarding: false,
labels: ['test'],
automerge: true,
});
const res = await mergeInheritedConfig(config);
expect(res.binarySource).toBeUndefined();
expect(logger.warn).toHaveBeenCalledWith(
{
warnings: [
{
message: 'some warning',
topic: 'Configuration Error',
},
],
},
'Found warnings in presets inside the inherited configuration.',
);
});
it('should throw error if presets fails validation with errors', async () => {
platform.getRawFile.mockResolvedValue(
'{"labels":["test"],"extends":[":automergeAll"]}',
);
jest
.spyOn(validation, 'validateConfig')
.mockResolvedValueOnce({
warnings: [],
errors: [],
})
.mockResolvedValueOnce({
warnings: [],
errors: [
{
message: 'some error',
topic: 'Configuration Error',
},
],
});
presets.resolveConfigPresets.mockResolvedValue({
labels: ['test'],
automerge: true,
});
await expect(mergeInheritedConfig(config)).rejects.toThrow(
CONFIG_VALIDATION,
);
expect(logger.warn).toHaveBeenCalledWith(
{
errors: [
{
message: 'some error',
topic: 'Configuration Error',
},
],
},
'Found errors in presets inside the inherited configuration.',
);
});
it('should remove global config from presets found in inherited config', async () => {
platform.getRawFile.mockResolvedValue(
'{"labels":["test"],"extends":[":automergeAll"]}',
);
jest.spyOn(validation, 'validateConfig').mockResolvedValue({
warnings: [],
errors: [],
});
presets.resolveConfigPresets.mockResolvedValue({
labels: ['test'],
automerge: true,
binarySource: 'docker', // global config option: should not be here
});
const res = await mergeInheritedConfig(config);
expect(res.labels).toEqual(['test']);
expect(logger.warn).not.toHaveBeenCalled();
expect(logger.debug).toHaveBeenCalledWith(
{
inheritedConfig: {
labels: ['test'],
automerge: true,
binarySource: 'docker',
},
filteredConfig: {
labels: ['test'],
automerge: true,
},
},
'Removed global config from inherited config presets.',
);
});
}); });
...@@ -2,6 +2,7 @@ import is from '@sindresorhus/is'; ...@@ -2,6 +2,7 @@ import is from '@sindresorhus/is';
import { dequal } from 'dequal'; import { dequal } from 'dequal';
import { mergeChildConfig, removeGlobalConfig } from '../../../config'; import { mergeChildConfig, removeGlobalConfig } from '../../../config';
import { parseFileConfig } from '../../../config/parse'; import { parseFileConfig } from '../../../config/parse';
import { resolveConfigPresets } from '../../../config/presets';
import type { RenovateConfig } from '../../../config/types'; import type { RenovateConfig } from '../../../config/types';
import { validateConfig } from '../../../config/validation'; import { validateConfig } from '../../../config/validation';
import { import {
...@@ -92,12 +93,49 @@ export async function mergeInheritedConfig( ...@@ -92,12 +93,49 @@ export async function mergeInheritedConfig(
'Found warnings in inherited configuration.', 'Found warnings in inherited configuration.',
); );
} }
const filteredConfig = removeGlobalConfig(inheritedConfig, true); let filteredConfig = removeGlobalConfig(inheritedConfig, true);
if (!dequal(inheritedConfig, filteredConfig)) { if (!dequal(inheritedConfig, filteredConfig)) {
logger.debug( logger.debug(
{ inheritedConfig, filteredConfig }, { inheritedConfig, filteredConfig },
'Removed global config from inherited config.', 'Removed global config from inherited config.',
); );
} }
if (is.nullOrUndefined(filteredConfig.extends)) {
return mergeChildConfig(config, filteredConfig);
}
logger.debug('Resolving presets found in inherited config');
const resolvedConfig = await resolveConfigPresets(
filteredConfig,
config,
config.ignorePresets,
);
logger.trace({ config: resolvedConfig }, 'Resolved inherited config');
const validationRes = await validateConfig('inherit', resolvedConfig);
if (validationRes.errors.length) {
logger.warn(
{ errors: validationRes.errors },
'Found errors in presets inside the inherited configuration.',
);
throw new Error(CONFIG_VALIDATION);
}
if (validationRes.warnings.length) {
logger.warn(
{ warnings: validationRes.warnings },
'Found warnings in presets inside the inherited configuration.',
);
}
// remove global config options once again, as resolved presets could have added some
filteredConfig = removeGlobalConfig(resolvedConfig, true);
if (!dequal(resolvedConfig, filteredConfig)) {
logger.debug(
{ inheritedConfig: resolvedConfig, filteredConfig },
'Removed global config from inherited config presets.',
);
}
return mergeChildConfig(config, filteredConfig); return mergeChildConfig(config, filteredConfig);
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment