From 6161d069e3383eea8fc89a77259df68cab4255e6 Mon Sep 17 00:00:00 2001
From: Jorge Villacorta <58479606+jorge-wonolo@users.noreply.github.com>
Date: Fri, 30 Jun 2023 00:58:17 -0400
Subject: [PATCH] feat(bundler): try matching lock file to package file first
 (#23032)

---
 lib/modules/manager/bundler/artifacts.ts   |  8 ++++++--
 lib/modules/manager/bundler/common.spec.ts | 20 +++++++++++++++++++-
 lib/modules/manager/bundler/common.ts      | 15 ++++++++++++++-
 lib/modules/manager/bundler/extract.ts     | 12 +++++++-----
 4 files changed, 46 insertions(+), 9 deletions(-)

diff --git a/lib/modules/manager/bundler/artifacts.ts b/lib/modules/manager/bundler/artifacts.ts
index 25055a1782..9f25465218 100644
--- a/lib/modules/manager/bundler/artifacts.ts
+++ b/lib/modules/manager/bundler/artifacts.ts
@@ -23,7 +23,11 @@ import type {
   UpdateArtifactsConfig,
   UpdateArtifactsResult,
 } from '../types';
-import { getBundlerConstraint, getRubyConstraint } from './common';
+import {
+  getBundlerConstraint,
+  getLockFilePath,
+  getRubyConstraint,
+} from './common';
 import {
   findAllAuthenticatable,
   getAuthenticationHeaderValue,
@@ -97,7 +101,7 @@ export async function updateArtifacts(
     logger.debug('Aborting Bundler artifacts due to previous failed attempt');
     throw new Error(existingError);
   }
-  const lockFileName = `${packageFileName}.lock`;
+  const lockFileName = await getLockFilePath(packageFileName);
   const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
   if (!existingLockFileContent) {
     logger.debug('No Gemfile.lock found');
diff --git a/lib/modules/manager/bundler/common.spec.ts b/lib/modules/manager/bundler/common.spec.ts
index 15727c7f5d..f7b7aeb870 100644
--- a/lib/modules/manager/bundler/common.spec.ts
+++ b/lib/modules/manager/bundler/common.spec.ts
@@ -4,7 +4,11 @@ import { fs, partial } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import type { UpdateArtifact } from '../types';
-import { getBundlerConstraint, getRubyConstraint } from './common';
+import {
+  getBundlerConstraint,
+  getLockFilePath,
+  getRubyConstraint,
+} from './common';
 
 jest.mock('../../../util/fs');
 
@@ -95,4 +99,18 @@ describe('modules/manager/bundler/common', () => {
       expect(version).toBeNull();
     });
   });
+
+  describe('getLockFileName', () => {
+    it('returns packageFileName.lock', async () => {
+      fs.localPathExists.mockResolvedValueOnce(true);
+      const lockFileName = await getLockFilePath('packageFileName');
+      expect(lockFileName).toBe('packageFileName.lock');
+    });
+
+    it('returns Gemfile.lock', async () => {
+      fs.localPathExists.mockResolvedValueOnce(false);
+      const lockFileName = await getLockFilePath('packageFileName');
+      expect(lockFileName).toBe('Gemfile.lock');
+    });
+  });
 });
diff --git a/lib/modules/manager/bundler/common.ts b/lib/modules/manager/bundler/common.ts
index 583e0a327e..8e35c77ea5 100644
--- a/lib/modules/manager/bundler/common.ts
+++ b/lib/modules/manager/bundler/common.ts
@@ -1,5 +1,9 @@
 import { logger } from '../../../logger';
-import { getSiblingFileName, readLocalFile } from '../../../util/fs';
+import {
+  getSiblingFileName,
+  localPathExists,
+  readLocalFile,
+} from '../../../util/fs';
 import { regEx } from '../../../util/regex';
 import type { UpdateArtifact } from '../types';
 
@@ -68,3 +72,12 @@ export function getBundlerConstraint(
   }
   return null;
 }
+
+export async function getLockFilePath(
+  packageFilePath: string
+): Promise<string> {
+  logger.debug(`Looking for lockfile for ${packageFilePath}`);
+  return (await localPathExists(`${packageFilePath}.lock`))
+    ? `${packageFilePath}.lock`
+    : `Gemfile.lock`;
+}
diff --git a/lib/modules/manager/bundler/extract.ts b/lib/modules/manager/bundler/extract.ts
index 10d6999857..81b500946b 100644
--- a/lib/modules/manager/bundler/extract.ts
+++ b/lib/modules/manager/bundler/extract.ts
@@ -5,7 +5,7 @@ import { newlineRegex, regEx } from '../../../util/regex';
 import { RubyVersionDatasource } from '../../datasource/ruby-version';
 import { RubyGemsDatasource } from '../../datasource/rubygems';
 import type { PackageDependency, PackageFileContent } from '../types';
-import { delimiters, extractRubyVersion } from './common';
+import { delimiters, extractRubyVersion, getLockFilePath } from './common';
 import { extractLockFileEntries } from './locked-version';
 
 function formatContent(input: string): string {
@@ -210,11 +210,13 @@ export async function extractPackageFile(
   }
 
   if (packageFile) {
-    const gemfileLock = `${packageFile}.lock`;
-    const lockContent = await readLocalFile(gemfileLock, 'utf8');
+    const gemfileLockPath = await getLockFilePath(packageFile);
+    const lockContent = await readLocalFile(gemfileLockPath, 'utf8');
     if (lockContent) {
-      logger.debug(`Found Gemfile.lock file packageFile: ${packageFile}`);
-      res.lockFiles = [gemfileLock];
+      logger.debug(
+        `Found lock file ${gemfileLockPath} for packageFile: ${packageFile}`
+      );
+      res.lockFiles = [gemfileLockPath];
       const lockedEntries = extractLockFileEntries(lockContent);
       for (const dep of res.deps) {
         // TODO: types (#7154)
-- 
GitLab