Skip to content
Snippets Groups Projects
Unverified Commit cb9eb4ca authored by Sergei Zharinov's avatar Sergei Zharinov Committed by GitHub
Browse files

feat(bazel): Support for Maven dependencies (#20351)

parent 36d5b078
Branches
Tags 34.133.0
No related merge requests found
......@@ -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',
],
},
]);
});
});
......@@ -10,6 +10,7 @@ interface Ctx {
results: RecordFragment[];
stack: NestedFragment[];
recordKey?: string;
subRecordKey?: string;
}
function emptyCtx(source: string): Ctx {
......@@ -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
......@@ -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 (
......@@ -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 (
......@@ -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: '',
......@@ -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));
......@@ -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(
......
......@@ -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',
],
},
]);
});
});
});
......@@ -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(
......
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,
}))
);
......@@ -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% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment