From 310d441728c3bcd0acaa697f0dd1ef669af8eaac Mon Sep 17 00:00:00 2001 From: Johannes Feichtner Date: Tue, 10 Sep 2024 23:56:22 +0200 Subject: [PATCH] feat(gradle-wrapper): add support for gradle daemon JVM discovery --- .../manager/gradle-wrapper/artifacts.ts | 3 +- .../manager/gradle-wrapper/util.spec.ts | 57 ++++++++++++++----- lib/modules/manager/gradle-wrapper/utils.ts | 45 ++++++++++++--- lib/modules/manager/gradle/artifacts.ts | 5 +- 4 files changed, 86 insertions(+), 24 deletions(-) diff --git a/lib/modules/manager/gradle-wrapper/artifacts.ts b/lib/modules/manager/gradle-wrapper/artifacts.ts index be7f31cf1ae1dd..b523ae69860ce0 100644 --- a/lib/modules/manager/gradle-wrapper/artifacts.ts +++ b/lib/modules/manager/gradle-wrapper/artifacts.ts @@ -177,7 +177,8 @@ export async function updateArtifacts({ { toolName: 'java', constraint: - config.constraints?.java ?? getJavaConstraint(config.currentValue), + config.constraints?.java ?? + (await getJavaConstraint(config.currentValue, gradlewFile)), }, ], }; diff --git a/lib/modules/manager/gradle-wrapper/util.spec.ts b/lib/modules/manager/gradle-wrapper/util.spec.ts index 9670f9de7b4212..5396da52fdf46e 100644 --- a/lib/modules/manager/gradle-wrapper/util.spec.ts +++ b/lib/modules/manager/gradle-wrapper/util.spec.ts @@ -1,10 +1,12 @@ import type { Stats } from 'node:fs'; import os from 'node:os'; +import { codeBlock } from 'common-tags'; import { fs, partial } from '../../../../test/util'; import { GlobalConfig } from '../../../config/global'; import { extractGradleVersion, getJavaConstraint, + getJvmConfiguration, gradleWrapperFileName, prepareGradleCommand, } from './utils'; @@ -16,34 +18,63 @@ describe('modules/manager/gradle-wrapper/util', () => { beforeEach(() => GlobalConfig.reset()); describe('getJavaConstraint()', () => { - it('return ^8.0.0 for global mode', () => { - expect(getJavaConstraint('4')).toBe('^8.0.0'); + it('return ^8.0.0 for global mode', async () => { + expect(await getJavaConstraint('4', '')).toBe('^8.0.0'); }); - it('return ^11.0.0 for docker mode and undefined gradle', () => { + it('return ^11.0.0 for docker mode and undefined gradle', async () => { GlobalConfig.set({ binarySource: 'docker' }); - expect(getJavaConstraint('')).toBe('^11.0.0'); + expect(await getJavaConstraint('', '')).toBe('^11.0.0'); }); - it('return ^8.0.0 for docker gradle < 5', () => { + it('return ^8.0.0 for docker gradle < 5', async () => { GlobalConfig.set({ binarySource: 'docker' }); - expect(getJavaConstraint('4.9')).toBe('^8.0.0'); + expect(await getJavaConstraint('4.9', '')).toBe('^8.0.0'); }); - it('return ^11.0.0 for docker gradle >=5 && <7', () => { + it('return ^11.0.0 for docker gradle >=5 && <7', async () => { GlobalConfig.set({ binarySource: 'docker' }); - expect(getJavaConstraint('6.0')).toBe('^11.0.0'); + expect(await getJavaConstraint('6.0', '')).toBe('^11.0.0'); }); - it('return ^16.0.0 for docker gradle >= 7', () => { + it('return ^16.0.0 for docker gradle >= 7', async () => { GlobalConfig.set({ binarySource: 'docker' }); - expect(getJavaConstraint('7.0.1')).toBe('^16.0.0'); + expect(await getJavaConstraint('7.0.1', '')).toBe('^16.0.0'); }); - it('return ^17.0.0 for docker gradle >= 7.3', () => { + it('return ^17.0.0 for docker gradle >= 7.3', async () => { GlobalConfig.set({ binarySource: 'docker' }); - expect(getJavaConstraint('7.3.0')).toBe('^17.0.0'); - expect(getJavaConstraint('8.0.1')).toBe('^17.0.0'); + expect(await getJavaConstraint('7.3.0', '')).toBe('^17.0.0'); + expect(await getJavaConstraint('8.0.1', '')).toBe('^17.0.0'); + }); + + it('returns toolChainVersion constraint if daemon JVM configured', async () => { + const daemonJvm = codeBlock` + #This file is generated by updateDaemonJvm + toolchainVersion=999 + `; + fs.readLocalFile.mockResolvedValue(daemonJvm); + expect(await getJavaConstraint('8.8', './gradlew')).toBe('^999.0.0'); + }); + }); + + describe('getJvmConfiguration', () => { + it('extracts toolChainVersion value', async () => { + const daemonJvm = codeBlock` + #This file is generated by updateDaemonJvm + toolchainVersion=21 + `; + fs.readLocalFile.mockResolvedValue(daemonJvm); + expect(await getJvmConfiguration('')).toBe('21'); + }); + + it('returns null if gradle-daemon-jvm.properties file not found', async () => { + fs.readLocalFile.mockResolvedValueOnce(null); + expect(await getJvmConfiguration('sub/gradlew')).toBeNull(); + expect(fs.readLocalFile).toHaveBeenCalledWith( + 'sub/gradle/gradle-daemon-jvm.properties', + 'utf8', + ); }); }); diff --git a/lib/modules/manager/gradle-wrapper/utils.ts b/lib/modules/manager/gradle-wrapper/utils.ts index 713a6f8b265e3f..dec99fc1fd660d 100644 --- a/lib/modules/manager/gradle-wrapper/utils.ts +++ b/lib/modules/manager/gradle-wrapper/utils.ts @@ -1,7 +1,8 @@ import os from 'node:os'; +import { dirname, join } from 'upath'; import { GlobalConfig } from '../../../config/global'; import { logger } from '../../../logger'; -import { chmodLocalFile, statLocalFile } from '../../../util/fs'; +import { chmodLocalFile, readLocalFile, statLocalFile } from '../../../util/fs'; import { newlineRegex, regEx } from '../../../util/regex'; import gradleVersioning from '../../versioning/gradle'; import type { GradleVersionExtract } from './types'; @@ -37,17 +38,20 @@ export async function prepareGradleCommand( return null; } -/** - * Find compatible java version for gradle. - * see https://docs.gradle.org/current/userguide/compatibility.html - * @param gradleVersion current gradle version - * @returns A Java semver range - */ -export function getJavaConstraint( +export async function getJavaConstraint( gradleVersion: string | null | undefined, -): string { + gradlewFile: string, +): Promise { const major = gradleVersion ? gradleVersioning.getMajor(gradleVersion) : null; const minor = gradleVersion ? gradleVersioning.getMinor(gradleVersion) : null; + + if (major && major >= 8 && minor && minor >= 8) { + const toolChainVersion = await getJvmConfiguration(gradlewFile); + if (toolChainVersion) { + return `^${toolChainVersion}.0.0`; + } + } + if (major && (major > 7 || (major >= 7 && minor && minor >= 3))) { return '^17.0.0'; } @@ -61,6 +65,29 @@ export function getJavaConstraint( return '^11.0.0'; } +// https://docs.gradle.org/current/userguide/gradle_daemon.html#sec:daemon_jvm_criteria +export async function getJvmConfiguration( + gradlewFile: string, +): Promise { + const daemonJvmFile = join( + dirname(gradlewFile), + 'gradle/gradle-daemon-jvm.properties', + ); + const daemonJvm = await readLocalFile(daemonJvmFile, 'utf8'); + if (daemonJvm) { + const TOOLCHAIN_VERSION_REGEX = regEx( + '^(?:toolchainVersion=)(?\\d+)$', + 'm', + ); + const toolChainMatch = TOOLCHAIN_VERSION_REGEX.exec(daemonJvm); + if (toolChainMatch?.groups) { + return toolChainMatch.groups.version; + } + } + + return null; +} + // https://regex101.com/r/IcOs7P/1 const DISTRIBUTION_URL_REGEX = regEx( '^(?:distributionUrl\\s*=\\s*)(?\\S*-(?\\d+\\.\\d+(?:\\.\\d+)?(?:-\\w+)*)-(?bin|all)\\.zip)\\s*$', diff --git a/lib/modules/manager/gradle/artifacts.ts b/lib/modules/manager/gradle/artifacts.ts index 1a1f3cd1938a7e..bf73a81e7b08b2 100644 --- a/lib/modules/manager/gradle/artifacts.ts +++ b/lib/modules/manager/gradle/artifacts.ts @@ -188,7 +188,10 @@ export async function updateArtifacts({ toolName: 'java', constraint: config.constraints?.java ?? - getJavaConstraint(await getGradleVersion(gradlewFile)), + (await getJavaConstraint( + await getGradleVersion(gradlewFile), + gradlewFile, + )), }, ], };