Skip to content
Snippets Groups Projects
Unverified Commit d0926c3d authored by Sebastian Poxhofer's avatar Sebastian Poxhofer Committed by GitHub
Browse files

fix(manager/regex): depName requirement if using the recursive strategy (#16225)


* fix(manager/regex): depName requirement if using the recursive strategy

* chore(manager/regex): implement code suggestions

* chore: replace Boolean with Is function

* chore: abort if match strings are empty

* refactor: do not parse regexes on each recursion and filter after all matches are processed

* fixup! test name

* Revert "chore: abort if match strings are empty"

This reverts commit bf41a92f

* refactor: extract inline function

* fix: out-of-bound check

* feat: allow currentDigest as valid alternative to currentValue

* Update lib/modules/manager/regex/utils.ts

Co-authored-by: default avatarMichael Kriese <michael.kriese@visualon.de>

* chore: prettier

Co-authored-by: default avatarMichael Kriese <michael.kriese@visualon.de>
parent ef35ccd8
No related merge requests found
...@@ -2314,7 +2314,7 @@ This can be used to narrow down the search area to prevent multiple matches. ...@@ -2314,7 +2314,7 @@ This can be used to narrow down the search area to prevent multiple matches.
But the `recursive` strategy still allows the matching of multiple dependencies as described below. But the `recursive` strategy still allows the matching of multiple dependencies as described below.
All matches of the first `matchStrings` pattern are detected, then each of these matches will used as basis be used as the input for the next `matchStrings` pattern, and so on. All matches of the first `matchStrings` pattern are detected, then each of these matches will used as basis be used as the input for the next `matchStrings` pattern, and so on.
If the next `matchStrings` pattern has multiple matches then it will split again. If the next `matchStrings` pattern has multiple matches then it will split again.
This process will be followed as long there is a match plus a next `matchingStrings` pattern is available or a dependency is detected. This process will be followed as long there is a match plus a next `matchingStrings` pattern is available.
Matched groups will be available in subsequent matching layers. Matched groups will be available in subsequent matching layers.
......
...@@ -419,4 +419,34 @@ describe('modules/manager/regex/index', () => { ...@@ -419,4 +419,34 @@ describe('modules/manager/regex/index', () => {
expect(res).toMatchSnapshot(); expect(res).toMatchSnapshot();
expect(res?.deps).toHaveLength(4); expect(res?.deps).toHaveLength(4);
}); });
it('extracts with recursive strategy and without depName', async () => {
const config: CustomExtractConfig = {
matchStrings: [
'jacoco\\s*{[^}]*}',
'toolVersion\\s*=\\s*\\"(?<currentValue>\\S*)\\"\\s*',
],
matchStringsStrategy: 'recursive',
depNameTemplate: 'org.jacoco:jacoco',
datasourceTemplate: 'maven',
};
const res = await extractPackageFile(
`
jacoco {
toolVersion = "0.8.7"
}
`,
'build.gradle.kts',
config
);
expect(res).toMatchObject({
deps: [
{
depName: 'org.jacoco:jacoco',
currentValue: '0.8.7',
datasource: 'maven',
},
],
});
});
}); });
import is from '@sindresorhus/is'; import is from '@sindresorhus/is';
import { regEx } from '../../../util/regex'; import { regEx } from '../../../util/regex';
import type { CustomExtractConfig, PackageDependency } from '../types'; import type { CustomExtractConfig, PackageDependency } from '../types';
import type { RecursionParameter } from './types';
import { import {
createDependency, createDependency,
isValidDependency,
mergeExtractionTemplate, mergeExtractionTemplate,
mergeGroups, mergeGroups,
regexMatchAll, regexMatchAll,
...@@ -22,7 +24,8 @@ export function handleAny( ...@@ -22,7 +24,8 @@ export function handleAny(
config config
) )
) )
.filter(is.truthy); .filter(is.truthy)
.filter(isValidDependency);
} }
export function handleCombination( export function handleCombination(
...@@ -44,43 +47,57 @@ export function handleCombination( ...@@ -44,43 +47,57 @@ export function handleCombination(
replaceString: match?.groups?.currentValue ? match[0] : undefined, replaceString: match?.groups?.currentValue ? match[0] : undefined,
})) }))
.reduce((base, addition) => mergeExtractionTemplate(base, addition)); .reduce((base, addition) => mergeExtractionTemplate(base, addition));
return [createDependency(extraction, config)].filter(is.truthy); return [createDependency(extraction, config)]
.filter(is.truthy)
.filter(isValidDependency);
} }
export function handleRecursive( export function handleRecursive(
content: string, content: string,
packageFile: string, packageFile: string,
config: CustomExtractConfig, config: CustomExtractConfig
index = 0,
combinedGroups: Record<string, string> = {}
): PackageDependency[] { ): PackageDependency[] {
const regexes = config.matchStrings.map((matchString) => const regexes = config.matchStrings.map((matchString) =>
regEx(matchString, 'g') regEx(matchString, 'g')
); );
return processRecursive({
content,
packageFile,
config,
index: 0,
combinedGroups: {},
regexes,
})
.filter(is.truthy)
.filter(isValidDependency);
}
function processRecursive(parameters: RecursionParameter): PackageDependency[] {
const {
content,
index,
combinedGroups,
regexes,
config,
}: RecursionParameter = parameters;
// abort if we have no matchString anymore // abort if we have no matchString anymore
if (!regexes[index]) { if (regexes.length === index) {
return []; const result = createDependency(
{
groups: combinedGroups,
replaceString: content,
},
config
);
return result ? [result] : [];
} }
return regexMatchAll(regexes[index], content) return regexMatchAll(regexes[index], content).flatMap((match) => {
.flatMap((match) => { return processRecursive({
// if we have a depName and a currentValue which have the minimal viable definition ...parameters,
if (match?.groups?.depName && match?.groups?.currentValue) { content: match[0],
return createDependency( index: index + 1,
{ combinedGroups: mergeGroups(combinedGroups, match.groups ?? {}),
groups: mergeGroups(combinedGroups, match.groups), });
replaceString: match[0], });
},
config
);
}
return handleRecursive(
match[0],
packageFile,
config,
index + 1,
mergeGroups(combinedGroups, match.groups ?? {})
);
})
.filter(is.truthy);
} }
import type { CustomExtractConfig } from '../types';
export interface ExtractionTemplate { export interface ExtractionTemplate {
groups: Record<string, string>; groups: Record<string, string>;
replaceString: string | undefined; replaceString: string | undefined;
} }
export interface RecursionParameter {
content: string;
packageFile: string;
config: CustomExtractConfig;
regexes: RegExp[];
index: number;
combinedGroups: Record<string, string>;
}
import { URL } from 'url'; import { URL } from 'url';
import is from '@sindresorhus/is';
import type { RegexManagerTemplates } from '../../../config/types'; import type { RegexManagerTemplates } from '../../../config/types';
import { logger } from '../../../logger'; import { logger } from '../../../logger';
import * as template from '../../../util/template'; import * as template from '../../../util/template';
...@@ -97,3 +98,16 @@ export function mergeExtractionTemplate( ...@@ -97,3 +98,16 @@ export function mergeExtractionTemplate(
replaceString: addition.replaceString ?? base.replaceString, replaceString: addition.replaceString ?? base.replaceString,
}; };
} }
export function isValidDependency({
depName,
currentValue,
currentDigest,
}: PackageDependency): boolean {
// check if all the fields are set
return (
is.nonEmptyStringAndNotWhitespace(depName) &&
(is.nonEmptyStringAndNotWhitespace(currentDigest) ||
is.nonEmptyStringAndNotWhitespace(currentValue))
);
}
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