Skip to content

Commit

Permalink
feat(manager/bundler): support groups within source blocks (#27424)
Browse files Browse the repository at this point in the history
Co-authored-by: Rhys Arkins <rhys@arkins.net>
  • Loading branch information
mustardnoise and rarkins committed Mar 24, 2024
1 parent 0445d3f commit 58bf6e3
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 40 deletions.
@@ -0,0 +1,12 @@
source 'https://hub.tech.my.domain.de/artifactory/api/gems/my-gems-prod-local/' do
gem 'sfn_my_dep1', "~> 1"
gem 'sfn_my_dep2', "~> 1"

group :test, :development do
gem 'internal_test_gem', "~> 1"
end

group :production do
gem 'internal_production_gem', "~> 1"
end
end
17 changes: 17 additions & 0 deletions lib/modules/manager/bundler/extract.spec.ts
Expand Up @@ -26,6 +26,9 @@ const sourceBlockWithNewLinesGemfileLock = Fixtures.get(
const sourceBlockWithNewLinesGemfile = Fixtures.get(
'Gemfile.sourceBlockWithNewLines',
);
const sourceBlockWithGroupsGemfile = Fixtures.get(
'Gemfile.sourceBlockWithGroups',
);

describe('modules/manager/bundler/extract', () => {
describe('extractPackageFile()', () => {
Expand Down Expand Up @@ -124,4 +127,18 @@ describe('modules/manager/bundler/extract', () => {
expect(res).toMatchSnapshot();
expect(res?.deps).toHaveLength(2);
});

it('parses source blocks with groups in Gemfile', async () => {
fs.readLocalFile.mockResolvedValueOnce(sourceBlockWithGroupsGemfile);
const res = await extractPackageFile(
sourceBlockWithGroupsGemfile,
'Gemfile',
);
expect(res?.deps).toMatchObject([
{ depName: 'internal_test_gem', currentValue: '"~> 1"' },
{ depName: 'internal_production_gem', currentValue: '"~> 1"' },
{ depName: 'sfn_my_dep1', currentValue: '"~> 1"' },
{ depName: 'sfn_my_dep2', currentValue: '"~> 1"' },
]);
});
});
109 changes: 69 additions & 40 deletions lib/modules/manager/bundler/extract.ts
Expand Up @@ -16,12 +16,70 @@ export async function extractPackageFile(
content: string,
packageFile?: string,
): Promise<PackageFileContent | null> {
let lineNumber: number;
async function processGroupBlock(
line: string,
repositoryUrl?: string,
trimGroupLine: boolean = false,
): Promise<void> {
const groupMatch = regEx(/^group\s+(.*?)\s+do/).exec(line);
if (groupMatch) {
const depTypes = groupMatch[1]
.split(',')
.map((group) => group.trim())
.map((group) => group.replace(regEx(/^:/), ''));

const groupLineNumber = lineNumber;
let groupContent = '';
let groupLine = '';

while (
lineNumber < lines.length &&
(trimGroupLine ? groupLine.trim() !== 'end' : groupLine !== 'end')
) {
lineNumber += 1;
groupLine = lines[lineNumber];

// istanbul ignore if
if (!is.string(groupLine)) {
logger.debug(
{ content, packageFile, type: 'groupLine' },
'Bundler parsing error',
);
groupLine = 'end';
}
if (trimGroupLine ? groupLine.trim() !== 'end' : groupLine !== 'end') {
groupContent += formatContent(groupLine);
}
}

const groupRes = await extractPackageFile(groupContent);
if (groupRes) {
res.deps = res.deps.concat(
groupRes.deps.map((dep) => {
const depObject = {
...dep,
depTypes,
managerData: {
lineNumber:
Number(dep.managerData?.lineNumber) + groupLineNumber + 1,
},
};
if (repositoryUrl) {
depObject.registryUrls = [repositoryUrl];
}
return depObject;
}),
);
}
}
}
const res: PackageFileContent = {
registryUrls: [],
deps: [],
};
const lines = content.split(newlineRegex);
for (let lineNumber = 0; lineNumber < lines.length; lineNumber += 1) {
for (lineNumber = 0; lineNumber < lines.length; lineNumber += 1) {
const line = lines[lineNumber];
let sourceMatch: RegExpMatchArray | null = null;
for (const delimiter of delimiters) {
Expand Down Expand Up @@ -61,44 +119,9 @@ export async function extractPackageFile(
dep.datasource = RubyGemsDatasource.id;
res.deps.push(dep);
}
const groupMatch = regEx(/^group\s+(.*?)\s+do/).exec(line);
if (groupMatch) {
const depTypes = groupMatch[1]
.split(',')
.map((group) => group.trim())
.map((group) => group.replace(regEx(/^:/), ''));
const groupLineNumber = lineNumber;
let groupContent = '';
let groupLine = '';
while (lineNumber < lines.length && groupLine !== 'end') {
lineNumber += 1;
groupLine = lines[lineNumber];
// istanbul ignore if
if (!is.string(groupLine)) {
logger.debug(
{ content, packageFile, type: 'groupLine' },
'Bundler parsing error',
);
groupLine = 'end';
}
if (groupLine !== 'end') {
groupContent += formatContent(groupLine);
}
}
const groupRes = await extractPackageFile(groupContent);
if (groupRes) {
res.deps = res.deps.concat(
groupRes.deps.map((dep) => ({
...dep,
depTypes,
managerData: {
lineNumber:
Number(dep.managerData?.lineNumber) + groupLineNumber + 1,
},
})),
);
}
}

await processGroupBlock(line);

for (const delimiter of delimiters) {
const sourceBlockMatch = regEx(
`^source\\s+${delimiter}(.*?)${delimiter}\\s+do`,
Expand All @@ -108,6 +131,7 @@ export async function extractPackageFile(
const sourceLineNumber = lineNumber;
let sourceContent = '';
let sourceLine = '';

while (lineNumber < lines.length && sourceLine.trim() !== 'end') {
lineNumber += 1;
sourceLine = lines[lineNumber];
Expand All @@ -119,11 +143,16 @@ export async function extractPackageFile(
);
sourceLine = 'end';
}
if (sourceLine !== 'end') {

await processGroupBlock(sourceLine.trim(), repositoryUrl, true);

if (sourceLine.trim() !== 'end') {
sourceContent += formatContent(sourceLine);
}
}

const sourceRes = await extractPackageFile(sourceContent);

if (sourceRes) {
res.deps = res.deps.concat(
sourceRes.deps.map((dep) => ({
Expand Down

0 comments on commit 58bf6e3

Please sign in to comment.