feat(manager/gradle): support separate registry URLs for plugins (#19606)

This commit is contained in:
Johannes Feichtner 2022-12-29 19:41:43 +01:00 committed by GitHub
parent 3c207f8e72
commit 0fc1731eed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 198 additions and 22 deletions

View file

@ -242,11 +242,7 @@ describe('modules/manager/gradle/extract', () => {
deps: [
{
depType: 'plugin',
registryUrls: [
'https://repo.maven.apache.org/maven2',
'https://example.com',
'https://plugins.gradle.org/m2/',
],
registryUrls: ['https://plugins.gradle.org/m2/'],
},
{
registryUrls: [
@ -314,6 +310,65 @@ describe('modules/manager/gradle/extract', () => {
},
]);
});
it('supports separate registry URLs for plugins', async () => {
const settingsFile = codeBlock`
pluginManagement {
repositories {
mavenLocal()
maven { url = "https://foo.bar/plugins" }
}
}
`;
const buildFile = codeBlock`
plugins {
id "foo.bar" version "1.2.3"
}
repositories {
maven { url = "https://foo.bar/deps" }
mavenCentral()
}
dependencies {
classpath "io.jsonwebtoken:jjwt-api:0.11.2"
}
`;
const fsMock = {
'build.gradle': buildFile,
'settings.gradle': settingsFile,
};
mockFs(fsMock);
const res = await extractAllPackageFiles(
{} as ExtractConfig,
Object.keys(fsMock)
);
expect(res).toMatchObject([
{
packageFile: 'settings.gradle',
deps: [],
},
{
packageFile: 'build.gradle',
deps: [
{
depName: 'foo.bar',
depType: 'plugin',
registryUrls: ['https://foo.bar/plugins'],
},
{
depName: 'io.jsonwebtoken:jjwt-api',
registryUrls: [
'https://foo.bar/deps',
'https://repo.maven.apache.org/maven2',
],
},
],
},
]);
});
});
describe('version catalogs', () => {

View file

@ -10,8 +10,10 @@ import {
usesGcv,
} from './extract/consistent-versions-plugin';
import { parseGradle, parseProps } from './parser';
import { REGISTRY_URLS } from './parser/common';
import type {
GradleManagerData,
PackageRegistry,
PackageVariables,
VariableRegistry,
} from './types';
@ -26,6 +28,23 @@ import {
const datasource = MavenDatasource.id;
function getRegistryUrlsForDep(
packageRegistries: PackageRegistry[],
dep: PackageDependency<GradleManagerData>
): string[] {
const scope = dep.depType === 'plugin' ? 'plugin' : 'dep';
const registryUrls = packageRegistries
.filter((item) => item.scope === scope)
.map((item) => item.registryUrl);
if (!registryUrls.length && scope === 'plugin') {
registryUrls.push(REGISTRY_URLS.gradlePluginPortal);
}
return [...new Set(registryUrls)];
}
export async function extractAllPackageFiles(
config: ExtractConfig,
packageFiles: string[]
@ -33,7 +52,7 @@ export async function extractAllPackageFiles(
const extractedDeps: PackageDependency<GradleManagerData>[] = [];
const varRegistry: VariableRegistry = {};
const packageFilesByName: Record<string, PackageFile> = {};
const packageRegistries: string[] = [];
const packageRegistries: PackageRegistry[] = [];
const reorderedFiles = reorderFiles(packageFiles);
const fileContents = await getFileContentMap(packageFiles, true);
@ -75,7 +94,11 @@ export async function extractAllPackageFiles(
vars: gradleVars,
} = parseGradle(content, vars, packageFile, fileContents);
for (const url of urls) {
if (!packageRegistries.includes(url)) {
const registryAlreadyKnown = packageRegistries.some(
(item) =>
item.registryUrl === url.registryUrl && item.scope === url.scope
);
if (!registryAlreadyKnown) {
packageRegistries.push(url);
}
}
@ -114,9 +137,7 @@ export async function extractAllPackageFiles(
};
}
dep.registryUrls = [
...new Set([...packageRegistries, ...(dep.registryUrls ?? [])]),
];
dep.registryUrls = getRegistryUrlsForDep(packageRegistries, dep);
if (!dep.depType) {
dep.depType = key.startsWith('buildSrc')

View file

@ -276,7 +276,6 @@ export function parseCatalog(
depType: 'plugin',
depName,
packageName: `${depName}:${depName}.gradle.plugin`,
registryUrls: ['https://plugins.gradle.org/m2/'],
currentValue,
commitMessageTopic: `plugin ${pluginName}`,
managerData: { fileReplacePosition },

View file

@ -545,7 +545,7 @@ describe('modules/manager/gradle/parser', () => {
${'jcenter()'} | ${REGISTRY_URLS.jcenter}
`('$input', ({ input, output }) => {
const { urls } = parseGradle(input);
expect(urls).toStrictEqual([output].filter(Boolean));
expect(urls).toMatchObject([{ registryUrl: output }]);
});
});
@ -587,11 +587,60 @@ describe('modules/manager/gradle/parser', () => {
${''} | ${'maven { setUrl("foo", "bar") }'} | ${null}
${'base="https://foo.bar"'} | ${'publishing { repositories { maven("${base}/baz") } }'} | ${null}
`('$def | $input', ({ def, input, url }) => {
const expected = [url].filter(Boolean);
const expected = url ? [{ registryUrl: url }] : [];
const { urls } = parseGradle([def, input].join('\n'));
expect(urls).toStrictEqual(expected);
expect(urls).toMatchObject(expected);
});
});
it('pluginManagement', () => {
const input = codeBlock`
pluginManagement {
def fooVersion = "1.2.3"
repositories {
mavenLocal()
maven { url = "https://foo.bar/plugins" }
gradlePluginPortal()
}
plugins {
id("foo.bar") version "$fooVersion"
}
}
dependencyResolutionManagement {
repositories {
maven { url = "https://foo.bar/deps" }
mavenCentral()
}
}
`;
const { deps, urls } = parseGradle(input);
expect(deps).toMatchObject([
{
depType: 'plugin',
depName: 'foo.bar',
currentValue: '1.2.3',
},
]);
expect(urls).toMatchObject([
{
registryUrl: 'https://foo.bar/plugins',
scope: 'plugin',
},
{
registryUrl: REGISTRY_URLS.gradlePluginPortal,
scope: 'plugin',
},
{
registryUrl: 'https://foo.bar/deps',
scope: 'dep',
},
{
registryUrl: REGISTRY_URLS.mavenCentral,
scope: 'dep',
},
]);
});
});
describe('version catalog', () => {

View file

@ -10,6 +10,7 @@ import { qVersionCatalogs } from './parser/version-catalogs';
import type {
Ctx,
GradleManagerData,
PackageRegistry,
PackageVariables,
ParseGradleResult,
} from './types';
@ -26,7 +27,7 @@ export function parseGradle(
): ParseGradleResult {
let vars: PackageVariables = { ...initVars };
const deps: PackageDependency<GradleManagerData>[] = [];
const urls: string[] = [];
const urls: PackageRegistry[] = [];
const query = q.tree<Ctx>({
type: 'root-tree',

View file

@ -216,7 +216,6 @@ export function handlePlugin(ctx: Ctx): Ctx {
depType: 'plugin',
depName,
packageName,
registryUrls: [REGISTRY_URLS.gradlePluginPortal],
commitMessageTopic: `plugin ${depName}`,
currentValue: pluginVersion[0].value,
managerData: {
@ -246,11 +245,22 @@ export function handlePlugin(ctx: Ctx): Ctx {
return ctx;
}
function isPluginRegistry(ctx: Ctx): boolean {
if (ctx.tokenMap.registryScope) {
const registryScope = loadFromTokenMap(ctx, 'registryScope')[0].value;
return registryScope === 'pluginManagement';
}
return false;
}
export function handlePredefinedRegistryUrl(ctx: Ctx): Ctx {
const registryName = loadFromTokenMap(ctx, 'registryUrl')[0].value;
ctx.registryUrls.push(
REGISTRY_URLS[registryName as keyof typeof REGISTRY_URLS]
);
ctx.registryUrls.push({
registryUrl: REGISTRY_URLS[registryName as keyof typeof REGISTRY_URLS],
scope: isPluginRegistry(ctx) ? 'plugin' : 'dep',
});
return ctx;
}
@ -281,7 +291,10 @@ export function handleCustomRegistryUrl(ctx: Ctx): Ctx {
try {
const { host, protocol } = url.parse(registryUrl);
if (host && protocol) {
ctx.registryUrls.push(registryUrl);
ctx.registryUrls.push({
registryUrl,
scope: isPluginRegistry(ctx) ? 'plugin' : 'dep',
});
}
} catch (e) {
// no-op

View file

@ -1,6 +1,8 @@
import { query as q } from 'good-enough-parser';
import { regEx } from '../../../../util/regex';
import type { Ctx } from '../types';
import { qApplyFrom } from './apply-from';
import { qAssignments } from './assignments';
import {
REGISTRY_URLS,
cleanupTempVars,
@ -13,6 +15,7 @@ import {
handleCustomRegistryUrl,
handlePredefinedRegistryUrl,
} from './handlers';
import { qPlugins } from './plugins';
// mavenCentral()
// mavenCentral { ... }
@ -94,8 +97,38 @@ const qCustomRegistryUrl = q
.handler(handleCustomRegistryUrl)
.handler(cleanupTempVars);
const qPluginManagement = q.sym<Ctx>('pluginManagement', storeVarToken).tree({
type: 'wrapped-tree',
startsWith: '{',
endsWith: '}',
preHandler: (ctx) => {
ctx.tmpTokenStore.registryScope = ctx.varTokens;
ctx.varTokens = [];
return ctx;
},
search: q
.handler<Ctx>((ctx) => {
if (ctx.tmpTokenStore.registryScope) {
ctx.tokenMap.registryScope = ctx.tmpTokenStore.registryScope;
}
return ctx;
})
.alt(
qAssignments,
qApplyFrom,
qPlugins,
qPredefinedRegistries,
qCustomRegistryUrl
),
postHandler: (ctx) => {
delete ctx.tmpTokenStore.registryScope;
return ctx;
},
});
export const qRegistryUrls = q.alt<Ctx>(
q.sym<Ctx>('publishing').tree(),
qPluginManagement,
qPredefinedRegistries,
qCustomRegistryUrl
);

View file

@ -16,7 +16,7 @@ export type VariableRegistry = Record<string, PackageVariables>;
export interface ParseGradleResult {
deps: PackageDependency<GradleManagerData>[];
urls: string[];
urls: PackageRegistry[];
vars: PackageVariables;
}
@ -67,6 +67,11 @@ export interface RichVersion {
export type GradleVersionPointerTarget = string | RichVersion;
export type GradleVersionCatalogVersion = string | VersionPointer | RichVersion;
export interface PackageRegistry {
registryUrl: string;
scope: 'dep' | 'plugin';
}
export interface Ctx {
readonly packageFile: string;
readonly fileContents: Record<string, string | null>;
@ -74,7 +79,7 @@ export interface Ctx {
globalVars: PackageVariables;
deps: PackageDependency<GradleManagerData>[];
registryUrls: string[];
registryUrls: PackageRegistry[];
varTokens: lexer.Token[];
tmpTokenStore: Record<string, lexer.Token[]>;