diff --git a/lib/modules/manager/bundler/__snapshots__/extract.spec.ts.snap b/lib/modules/manager/bundler/__snapshots__/extract.spec.ts.snap index 02de76a63df696..740bd04292fe2a 100644 --- a/lib/modules/manager/bundler/__snapshots__/extract.spec.ts.snap +++ b/lib/modules/manager/bundler/__snapshots__/extract.spec.ts.snap @@ -505,11 +505,14 @@ exports[`modules/manager/bundler/extract extractPackageFile() parse mastodon Gem }, }, { - "datasource": "rubygems", + "currentDigest": "0b799ead604f900ed50685e9b2d469cd2befba5b", + "datasource": "git-refs", "depName": "health_check", "managerData": { "lineNumber": 53, }, + "packageName": "https://github.com/ianheggie/health_check", + "sourceUrl": "https://github.com/ianheggie/health_check", }, { "currentValue": "'~> 4.3'", @@ -591,11 +594,14 @@ exports[`modules/manager/bundler/extract extractPackageFile() parse mastodon Gem }, }, { - "datasource": "rubygems", + "currentDigest": "fd184883048b922b176939f851338d0a4971a532", + "datasource": "git-refs", "depName": "nilsimsa", "managerData": { "lineNumber": 63, }, + "packageName": "https://github.com/witgo/nilsimsa", + "sourceUrl": "https://github.com/witgo/nilsimsa", }, { "currentValue": "'~> 1.10'", @@ -660,11 +666,14 @@ exports[`modules/manager/bundler/extract extractPackageFile() parse mastodon Gem }, }, { - "datasource": "rubygems", + "currentDigest": "58465d2e213991f8afb13b984854a49fcdcc980c", + "datasource": "git-refs", "depName": "posix-spawn", "managerData": { "lineNumber": 71, }, + "packageName": "https://github.com/rtomayko/posix-spawn", + "sourceUrl": "https://github.com/rtomayko/posix-spawn", }, { "currentValue": "'~> 2.1'", @@ -899,11 +908,14 @@ exports[`modules/manager/bundler/extract extractPackageFile() parse mastodon Gem }, }, { - "datasource": "rubygems", + "currentDigest": "e742697a0906e74e8bb777ef98137bc3955d981d", + "datasource": "git-refs", "depName": "json-ld", "managerData": { "lineNumber": 99, }, + "packageName": "https://github.com/ruby-rdf/json-ld.git", + "sourceUrl": "https://github.com/ruby-rdf/json-ld", }, { "currentValue": "'~> 3.0'", @@ -1494,11 +1506,13 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi }, }, { - "datasource": "rubygems", + "datasource": "git-refs", "depName": "webpacker", "managerData": { "lineNumber": 16, }, + "packageName": "https://github.com/rails/webpacker", + "sourceUrl": "https://github.com/rails/webpacker", }, { "currentValue": ""~> 3.1.11"", @@ -1681,7 +1695,8 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi }, }, { - "datasource": "rubygems", + "currentValue": "update-pg", + "datasource": "git-refs", "depName": "queue_classic", "depTypes": [ "job", @@ -1689,6 +1704,8 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi "managerData": { "lineNumber": 54, }, + "packageName": "https://github.com/rafaelfranca/queue_classic", + "sourceUrl": "https://github.com/rafaelfranca/queue_classic", }, { "datasource": "rubygems", @@ -1791,7 +1808,8 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi }, }, { - "datasource": "rubygems", + "currentValue": "close-race", + "datasource": "git-refs", "depName": "websocket-client-simple", "depTypes": [ "cable", @@ -1799,6 +1817,8 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi "managerData": { "lineNumber": 71, }, + "packageName": "https://github.com/matthewd/websocket-client-simple", + "sourceUrl": "https://github.com/matthewd/websocket-client-simple", }, { "datasource": "rubygems", @@ -2024,15 +2044,19 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi }, }, { - "datasource": "rubygems", + "currentValue": "master", + "datasource": "git-refs", "depName": "activerecord-jdbcsqlite3-adapter", "lockedVersion": "52.1-java", "managerData": { "lineNumber": 129, }, + "packageName": "https://github.com/jruby/activerecord-jdbc-adapter", + "sourceUrl": "https://github.com/jruby/activerecord-jdbc-adapter", }, { - "datasource": "rubygems", + "currentValue": "master", + "datasource": "git-refs", "depName": "activerecord-jdbcmysql-adapter", "depTypes": [ "db", @@ -2041,9 +2065,12 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi "managerData": { "lineNumber": 131, }, + "packageName": "https://github.com/jruby/activerecord-jdbc-adapter", + "sourceUrl": "https://github.com/jruby/activerecord-jdbc-adapter", }, { - "datasource": "rubygems", + "currentValue": "master", + "datasource": "git-refs", "depName": "activerecord-jdbcpostgresql-adapter", "depTypes": [ "db", @@ -2052,6 +2079,8 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi "managerData": { "lineNumber": 132, }, + "packageName": "https://github.com/jruby/activerecord-jdbc-adapter", + "sourceUrl": "https://github.com/jruby/activerecord-jdbc-adapter", }, { "currentValue": "">= 1.3.0"", @@ -2104,11 +2133,14 @@ exports[`modules/manager/bundler/extract extractPackageFile() parses rails Gemfi }, }, { - "datasource": "rubygems", + "currentValue": "master", + "datasource": "git-refs", "depName": "activerecord-oracle_enhanced-adapter", "managerData": { "lineNumber": 154, }, + "packageName": "https://github.com/rsim/oracle-enhanced", + "sourceUrl": "https://github.com/rsim/oracle-enhanced", }, { "datasource": "rubygems", diff --git a/lib/modules/manager/bundler/extract.spec.ts b/lib/modules/manager/bundler/extract.spec.ts index 8e58870bed9922..78ae44e63f1e4b 100644 --- a/lib/modules/manager/bundler/extract.spec.ts +++ b/lib/modules/manager/bundler/extract.spec.ts @@ -195,4 +195,40 @@ describe('modules/manager/bundler/extract', () => { ], }); }); + + it('parses git refs in Gemfile', async () => { + const gitRefGemfile = codeBlock` + gem 'foo', git: 'https://github.com/foo/foo', ref: 'fd184883048b922b176939f851338d0a4971a532' + gem 'bar', git: 'https://github.com/bar/bar', tag: 'v1.0.0' + gem 'baz', github: 'baz/baz', branch: 'master' + `; + + fs.readLocalFile.mockResolvedValueOnce(gitRefGemfile); + const res = await extractPackageFile(gitRefGemfile, 'Gemfile'); + expect(res).toMatchObject({ + deps: [ + { + depName: 'foo', + packageName: 'https://github.com/foo/foo', + sourceUrl: 'https://github.com/foo/foo', + currentDigest: 'fd184883048b922b176939f851338d0a4971a532', + datasource: 'git-refs', + }, + { + depName: 'bar', + packageName: 'https://github.com/bar/bar', + sourceUrl: 'https://github.com/bar/bar', + currentValue: 'v1.0.0', + datasource: 'git-refs', + }, + { + depName: 'baz', + packageName: 'https://github.com/baz/baz', + sourceUrl: 'https://github.com/baz/baz', + currentValue: 'master', + datasource: 'git-refs', + }, + ], + }); + }); }); diff --git a/lib/modules/manager/bundler/extract.ts b/lib/modules/manager/bundler/extract.ts index e84cd65cb36f41..2ae134a75ca2f7 100644 --- a/lib/modules/manager/bundler/extract.ts +++ b/lib/modules/manager/bundler/extract.ts @@ -2,6 +2,7 @@ import is from '@sindresorhus/is'; import { logger } from '../../../logger'; import { readLocalFile } from '../../../util/fs'; import { newlineRegex, regEx } from '../../../util/regex'; +import { GitRefsDatasource } from '../../datasource/git-refs'; import { RubyVersionDatasource } from '../../datasource/ruby-version'; import { RubygemsDatasource } from '../../datasource/rubygems'; import type { PackageDependency, PackageFileContent } from '../types'; @@ -12,6 +13,16 @@ function formatContent(input: string): string { return input.replace(regEx(/^ {2}/), '') + '\n'; //remove leading whitespace and add a new line at the end } +const variableMatchRegex = regEx( + `^(?\\w+)\\s*=\\s*['"](?[^'"]+)['"]`, +); +const gemGitRefsMatchRegex = regEx( + `^\\s*gem\\s+(['"])(?[^'"]+)['"]((\\s*,\\s*git:\\s*['"](?[^'"]+)['"])|(\\s*,\\s*github:\\s*['"](?[^'"]+)['"]))(\\s*,\\s*branch:\\s*['"](?[^'"]+)['"])?(\\s*,\\s*ref:\\s*['"](?[^'"]+)['"])?(\\s*,\\s*tag:\\s*['"](?[^'"]+)['"])?`, +); +const gemMatchRegex = regEx( + `^\\s*gem\\s+(['"])(?[^'"]+)(['"])(\\s*,\\s*(?(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?(\\s*,\\s*source:\\s*(['"](?[^'"]+)['"]|(?[^'"]+)))?`, +); + export async function extractPackageFile( content: string, packageFile?: string, @@ -114,9 +125,6 @@ export async function extractPackageFile( }); } - const variableMatchRegex = regEx( - `^(?\\w+)\\s*=\\s*['"](?[^'"]+)['"]`, - ); const variableMatch = variableMatchRegex.exec(line); if (variableMatch) { if (variableMatch.groups?.key) { @@ -124,26 +132,47 @@ export async function extractPackageFile( } } - const gemMatchRegex = regEx( - `^\\s*gem\\s+(['"])(?[^'"]+)(['"])(\\s*,\\s*(?(['"])[^'"]+['"](\\s*,\\s*['"][^'"]+['"])?))?(\\s*,\\s*source:\\s*(['"](?[^'"]+)['"]|(?[^'"]+)))?`, - ); - const gemMatch = gemMatchRegex.exec(line); - if (gemMatch) { + const gemGitRefsMatch = gemGitRefsMatchRegex.exec(line)?.groups; + const gemMatch = gemMatchRegex.exec(line)?.groups; + + if (gemGitRefsMatch) { const dep: PackageDependency = { - depName: gemMatch.groups?.depName, + depName: gemGitRefsMatch.depName, managerData: { lineNumber }, }; - if (gemMatch.groups?.currentValue) { - const currentValue = gemMatch.groups.currentValue; - dep.currentValue = currentValue; + if (gemGitRefsMatch.gitUrl) { + const gitUrl = gemGitRefsMatch.gitUrl; + dep.packageName = gitUrl; + + if (gitUrl.startsWith('https://')) { + dep.sourceUrl = gitUrl.replace(/\.git$/, ''); + } + } else if (gemGitRefsMatch.repoName) { + dep.packageName = `https://github.com/${gemGitRefsMatch.repoName}`; + dep.sourceUrl = dep.packageName; + } + if (gemGitRefsMatch.refName) { + dep.currentDigest = gemGitRefsMatch.refName; + } else if (gemGitRefsMatch.branchName) { + dep.currentValue = gemGitRefsMatch.branchName; + } else if (gemGitRefsMatch.tagName) { + dep.currentValue = gemGitRefsMatch.tagName; } - if (gemMatch.groups?.registryUrl) { - const registryUrl = gemMatch.groups.registryUrl; - dep.registryUrls = [registryUrl]; + dep.datasource = GitRefsDatasource.id; + res.deps.push(dep); + } else if (gemMatch) { + const dep: PackageDependency = { + depName: gemMatch.depName, + managerData: { lineNumber }, + }; + if (gemMatch.currentValue) { + const currentValue = gemMatch.currentValue; + dep.currentValue = currentValue; } - if (gemMatch.groups?.sourceName) { - const registryUrl = variables[gemMatch.groups.sourceName]; - dep.registryUrls = [registryUrl]; + if (gemMatch.registryUrl) { + dep.registryUrls = [gemMatch.registryUrl]; + } else if (gemMatch.sourceName) { + dep.registryUrls = [variables[gemMatch.sourceName]]; } dep.datasource = RubygemsDatasource.id; res.deps.push(dep);