diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index 3de149ac8ce186cd18f0ba7ba340d50d631b9333..0bc3da808d4393f8c0c3915c39d4df64f55f8f08 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -2027,6 +2027,8 @@ All matches of the first `matchStrings` pattern are detected, then each of these 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. +Matched groups will be available in subsequent matching layers. + This is an example how this can work. The first regex manager will only upgrade `grafana/loki` as looks for the `backup` key then looks for the `test` key and then uses this result for extraction of necessary attributes. However, the second regex manager will upgrade both definitions as its first `matchStrings` matches both `test` keys. diff --git a/lib/manager/regex/__snapshots__/index.spec.ts.snap b/lib/manager/regex/__snapshots__/index.spec.ts.snap index bcc3505484bc79b2b36816fcf4b72740cb5599dd..c5f0f8e34fe21c35aed20febbd256f3ef375065a 100644 --- a/lib/manager/regex/__snapshots__/index.spec.ts.snap +++ b/lib/manager/regex/__snapshots__/index.spec.ts.snap @@ -311,6 +311,52 @@ exports[`manager/regex/index extracts with recursive strategy and fail because o exports[`manager/regex/index extracts with recursive strategy and fail because there is no match 1`] = `null`; +exports[`manager/regex/index extracts with recursive strategy and merged groups 1`] = ` +Object { + "depNameTemplate": "{{{ first }}}/{{{ second }}}/{{{ depName }}}", + "deps": Array [ + Object { + "currentValue": "v2.19.0", + "datasource": "docker", + "depName": "group1/group1/prom/prometheus", + "replaceString": "\\"name\\": \\"prom/prometheus\\", + \\"type\\": \\"docker\\", + \\"value\\": \\"v2.19.0\\"", + }, + Object { + "currentValue": "7.2.2", + "datasource": "docker", + "depName": "group2/group2/grafana/grafana", + "replaceString": "\\"name\\": \\"grafana/grafana\\", + \\"type\\": \\"docker\\", + \\"value\\": \\"7.2.2\\"", + }, + Object { + "currentValue": "1.6.1", + "datasource": "docker", + "depName": "backup/backup/grafana/loki", + "replaceString": "\\"name\\": \\"grafana/loki\\", + \\"type\\": \\"docker\\", + \\"value\\": \\"1.6.1\\"", + }, + Object { + "currentValue": "3.9.0", + "datasource": "docker", + "depName": "setup/setup/python", + "replaceString": "\\"name\\": \\"python\\", + \\"type\\": \\"docker\\", + \\"value\\": \\"3.9.0\\"", + }, + ], + "matchStrings": Array [ + "\\"(?<first>[^\\"]*)\\":\\\\s*{[^}]*}", + "\\"(?<second>[^\\"]*)\\":\\\\s*\\\\{[^}]*}", + "\\"name\\":\\\\s*\\"(?<depName>.*)\\"[^\\"]*\\"type\\":\\\\s*\\"(?<datasource>.*)\\"[^\\"]*\\"value\\":\\\\s*\\"(?<currentValue>.*)\\"", + ], + "matchStringsStrategy": "recursive", +} +`; + exports[`manager/regex/index extracts with recursive strategy and multiple layers 1`] = ` Object { "deps": Array [ diff --git a/lib/manager/regex/index.spec.ts b/lib/manager/regex/index.spec.ts index 13258f85f2d53de66a00baadb9c6589a73ed5775..cec3ca01d3bff9a156da35faf83e1268dda6d3c4 100644 --- a/lib/manager/regex/index.spec.ts +++ b/lib/manager/regex/index.spec.ts @@ -350,4 +350,22 @@ describe('manager/regex/index', () => { expect(res).toMatchSnapshot(); expect(res).toBeNull(); }); + it('extracts with recursive strategy and merged groups', async () => { + const config: CustomExtractConfig = { + matchStrings: [ + '"(?<first>[^"]*)":\\s*{[^}]*}', + '"(?<second>[^"]*)":\\s*\\{[^}]*}', + '"name":\\s*"(?<depName>.*)"[^"]*"type":\\s*"(?<datasource>.*)"[^"]*"value":\\s*"(?<currentValue>.*)"', + ], + matchStringsStrategy: 'recursive', + depNameTemplate: '{{{ first }}}/{{{ second }}}/{{{ depName }}}', + }; + const res = await extractPackageFile( + exampleJsonContent, + 'example.json', + config + ); + expect(res).toMatchSnapshot(); + expect(res.deps).toHaveLength(4); + }); }); diff --git a/lib/manager/regex/index.ts b/lib/manager/regex/index.ts index 7a866a6f70e4323b994edd4156c82536b7578853..4558010c2c688c69b101c3720088c0eb54e261e8 100644 --- a/lib/manager/regex/index.ts +++ b/lib/manager/regex/index.ts @@ -121,21 +121,17 @@ function mergeGroups( mergedGroup: Record<string, string>, secondGroup: Record<string, string> ): Record<string, string> { - const resultGroup = {}; + const resultGroup = Object.create(null); // prevent prototype pollution - Object.keys(mergedGroup) - .filter((key) => validMatchFields.includes(key)) // prevent prototype pollution - .forEach( - // eslint-disable-next-line no-return-assign - (key) => (resultGroup[key] = mergedGroup[key]) - ); - Object.keys(secondGroup) - .filter((key) => validMatchFields.includes(key)) // prevent prototype pollution - .forEach((key) => { - if (secondGroup[key] && secondGroup[key] !== '') { - resultGroup[key] = secondGroup[key]; - } - }); + Object.keys(mergedGroup).forEach( + // eslint-disable-next-line no-return-assign + (key) => (resultGroup[key] = mergedGroup[key]) + ); + Object.keys(secondGroup).forEach((key) => { + if (secondGroup[key] && secondGroup[key] !== '') { + resultGroup[key] = secondGroup[key]; + } + }); return resultGroup; } @@ -172,7 +168,8 @@ function handleRecursive( content: string, packageFile: string, config: CustomExtractConfig, - index = 0 + index = 0, + combinedGroups: Record<string, string> = {} ): PackageDependency[] { const regexes = config.matchStrings.map((matchString) => regEx(matchString, 'g') @@ -184,9 +181,20 @@ function handleRecursive( return regexMatchAll(regexes[index], content).flatMap((match) => { // if we have a depName and a currentValue with have the minimal viable definition if (match?.groups?.depName && match?.groups?.currentValue) { - return createDependency(match, null, config); + return createDependency( + match, + mergeGroups(combinedGroups, match.groups), + config + ); } - return handleRecursive(match[0], packageFile, config, index + 1); + + return handleRecursive( + match[0], + packageFile, + config, + index + 1, + mergeGroups(combinedGroups, match.groups || {}) + ); }); }