diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 37c6c1f031734d4ede061daa24f1a4f1e436911f..c0371493c328f732e61bdd0ed07c44afbfb86a45 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -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. 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. -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. diff --git a/lib/modules/manager/regex/index.spec.ts b/lib/modules/manager/regex/index.spec.ts index 3437cec7fa26d3a691ad117b07b8c52f41d671c3..d683f544830c37b7cfaf0ad246dee4f4721da5c6 100644 --- a/lib/modules/manager/regex/index.spec.ts +++ b/lib/modules/manager/regex/index.spec.ts @@ -419,4 +419,34 @@ describe('modules/manager/regex/index', () => { expect(res).toMatchSnapshot(); 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', + }, + ], + }); + }); }); diff --git a/lib/modules/manager/regex/strategies.ts b/lib/modules/manager/regex/strategies.ts index 13f07260e011a18c7917c9aeadd6a9034525ae08..22d06d5415f16bf845049d1543839b3c72b5a434 100644 --- a/lib/modules/manager/regex/strategies.ts +++ b/lib/modules/manager/regex/strategies.ts @@ -1,8 +1,10 @@ import is from '@sindresorhus/is'; import { regEx } from '../../../util/regex'; import type { CustomExtractConfig, PackageDependency } from '../types'; +import type { RecursionParameter } from './types'; import { createDependency, + isValidDependency, mergeExtractionTemplate, mergeGroups, regexMatchAll, @@ -22,7 +24,8 @@ export function handleAny( config ) ) - .filter(is.truthy); + .filter(is.truthy) + .filter(isValidDependency); } export function handleCombination( @@ -44,43 +47,57 @@ export function handleCombination( replaceString: match?.groups?.currentValue ? match[0] : undefined, })) .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( content: string, packageFile: string, - config: CustomExtractConfig, - index = 0, - combinedGroups: Record<string, string> = {} + config: CustomExtractConfig ): PackageDependency[] { const regexes = config.matchStrings.map((matchString) => 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 - if (!regexes[index]) { - return []; + if (regexes.length === index) { + const result = createDependency( + { + groups: combinedGroups, + replaceString: content, + }, + config + ); + return result ? [result] : []; } - return regexMatchAll(regexes[index], content) - .flatMap((match) => { - // if we have a depName and a currentValue which have the minimal viable definition - if (match?.groups?.depName && match?.groups?.currentValue) { - return createDependency( - { - groups: mergeGroups(combinedGroups, match.groups), - replaceString: match[0], - }, - config - ); - } - - return handleRecursive( - match[0], - packageFile, - config, - index + 1, - mergeGroups(combinedGroups, match.groups ?? {}) - ); - }) - .filter(is.truthy); + return regexMatchAll(regexes[index], content).flatMap((match) => { + return processRecursive({ + ...parameters, + content: match[0], + index: index + 1, + combinedGroups: mergeGroups(combinedGroups, match.groups ?? {}), + }); + }); } diff --git a/lib/modules/manager/regex/types.ts b/lib/modules/manager/regex/types.ts index 565cf06d0243e711e042190cdb4a2268401bd196..f0840a4e8608080f4806ae8e904639a02222b7d5 100644 --- a/lib/modules/manager/regex/types.ts +++ b/lib/modules/manager/regex/types.ts @@ -1,4 +1,15 @@ +import type { CustomExtractConfig } from '../types'; + export interface ExtractionTemplate { groups: Record<string, string>; replaceString: string | undefined; } + +export interface RecursionParameter { + content: string; + packageFile: string; + config: CustomExtractConfig; + regexes: RegExp[]; + index: number; + combinedGroups: Record<string, string>; +} diff --git a/lib/modules/manager/regex/utils.ts b/lib/modules/manager/regex/utils.ts index 9efd4946cc4ce87208130fedfa9252b7fd877b7a..3158298f3b4e40ff85ce04413475c62207bc7450 100644 --- a/lib/modules/manager/regex/utils.ts +++ b/lib/modules/manager/regex/utils.ts @@ -1,4 +1,5 @@ import { URL } from 'url'; +import is from '@sindresorhus/is'; import type { RegexManagerTemplates } from '../../../config/types'; import { logger } from '../../../logger'; import * as template from '../../../util/template'; @@ -97,3 +98,16 @@ export function mergeExtractionTemplate( 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)) + ); +}