From f239c6318ce6233ee840e7f03bc9e5a43ec30b6c Mon Sep 17 00:00:00 2001 From: Julien Tanay Date: Tue, 5 Nov 2024 12:46:46 +0100 Subject: [PATCH] feat(bundler): add source variables support --- lib/modules/manager/bundler/extract.spec.ts | 20 ++++++++++ lib/modules/manager/bundler/extract.ts | 43 +++++++++++++++++---- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/lib/modules/manager/bundler/extract.spec.ts b/lib/modules/manager/bundler/extract.spec.ts index cbee5d033f7c3e9..88bc3df29252c23 100644 --- a/lib/modules/manager/bundler/extract.spec.ts +++ b/lib/modules/manager/bundler/extract.spec.ts @@ -1,4 +1,5 @@ import is from '@sindresorhus/is'; +import { codeBlock } from 'common-tags'; import { Fixtures } from '../../../../test/fixtures'; import { fs } from '../../../../test/util'; import { isValid } from '../../versioning/ruby'; @@ -29,6 +30,15 @@ const sourceBlockWithNewLinesGemfile = Fixtures.get( const sourceBlockWithGroupsGemfile = Fixtures.get( 'Gemfile.sourceBlockWithGroups', ); +const sourceVariableGemfile = codeBlock` +source "https://rubygems.org" +ruby '~> 1.5.3' +example = 'https://gems.example.com' + +source example do + gem "some_internal_gem" +end +`; describe('modules/manager/bundler/extract', () => { describe('extractPackageFile()', () => { @@ -141,4 +151,14 @@ describe('modules/manager/bundler/extract', () => { { depName: 'sfn_my_dep2', currentValue: '"~> 1"' }, ]); }); + + it('parses source variable in Gemfile', async () => { + fs.readLocalFile.mockResolvedValueOnce(sourceVariableGemfile); + const res = await extractPackageFile(sourceVariableGemfile, 'Gemfile'); + expect(res?.deps).toHaveLength(2); + expect(res?.deps[1]).toMatchObject({ + depName: 'some_internal_gem', + registryUrls: ['https://gems.example.com'], + }); + }); }); diff --git a/lib/modules/manager/bundler/extract.ts b/lib/modules/manager/bundler/extract.ts index 54995620a9c487e..8087f2afadd2168 100644 --- a/lib/modules/manager/bundler/extract.ts +++ b/lib/modules/manager/bundler/extract.ts @@ -78,6 +78,9 @@ export async function extractPackageFile( registryUrls: [], deps: [], }; + + const variables: Record = {}; + const lines = content.split(newlineRegex); for (lineNumber = 0; lineNumber < lines.length; lineNumber += 1) { const line = lines[lineNumber]; @@ -85,12 +88,20 @@ export async function extractPackageFile( for (const delimiter of delimiters) { sourceMatch = sourceMatch ?? - regEx(`^source ${delimiter}([^${delimiter}]+)${delimiter}\\s*$`).exec( - line, - ); + regEx( + `^source ((${delimiter}(?[^${delimiter}]+)${delimiter})|(?\\w+))\\s*$`, + ).exec(line); } if (sourceMatch) { - res.registryUrls?.push(sourceMatch[1]); + if (sourceMatch.groups?.registryUrl) { + res.registryUrls?.push(sourceMatch.groups.registryUrl); + } + if (sourceMatch.groups?.sourceName) { + const registryUrl = variables[sourceMatch.groups.sourceName]; + if (registryUrl) { + res.registryUrls?.push(registryUrl); + } + } } const rubyMatch = extractRubyVersion(line); @@ -103,8 +114,18 @@ export async function extractPackageFile( }); } + const variableMatchRegex = regEx( + `^(?\\w+)\\s*=\\s*['"](?[^'"]+)['"]`, + ); + const variableMatch = variableMatchRegex.exec(line); + if (variableMatch) { + if (variableMatch.groups?.key) { + variables[variableMatch.groups?.key] = variableMatch.groups?.value; + } + } + const gemMatchRegex = regEx( - `^\\s*gem\\s+(['"])(?[^'"]+)(['"])(\\s*,\\s*(?(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?`, + `^\\s*gem\\s+(['"])(?[^'"]+)(['"])(\\s*,\\s*(?(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?(\\s*,\\s*source:\\s*(['"](?[^'"]+)['"]|(?[^'"]+)))?`, ); const gemMatch = gemMatchRegex.exec(line); if (gemMatch) { @@ -124,10 +145,18 @@ export async function extractPackageFile( for (const delimiter of delimiters) { const sourceBlockMatch = regEx( - `^source\\s+${delimiter}(.*?)${delimiter}\\s+do`, + `^source\\s+((${delimiter}(?[^${delimiter}]+)${delimiter})|(?\\w+))\\s+do`, ).exec(line); if (sourceBlockMatch) { - const repositoryUrl = sourceBlockMatch[1]; + let repositoryUrl = ''; + if (sourceBlockMatch.groups?.registryUrl) { + repositoryUrl = sourceBlockMatch.groups.registryUrl; + } + if (sourceBlockMatch.groups?.sourceName) { + if (variables[sourceBlockMatch.groups.sourceName]) { + repositoryUrl = variables[sourceBlockMatch.groups.sourceName]; + } + } const sourceLineNumber = lineNumber; let sourceContent = ''; let sourceLine = '';