From f680dcb899441b366d6eaab4eb54cb063ca9f8ef Mon Sep 17 00:00:00 2001
From: Janus Troelsen <ysangkok@gmail.com>
Date: Thu, 31 Oct 2024 02:02:24 -0600
Subject: [PATCH] feat(versioning): allow versionings to provide isSame and use
 to detect majors (#32207)

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
---
 lib/modules/versioning/generic.spec.ts               | 10 ++++++++++
 lib/modules/versioning/generic.ts                    | 10 ++++++++++
 lib/modules/versioning/index.spec.ts                 |  1 +
 lib/modules/versioning/types.ts                      |  6 ++++++
 lib/workers/repository/process/lookup/update-type.ts |  6 ++++++
 5 files changed, 33 insertions(+)

diff --git a/lib/modules/versioning/generic.spec.ts b/lib/modules/versioning/generic.spec.ts
index 5aafc0f5c5..5fbcc0717c 100644
--- a/lib/modules/versioning/generic.spec.ts
+++ b/lib/modules/versioning/generic.spec.ts
@@ -68,6 +68,7 @@ describe('modules/versioning/generic', () => {
         'getPatch',
         'isCompatible',
         'isGreaterThan',
+        'isSame',
         'isSingleVersion',
         'isStable',
         'isValid',
@@ -183,5 +184,14 @@ describe('modules/versioning/generic', () => {
         api.getSatisfyingVersion(['1.1.1', '2.2.2', '3.3.3'], '1.2.3'),
       ).toBeNull();
     });
+
+    it('isSame', () => {
+      expect(api.isSame('major', '4.5.6', '4.6.0')).toBe(true);
+      expect(api.isSame('major', '4.5.6', '5.0.0')).toBe(false);
+      expect(api.isSame('minor', '4.5.6', '5.5.0')).toBe(true);
+      expect(api.isSame('minor', '4.5.6', '4.6.0')).toBe(false);
+      expect(api.isSame('patch', '4.5.6', '5.5.6')).toBe(true);
+      expect(api.isSame('patch', '4.5.6', '4.6.0')).toBe(false);
+    });
   });
 });
diff --git a/lib/modules/versioning/generic.ts b/lib/modules/versioning/generic.ts
index 35710926d3..724d12d84f 100644
--- a/lib/modules/versioning/generic.ts
+++ b/lib/modules/versioning/generic.ts
@@ -149,4 +149,14 @@ export abstract class GenericVersioningApi<
   matches(version: string, range: string): boolean {
     return this.equals(version, range);
   }
+
+  isSame(type: 'major' | 'minor' | 'patch', a: string, b: string): boolean {
+    if (type === 'major') {
+      return this.getMajor(a)! === this.getMajor(b)!;
+    }
+    if (type === 'minor') {
+      return this.getMinor(a)! === this.getMinor(b)!;
+    }
+    return this.getPatch(a)! === this.getPatch(b)!;
+  }
 }
diff --git a/lib/modules/versioning/index.spec.ts b/lib/modules/versioning/index.spec.ts
index ca4369fca7..3de40de933 100644
--- a/lib/modules/versioning/index.spec.ts
+++ b/lib/modules/versioning/index.spec.ts
@@ -86,6 +86,7 @@ describe('modules/versioning/index', () => {
       'toString',
       'valueOf',
       'subset',
+      'isSame',
     ];
     const npmApi = Object.keys(allVersioning.get(semverVersioning.id))
       .filter((val) => !optionalFunctions.includes(val))
diff --git a/lib/modules/versioning/types.ts b/lib/modules/versioning/types.ts
index e9e686470e..f1e66ac15b 100644
--- a/lib/modules/versioning/types.ts
+++ b/lib/modules/versioning/types.ts
@@ -129,6 +129,12 @@ export interface VersioningApi {
    * Return whether unstable-to-unstable upgrades within the same major version are allowed.
    */
   allowUnstableMajorUpgrades?: boolean;
+
+  /**
+   * Check whether the `type` in the `a` and `b` version numbers match.
+   * Both `a` and `b` must pass `isVersion`.
+   */
+  isSame?(type: 'major' | 'minor' | 'patch', a: string, b: string): boolean;
 }
 
 export interface VersioningApiConstructor {
diff --git a/lib/workers/repository/process/lookup/update-type.ts b/lib/workers/repository/process/lookup/update-type.ts
index e919cfd2cb..6698e8f1e9 100644
--- a/lib/workers/repository/process/lookup/update-type.ts
+++ b/lib/workers/repository/process/lookup/update-type.ts
@@ -14,6 +14,12 @@ export function getUpdateType(
   currentVersion: string,
   newVersion: string,
 ): UpdateType {
+  if (
+    versioningApi.isSame &&
+    !versioningApi.isSame('major', newVersion, currentVersion)
+  ) {
+    return 'major';
+  }
   if (
     versioningApi.getMajor(newVersion)! >
     versioningApi.getMajor(currentVersion)!
-- 
GitLab