Skip to content

Commit

Permalink
refactor(validation): handle custom managers separately (#24272)
Browse files Browse the repository at this point in the history
  • Loading branch information
RahulGautamSingh committed Sep 12, 2023
1 parent a14e455 commit 69f56cb
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 49 deletions.
63 changes: 62 additions & 1 deletion lib/config/validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,13 +444,51 @@ describe('config/validation', () => {
`);
});

it('errors if invalid regexManager customType', async () => {
const config = {
regexManagers: [
{
customType: 'unknown',
fileMatch: ['some-file'],
matchStrings: ['^(?<depName>foo)(?<currentValue>bar)$'],
datasourceTemplate: 'maven',
versioningTemplate: 'gradle',
},
],
};
const { warnings, errors } = await configValidation.validateConfig(
config as any,
true
);
expect(warnings).toHaveLength(0);
expect(errors).toHaveLength(1);
expect(errors).toMatchInlineSnapshot(`
[
{
"message": "Invalid customType: unknown. Key is not a custom manager",
"topic": "Configuration Error",
},
]
`);
});

it('errors if empty regexManager matchStrings', async () => {
const config = {
regexManagers: [
{ customType: 'regex', fileMatch: ['foo'], matchStrings: [] },
{
customType: 'regex',
fileMatch: ['foo'],
matchStrings: [],
depNameTemplate: 'foo',
datasourceTemplate: 'bar',
currentValueTemplate: 'baz',
},
{
customType: 'regex',
fileMatch: ['foo'],
depNameTemplate: 'foo',
datasourceTemplate: 'bar',
currentValueTemplate: 'baz',
},
],
};
Expand Down Expand Up @@ -499,6 +537,9 @@ describe('config/validation', () => {
customType: 'regex',
fileMatch: ['Dockerfile'],
matchStrings: ['***$}{]]['],
depNameTemplate: 'foo',
datasourceTemplate: 'bar',
currentValueTemplate: 'baz',
},
],
};
Expand All @@ -510,6 +551,26 @@ describe('config/validation', () => {
expect(errors).toHaveLength(1);
});

// testing if we get all errors at once or not (possible), this does not include customType or fileMatch
// since they are common to all custom managers
it('validates all possible regex manager options', async () => {
const config: RenovateConfig = {
regexManagers: [
{
customType: 'regex',
fileMatch: ['Dockerfile'],
matchStrings: ['***$}{]]['], // invalid matchStrings regex, no depName, datasource and currentValue
},
],
};
const { warnings, errors } = await configValidation.validateConfig(
config,
true
);
expect(warnings).toHaveLength(0);
expect(errors).toHaveLength(4);
});

it('passes if regexManager fields are present', async () => {
const config: RenovateConfig = {
regexManagers: [
Expand Down
119 changes: 71 additions & 48 deletions lib/config/validation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import is from '@sindresorhus/is';
import { allManagersList, getManagerList } from '../modules/manager';
import { isCustomManager } from '../modules/manager/custom';
import type { RegexManagerConfig } from '../modules/manager/custom/regex/types';
import { configRegexPredicate, isConfigRegex, regEx } from '../util/regex';
import * as template from '../util/template';
import {
Expand All @@ -10,6 +12,8 @@ import { migrateConfig } from './migration';
import { getOptions } from './options';
import { resolveConfigPresets } from './presets';
import type {
CustomManager,
RegexManagerTemplates,
RenovateConfig,
RenovateOptions,
ValidationMessage,
Expand Down Expand Up @@ -416,8 +420,7 @@ export async function validateConfig(
'autoReplaceStringTemplate',
'depTypeTemplate',
];
// TODO: fix types #22198
for (const regexManager of val as any[]) {
for (const regexManager of val as CustomManager[]) {
if (
Object.keys(regexManager).some(
(k) => !allowedKeys.includes(k)
Expand All @@ -432,49 +435,19 @@ export async function validateConfig(
', '
)}`,
});
} else if (is.nonEmptyString(regexManager.customType)) {
} else if (
is.nonEmptyString(regexManager.customType) &&
isCustomManager(regexManager.customType)
) {
if (is.nonEmptyArray(regexManager.fileMatch)) {
if (is.nonEmptyArray(regexManager.matchStrings)) {
let validRegex = false;
for (const matchString of regexManager.matchStrings) {
try {
regEx(matchString);
validRegex = true;
} catch (e) {
errors.push({
topic: 'Configuration Error',
message: `Invalid regExp for ${currentPath}: \`${String(
matchString
)}\``,
});
}
}
if (validRegex) {
const mandatoryFields = [
'depName',
'currentValue',
'datasource',
];
for (const field of mandatoryFields) {
if (
!regexManager[`${field}Template`] &&
!regexManager.matchStrings.some(
(matchString: string) =>
matchString.includes(`(?<${field}>`)
)
) {
errors.push({
topic: 'Configuration Error',
message: `Regex Managers must contain ${field}Template configuration or regex group named ${field}`,
});
}
}
}
} else {
errors.push({
topic: 'Configuration Error',
message: `Each Regex Manager must contain a non-empty matchStrings array`,
});
switch (regexManager.customType) {
case 'regex':
validateRegexManagerFields(
regexManager,
currentPath,
errors
);
break;
}
} else {
errors.push({
Expand All @@ -483,10 +456,20 @@ export async function validateConfig(
});
}
} else {
errors.push({
topic: 'Configuration Error',
message: `Each Regex Manager must contain a non-empty customType string`,
});
if (
is.emptyString(regexManager.customType) ||
is.undefined(regexManager.customType)
) {
errors.push({
topic: 'Configuration Error',
message: `Each Regex Manager must contain a non-empty customType string`,
});
} else {
errors.push({
topic: 'Configuration Error',
message: `Invalid customType: ${regexManager.customType}. Key is not a custom manager`,
});
}
}
}
}
Expand Down Expand Up @@ -672,3 +655,43 @@ export async function validateConfig(
warnings.sort(sortAll);
return { errors, warnings };
}

function validateRegexManagerFields(
regexManager: RegexManagerConfig,
currentPath: string,
errors: ValidationMessage[]
): void {
if (is.nonEmptyArray(regexManager.matchStrings)) {
for (const matchString of regexManager.matchStrings) {
try {
regEx(matchString);
} catch (e) {
errors.push({
topic: 'Configuration Error',
message: `Invalid regExp for ${currentPath}: \`${matchString}\``,
});
}
}
} else {
errors.push({
topic: 'Configuration Error',
message: `Each Regex Manager must contain a non-empty matchStrings array`,
});
}

const mandatoryFields = ['depName', 'currentValue', 'datasource'];
for (const field of mandatoryFields) {
const templateField = `${field}Template` as keyof RegexManagerTemplates;
if (
!regexManager[templateField] &&
!regexManager.matchStrings.some((matchString) =>
matchString.includes(`(?<${field}>`)
)
) {
errors.push({
topic: 'Configuration Error',
message: `Regex Managers must contain ${field}Template configuration or regex group named ${field}`,
});
}
}
}

0 comments on commit 69f56cb

Please sign in to comment.