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: [ deps: [
{ {
depType: 'plugin', depType: 'plugin',
registryUrls: [ registryUrls: ['https://plugins.gradle.org/m2/'],
'https://repo.maven.apache.org/maven2',
'https://example.com',
'https://plugins.gradle.org/m2/',
],
}, },
{ {
registryUrls: [ 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', () => { describe('version catalogs', () => {

View file

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

View file

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

View file

@ -545,7 +545,7 @@ describe('modules/manager/gradle/parser', () => {
${'jcenter()'} | ${REGISTRY_URLS.jcenter} ${'jcenter()'} | ${REGISTRY_URLS.jcenter}
`('$input', ({ input, output }) => { `('$input', ({ input, output }) => {
const { urls } = parseGradle(input); 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} ${''} | ${'maven { setUrl("foo", "bar") }'} | ${null}
${'base="https://foo.bar"'} | ${'publishing { repositories { maven("${base}/baz") } }'} | ${null} ${'base="https://foo.bar"'} | ${'publishing { repositories { maven("${base}/baz") } }'} | ${null}
`('$def | $input', ({ def, input, url }) => { `('$def | $input', ({ def, input, url }) => {
const expected = [url].filter(Boolean); const expected = url ? [{ registryUrl: url }] : [];
const { urls } = parseGradle([def, input].join('\n')); 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', () => { describe('version catalog', () => {

View file

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

View file

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

View file

@ -1,6 +1,8 @@
import { query as q } from 'good-enough-parser'; import { query as q } from 'good-enough-parser';
import { regEx } from '../../../../util/regex'; import { regEx } from '../../../../util/regex';
import type { Ctx } from '../types'; import type { Ctx } from '../types';
import { qApplyFrom } from './apply-from';
import { qAssignments } from './assignments';
import { import {
REGISTRY_URLS, REGISTRY_URLS,
cleanupTempVars, cleanupTempVars,
@ -13,6 +15,7 @@ import {
handleCustomRegistryUrl, handleCustomRegistryUrl,
handlePredefinedRegistryUrl, handlePredefinedRegistryUrl,
} from './handlers'; } from './handlers';
import { qPlugins } from './plugins';
// mavenCentral() // mavenCentral()
// mavenCentral { ... } // mavenCentral { ... }
@ -94,8 +97,38 @@ const qCustomRegistryUrl = q
.handler(handleCustomRegistryUrl) .handler(handleCustomRegistryUrl)
.handler(cleanupTempVars); .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>( export const qRegistryUrls = q.alt<Ctx>(
q.sym<Ctx>('publishing').tree(), q.sym<Ctx>('publishing').tree(),
qPluginManagement,
qPredefinedRegistries, qPredefinedRegistries,
qCustomRegistryUrl qCustomRegistryUrl
); );

View file

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