mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-11-22 08:11:07 -05:00
Register pre-installed JDKs in .m2/toolchains.xml
Since adding these to the `org.gradle.java.installations.fromEnv` property is problematic (#1024), this mechanism allows the default toolchains to be discovered by Gradle via a different mechanism. The default JDK installations are added to `~/.m2/toolchains.xml` such that they are discoverable by Gradle toolchain support. The `setup-java` action also writes to this file, so we merge with any existing content: this allows both pre-installed and "setup" JDKs to be automatically detected by Gradle.
This commit is contained in:
parent
7c57ba1136
commit
d4e24dfc10
5 changed files with 110 additions and 52 deletions
|
@ -21,6 +21,7 @@ jobs:
|
||||||
# Test that pre-installed runner JDKs are detected
|
# Test that pre-installed runner JDKs are detected
|
||||||
pre-installed-toolchains:
|
pre-installed-toolchains:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: ${{fromJSON(inputs.runner-os)}}
|
os: ${{fromJSON(inputs.runner-os)}}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
@ -35,7 +36,7 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: |
|
run: |
|
||||||
gradle -q javaToolchains > output.txt
|
gradle --info javaToolchains > output.txt
|
||||||
cat output.txt
|
cat output.txt
|
||||||
- name: Verify detected toolchains
|
- name: Verify detected toolchains
|
||||||
shell: bash
|
shell: bash
|
||||||
|
@ -44,10 +45,12 @@ jobs:
|
||||||
grep -q 'Eclipse Temurin JDK 1.8' output.txt || (echo "::error::Did not detect preinstalled JDK 1.8" && exit 1)
|
grep -q 'Eclipse Temurin JDK 1.8' output.txt || (echo "::error::Did not detect preinstalled JDK 1.8" && exit 1)
|
||||||
grep -q 'Eclipse Temurin JDK 11' output.txt || (echo "::error::Did not detect preinstalled JDK 11" && exit 1)
|
grep -q 'Eclipse Temurin JDK 11' output.txt || (echo "::error::Did not detect preinstalled JDK 11" && exit 1)
|
||||||
grep -q 'Eclipse Temurin JDK 17' output.txt || (echo "::error::Did not detect preinstalled JDK 17" && exit 1)
|
grep -q 'Eclipse Temurin JDK 17' output.txt || (echo "::error::Did not detect preinstalled JDK 17" && exit 1)
|
||||||
|
grep -q 'Eclipse Temurin JDK 21' output.txt || (echo "::error::Did not detect preinstalled JDK 21" && exit 1)
|
||||||
|
|
||||||
# Test that JDKs provisioned by setup-java are detected
|
# Test that JDKs provisioned by setup-java are detected
|
||||||
setup-java-installed-toolchain:
|
setup-java-installed-toolchain:
|
||||||
strategy:
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: ${{fromJSON(inputs.runner-os)}}
|
os: ${{fromJSON(inputs.runner-os)}}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
@ -72,42 +75,19 @@ jobs:
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: |
|
run: |
|
||||||
gradle -q javaToolchains > output.txt
|
gradle --info javaToolchains > output.txt
|
||||||
cat output.txt
|
cat output.txt
|
||||||
- name: Verify detected toolchains
|
- name: Verify setup JDKs are detected
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: |
|
run: |
|
||||||
grep -q 'Eclipse Temurin JDK 16' output.txt || (echo "::error::Did not detect setup-java installed JDK 16" && exit 1)
|
grep -q 'Eclipse Temurin JDK 16' output.txt || (echo "::error::Did not detect setup-java installed JDK 16" && exit 1)
|
||||||
grep -q 'Eclipse Temurin JDK 20' output.txt || (echo "::error::Did not detect setup-java installed JDK 20" && exit 1)
|
grep -q 'Eclipse Temurin JDK 20' output.txt || (echo "::error::Did not detect setup-java installed JDK 20" && exit 1)
|
||||||
|
- name: Verify pre-installed toolchains are detected
|
||||||
# Test that predefined JDK detection property is not overwritten by action
|
|
||||||
check-no-overwrite:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: ${{fromJSON(inputs.runner-os)}}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
- name: Download distribution if required
|
|
||||||
uses: ./.github/actions/download-dist
|
|
||||||
- name: Configure java installations env var in Gradle User Home
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ~/.gradle
|
grep -q 'Eclipse Temurin JDK 1.8' output.txt || (echo "::error::Did not detect preinstalled JDK 1.8" && exit 1)
|
||||||
echo "org.gradle.java.installations.fromEnv=XXXXX" > ~/.gradle/gradle.properties
|
grep -q 'Eclipse Temurin JDK 11' output.txt || (echo "::error::Did not detect preinstalled JDK 11" && exit 1)
|
||||||
- name: Setup Gradle
|
grep -q 'Eclipse Temurin JDK 17' output.txt || (echo "::error::Did not detect preinstalled JDK 17" && exit 1)
|
||||||
uses: ./
|
grep -q 'Eclipse Temurin JDK 21' output.txt || (echo "::error::Did not detect preinstalled JDK 21" && exit 1)
|
||||||
- name: Check gradle.properties
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cat ~/.gradle/gradle.properties
|
|
||||||
if grep -q 'org.gradle.java.installations.fromEnv=JAVA_HOME' ~/.gradle/gradle.properties ; then
|
|
||||||
echo 'Found overwritten fromEnv'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if ! grep -q 'org.gradle.java.installations.fromEnv=XXXXX' ~/.gradle/gradle.properties ; then
|
|
||||||
echo 'Did NOT find original fromEnv'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
|
@ -17,23 +17,18 @@ export class GradleStateCache {
|
||||||
private cacheName: string
|
private cacheName: string
|
||||||
private cacheDescription: string
|
private cacheDescription: string
|
||||||
|
|
||||||
|
protected readonly userHome: string
|
||||||
protected readonly gradleUserHome: string
|
protected readonly gradleUserHome: string
|
||||||
|
|
||||||
constructor(gradleUserHome: string) {
|
constructor(userHome: string, gradleUserHome: string) {
|
||||||
|
this.userHome = userHome
|
||||||
this.gradleUserHome = gradleUserHome
|
this.gradleUserHome = gradleUserHome
|
||||||
this.cacheName = 'gradle'
|
this.cacheName = 'gradle'
|
||||||
this.cacheDescription = 'Gradle User Home'
|
this.cacheDescription = 'Gradle User Home'
|
||||||
}
|
}
|
||||||
|
|
||||||
init(): void {
|
init(): void {
|
||||||
// Copy init-scripts to Gradle User Home
|
this.initializeGradleUserHome()
|
||||||
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
|
||||||
fs.mkdirSync(actionCacheDir, {recursive: true})
|
|
||||||
|
|
||||||
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
|
|
||||||
fs.mkdirSync(initScriptsDir, {recursive: true})
|
|
||||||
|
|
||||||
this.initializeGradleUserHome(this.gradleUserHome, initScriptsDir)
|
|
||||||
|
|
||||||
// Export the GRADLE_ENCRYPTION_KEY variable if provided
|
// Export the GRADLE_ENCRYPTION_KEY variable if provided
|
||||||
const encryptionKey = params.getCacheEncryptionKey()
|
const encryptionKey = params.getCacheEncryptionKey()
|
||||||
|
@ -188,8 +183,21 @@ export class GradleStateCache {
|
||||||
return path.resolve(this.gradleUserHome, rawPath)
|
return path.resolve(this.gradleUserHome, rawPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
|
private initializeGradleUserHome(): void {
|
||||||
// Copy init scripts from src/resources
|
// Create a directory for storing action metadata
|
||||||
|
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
||||||
|
fs.mkdirSync(actionCacheDir, {recursive: true})
|
||||||
|
|
||||||
|
this.copyInitScripts()
|
||||||
|
|
||||||
|
// Copy the default toolchain definitions to `~/.m2/toolchains.xml`
|
||||||
|
this.registerToolchains()
|
||||||
|
}
|
||||||
|
|
||||||
|
private copyInitScripts(): void {
|
||||||
|
// Copy init scripts from src/resources to Gradle UserHome
|
||||||
|
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
|
||||||
|
fs.mkdirSync(initScriptsDir, {recursive: true})
|
||||||
const initScriptFilenames = [
|
const initScriptFilenames = [
|
||||||
'gradle-build-action.build-result-capture.init.gradle',
|
'gradle-build-action.build-result-capture.init.gradle',
|
||||||
'gradle-build-action.build-result-capture-service.plugin.groovy',
|
'gradle-build-action.build-result-capture-service.plugin.groovy',
|
||||||
|
@ -198,15 +206,36 @@ export class GradleStateCache {
|
||||||
'gradle-build-action.inject-gradle-enterprise.init.gradle'
|
'gradle-build-action.inject-gradle-enterprise.init.gradle'
|
||||||
]
|
]
|
||||||
for (const initScriptFilename of initScriptFilenames) {
|
for (const initScriptFilename of initScriptFilenames) {
|
||||||
const initScriptContent = this.readInitScriptAsString(initScriptFilename)
|
const initScriptContent = this.readResourceFileAsString('init-scripts', initScriptFilename)
|
||||||
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
|
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
|
||||||
fs.writeFileSync(initScriptPath, initScriptContent)
|
fs.writeFileSync(initScriptPath, initScriptContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private readInitScriptAsString(resource: string): string {
|
private registerToolchains(): void {
|
||||||
|
const preInstalledToolchains = this.readResourceFileAsString('toolchains.xml')
|
||||||
|
const m2dir = path.resolve(this.userHome, '.m2')
|
||||||
|
const toolchainXmlTarget = path.resolve(m2dir, 'toolchains.xml')
|
||||||
|
if (!fs.existsSync(toolchainXmlTarget)) {
|
||||||
|
// Write a new toolchains.xml file if it doesn't exist
|
||||||
|
fs.mkdirSync(m2dir, {recursive: true})
|
||||||
|
fs.writeFileSync(toolchainXmlTarget, preInstalledToolchains)
|
||||||
|
|
||||||
|
core.info(`Wrote default JDK locations to ${toolchainXmlTarget}`)
|
||||||
|
} else {
|
||||||
|
// Merge into an existing toolchains.xml file
|
||||||
|
const existingToolchainContent = fs.readFileSync(toolchainXmlTarget, 'utf8')
|
||||||
|
const appendedContent = preInstalledToolchains.split('<toolchains>').pop()!
|
||||||
|
const mergedContent = existingToolchainContent.replace('</toolchains>', appendedContent)
|
||||||
|
|
||||||
|
fs.writeFileSync(toolchainXmlTarget, mergedContent)
|
||||||
|
core.info(`Merged default JDK locations into ${toolchainXmlTarget}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readResourceFileAsString(...paths: string[]): string {
|
||||||
// Resolving relative to __dirname will allow node to find the resource at runtime
|
// Resolving relative to __dirname will allow node to find the resource at runtime
|
||||||
const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', 'init-scripts', resource)
|
const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', ...paths)
|
||||||
return fs.readFileSync(absolutePath, 'utf8')
|
return fs.readFileSync(absolutePath, 'utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {CacheCleaner} from './cache-cleaner'
|
||||||
|
|
||||||
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
||||||
|
|
||||||
export async function restore(gradleUserHome: string, cacheListener: CacheListener): Promise<void> {
|
export async function restore(userHome: string, gradleUserHome: string, cacheListener: CacheListener): Promise<void> {
|
||||||
// Bypass restore cache on all but first action step in workflow.
|
// Bypass restore cache on all but first action step in workflow.
|
||||||
if (process.env[CACHE_RESTORED_VAR]) {
|
if (process.env[CACHE_RESTORED_VAR]) {
|
||||||
core.info('Cache only restored on first action step.')
|
core.info('Cache only restored on first action step.')
|
||||||
|
@ -21,7 +21,7 @@ export async function restore(gradleUserHome: string, cacheListener: CacheListen
|
||||||
}
|
}
|
||||||
core.exportVariable(CACHE_RESTORED_VAR, true)
|
core.exportVariable(CACHE_RESTORED_VAR, true)
|
||||||
|
|
||||||
const gradleStateCache = new GradleStateCache(gradleUserHome)
|
const gradleStateCache = new GradleStateCache(userHome, gradleUserHome)
|
||||||
|
|
||||||
if (isCacheDisabled()) {
|
if (isCacheDisabled()) {
|
||||||
core.info('Cache is disabled: will not restore state from previous builds.')
|
core.info('Cache is disabled: will not restore state from previous builds.')
|
||||||
|
@ -65,6 +65,7 @@ export async function restore(gradleUserHome: string, cacheListener: CacheListen
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(
|
export async function save(
|
||||||
|
userHome: string,
|
||||||
gradleUserHome: string,
|
gradleUserHome: string,
|
||||||
cacheListener: CacheListener,
|
cacheListener: CacheListener,
|
||||||
daemonController: DaemonController
|
daemonController: DaemonController
|
||||||
|
@ -98,6 +99,6 @@ export async function save(
|
||||||
}
|
}
|
||||||
|
|
||||||
await core.group('Caching Gradle state', async () => {
|
await core.group('Caching Gradle state', async () => {
|
||||||
return new GradleStateCache(gradleUserHome).save(cacheListener)
|
return new GradleStateCache(userHome, gradleUserHome).save(cacheListener)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
44
src/resources/toolchains.xml
Normal file
44
src/resources/toolchains.xml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<toolchains>
|
||||||
|
<!-- JDK Toolchains installed by default on GitHub-hosted runners -->
|
||||||
|
<toolchain>
|
||||||
|
<type>jdk</type>
|
||||||
|
<provides>
|
||||||
|
<version>8</version>
|
||||||
|
<vendor>Eclipse Temurin</vendor>
|
||||||
|
</provides>
|
||||||
|
<configuration>
|
||||||
|
<jdkHome>${env.JAVA_HOME_8_X64}</jdkHome>
|
||||||
|
</configuration>
|
||||||
|
</toolchain>
|
||||||
|
<toolchain>
|
||||||
|
<type>jdk</type>
|
||||||
|
<provides>
|
||||||
|
<version>11</version>
|
||||||
|
<vendor>Eclipse Temurin</vendor>
|
||||||
|
</provides>
|
||||||
|
<configuration>
|
||||||
|
<jdkHome>${env.JAVA_HOME_11_X64}</jdkHome>
|
||||||
|
</configuration>
|
||||||
|
</toolchain>
|
||||||
|
<toolchain>
|
||||||
|
<type>jdk</type>
|
||||||
|
<provides>
|
||||||
|
<version>17</version>
|
||||||
|
<vendor>Eclipse Temurin</vendor>
|
||||||
|
</provides>
|
||||||
|
<configuration>
|
||||||
|
<jdkHome>${env.JAVA_HOME_17_X64}</jdkHome>
|
||||||
|
</configuration>
|
||||||
|
</toolchain>
|
||||||
|
<toolchain>
|
||||||
|
<type>jdk</type>
|
||||||
|
<provides>
|
||||||
|
<version>21</version>
|
||||||
|
<vendor>Eclipse Temurin</vendor>
|
||||||
|
</provides>
|
||||||
|
<configuration>
|
||||||
|
<jdkHome>${env.JAVA_HOME_21_X64}</jdkHome>
|
||||||
|
</configuration>
|
||||||
|
</toolchain>
|
||||||
|
</toolchains>
|
|
@ -13,10 +13,12 @@ import {CacheListener} from './cache-reporting'
|
||||||
import {DaemonController} from './daemon-controller'
|
import {DaemonController} from './daemon-controller'
|
||||||
|
|
||||||
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
||||||
|
const USER_HOME = 'USER_HOME'
|
||||||
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
||||||
const CACHE_LISTENER = 'CACHE_LISTENER'
|
const CACHE_LISTENER = 'CACHE_LISTENER'
|
||||||
|
|
||||||
export async function setup(): Promise<void> {
|
export async function setup(): Promise<void> {
|
||||||
|
const userHome = await determineUserHome()
|
||||||
const gradleUserHome = await determineGradleUserHome()
|
const gradleUserHome = await determineGradleUserHome()
|
||||||
|
|
||||||
// Bypass setup on all but first action step in workflow.
|
// Bypass setup on all but first action step in workflow.
|
||||||
|
@ -29,11 +31,12 @@ export async function setup(): Promise<void> {
|
||||||
// Record setup complete: visible in post-action, to control action completion
|
// Record setup complete: visible in post-action, to control action completion
|
||||||
core.saveState(GRADLE_SETUP_VAR, true)
|
core.saveState(GRADLE_SETUP_VAR, true)
|
||||||
|
|
||||||
// Save the Gradle User Home for use in the post-action step.
|
// Save the User Home and Gradle User Home for use in the post-action step.
|
||||||
|
core.saveState(USER_HOME, userHome)
|
||||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||||
|
|
||||||
const cacheListener = new CacheListener()
|
const cacheListener = new CacheListener()
|
||||||
await caches.restore(gradleUserHome, cacheListener)
|
await caches.restore(userHome, gradleUserHome, cacheListener)
|
||||||
|
|
||||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||||
|
|
||||||
|
@ -49,11 +52,12 @@ export async function complete(): Promise<void> {
|
||||||
|
|
||||||
const buildResults = loadBuildResults()
|
const buildResults = loadBuildResults()
|
||||||
|
|
||||||
|
const userHome = core.getState(USER_HOME)
|
||||||
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
||||||
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
||||||
const daemonController = new DaemonController(buildResults)
|
const daemonController = new DaemonController(buildResults)
|
||||||
|
|
||||||
await caches.save(gradleUserHome, cacheListener, daemonController)
|
await caches.save(userHome, gradleUserHome, cacheListener, daemonController)
|
||||||
|
|
||||||
await jobSummary.generateJobSummary(buildResults, cacheListener)
|
await jobSummary.generateJobSummary(buildResults, cacheListener)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue