From aa0c7d284ba16e209ffb9b7e93bb526c06f7be22 Mon Sep 17 00:00:00 2001
From: Omer Cohen <omer@descope.com>
Date: Fri, 20 Dec 2024 15:20:17 +0200
Subject: [PATCH] feat(manager/gomod): option to always run go mod vendor
 (#33066)

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 docs/usage/configuration-options.md         |   1 +
 lib/config/options/index.ts                 |   1 +
 lib/modules/manager/gomod/artifacts.spec.ts | 106 ++++++++++++++++++++
 lib/modules/manager/gomod/artifacts.ts      |   6 +-
 4 files changed, 111 insertions(+), 3 deletions(-)

diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index d96d93cf9e..a55d0fa601 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -3357,6 +3357,7 @@ Table with options:
 | `gomodTidyE`                 | Run `go mod tidy -e` after Go module updates.                                                                                                              |
 | `gomodUpdateImportPaths`     | Update source import paths on major module updates, using [mod](https://github.com/marwan-at-work/mod).                                                    |
 | `gomodSkipVendor`            | Never run `go mod vendor` after Go module updates.                                                                                                         |
+| `gomodVendor`                | Always run `go mod vendor` after Go module updates even if vendor files aren't detected.                                                                   |
 | `helmUpdateSubChartArchives` | Update subchart archives in the `/charts` folder.                                                                                                          |
 | `npmDedupe`                  | Run `npm install` with `--prefer-dedupe` for npm >= 7 or `npm dedupe` after `package-lock.json` update for npm <= 6.                                       |
 | `pnpmDedupe`                 | Run `pnpm dedupe --config.ignore-scripts=true` after `pnpm-lock.yaml` updates.                                                                             |
diff --git a/lib/config/options/index.ts b/lib/config/options/index.ts
index 263cc69f12..c5f677d67e 100644
--- a/lib/config/options/index.ts
+++ b/lib/config/options/index.ts
@@ -2390,6 +2390,7 @@ const options: RenovateOptions[] = [
       'gomodTidyE',
       'gomodUpdateImportPaths',
       'gomodSkipVendor',
+      'gomodVendor',
       'helmUpdateSubChartArchives',
       'npmDedupe',
       'pnpmDedupe',
diff --git a/lib/modules/manager/gomod/artifacts.spec.ts b/lib/modules/manager/gomod/artifacts.spec.ts
index 308904a603..4310e6887c 100644
--- a/lib/modules/manager/gomod/artifacts.spec.ts
+++ b/lib/modules/manager/gomod/artifacts.spec.ts
@@ -189,6 +189,112 @@ describe('modules/manager/gomod/artifacts', () => {
     ]);
   });
 
+  it('runs go mod vendor with gomodVendor', async () => {
+    fs.readLocalFile.mockResolvedValueOnce('Current go.sum');
+    fs.readLocalFile.mockResolvedValueOnce('New go.sum');
+    fs.readLocalFile.mockResolvedValueOnce('New go.mod');
+
+    const execSnapshots = mockExecAll();
+    git.getRepoStatus.mockResolvedValueOnce(
+      partial<StatusResult>({
+        modified: ['go.sum'],
+        not_added: [],
+        deleted: [],
+      }),
+    );
+    const res = await gomod.updateArtifacts({
+      packageFileName: 'go.mod',
+      updatedDeps: [],
+      newPackageFileContent: gomod1,
+      config: {
+        ...config,
+        postUpdateOptions: ['gomodVendor'],
+      },
+    });
+    expect(res).toEqual([
+      {
+        file: {
+          contents: 'New go.sum',
+          path: 'go.sum',
+          type: 'addition',
+        },
+      },
+      {
+        file: {
+          contents: 'New go.mod',
+          path: 'go.mod',
+          type: 'addition',
+        },
+      },
+    ]);
+
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: 'go get -d -t ./...',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+      {
+        cmd: 'go mod vendor',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
+  });
+
+  it('runs go work vendor with gomodVendor and go.work', async () => {
+    fs.readLocalFile.mockResolvedValueOnce('Current go.sum');
+    fs.findLocalSiblingOrParent.mockResolvedValueOnce('go.work');
+    const execSnapshots = mockExecAll();
+    git.getRepoStatus.mockResolvedValueOnce(
+      partial<StatusResult>({
+        modified: ['go.sum'],
+        not_added: [],
+        deleted: [],
+      }),
+    );
+    fs.readLocalFile.mockResolvedValueOnce('New go.sum');
+    fs.readLocalFile.mockResolvedValueOnce('New go.mod');
+    const res = await gomod.updateArtifacts({
+      packageFileName: 'go.mod',
+      updatedDeps: [],
+      newPackageFileContent: gomod1,
+      config: {
+        ...config,
+        postUpdateOptions: ['gomodVendor'],
+      },
+    });
+    expect(res).toEqual([
+      {
+        file: {
+          contents: 'New go.sum',
+          path: 'go.sum',
+          type: 'addition',
+        },
+      },
+      {
+        file: {
+          contents: 'New go.mod',
+          path: 'go.mod',
+          type: 'addition',
+        },
+      },
+    ]);
+
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: 'go get -d -t ./...',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+      {
+        cmd: 'go work vendor',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+      {
+        cmd: 'go work sync',
+        options: { cwd: '/tmp/github/some/repo' },
+      },
+    ]);
+  });
+
   it('supports vendor directory update', async () => {
     const foo = join('vendor/github.com/foo/foo/go.mod');
     const bar = join('vendor/github.com/bar/bar/go.mod');
diff --git a/lib/modules/manager/gomod/artifacts.ts b/lib/modules/manager/gomod/artifacts.ts
index bf5f65a41d..e77b357251 100644
--- a/lib/modules/manager/gomod/artifacts.ts
+++ b/lib/modules/manager/gomod/artifacts.ts
@@ -134,9 +134,9 @@ export async function updateArtifacts({
   const vendorDir = upath.join(goModDir, 'vendor/');
   const vendorModulesFileName = upath.join(vendorDir, 'modules.txt');
   const useVendor =
-    !config.postUpdateOptions?.includes('gomodSkipVendor') &&
-    (await readLocalFile(vendorModulesFileName)) !== null;
-
+    !!config.postUpdateOptions?.includes('gomodVendor') ||
+    (!config.postUpdateOptions?.includes('gomodSkipVendor') &&
+      (await readLocalFile(vendorModulesFileName)) !== null);
   let massagedGoMod = newGoModContent;
 
   if (config.postUpdateOptions?.includes('gomodMassage')) {
-- 
GitLab