Skip to content

Commit

Permalink
feat(bazel): Support for Maven dependencies (#20351)
Browse files Browse the repository at this point in the history
  • Loading branch information
zharinov committed Feb 12, 2023
1 parent 36d5b07 commit cb9eb4c
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 18 deletions.
40 changes: 40 additions & 0 deletions lib/modules/manager/bazel/parser.spec.ts
Expand Up @@ -218,4 +218,44 @@ describe('modules/manager/bazel/parser', () => {
},
]);
});

it('parses Maven', () => {
const input = codeBlock`
maven_install(
artifacts = [
"com.example1:foo:1.1.1",
maven.artifact(
group = "com.example2",
artifact = "bar",
version = "2.2.2",
)
],
repositories = [
"https://example1.com/maven2",
"https://example2.com/maven2",
]
)
`;

const res = parse(input);

expect(res?.map(extract)).toEqual([
{
rule: 'maven_install',
artifacts: [
'com.example1:foo:1.1.1',
{
_function: 'maven.artifact',
artifact: 'bar',
group: 'com.example2',
version: '2.2.2',
},
],
repositories: [
'https://example1.com/maven2',
'https://example2.com/maven2',
],
},
]);
});
});
120 changes: 105 additions & 15 deletions lib/modules/manager/bazel/parser.ts
Expand Up @@ -10,6 +10,7 @@ interface Ctx {
results: RecordFragment[];
stack: NestedFragment[];
recordKey?: string;
subRecordKey?: string;
}

