From ef1cf5906c761961464ff381cd31a61c85e22b22 Mon Sep 17 00:00:00 2001
From: Jonas Rutishauser <jonas.rutishauser@alumni.ethz.ch>
Date: Thu, 16 Jan 2025 09:52:17 +0100
Subject: [PATCH] feat(datasource/maven): Use relocation information (#32636)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 lib/modules/datasource/maven/index.spec.ts | 67 ++++++++++++++++++++++
 lib/modules/datasource/maven/types.ts      |  7 ++-
 lib/modules/datasource/maven/util.ts       | 17 ++++++
 3 files changed, 90 insertions(+), 1 deletion(-)

diff --git a/lib/modules/datasource/maven/index.spec.ts b/lib/modules/datasource/maven/index.spec.ts
index 4a87d5323f..d8d903184d 100644
--- a/lib/modules/datasource/maven/index.spec.ts
+++ b/lib/modules/datasource/maven/index.spec.ts
@@ -1,5 +1,6 @@
 import { HeadObjectCommand, S3Client } from '@aws-sdk/client-s3';
 import { mockClient } from 'aws-sdk-client-mock';
+import { codeBlock } from 'common-tags';
 import { GoogleAuth as _googleAuth } from 'google-auth-library';
 import { DateTime } from 'luxon';
 import type { Release, ReleaseResult } from '..';
@@ -315,6 +316,72 @@ describe('modules/datasource/maven/index', () => {
     expect(res?.sourceUrl).toBe('https://github.com/example/test');
   });
 
+  describe('supports relocation', () => {
+    it('with only groupId present', async () => {
+      const pom = codeBlock`
+        <project>
+          <distributionManagement>
+            <relocation>
+              <groupId>io.example</groupId>
+            </relocation>
+          </distributionManagement>
+        </project>
+      `;
+      mockGenericPackage({ pom });
+
+      const res = await get();
+
+      expect(res).toMatchObject({
+        replacementName: 'io.example:package',
+        replacementVersion: '2.0.0',
+      });
+    });
+
+    it('with only artifactId present', async () => {
+      const pom = codeBlock`
+        <project>
+          <distributionManagement>
+            <relocation>
+              <artifactId>foo</artifactId>
+            </relocation>
+          </distributionManagement>
+        </project>
+      `;
+      mockGenericPackage({ pom });
+
+      const res = await get();
+
+      expect(res).toMatchObject({
+        replacementName: 'org.example:foo',
+        replacementVersion: '2.0.0',
+      });
+    });
+
+    it('with all elments present', async () => {
+      const pom = codeBlock`
+        <project>
+          <distributionManagement>
+            <relocation>
+              <groupId>io.example</groupId>
+              <artifactId>foo</artifactId>
+              <version>1.2.3</version>
+              <message>test relocation</message>
+            </relocation>
+          </distributionManagement>
+        </project>
+      `;
+      mockGenericPackage({ pom });
+
+      const res = await get();
+
+      expect(res).toMatchObject({
+        replacementName: 'io.example:foo',
+        replacementVersion: '1.2.3',
+        deprecationMessage: 'test relocation',
+      });
+    });
+  });
+
   it('removes authentication header after redirect', async () => {
     const frontendHost = 'frontend_for_private_s3_repository';
     const frontendUrl = `https://${frontendHost}/maven2`;
diff --git a/lib/modules/datasource/maven/types.ts b/lib/modules/datasource/maven/types.ts
index 853326887e..f367802cd2 100644
--- a/lib/modules/datasource/maven/types.ts
+++ b/lib/modules/datasource/maven/types.ts
@@ -18,7 +18,12 @@ export type HttpResourceCheckResult = 'found' | 'not-found' | 'error' | Date;
 
 export type DependencyInfo = Pick<
   ReleaseResult,
-  'homepage' | 'sourceUrl' | 'packageScope'
+  | 'homepage'
+  | 'sourceUrl'
+  | 'packageScope'
+  | 'replacementName'
+  | 'replacementVersion'
+  | 'deprecationMessage'
 >;
 
 export interface MavenFetchSuccess<T = string> {
diff --git a/lib/modules/datasource/maven/util.ts b/lib/modules/datasource/maven/util.ts
index dd6fd41961..696836fd41 100644
--- a/lib/modules/datasource/maven/util.ts
+++ b/lib/modules/datasource/maven/util.ts
@@ -543,6 +543,23 @@ export async function getDependencyInfo(
     }
   }
 
+  const relocation = pomContent.descendantWithPath(
+    'distributionManagement.relocation',
+  );
+  if (relocation) {
+    const relocationGroup =
+      relocation.valueWithPath('groupId') ?? dependency.group;
+    const relocationName =
+      relocation.valueWithPath('artifactId') ?? dependency.name;
+    result.replacementName = `${relocationGroup}:${relocationName}`;
+    const relocationVersion = relocation.valueWithPath('version');
+    result.replacementVersion = relocationVersion ?? version;
+    const relocationMessage = relocation.valueWithPath('message');
+    if (relocationMessage) {
+      result.deprecationMessage = relocationMessage;
+    }
+  }
+
   const groupId = pomContent.valueWithPath('groupId');
   if (groupId) {
     result.packageScope = groupId;
-- 
GitLab