diff --git a/.github/workflows/integ-test-detect-java-toolchains.yml b/.github/workflows/integ-test-detect-java-toolchains.yml
index 1d0efc1..8277d20 100644
--- a/.github/workflows/integ-test-detect-java-toolchains.yml
+++ b/.github/workflows/integ-test-detect-java-toolchains.yml
@@ -21,6 +21,7 @@ jobs:
# Test that pre-installed runner JDKs are detected
pre-installed-toolchains:
strategy:
+ fail-fast: false
matrix:
os: ${{fromJSON(inputs.runner-os)}}
runs-on: ${{ matrix.os }}
@@ -35,7 +36,7 @@ jobs:
shell: bash
working-directory: .github/workflow-samples/groovy-dsl
run: |
- gradle -q javaToolchains > output.txt
+ gradle --info javaToolchains > output.txt
cat output.txt
- name: Verify detected toolchains
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 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 21' output.txt || (echo "::error::Did not detect preinstalled JDK 21" && exit 1)
# Test that JDKs provisioned by setup-java are detected
setup-java-installed-toolchain:
strategy:
+ fail-fast: false
matrix:
os: ${{fromJSON(inputs.runner-os)}}
runs-on: ${{ matrix.os }}
@@ -72,42 +75,19 @@ jobs:
shell: bash
working-directory: .github/workflow-samples/groovy-dsl
run: |
- gradle -q javaToolchains > output.txt
+ gradle --info javaToolchains > output.txt
cat output.txt
- - name: Verify detected toolchains
+ - name: Verify setup JDKs are detected
shell: bash
working-directory: .github/workflow-samples/groovy-dsl
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 20' output.txt || (echo "::error::Did not detect setup-java installed JDK 20" && exit 1)
-
- # 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
+ - name: Verify pre-installed toolchains are detected
shell: bash
+ working-directory: .github/workflow-samples/groovy-dsl
run: |
- mkdir -p ~/.gradle
- echo "org.gradle.java.installations.fromEnv=XXXXX" > ~/.gradle/gradle.properties
- - name: Setup Gradle
- uses: ./
- - 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
+ 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 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)
diff --git a/src/cache-base.ts b/src/cache-base.ts
index d1a4093..d63ee3f 100644
--- a/src/cache-base.ts
+++ b/src/cache-base.ts
@@ -17,23 +17,18 @@ export class GradleStateCache {
private cacheName: string
private cacheDescription: string
+ protected readonly userHome: string
protected readonly gradleUserHome: string
- constructor(gradleUserHome: string) {
+ constructor(userHome: string, gradleUserHome: string) {
+ this.userHome = userHome
this.gradleUserHome = gradleUserHome
this.cacheName = 'gradle'
this.cacheDescription = 'Gradle User Home'
}
init(): void {
- // Copy init-scripts to Gradle User Home
- 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)
+ this.initializeGradleUserHome()
// Export the GRADLE_ENCRYPTION_KEY variable if provided
const encryptionKey = params.getCacheEncryptionKey()
@@ -188,8 +183,21 @@ export class GradleStateCache {
return path.resolve(this.gradleUserHome, rawPath)
}
- private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
- // Copy init scripts from src/resources
+ private initializeGradleUserHome(): void {
+ // 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 = [
'gradle-build-action.build-result-capture.init.gradle',
'gradle-build-action.build-result-capture-service.plugin.groovy',
@@ -198,15 +206,36 @@ export class GradleStateCache {
'gradle-build-action.inject-gradle-enterprise.init.gradle'
]
for (const initScriptFilename of initScriptFilenames) {
- const initScriptContent = this.readInitScriptAsString(initScriptFilename)
+ const initScriptContent = this.readResourceFileAsString('init-scripts', initScriptFilename)
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
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('').pop()!
+ const mergedContent = existingToolchainContent.replace('', 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
- const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', 'init-scripts', resource)
+ const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', ...paths)
return fs.readFileSync(absolutePath, 'utf8')
}
diff --git a/src/caches.ts b/src/caches.ts
index f2b7d82..2980444 100644
--- a/src/caches.ts
+++ b/src/caches.ts
@@ -13,7 +13,7 @@ import {CacheCleaner} from './cache-cleaner'
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
-export async function restore(gradleUserHome: string, cacheListener: CacheListener): Promise {
+export async function restore(userHome: string, gradleUserHome: string, cacheListener: CacheListener): Promise {
// Bypass restore cache on all but first action step in workflow.
if (process.env[CACHE_RESTORED_VAR]) {
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)
- const gradleStateCache = new GradleStateCache(gradleUserHome)
+ const gradleStateCache = new GradleStateCache(userHome, gradleUserHome)
if (isCacheDisabled()) {
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(
+ userHome: string,
gradleUserHome: string,
cacheListener: CacheListener,
daemonController: DaemonController
@@ -98,6 +99,6 @@ export async function save(
}
await core.group('Caching Gradle state', async () => {
- return new GradleStateCache(gradleUserHome).save(cacheListener)
+ return new GradleStateCache(userHome, gradleUserHome).save(cacheListener)
})
}
diff --git a/src/resources/toolchains.xml b/src/resources/toolchains.xml
new file mode 100644
index 0000000..f9ba374
--- /dev/null
+++ b/src/resources/toolchains.xml
@@ -0,0 +1,44 @@
+
+
+
+
+ jdk
+
+ 8
+ Eclipse Temurin
+
+
+ ${env.JAVA_HOME_8_X64}
+
+
+
+ jdk
+
+ 11
+ Eclipse Temurin
+
+
+ ${env.JAVA_HOME_11_X64}
+
+
+
+ jdk
+
+ 17
+ Eclipse Temurin
+
+
+ ${env.JAVA_HOME_17_X64}
+
+
+
+ jdk
+
+ 21
+ Eclipse Temurin
+
+
+ ${env.JAVA_HOME_21_X64}
+
+
+
diff --git a/src/setup-gradle.ts b/src/setup-gradle.ts
index 84ecce7..b1f1c93 100644
--- a/src/setup-gradle.ts
+++ b/src/setup-gradle.ts
@@ -13,10 +13,12 @@ import {CacheListener} from './cache-reporting'
import {DaemonController} from './daemon-controller'
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
+const USER_HOME = 'USER_HOME'
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
const CACHE_LISTENER = 'CACHE_LISTENER'
export async function setup(): Promise {
+ const userHome = await determineUserHome()
const gradleUserHome = await determineGradleUserHome()
// Bypass setup on all but first action step in workflow.
@@ -29,11 +31,12 @@ export async function setup(): Promise {
// Record setup complete: visible in post-action, to control action completion
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)
const cacheListener = new CacheListener()
- await caches.restore(gradleUserHome, cacheListener)
+ await caches.restore(userHome, gradleUserHome, cacheListener)
core.saveState(CACHE_LISTENER, cacheListener.stringify())
@@ -49,11 +52,12 @@ export async function complete(): Promise {
const buildResults = loadBuildResults()
+ const userHome = core.getState(USER_HOME)
const gradleUserHome = core.getState(GRADLE_USER_HOME)
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
const daemonController = new DaemonController(buildResults)
- await caches.save(gradleUserHome, cacheListener, daemonController)
+ await caches.save(userHome, gradleUserHome, cacheListener, daemonController)
await jobSummary.generateJobSummary(buildResults, cacheListener)