aboutsummaryrefslogtreecommitdiffhomepage
path: root/lib/config/validation.ts
diff options
context:
space:
mode:
Diffstat (limited to 'lib/config/validation.ts')
-rw-r--r--lib/config/validation.ts151
1 files changed, 19 insertions, 132 deletions
diff --git a/lib/config/validation.ts b/lib/config/validation.ts
index d139cb42c10..551c07ba9f0 100644
--- a/lib/config/validation.ts
+++ b/lib/config/validation.ts
@@ -1,11 +1,6 @@
import is from '@sindresorhus/is';
-import { logger } from '../logger';
import { allManagersList, getManagerList } from '../modules/manager';
import { isCustomManager } from '../modules/manager/custom';
-import type {
- RegexManagerConfig,
- RegexManagerTemplates,
-} from '../modules/manager/custom/regex/types';
import type { CustomManager } from '../modules/manager/custom/types';
import type { HostRule } from '../types';
import { getExpression } from '../util/jsonata';
@@ -39,6 +34,13 @@ import { allowedStatusCheckStrings } from './types';
import * as managerValidator from './validation-helpers/managers';
import * as matchBaseBranchesValidator from './validation-helpers/match-base-branches';
import * as regexOrGlobValidator from './validation-helpers/regex-glob-matchers';
+import {
+ getParentName,
+ isFalseGlobal,
+ validateNumber,
+ validatePlainObject,
+ validateRegexManagerFields,
+} from './validation-helpers/utils';
const options = getOptions();
@@ -84,42 +86,6 @@ function isIgnored(key: string): boolean {
return ignoredNodes.includes(key);
}
-function validatePlainObject(val: Record<string, unknown>): true | string {
- for (const [key, value] of Object.entries(val)) {
- if (!is.string(value)) {
- return key;
- }
- }
- return true;
-}
-
-function validateNumber(
- key: string,
- val: unknown,
- currentPath?: string,
- subKey?: string,
-): ValidationMessage[] {
- const errors: ValidationMessage[] = [];
- const path = `${currentPath}${subKey ? '.' + subKey : ''}`;
- if (is.number(val)) {
- if (val < 0 && !optionAllowsNegativeIntegers.has(key)) {
- errors.push({
- topic: 'Configuration Error',
- message: `Configuration option \`${path}\` should be a positive integer. Found negative value instead.`,
- });
- }
- } else {
- errors.push({
- topic: 'Configuration Error',
- message: `Configuration option \`${path}\` should be an integer. Found: ${JSON.stringify(
- val,
- )} (${typeof val}).`,
- });
- }
-
- return errors;
-}
-
function getUnsupportedEnabledManagers(enabledManagers: string[]): string[] {
return enabledManagers.filter(
(manager) => !allManagersList.includes(manager.replace('custom.', '')),
@@ -186,16 +152,6 @@ function initOptions(): void {
optionsInitialized = true;
}
-export function getParentName(parentPath: string | undefined): string {
- return parentPath
- ? parentPath
- .replace(regEx(/\.?encrypted$/), '')
- .replace(regEx(/\[\d+\]$/), '')
- .split('.')
- .pop()!
- : '.';
-}
-
export async function validateConfig(
configType: 'global' | 'inherit' | 'repo',
config: RenovateConfig,
@@ -370,7 +326,8 @@ export async function validateConfig(
});
}
} else if (type === 'integer') {
- errors.push(...validateNumber(key, val, currentPath));
+ const allowsNegative = optionAllowsNegativeIntegers.has(key);
+ errors.push(...validateNumber(key, val, allowsNegative, currentPath));
} else if (type === 'array' && val) {
if (is.array(val)) {
for (const [subIndex, subval] of val.entries()) {
@@ -865,65 +822,6 @@ export async function validateConfig(
return { errors, warnings };
}
-function hasField(
- customManager: Partial<RegexManagerConfig>,
- field: string,
-): boolean {
- const templateField = `${field}Template` as keyof RegexManagerTemplates;
- return !!(
- customManager[templateField] ??
- customManager.matchStrings?.some((matchString) =>
- matchString.includes(`(?<${field}>`),
- )
- );
-}
-
-function validateRegexManagerFields(
- customManager: Partial<RegexManagerConfig>,
- currentPath: string,
- errors: ValidationMessage[],
-): void {
- if (is.nonEmptyArray(customManager.matchStrings)) {
- for (const matchString of customManager.matchStrings) {
- try {
- regEx(matchString);
- } catch (err) {
- logger.debug(
- { err },
- 'customManager.matchStrings regEx validation error',
- );
- errors.push({
- topic: 'Configuration Error',
- message: `Invalid regExp for ${currentPath}: \`${matchString}\``,
- });
- }
- }
- } else {
- errors.push({
- topic: 'Configuration Error',
- message: `Each Custom Manager must contain a non-empty matchStrings array`,
- });
- }
-
- const mandatoryFields = ['currentValue', 'datasource'];
- for (const field of mandatoryFields) {
- if (!hasField(customManager, field)) {
- errors.push({
- topic: 'Configuration Error',
- message: `Regex Managers must contain ${field}Template configuration or regex group named ${field}`,
- });
- }
- }
-
- const nameFields = ['depName', 'packageName'];
- if (!nameFields.some((field) => hasField(customManager, field))) {
- errors.push({
- topic: 'Configuration Error',
- message: `Regex Managers must contain depName or packageName regex groups or templates`,
- });
- }
-}
-
/**
* Basic validation for global config options
*/
@@ -1013,7 +911,8 @@ async function validateGlobalConfig(
});
}
} else if (type === 'integer') {
- warnings.push(...validateNumber(key, val, currentPath));
+ const allowsNegative = optionAllowsNegativeIntegers.has(key);
+ warnings.push(...validateNumber(key, val, allowsNegative, currentPath));
} else if (type === 'boolean') {
if (val !== true && val !== false) {
warnings.push({
@@ -1079,8 +978,15 @@ async function validateGlobalConfig(
}
} else if (key === 'cacheTtlOverride') {
for (const [subKey, subValue] of Object.entries(val)) {
+ const allowsNegative = optionAllowsNegativeIntegers.has(key);
warnings.push(
- ...validateNumber(key, subValue, currentPath, subKey),
+ ...validateNumber(
+ key,
+ subValue,
+ allowsNegative,
+ currentPath,
+ subKey,
+ ),
);
}
} else {
@@ -1101,22 +1007,3 @@ async function validateGlobalConfig(
}
}
}
-
-/** An option is a false global if it has the same name as a global only option
- * but is actually just the field of a non global option or field an children of the non global option
- * eg. token: it's global option used as the bot's token as well and
- * also it can be the token used for a platform inside the hostRules configuration
- */
-function isFalseGlobal(optionName: string, parentPath?: string): boolean {
- if (parentPath?.includes('hostRules')) {
- if (
- optionName === 'token' ||
- optionName === 'username' ||
- optionName === 'password'
- ) {
- return true;
- }
- }
-
- return false;
-}