diff --git a/lib/modules/manager/bundler/artifacts.spec.ts b/lib/modules/manager/bundler/artifacts.spec.ts index a4611a22d843a8..497d293a06b552 100644 --- a/lib/modules/manager/bundler/artifacts.spec.ts +++ b/lib/modules/manager/bundler/artifacts.spec.ts @@ -126,7 +126,10 @@ describe('modules/manager/bundler/artifacts', () => { it('works for default binarySource', async () => { fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); - fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce(null); // .ruby-version + fs.readLocalFile.mockResolvedValueOnce(null); // .tool-versions + fs.localPathExists.mockResolvedValueOnce(true); // Gemfile.lock + fs.readLocalFile.mockResolvedValueOnce(null); // Gemfile.lock const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValueOnce( partial({ @@ -150,7 +153,10 @@ describe('modules/manager/bundler/artifacts', () => { it('works explicit global binarySource', async () => { GlobalConfig.set({ ...adminConfig, binarySource: 'global' }); fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); - fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce(null); // .ruby-version + fs.readLocalFile.mockResolvedValueOnce(null); // .tool-versions + fs.localPathExists.mockResolvedValueOnce(true); // Gemfile.lock + fs.readLocalFile.mockResolvedValueOnce(null); // Gemfile.lock const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValueOnce( partial({ @@ -173,7 +179,10 @@ describe('modules/manager/bundler/artifacts', () => { it('supports conservative mode and updateType option', async () => { fs.readLocalFile.mockResolvedValueOnce('Current Gemfile.lock'); - fs.readLocalFile.mockResolvedValueOnce(null); + fs.readLocalFile.mockResolvedValueOnce(null); // .ruby-version + fs.readLocalFile.mockResolvedValueOnce(null); // .tool-versions + fs.localPathExists.mockResolvedValueOnce(true); // Gemfile.lock + fs.readLocalFile.mockResolvedValueOnce(null); // Gemfile.lock const execSnapshots = mockExecAll(); git.getRepoStatus.mockResolvedValueOnce( partial({ diff --git a/lib/modules/manager/bundler/common.spec.ts b/lib/modules/manager/bundler/common.spec.ts index f7b7aeb8705ec6..ced2e45fbbbc11 100644 --- a/lib/modules/manager/bundler/common.spec.ts +++ b/lib/modules/manager/bundler/common.spec.ts @@ -68,7 +68,7 @@ describe('modules/manager/bundler/common', () => { expect(version).toBe('2.1.0'); }); - it('extracts from lockfile', async () => { + it('extracts from gemfile', async () => { const config = partial({ packageFileName: 'Gemfile', newPackageFileContent: gemfile, @@ -84,9 +84,37 @@ describe('modules/manager/bundler/common', () => { newPackageFileContent: '', config: {}, }); - fs.readLocalFile.mockResolvedValueOnce('ruby-1.2.3'); + fs.readLocalFile.mockResolvedValueOnce('2.7.8'); + const version = await getRubyConstraint(config); + expect(version).toBe('2.7.8'); + }); + + it('extracts from .tool-versions', async () => { + const config = partial({ + packageFileName: 'Gemfile', + newPackageFileContent: '', + config: {}, + }); + fs.readLocalFile + .mockResolvedValueOnce(null) + .mockResolvedValueOnce('python\t3.8.10\nruby\t3.3.4\n'); + const version = await getRubyConstraint(config); + expect(version).toBe('3.3.4'); + }); + + it('extracts from lockfile', async () => { + const config = partial({ + packageFileName: 'Gemfile', + newPackageFileContent: '', + config: {}, + }); + fs.localPathExists.mockResolvedValueOnce(true); + fs.readLocalFile + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(Fixtures.get('Gemfile.rubyci.lock')); const version = await getRubyConstraint(config); - expect(version).toBe('1.2.3'); + expect(version).toBe('2.6.5'); }); it('returns null', async () => { diff --git a/lib/modules/manager/bundler/common.ts b/lib/modules/manager/bundler/common.ts index cf85fee10eda70..0b6c4fdb7410fc 100644 --- a/lib/modules/manager/bundler/common.ts +++ b/lib/modules/manager/bundler/common.ts @@ -34,17 +34,24 @@ export async function getRubyConstraint( logger.debug('Using ruby version from gemfile'); return rubyMatch; } - const rubyVersionFile = getSiblingFileName( - packageFileName, - '.ruby-version', - ); - const rubyVersionFileContent = await readLocalFile(rubyVersionFile, 'utf8'); - if (rubyVersionFileContent) { - logger.debug('Using ruby version specified in .ruby-version'); - return rubyVersionFileContent - .replace(regEx(/^ruby-/), '') - .replace(regEx(/\n/g), '') - .trim(); + for (const file of ['.ruby-version', '.tool-versions']) { + const rubyVersion = ( + await readLocalFile(getSiblingFileName(packageFileName, file), 'utf8') + )?.match(regEx(/^(?:ruby(?:-|\s+))?(\d[\d.]*)/m))?.[1]; + if (rubyVersion) { + logger.debug(`Using ruby version specified in ${file}`); + return rubyVersion; + } + } + const lockFile = await getLockFilePath(packageFileName); + if (lockFile) { + const rubyVersion = (await readLocalFile(lockFile, 'utf8'))?.match( + regEx(/^ {3}ruby (\d[\d.]*)(?:[a-z]|\s|$)/m), + )?.[1]; + if (rubyVersion) { + logger.debug(`Using ruby version specified in lock file`); + return rubyVersion; + } } } return null;