From 468b0d14739c7c0dddc76f37830d0655c330007a Mon Sep 17 00:00:00 2001 From: RahulGautamSingh <rahultesnik@gmail.com> Date: Wed, 8 May 2024 12:27:55 +0545 Subject: [PATCH] feat(manager/maven): support `.mvn/extensions.xml` (#28893) --- lib/modules/manager/maven/extract.spec.ts | 63 +++++++++++++++++++++++ lib/modules/manager/maven/extract.ts | 58 +++++++++++++++++++++ lib/modules/manager/maven/index.ts | 6 ++- 3 files changed, 126 insertions(+), 1 deletion(-) diff --git a/lib/modules/manager/maven/extract.spec.ts b/lib/modules/manager/maven/extract.spec.ts index 3ff8e84aae..d6689de423 100644 --- a/lib/modules/manager/maven/extract.spec.ts +++ b/lib/modules/manager/maven/extract.spec.ts @@ -3,6 +3,7 @@ import { Fixtures } from '../../../../test/fixtures'; import { fs } from '../../../../test/util'; import { extractAllPackageFiles, + extractExtensions, extractPackage, extractRegistries, resolveParents, @@ -365,6 +366,27 @@ describe('modules/manager/maven/extract', () => { }); }); + describe('extractExtensions', () => { + it('returns null for invalid xml files', () => { + expect(extractExtensions('', '.mvn/extensions.xml')).toBeNull(); + expect( + extractExtensions('invalid xml content', '.mvn/extensions.xml'), + ).toBeNull(); + expect( + extractExtensions('<foobar></foobar>', '.mvn/extensions.xml'), + ).toBeNull(); + expect( + extractExtensions('<extensions></extensions>', '.mvn/extensions.xml'), + ).toBeNull(); + expect( + extractExtensions( + '<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0"></extensions>', + '.mvn/extensions.xml', + ), + ).toBeNull(); + }); + }); + describe('extractAllPackageFiles', () => { it('should return empty if package has no content', async () => { fs.readLocalFile.mockResolvedValueOnce(''); @@ -706,6 +728,47 @@ describe('modules/manager/maven/extract', () => { ]); }); + it('should extract from .mvn/extensions.xml file', async () => { + fs.readLocalFile.mockResolvedValueOnce(codeBlock` + <extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd"> + <extension> + <groupId>io.jenkins.tools.incrementals</groupId> + <artifactId>git-changelist-maven-extension</artifactId> + <version>1.6</version> + </extension> + </extensions> + `); + const res = await extractAllPackageFiles({}, ['.mvn/extensions.xml']); + expect(res).toMatchObject([ + { + packageFile: '.mvn/extensions.xml', + deps: [ + { + datasource: 'maven', + depName: + 'io.jenkins.tools.incrementals:git-changelist-maven-extension', + currentValue: '1.6', + depType: 'build', + fileReplacePosition: 372, + registryUrls: ['https://repo.maven.apache.org/maven2'], + }, + ], + }, + ]); + }); + + it('should return empty array if extensions file is invalid or empty', async () => { + fs.readLocalFile + .mockResolvedValueOnce('') + .mockResolvedValueOnce('invalid xml content'); + expect( + await extractAllPackageFiles({}, [ + '.mvn/extensions.xml', + 'grp/.mvn/extensions.xml', + ]), + ).toBeEmptyArray(); + }); + describe('root pom handling', () => { it('should skip root pom.xml', async () => { fs.readLocalFile.mockResolvedValueOnce(codeBlock` diff --git a/lib/modules/manager/maven/extract.ts b/lib/modules/manager/maven/extract.ts index 061c28fd92..c19b2de1c4 100644 --- a/lib/modules/manager/maven/extract.ts +++ b/lib/modules/manager/maven/extract.ts @@ -15,6 +15,12 @@ const supportedNamespaces = [ 'http://maven.apache.org/SETTINGS/1.2.0', ]; +const supportedExtensionsNamespaces = [ + 'http://maven.apache.org/EXTENSIONS/1.0.0', + 'http://maven.apache.org/EXTENSIONS/1.1.0', + 'http://maven.apache.org/EXTENSIONS/1.2.0', +]; + function parsePom(raw: string, packageFile: string): XmlDocument | null { let project: XmlDocument; try { @@ -39,6 +45,27 @@ function parsePom(raw: string, packageFile: string): XmlDocument | null { return null; } +function parseExtensions(raw: string, packageFile: string): XmlDocument | null { + let extensions: XmlDocument; + try { + extensions = new XmlDocument(raw); + } catch (err) { + logger.debug({ packageFile }, `Failed to parse as XML`); + return null; + } + const { name, attr, children } = extensions; + if (name !== 'extensions') { + return null; + } + if (!supportedExtensionsNamespaces.includes(attr.xmlns)) { + return null; + } + if (!is.nonEmptyArray(children)) { + return null; + } + return extensions; +} + function containsPlaceholder(str: string | null | undefined): boolean { return !!str && regEx(/\${[^}]*?}/).test(str); } @@ -476,6 +503,30 @@ function cleanResult(packageFiles: MavenInterimPackageFile[]): PackageFile[] { return packageFiles; } +export function extractExtensions( + rawContent: string, + packageFile: string, +): PackageFile | null { + if (!rawContent) { + return null; + } + + const extensions = parseExtensions(rawContent, packageFile); + if (!extensions) { + return null; + } + + const result: MavenInterimPackageFile = { + datasource: MavenDatasource.id, + packageFile, + deps: [], + }; + + result.deps = deepExtract(extensions); + + return result; +} + export async function extractAllPackageFiles( _config: ExtractConfig, packageFiles: string[], @@ -498,6 +549,13 @@ export async function extractAllPackageFiles( ); additionalRegistryUrls.push(...registries); } + } else if (packageFile.endsWith('.mvn/extensions.xml')) { + const extensions = extractExtensions(content, packageFile); + if (extensions) { + packages.push(extensions); + } else { + logger.trace({ packageFile }, 'can not read extensions'); + } } else { const pkg = extractPackage(content, packageFile); if (pkg) { diff --git a/lib/modules/manager/maven/index.ts b/lib/modules/manager/maven/index.ts index 8d12ca432b..bd282a2339 100644 --- a/lib/modules/manager/maven/index.ts +++ b/lib/modules/manager/maven/index.ts @@ -6,7 +6,11 @@ export { extractAllPackageFiles } from './extract'; export { bumpPackageVersion, updateDependency } from './update'; export const defaultConfig = { - fileMatch: ['(^|/|\\.)pom\\.xml$', '^(((\\.mvn)|(\\.m2))/)?settings\\.xml$'], + fileMatch: [ + '(^|/|\\.)pom\\.xml$', + '^(((\\.mvn)|(\\.m2))/)?settings\\.xml$', + '(^|/)\\.mvn/extensions\\.xml$', + ], versioning: mavenVersioning.id, }; -- GitLab