function emptyCtx(source: string): Ctx {
Expand Down Expand Up @@ -45,9 +46,20 @@ function extractTreeValue(
* - `tag = "1.2.3"`
* - `name = "foobar"`
* - `deps = ["foo", "bar"]`
* - `
* artifacts = [
maven.artifact(
group = "com.example1",
artifact = "foobar",
version = "1.2.3",
)
]
`
**/
const kwParams = q
.sym<Ctx>((ctx, { value: recordKey }) => ({ ...ctx, recordKey }))
.sym<Ctx>((ctx, { value: recordKey }) => {
return { ...ctx, recordKey };
})
.op('=')
.alt(
// string
Expand All @@ -59,10 +71,12 @@ const kwParams = q
}
return ctx;
}),
// array of strings
// array of strings or calls
q.tree({
type: 'wrapped-tree',
maxDepth: 1,
startsWith: '[',
endsWith: ']',
preHandler: (ctx, tree) => {
const parentRecord = currentFragment(ctx) as RecordFragment;
if (
Expand All @@ -80,17 +94,93 @@ const kwParams = q
}
return ctx;
},
search: q.str<Ctx>((ctx, { value, offset }) => {
const parentRecord = currentFragment(ctx);
if (parentRecord.type === 'record' && ctx.recordKey) {
const key = ctx.recordKey;
const array = parentRecord.children[key];
if (array.type === 'array') {
array.children.push({ type: 'string', value, offset });
search: q.alt(
q.str<Ctx>((ctx, { value, offset }) => {
const parentRecord = currentFragment(ctx);
if (parentRecord.type === 'record' && ctx.recordKey) {
const key = ctx.recordKey;
const array = parentRecord.children[key];
if (array.type === 'array') {
array.children.push({ type: 'string', value, offset });
}
}
}
return ctx;
}),
return ctx;
}),
q
.sym<Ctx>()
.handler(recordStartHandler)
.handler((ctx, { value, offset }) => {
const ruleFragment = currentFragment(ctx);
if (ruleFragment.type === 'record') {
ruleFragment.children._function = {
type: 'string',
value,
offset,
};
}
return ctx;
})
.many(
q.op<Ctx>('.').sym((ctx, { value }) => {
const ruleFragment = currentFragment(ctx);
if (
ruleFragment.type === 'record' &&
ruleFragment.children._function
) {
ruleFragment.children._function.value += `.${value}`;
}
return ctx;
}),
0,
3
)
.tree({
type: 'wrapped-tree',
maxDepth: 1,
startsWith: '(',
endsWith: ')',
search: q
.sym<Ctx>((ctx, { value: subRecordKey }) => ({
...ctx,
subRecordKey,
}))
.op('=')
.str((ctx, { value: subRecordValue, offset }) => {
const subRecordKey = ctx.subRecordKey!;
const ruleFragment = currentFragment(ctx);
if (ruleFragment.type === 'record') {
ruleFragment.children[subRecordKey] = {
type: 'string',
value: subRecordValue,
offset,
};
}
delete ctx.subRecordKey;
return ctx;
}),
postHandler: (ctx, tree) => {
const callFrag = currentFragment(ctx);
ctx.stack.pop();
if (callFrag.type === 'record' && tree.type === 'wrapped-tree') {
callFrag.value = extractTreeValue(
ctx.source,
tree,
callFrag.offset
);

const parentRecord = currentFragment(ctx);
if (parentRecord.type === 'record' && ctx.recordKey) {
const key = ctx.recordKey;
const array = parentRecord.children[key];
if (array.type === 'array') {
array.children.push(callFrag);
}
}
}
return ctx;
},
})
),
postHandler: (ctx, tree) => {
const parentRecord = currentFragment(ctx);
if (
Expand Down Expand Up @@ -140,7 +230,7 @@ function ruleCall(
});
}

function ruleStartHandler(ctx: Ctx, { offset }: lexer.Token): Ctx {
function recordStartHandler(ctx: Ctx, { offset }: lexer.Token): Ctx {
ctx.stack.push({
type: 'record',
value: '',
Expand All @@ -166,7 +256,7 @@ function ruleNameHandler(ctx: Ctx, { value, offset }: lexer.Token): Ctx {
*/
const regularRule = q
.sym<Ctx>(supportedRulesRegex, (ctx, token) =>
ruleNameHandler(ruleStartHandler(ctx, token), token)
ruleNameHandler(recordStartHandler(ctx, token), token)
)
.join(ruleCall(kwParams));

Expand All @@ -176,7 +266,7 @@ const regularRule = q
* - `maybe(go_repository, ...)`
*/
const maybeRule = q
.sym<Ctx>('maybe', ruleStartHandler)
.sym<Ctx>('maybe', recordStartHandler)
.join(
ruleCall(
q.alt(
Expand Down
44 changes: 44 additions & 0 deletions lib/modules/manager/bazel/rules/index.spec.ts
Expand Up @@ -348,4 +348,48 @@ describe('modules/manager/bazel/rules/index', () => {
]);
});
});

describe('maven', () => {
it('extracts maven dependencies', () => {
expect(
extractDepsFromFragmentData({
rule: 'maven_install',
artifacts: [
'com.example1:foo:1.1.1',
{
artifact: 'bar',
function: 'maven.artifact',
group: 'com.example2',
version: '2.2.2',
},
],
repositories: [
'https://example1.com/maven2',
'https://example2.com/maven2',
],
})
).toEqual([
{
currentValue: '1.1.1',
datasource: 'maven',
depType: 'maven_install',
depName: 'com.example1:foo',
registryUrls: [
'https://example1.com/maven2',
'https://example2.com/maven2',
],
},
{
currentValue: '2.2.2',
datasource: 'maven',
depType: 'maven_install',
depName: 'com.example2:bar',
registryUrls: [
'https://example1.com/maven2',
'https://example2.com/maven2',
],
},
]);
});
});
});
17 changes: 15 additions & 2 deletions lib/modules/manager/bazel/rules/index.ts
Expand Up @@ -6,14 +6,27 @@ import { DockerTarget, dockerRules } from './docker';
import { GitTarget, gitRules } from './git';
import { GoTarget, goRules } from './go';
import { HttpTarget, httpRules } from './http';
import { MavenTarget, mavenRules } from './maven';

const Target = z.union([DockerTarget, GitTarget, GoTarget, HttpTarget]);
const Target = z.union([
DockerTarget,
GitTarget,
GoTarget,
HttpTarget,
MavenTarget,
]);

/**
* Gather all rule names supported by Renovate in order to speed up parsing
* by filtering out other syntactically correct rules we don't support yet.
*/
const supportedRules = [...dockerRules, ...gitRules, ...goRules, ...httpRules];
const supportedRules = [
...dockerRules,
...gitRules,
...goRules,
...httpRules,
...mavenRules,
];
export const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`);

export function extractDepsFromFragmentData(
Expand Down
51 changes: 51 additions & 0 deletions lib/modules/manager/bazel/rules/maven.ts
@@ -0,0 +1,51 @@
import is from '@sindresorhus/is';
import { z } from 'zod';
import { MavenDatasource } from '../../../datasource/maven';
import type { PackageDependency } from '../../types';

export const mavenRules = ['maven_install'] as const;

const ArtifactSpec = z.object({
group: z.string(),
artifact: z.string(),
version: z.string(),
});
type ArtifactSpec = z.infer<typeof ArtifactSpec>;

export const MavenTarget = z
.object({
rule: z.enum(mavenRules),
artifacts: z
.union([z.string(), ArtifactSpec])
.array()
.transform((xs) => {
const result: ArtifactSpec[] = [];
for (const x of xs) {
if (is.string(x)) {
const [group, artifact, version] = x.split(':');
if (group && artifact && version) {
result.push({ group, artifact, version });
}
} else {
result.push(x);
}
}

return result;
}),
repositories: z.array(z.string()).optional(),
})
.transform(
({
rule: depType,
artifacts,
repositories: registryUrls,
}): PackageDependency[] =>
artifacts.map(({ group, artifact, version: currentValue }) => ({
datasource: MavenDatasource.id,
depName: `${group}:${artifact}`,
currentValue,
depType,
registryUrls,
}))
);
3 changes: 2 additions & 1 deletion lib/modules/manager/bazel/types.ts
Expand Up @@ -56,6 +56,7 @@ export type FragmentData =
export type FragmentPath =
| [number]
| [number, string]
| [number, string, number];
| [number, string, number]
| [number, string, number, string];

export type FragmentUpdater = string | ((_: string) => string);

0 comments on commit cb9eb4c

Please sign in to comment.