From 7b627e390000be58196ce805ae2a428887e661d3 Mon Sep 17 00:00:00 2001
From: Maxime Brunet <max@brnt.mx>
Date: Mon, 13 Mar 2023 22:09:15 -0700
Subject: [PATCH] fix(gomod): skip updating import path for incompatible
 versions (#20812)

---
 lib/modules/manager/gomod/artifacts.spec.ts | 49 +++++++++++++++++++++
 lib/modules/manager/gomod/artifacts.ts      |  7 ++-
 lib/modules/manager/gomod/update.spec.ts    | 21 ++++++++-
 lib/modules/manager/gomod/update.ts         |  3 +-
 4 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/lib/modules/manager/gomod/artifacts.spec.ts b/lib/modules/manager/gomod/artifacts.spec.ts
index 4c913e7fdf..667eee6bdd 100644
--- a/lib/modules/manager/gomod/artifacts.spec.ts
+++ b/lib/modules/manager/gomod/artifacts.spec.ts
@@ -1434,6 +1434,55 @@ describe('modules/manager/gomod/artifacts', () => {
     ]);
   });
 
+  it('skips updating import paths when incompatible version', async () => {
+    fs.readLocalFile
+      .mockResolvedValueOnce('Current go.sum')
+      .mockResolvedValueOnce(null); // vendor modules filename
+    const execSnapshots = mockExecAll();
+    git.getRepoStatus.mockResolvedValueOnce(
+      partial<StatusResult>({
+        modified: ['go.sum'],
+      })
+    );
+    fs.readLocalFile
+      .mockResolvedValueOnce('New go.sum')
+      .mockResolvedValueOnce('New go.mod');
+    expect(
+      await gomod.updateArtifacts({
+        packageFileName: 'go.mod',
+        updatedDeps: [
+          {
+            depName: 'github.com/docker/docker',
+            newVersion: 'v23.0.0+incompatible',
+          },
+        ],
+        newPackageFileContent: gomod1,
+        config: {
+          ...config,
+          updateType: 'major',
+          postUpdateOptions: ['gomodUpdateImportPaths'],
+        },
+      })
+    ).toEqual([
+      { file: { type: 'addition', path: 'go.sum', contents: 'New go.sum' } },
+      { file: { type: 'addition', path: 'go.mod', contents: 'New go.mod' } },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: 'go get -d -t ./...',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+      {
+        cmd: 'go mod tidy',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+      {
+        cmd: 'go mod tidy',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
+  });
+
   it('skips gomodTidy without gomodUpdateImportPaths on major update', async () => {
     fs.readLocalFile.mockResolvedValueOnce('Current go.sum');
     fs.readLocalFile.mockResolvedValueOnce(null); // vendor modules filename
diff --git a/lib/modules/manager/gomod/artifacts.ts b/lib/modules/manager/gomod/artifacts.ts
index 570ffed67a..6ef9025014 100644
--- a/lib/modules/manager/gomod/artifacts.ts
+++ b/lib/modules/manager/gomod/artifacts.ts
@@ -129,14 +129,17 @@ function getUpdateImportPathCmds(
   }
 
   const updateImportCommands = updatedDeps
-    .filter(({ newVersion }) => valid(newVersion))
+    .filter(
+      ({ newVersion }) =>
+        valid(newVersion) && !newVersion!.endsWith('+incompatible')
+    )
     .map(({ depName, newVersion }) => ({
       depName: depName!,
       newMajor: major(newVersion!),
     }))
     // Skip path updates going from v0 to v1
     .filter(
-      ({ depName, newMajor }) => depName.startsWith('gopkg.in') || newMajor > 1
+      ({ depName, newMajor }) => depName.startsWith('gopkg.in/') || newMajor > 1
     )
 
     .map(
diff --git a/lib/modules/manager/gomod/update.spec.ts b/lib/modules/manager/gomod/update.spec.ts
index 240eb184ee..6270bb8cc7 100644
--- a/lib/modules/manager/gomod/update.spec.ts
+++ b/lib/modules/manager/gomod/update.spec.ts
@@ -96,7 +96,7 @@ describe('modules/manager/gomod/update', () => {
         depType: 'require',
       };
       const res = updateDependency({ fileContent: gomod1, upgrade });
-      expect(res).not.toEqual(gomod2);
+      expect(res).not.toEqual(gomod1);
       expect(res).toContain('github.com/pkg/errors/v2 v2.0.0');
     });
 
@@ -112,10 +112,27 @@ describe('modules/manager/gomod/update', () => {
       };
       const res = updateDependency({ fileContent: gomod1, upgrade });
       expect(res).toMatchSnapshot();
-      expect(res).not.toEqual(gomod2);
+      expect(res).not.toEqual(gomod1);
       expect(res).toContain('gopkg.in/russross/blackfriday.v2 v2.0.0');
     });
 
+    it('skip replacing incompatible major updates', () => {
+      const upgrade = {
+        depName: 'github.com/Azure/azure-sdk-for-go',
+        managerData: { lineNumber: 8 },
+        newMajor: 26,
+        updateType: 'major' as UpdateType,
+        currentValue: 'v25.1.0+incompatible',
+        newValue: 'v26.0.0+incompatible',
+        depType: 'require',
+      };
+      const res = updateDependency({ fileContent: gomod1, upgrade });
+      expect(res).not.toEqual(gomod1);
+      expect(res).toContain(
+        'github.com/Azure/azure-sdk-for-go v26.0.0+incompatible'
+      );
+    });
+
     it('returns null if mismatch', () => {
       const upgrade = {
         depName: 'github.com/aws/aws-sdk-go',
diff --git a/lib/modules/manager/gomod/update.ts b/lib/modules/manager/gomod/update.ts
index 47202948da..d2839abce3 100644
--- a/lib/modules/manager/gomod/update.ts
+++ b/lib/modules/manager/gomod/update.ts
@@ -105,7 +105,8 @@ export function updateDependency({
         );
       } else if (
         upgrade.newMajor! > 1 &&
-        !newLine.includes(`/v${upgrade.newMajor}`)
+        !newLine.includes(`/v${upgrade.newMajor}`) &&
+        !upgrade.newValue!.endsWith('+incompatible')
       ) {
         if (depName === depNameNoVersion) {
           // If package currently has no version, pin to latest one.
-- 
GitLab