mirror of
https://github.com/gradle/gradle-build-action.git
synced 2025-10-20 15:18:56 +08:00
Compare commits
60 Commits
v2.10.0
...
v3-prerele
Author | SHA1 | Date | |
---|---|---|---|
|
6c7537229b | ||
|
650620f9f9 | ||
|
d4e24dfc10 | ||
|
7c57ba1136 | ||
|
32bab5b15a | ||
|
270f30ba56 | ||
|
c00a847f3f | ||
|
e2aa3f332c | ||
|
ff5d63e9dc | ||
|
4faac45dc8 | ||
|
45ed60450e | ||
|
72abd931ce | ||
|
a4107da76d | ||
|
d16a3f4093 | ||
|
333078158e | ||
|
34a07dced0 | ||
|
24e9e9dc6b | ||
|
bc72ac9e9d | ||
|
c791d32284 | ||
|
ceb0c736c1 | ||
|
3f2ca32cb0 | ||
|
5d2dd0dea4 | ||
|
e865911745 | ||
|
0538e78c32 | ||
|
a4dabb3a70 | ||
|
51b7a82e8e | ||
|
92cb3fe7e8 | ||
|
e843ea4565 | ||
|
14b4921945 | ||
|
9c7269b85b | ||
|
a7b743845f | ||
|
e6566cde89 | ||
|
89f8dcd819 | ||
|
93050d1483 | ||
|
df38ec05e0 | ||
|
b3f092e821 | ||
|
63ce84df08 | ||
|
57f3f23714 | ||
|
38785d7d62 | ||
|
a738af78ea | ||
|
ae24bf6608 | ||
|
334a4b8d4d | ||
|
009bd36b91 | ||
|
9d6738618d | ||
|
f053e6b7e7 | ||
|
c821b7c4f1 | ||
|
982da8e78c | ||
|
a0fc8606d2 | ||
|
a1980784de | ||
|
89e46180c6 | ||
|
f95e9c7459 | ||
|
8cbcb9948b | ||
|
a71aff6a12 | ||
|
77699bae74 | ||
|
dc5927259f | ||
|
4f0075d967 | ||
|
e1f9864a52 | ||
|
76d5a9b475 | ||
|
39d8c6d06e | ||
|
0280eb7de5 |
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "com.gradle.enterprise" version "3.15.1"
|
id "com.gradle.enterprise" version "3.16.1"
|
||||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12"
|
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@@ -8,7 +8,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api("org.apache.commons:commons-math3:3.6.1")
|
api("org.apache.commons:commons-math3:3.6.1")
|
||||||
implementation("com.google.guava:guava:32.1.3-jre")
|
implementation("com.google.guava:guava:33.0.0-jre")
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
|
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.gradle.enterprise") version "3.15.1"
|
id("com.gradle.enterprise") version "3.16.1"
|
||||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.12"
|
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.12.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "com.gradle.build-scan" version "3.15.1"
|
id "com.gradle.build-scan" version "3.16.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "com.gradle.enterprise" version "3.15.1"
|
id "com.gradle.enterprise" version "3.16.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
6
.github/workflows/ci-codeql.yml
vendored
6
.github/workflows/ci-codeql.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@@ -53,7 +53,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@@ -67,4 +67,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@v3
|
||||||
|
14
.github/workflows/ci-full-check.yml
vendored
14
.github/workflows/ci-full-check.yml
vendored
@@ -25,12 +25,14 @@ jobs:
|
|||||||
cache-key-prefix: ${{github.run_number}}-
|
cache-key-prefix: ${{github.run_number}}-
|
||||||
|
|
||||||
caching-config:
|
caching-config:
|
||||||
uses: ./.github/workflows/integ-test-action-inputs-caching.yml
|
uses: ./.github/workflows/integ-test-caching-config.yml
|
||||||
with:
|
with:
|
||||||
cache-key-prefix: ${{github.run_number}}-
|
cache-key-prefix: ${{github.run_number}}-
|
||||||
|
|
||||||
dependency-graph:
|
dependency-graph:
|
||||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
with:
|
with:
|
||||||
cache-key-prefix: ${{github.run_number}}-
|
cache-key-prefix: ${{github.run_number}}-
|
||||||
|
|
||||||
@@ -56,10 +58,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache-key-prefix: ${{github.run_number}}-
|
cache-key-prefix: ${{github.run_number}}-
|
||||||
|
|
||||||
# restore-configuration-cache:
|
restore-configuration-cache:
|
||||||
# uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||||
# with:
|
with:
|
||||||
# cache-key-prefix: ${{github.run_number}}-
|
cache-key-prefix: ${{github.run_number}}-
|
||||||
|
secrets:
|
||||||
|
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
|
||||||
restore-custom-gradle-home:
|
restore-custom-gradle-home:
|
||||||
uses: ./.github/workflows/integ-test-restore-custom-gradle-home.yml
|
uses: ./.github/workflows/integ-test-restore-custom-gradle-home.yml
|
||||||
|
4
.github/workflows/ci-init-script-check.yml
vendored
4
.github/workflows/ci-init-script-check.yml
vendored
@@ -15,12 +15,12 @@ jobs:
|
|||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 8
|
java-version: 8
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2.9.0 # Use a released version to avoid breakages
|
uses: gradle/gradle-build-action@v2.11.0 # Use a released version to avoid breakages
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
working-directory: test/init-scripts
|
working-directory: test/init-scripts
|
||||||
run: ./gradlew check
|
run: ./gradlew check
|
||||||
|
20
.github/workflows/ci-quick-check.yml
vendored
20
.github/workflows/ci-quick-check.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
- name: Configure Gradle as default for unit test
|
- name: Configure Gradle as default for unit test
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
gradle-version: 8.4
|
gradle-version: 8.5
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: |
|
run: |
|
||||||
npm install
|
npm install
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
|
|
||||||
caching-config:
|
caching-config:
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
uses: ./.github/workflows/integ-test-action-inputs-caching.yml
|
uses: ./.github/workflows/integ-test-caching-config.yml
|
||||||
with:
|
with:
|
||||||
runner-os: '["ubuntu-latest"]'
|
runner-os: '["ubuntu-latest"]'
|
||||||
download-dist: true
|
download-dist: true
|
||||||
@@ -53,6 +53,8 @@ jobs:
|
|||||||
dependency-graph:
|
dependency-graph:
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
with:
|
with:
|
||||||
runner-os: '["ubuntu-latest"]'
|
runner-os: '["ubuntu-latest"]'
|
||||||
download-dist: true
|
download-dist: true
|
||||||
@@ -87,12 +89,14 @@ jobs:
|
|||||||
runner-os: '["ubuntu-latest"]'
|
runner-os: '["ubuntu-latest"]'
|
||||||
download-dist: true
|
download-dist: true
|
||||||
|
|
||||||
# restore-configuration-cache:
|
restore-configuration-cache:
|
||||||
# needs: build-distribution
|
needs: build-distribution
|
||||||
# uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||||
# with:
|
with:
|
||||||
# runner-os: '["ubuntu-latest"]'
|
runner-os: '["ubuntu-latest"]'
|
||||||
# download-dist: true
|
download-dist: true
|
||||||
|
secrets:
|
||||||
|
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
|
||||||
restore-containerized-gradle-home:
|
restore-containerized-gradle-home:
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
|
2
.github/workflows/ci-verify-outputs.yml
vendored
2
.github/workflows/ci-verify-outputs.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
|||||||
id: diff
|
id: diff
|
||||||
|
|
||||||
# If index.js was different than expected, upload the expected version as an artifact
|
# If index.js was different than expected, upload the expected version as an artifact
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
|
23
.github/workflows/demo-job-summary.yml
vendored
23
.github/workflows/demo-job-summary.yml
vendored
@@ -8,7 +8,7 @@ env:
|
|||||||
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
run-gradle-builds:
|
many-gradle-builds:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
@@ -42,6 +42,27 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: ./gradlew not-a-real-task
|
run: ./gradlew not-a-real-task
|
||||||
|
|
||||||
|
successful-builds-with-no-summary:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Build distribution
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
add-job-summary: on-failure
|
||||||
|
- name: Build kotlin-dsl project
|
||||||
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
|
run: ./gradlew assemble
|
||||||
|
- name: Build kotlin-dsl project without Build Scan®
|
||||||
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
|
run: ./gradlew assemble check --no-scan
|
||||||
|
|
||||||
pre-existing-gradle-home:
|
pre-existing-gradle-home:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
46
.github/workflows/demo-pr-build-scan-comment.yml
vendored
46
.github/workflows/demo-pr-build-scan-comment.yml
vendored
@@ -2,26 +2,50 @@ name: Demo adding Build Scan® comment to PR
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [assigned, review_requested]
|
types: [assigned, review_requested]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
gradle:
|
successful-build-with-always-comment:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout project sources
|
- name: Checkout project sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: ./
|
uses: ./
|
||||||
|
with:
|
||||||
|
add-job-summary-as-pr-comment: always
|
||||||
- name: Run build with Gradle wrapper
|
- name: Run build with Gradle wrapper
|
||||||
id: gradle
|
id: gradle
|
||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew build --scan
|
run: ./gradlew build --scan
|
||||||
- name: "Add Build Scan URL as PR comment"
|
|
||||||
uses: actions/github-script@v7
|
successful-build-with-comment-on-failure:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout project sources
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
add-job-summary-as-pr-comment: on-failure
|
||||||
script: |
|
- name: Run build with Gradle wrapper
|
||||||
github.rest.issues.createComment({
|
id: gradle
|
||||||
issue_number: context.issue.number,
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
owner: context.repo.owner,
|
run: ./gradlew build --scan
|
||||||
repo: context.repo.repo,
|
|
||||||
body: 'PR ready for review: ${{ steps.gradle.outputs.build-scan-url }}'
|
failing-build-with-comment-on-failure:
|
||||||
})
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout project sources
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Setup Gradle
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
add-job-summary-as-pr-comment: on-failure
|
||||||
|
- name: Run build with Gradle wrapper
|
||||||
|
id: gradle
|
||||||
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
|
run: ./gradlew no-a-real-task --scan
|
||||||
|
continue-on-error: true
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
name: Test action inputs for caching
|
name: Test caching configuration
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_call:
|
workflow_call:
|
||||||
@@ -38,7 +38,8 @@ jobs:
|
|||||||
enterprise
|
enterprise
|
||||||
# Exclude build-cache from main cache entry
|
# Exclude build-cache from main cache entry
|
||||||
gradle-home-cache-excludes: |
|
gradle-home-cache-excludes: |
|
||||||
caches/build-cache-1
|
caches/build-cache-*
|
||||||
|
caches/*/executionHistory
|
||||||
- name: Build using Gradle wrapper
|
- name: Build using Gradle wrapper
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: ./gradlew test
|
run: ./gradlew test
|
||||||
@@ -63,7 +64,8 @@ jobs:
|
|||||||
caches
|
caches
|
||||||
enterprise
|
enterprise
|
||||||
gradle-home-cache-excludes: |
|
gradle-home-cache-excludes: |
|
||||||
caches/build-cache-1
|
caches/build-cache-*
|
||||||
|
caches/*/executionHistory
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
- name: Execute Gradle build with --offline
|
- name: Execute Gradle build with --offline
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
@@ -12,6 +12,9 @@ on:
|
|||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
||||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}
|
||||||
@@ -31,7 +34,7 @@ jobs:
|
|||||||
- name: Setup Gradle for dependency-graph generate
|
- name: Setup Gradle for dependency-graph generate
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
dependency-graph: generate
|
dependency-graph: generate-and-upload
|
||||||
- name: Run gradle build
|
- name: Run gradle build
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
@@ -55,7 +58,7 @@ jobs:
|
|||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
|
|
||||||
submit:
|
submit:
|
||||||
needs: [groovy-generate, kotlin-generate]
|
needs: [groovy-generate]
|
||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
@@ -80,7 +83,7 @@ jobs:
|
|||||||
- name: Setup Gradle for dependency-graph generate
|
- name: Setup Gradle for dependency-graph generate
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
dependency-graph: generate
|
dependency-graph: generate-and-submit
|
||||||
- id: gradle-assemble
|
- id: gradle-assemble
|
||||||
run: ./gradlew assemble
|
run: ./gradlew assemble
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
|
@@ -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 }}
|
||||||
@@ -57,12 +60,12 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java 20
|
- name: Setup Java 20
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '20'
|
java-version: '20'
|
||||||
- name: Setup Java 16
|
- name: Setup Java 16
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: '16'
|
java-version: '16'
|
||||||
@@ -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
|
|
||||||
|
2
.github/workflows/integ-test-execution.yml
vendored
2
.github/workflows/integ-test-execution.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 8
|
java-version: 8
|
||||||
|
@@ -21,8 +21,8 @@ env:
|
|||||||
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
||||||
GRADLE_ENTERPRISE_URL: https://ge.solutions-team.gradle.com
|
GRADLE_ENTERPRISE_URL: https://ge.solutions-team.gradle.com
|
||||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15.1
|
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1
|
||||||
GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.12
|
GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.12.1
|
||||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
|
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -38,7 +38,7 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 8
|
java-version: 8
|
||||||
|
@@ -86,7 +86,7 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 8
|
java-version: 8
|
||||||
|
@@ -11,6 +11,9 @@ on:
|
|||||||
download-dist:
|
download-dist:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
|
secrets:
|
||||||
|
GRADLE_ENCRYPTION_KEY:
|
||||||
|
required: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
||||||
@@ -34,9 +37,11 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Groovy build with configuration-cache enabled
|
- name: Groovy build with configuration-cache enabled
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: gradle test --configuration-cache
|
||||||
|
|
||||||
verify-build-groovy:
|
verify-build-groovy:
|
||||||
env:
|
env:
|
||||||
@@ -55,10 +60,12 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Groovy build with configuration-cache enabled
|
- name: Groovy build with configuration-cache enabled
|
||||||
id: execute
|
id: execute
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: gradle test --configuration-cache
|
||||||
- name: Check that configuration-cache was used
|
- name: Check that configuration-cache was used
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
@@ -88,9 +95,11 @@ jobs:
|
|||||||
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
|
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
|
||||||
with:
|
with:
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Check execute Gradle build with configuration cache enabled (but not restored)
|
- name: Check execute Gradle build with configuration cache enabled (but not restored)
|
||||||
working-directory: .github/workflow-samples/groovy-dsl
|
working-directory: .github/workflow-samples/groovy-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: gradle test --configuration-cache
|
||||||
|
|
||||||
seed-build-kotlin:
|
seed-build-kotlin:
|
||||||
env:
|
env:
|
||||||
@@ -108,9 +117,11 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Execute 'help' with configuration-cache enabled
|
- name: Execute 'help' with configuration-cache enabled
|
||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew help --configuration-cache
|
run: gradle help --configuration-cache
|
||||||
|
|
||||||
modify-build-kotlin:
|
modify-build-kotlin:
|
||||||
env:
|
env:
|
||||||
@@ -129,9 +140,11 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Execute 'test' with configuration-cache enabled
|
- name: Execute 'test' with configuration-cache enabled
|
||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: gradle test --configuration-cache
|
||||||
|
|
||||||
# Test restore configuration-cache from the third build invocation
|
# Test restore configuration-cache from the third build invocation
|
||||||
verify-build-kotlin:
|
verify-build-kotlin:
|
||||||
@@ -151,10 +164,12 @@ jobs:
|
|||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
- name: Execute 'test' again with configuration-cache enabled
|
- name: Execute 'test' again with configuration-cache enabled
|
||||||
id: execute
|
id: execute
|
||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew test --configuration-cache
|
run: gradle test --configuration-cache
|
||||||
- name: Check that configuration-cache was used
|
- name: Check that configuration-cache was used
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
|
@@ -24,7 +24,7 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
@@ -47,7 +47,7 @@ jobs:
|
|||||||
- name: Download distribution if required
|
- name: Download distribution if required
|
||||||
uses: ./.github/actions/download-dist
|
uses: ./.github/actions/download-dist
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
# Configuration file for asdf version manager
|
# Configuration file for asdf version manager
|
||||||
nodejs 16.18.1
|
nodejs 20.10.0
|
||||||
gradle 8.4
|
gradle 8.5
|
||||||
|
260
README.md
260
README.md
@@ -4,7 +4,7 @@ This GitHub Action can be used to configure Gradle and optionally execute a Grad
|
|||||||
|
|
||||||
## Why use the `gradle-build-action`?
|
## Why use the `gradle-build-action`?
|
||||||
|
|
||||||
It is possible to directly invoke Gradle in your workflow, and the `actions/setup-java@v3` action provides a simple way to cache Gradle dependencies.
|
It is possible to directly invoke Gradle in your workflow, and the `actions/setup-java@v4` action provides a simple way to cache Gradle dependencies.
|
||||||
|
|
||||||
However, the `gradle-build-action` offers a number of advantages over this approach:
|
However, the `gradle-build-action` offers a number of advantages over this approach:
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
@@ -85,7 +85,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
@@ -97,7 +97,6 @@ jobs:
|
|||||||
- run: echo "The release-candidate version was ${{ steps.setup-gradle.outputs.gradle-version }}"
|
- run: echo "The release-candidate version was ${{ steps.setup-gradle.outputs.gradle-version }}"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Caching build state between Jobs
|
## Caching build state between Jobs
|
||||||
|
|
||||||
The `gradle-build-action` will use the GitHub Actions cache to save and restore reusable state that may be speed up a subsequent build invocation. This includes most content that is downloaded from the internet as part of a build, as well as expensive to create content like compiled build scripts, transformed Jar files, etc.
|
The `gradle-build-action` will use the GitHub Actions cache to save and restore reusable state that may be speed up a subsequent build invocation. This includes most content that is downloaded from the internet as part of a build, as well as expensive to create content like compiled build scripts, transformed Jar files, etc.
|
||||||
@@ -161,6 +160,31 @@ If you want override the default and have the `gradle-build-action` caches overw
|
|||||||
cache-overwrite-existing: true
|
cache-overwrite-existing: true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Saving configuration-cache data
|
||||||
|
|
||||||
|
When Gradle is executed with the [configuration-cache](https://docs.gradle.org/current/userguide/configuration_cache.html) enabled, the configuration-cache data is stored
|
||||||
|
in the project directory, at `<project-dir>/.gradle/configuration-cache`. Due to the way the configuration-cache works, [this file may contain stored credentials and other
|
||||||
|
secrets](https://docs.gradle.org/release-nightly/userguide/configuration_cache.html#config_cache:secrets), and this data needs to be encrypted in order to be safely stored in the GitHub Actions cache.
|
||||||
|
|
||||||
|
In order to benefit from configuration caching in your GitHub Actions workflow, you must:
|
||||||
|
- Execute your build with Gradle 8.6 or newer. This can be achieved directly, or via the Gradle Wrapper.
|
||||||
|
- Enable the configuration cache for your build.
|
||||||
|
- Generate a [valid Gradle encryption key](https://docs.gradle.org/8.6-rc-1/userguide/configuration_cache.html#config_cache:secrets:configuring_encryption_key) and save it as a [GitHub Actions secret](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions).
|
||||||
|
- Provide the secret key via the `cache-encryption-key` action parameter.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
gradle-with-configuration-cache:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: gradle/gradle-build-action@v3
|
||||||
|
with:
|
||||||
|
gradle-version: 8.6-rc-1
|
||||||
|
cache-encryption-key: ${{ secrets.GradleEncryptionKey }}
|
||||||
|
- run: gradle build --configuration-cache
|
||||||
|
```
|
||||||
|
|
||||||
### Incompatibility with other caching mechanisms
|
### Incompatibility with other caching mechanisms
|
||||||
|
|
||||||
When using `gradle-build-action` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
When using `gradle-build-action` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
||||||
@@ -235,9 +259,9 @@ This allows the most recent state to always be available in the GitHub actions c
|
|||||||
### Finding a matching cache entry
|
### Finding a matching cache entry
|
||||||
|
|
||||||
In most cases, no exact match will exist for the cache key. Instead, the Gradle User Home will be restored for the closest matching cache entry, using a set of "restore keys". The entries will be matched with the following precedence:
|
In most cases, no exact match will exist for the cache key. Instead, the Gradle User Home will be restored for the closest matching cache entry, using a set of "restore keys". The entries will be matched with the following precedence:
|
||||||
- An exact match on OS, workflow, job, matrix and Git SHA
|
- An exact match on OS, workflow name, job id, matrix and Git SHA
|
||||||
- The most recent entry saved for the same OS, workflow, job and matrix values
|
- The most recent entry saved for the same OS, workflow name, job id and matrix values
|
||||||
- The most recent entry saved for the same OS, workflow and job
|
- The most recent entry saved for the same OS and job id
|
||||||
- The most recent entry saved for the same OS
|
- The most recent entry saved for the same OS
|
||||||
|
|
||||||
Due to branch scoping of cache entries, the above match will be first performed for entries from the same branch, and then for the default ('main') branch.
|
Due to branch scoping of cache entries, the above match will be first performed for entries from the same branch, and then for the default ('main') branch.
|
||||||
@@ -344,62 +368,57 @@ gradle-home-cache-cleanup: true
|
|||||||
|
|
||||||
## Build reporting
|
## Build reporting
|
||||||
|
|
||||||
The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, and reports these via
|
The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, including the root project,
|
||||||
a Job Summary, visible in the GitHub Actions UI. For each Gradle execution, details about the invocation are listed, together with
|
requested tasks, build outcome and any Build Scan link generated. Details of cache entries read and written are also collected.
|
||||||
a link to any Build Scan® published.
|
These details are compiled into a Job Summary, which is visible in the GitHub Actions UI.
|
||||||
|
|
||||||
Generation of a Job Summary is enabled by default. If this is not desired, it can be disable as follows:
|
Generation of a Job Summary is enabled by default for all Jobs using the `gradle-build-action`. This feature can be configured
|
||||||
|
so that a Job Summary is never generated, or so that a Job Summary is only generated on build failure:
|
||||||
```yaml
|
```yaml
|
||||||
generate-job-summary: false
|
add-job-summary: 'on-failure' # Valid values are 'always' (default), 'never', and 'on-failure'
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the action collects information about Gradle invocations via an [Initialization Script](https://docs.gradle.org/current/userguide/init_scripts.html#sec:using_an_init_script)
|
### Adding Job Summary as a Pull Request comment
|
||||||
located at `USER_HOME/.gradle/init.d/build-result-capture.init.gradle`.
|
|
||||||
If you are using init scripts for the [Gradle Enterprise Gradle Plugin](https://plugins.gradle.org/plugin/com.gradle.enterprise) like
|
|
||||||
[`scans-init.gradle` or `gradle-enterprise-init.gradle`](https://docs.gradle.com/enterprise/gradle-plugin/#scans_gradle_com),
|
|
||||||
you'll need to ensure these files are applied prior to `build-result-capture.init.gradle`.
|
|
||||||
Since Gradle applies init scripts in alphabetical order, one way to ensure this is via file naming.
|
|
||||||
|
|
||||||
### Build Scan® link as Step output
|
It is sometimes more convenient to view the results of a GitHub Actions Job directly from the Pull Request that triggered
|
||||||
|
the Job. For this purpose you can configure the action so that Job Summary data is added as a Pull Request comment.
|
||||||
|
|
||||||
As well as reporting the [Build Scan](https://gradle.com/build-scans/) link in the Job Summary,
|
|
||||||
the `gradle-build-action` action makes this link available as a Step output named `build-scan-url`.
|
|
||||||
|
|
||||||
You can then use that link in subsequent actions of your workflow. For example:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# .github/workflows/gradle-build-pr.yml
|
name: CI
|
||||||
name: Run Gradle on PRs
|
on:
|
||||||
on: pull_request
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
gradle:
|
run-gradle-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout project sources
|
- name: Checkout project sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v3
|
||||||
- name: Run build with Gradle wrapper
|
|
||||||
id: gradle
|
|
||||||
run: ./gradlew build --scan
|
|
||||||
- name: "Add Build Scan URL as PR comment"
|
|
||||||
uses: actions/github-script@v5
|
|
||||||
if: github.event_name == 'pull_request' && failure()
|
|
||||||
with:
|
with:
|
||||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
add-job-summary-as-pr-comment: on-failure # Valid values are 'never' (default), 'always', and 'on-failure'
|
||||||
script: |
|
- run: ./gradlew build --scan
|
||||||
github.rest.issues.createComment({
|
|
||||||
issue_number: context.issue.number,
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
body: '❌ ${{ github.workflow }} failed: ${{ steps.gradle.outputs.build-scan-url }}'
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Saving build outputs
|
Note that in order to add a Pull Request comment, the workflow must be configured with the `pull-requests: write` permission.
|
||||||
|
|
||||||
By default, a GitHub Actions workflow using `gradle-build-action` will record the log output and any Build Scan links for your build,
|
|
||||||
but any output files generated by the build will not be saved.
|
### Build Scan® link as Step output
|
||||||
|
|
||||||
|
As well as reporting all [Build Scan](https://gradle.com/build-scans/) links in the Job Summary,
|
||||||
|
the `gradle-build-action` action makes this link available an an output of any Step that executes Gradle.
|
||||||
|
|
||||||
|
The output name is `build-scan-url`. You can then use the build scan link in subsequent actions of your workflow.
|
||||||
|
|
||||||
|
### Saving arbitrary build outputs
|
||||||
|
|
||||||
|
By default, a GitHub Actions workflow using `gradle-build-action` will record the log output and any Build Scan
|
||||||
|
links for your build, but any output files generated by the build will not be saved.
|
||||||
|
|
||||||
To save selected files from your build execution, you can use the core [Upload-Artifact](https://github.com/actions/upload-artifact) action.
|
To save selected files from your build execution, you can use the core [Upload-Artifact](https://github.com/actions/upload-artifact) action.
|
||||||
For example:
|
For example:
|
||||||
@@ -417,107 +436,19 @@ jobs:
|
|||||||
run: ./gradlew build --scan
|
run: ./gradlew build --scan
|
||||||
- name: Upload build reports
|
- name: Upload build reports
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: build-reports
|
name: build-reports
|
||||||
path: build/reports/
|
path: build/reports/
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use the action to invoke Gradle
|
### Use of custom init-scripts in Gradle User Home
|
||||||
|
|
||||||
If the `gradle-build-action` is configured with an `arguments` input, then Gradle will execute a Gradle build with the arguments provided. NOTE: We recommend using the `gradle-build-action` as a "Setup Gradle" step as described above, with Gradle being invoked via a regular `run` command.
|
Note that the action collects information about Gradle invocations via an [Initialization Script](https://docs.gradle.org/current/userguide/init_scripts.html#sec:using_an_init_script)
|
||||||
|
located at `USER_HOME/.gradle/init.d/gradle-build-action.build-result-capture.init.gradle`.
|
||||||
|
|
||||||
If no `arguments` are provided, the action will not execute Gradle, but will still cache Gradle state and configure build-scan capture for all subsequent Gradle executions.
|
If you are adding any custom init scripts to the `USER_HOME/.gradle/init.d` directory, it may be necessary to ensure these files are applied prior to `gradle-build-action.build-result-capture.init.gradle`.
|
||||||
|
Since Gradle applies init scripts in alphabetical order, one way to ensure this is via file naming.
|
||||||
```yaml
|
|
||||||
name: Run Gradle on PRs
|
|
||||||
on: pull_request
|
|
||||||
jobs:
|
|
||||||
gradle:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: temurin
|
|
||||||
java-version: 11
|
|
||||||
|
|
||||||
- name: Setup and execute Gradle 'test' task
|
|
||||||
uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: test
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multiple Gradle executions in the same Job
|
|
||||||
|
|
||||||
It is possible to configure multiple Gradle executions to run sequentially in the same job.
|
|
||||||
The initial Action step will perform the Gradle setup.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: assemble
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: check
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gradle command-line arguments
|
|
||||||
|
|
||||||
The `arguments` input can be used to pass arbitrary arguments to the `gradle` command line.
|
|
||||||
Arguments can be supplied in a single line, or as a multi-line input.
|
|
||||||
|
|
||||||
Here are some valid examples:
|
|
||||||
```yaml
|
|
||||||
arguments: build
|
|
||||||
arguments: check --scan
|
|
||||||
arguments: some arbitrary tasks
|
|
||||||
arguments: build -PgradleProperty=foo
|
|
||||||
arguments: |
|
|
||||||
build
|
|
||||||
--scan
|
|
||||||
-PgradleProperty=foo
|
|
||||||
-DsystemProperty=bar
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need to pass environment variables, use the GitHub Actions workflow syntax:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
env:
|
|
||||||
CI: true
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Gradle build located in a subdirectory
|
|
||||||
|
|
||||||
By default, the action will execute Gradle in the root directory of your project.
|
|
||||||
Use the `build-root-directory` input to target a Gradle build in a subdirectory.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
build-root-directory: some/subdirectory
|
|
||||||
```
|
|
||||||
|
|
||||||
### Using a specific Gradle executable
|
|
||||||
|
|
||||||
The action will first look for a Gradle wrapper script in the root directory of your project.
|
|
||||||
If not found, `gradle` will be executed from the PATH.
|
|
||||||
Use the `gradle-executable` input to execute using a specific Gradle installation.
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: gradle/gradle-build-action@v2
|
|
||||||
with:
|
|
||||||
arguments: build
|
|
||||||
gradle-executable: /path/to/installed/gradle
|
|
||||||
```
|
|
||||||
|
|
||||||
This mechanism can also be used to target a Gradle wrapper script that is located in a non-default location.
|
|
||||||
|
|
||||||
## Support for GitHub Enterprise Server (GHES)
|
## Support for GitHub Enterprise Server (GHES)
|
||||||
|
|
||||||
@@ -546,9 +477,9 @@ You enable GitHub Dependency Graph support by setting the `dependency-graph` act
|
|||||||
| `generate-and-submit` | As per `generate`, but any generated dependency graph snapshots will be submitted at the end of the job. |
|
| `generate-and-submit` | As per `generate`, but any generated dependency graph snapshots will be submitted at the end of the job. |
|
||||||
| `download-and-submit` | Download any previously saved dependency graph snapshots, submitting them via the Dependency Submission API. This can be useful to collect all snapshots in a matrix of builds and submit them in one step. |
|
| `download-and-submit` | Download any previously saved dependency graph snapshots, submitting them via the Dependency Submission API. This can be useful to collect all snapshots in a matrix of builds and submit them in one step. |
|
||||||
|
|
||||||
Example of a simple workflow that generates and submits a dependency graph:
|
Example of a CI workflow that generates and submits a dependency graph:
|
||||||
```yaml
|
```yaml
|
||||||
name: Submit dependency graph
|
name: CI build
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
@@ -564,11 +495,12 @@ jobs:
|
|||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
dependency-graph: generate-and-submit
|
dependency-graph: generate-and-submit
|
||||||
- name: Run a build and generate the dependency graph which will be submitted post-job
|
- name: Run the usual CI build (dependency-graph will be generated and submitted post-job)
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
```
|
```
|
||||||
|
|
||||||
The `contents: write` permission is not required to generate the dependency graph, but is required in order to submit the graph via the GitHub API. This permission will need to be explicitly enabled in the workflow file for dependency graph submission to succeed.
|
The `contents: write` permission is required in order to submit (but not generate) the dependency graph file.
|
||||||
|
Depending on [repository settings](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#permissions-for-the-github_token), this permission may be available by default or may need to be explicitly enabled in the workflow file (as above).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> [!IMPORTANT]
|
||||||
> The above configuration will work for workflows that run as a result of commits to a repository branch,
|
> The above configuration will work for workflows that run as a result of commits to a repository branch,
|
||||||
@@ -577,6 +509,27 @@ The `contents: write` permission is not required to generate the dependency grap
|
|||||||
> for a PR submitted from a forked repository.
|
> for a PR submitted from a forked repository.
|
||||||
> For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows).
|
> For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows).
|
||||||
|
|
||||||
|
### Using a custom plugin repository
|
||||||
|
|
||||||
|
By default, the action downloads the `github-dependency-graph-gradle-plugin` from the Gradle Plugin Portal (https://plugins.gradle.org). If your GitHub Actions environment does not have access to this URL, you can specify a custom plugin repository to use.
|
||||||
|
Do so by setting the `GRADLE_PLUGIN_REPOSITORY_URL` environment variable with your Gradle invocation.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Gradle to generate and submit dependency graphs
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
dependency-graph: generate-and-submit
|
||||||
|
- name: Run a build, resolving the 'dependency-graph' plugin from the plugin portal proxy
|
||||||
|
run: ./gradlew build
|
||||||
|
env:
|
||||||
|
GRADLE_PLUGIN_REPOSITORY_URL: "https://gradle-plugins-proxy.mycorp.com"
|
||||||
|
```
|
||||||
|
|
||||||
### Integrating the `dependency-review-action`
|
### Integrating the `dependency-review-action`
|
||||||
|
|
||||||
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
|
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
|
||||||
@@ -683,7 +636,7 @@ jobs:
|
|||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
dependency-graph: generate-and-submit
|
dependency-graph: generate-and-submit
|
||||||
- name: Run a build, generating the dependency graph from 'runtimeClasspath' configurations
|
- name: Run a build, generating the dependency graph from any resolved 'runtimeClasspath' configurations
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
env:
|
env:
|
||||||
DEPENDENCY_GRAPH_INCLUDE_PROJECTS: "^:(?!buildSrc).*"
|
DEPENDENCY_GRAPH_INCLUDE_PROJECTS: "^:(?!buildSrc).*"
|
||||||
@@ -692,10 +645,10 @@ jobs:
|
|||||||
|
|
||||||
### Use a dedicated workflow for dependency graph generation
|
### Use a dedicated workflow for dependency graph generation
|
||||||
|
|
||||||
Instead of generating a dependency graph from your existing CI workflow, it's possible to create a separate dedicated workflow (or Job) that is solely intended for generating a dependency graph.
|
Instead of generating a dependency graph from your existing CI workflow, it's possible to create a separate dedicated workflow (or Job) that is intended for generating a dependency graph.
|
||||||
Such a workflow will still need to execute Gradle, but can do so in a way that is targeted at resolving exactly the dependencies required.
|
Such a workflow will still need to execute Gradle, but can do so in a way that is targeted at resolving the specific dependencies required.
|
||||||
|
|
||||||
For example, the following workflow will report only those dependencies that are part of the `runtimeClasspath` or the `my-app` project.
|
For example, the following workflow will report those dependencies that are resolved in order to build the `distributionZip` for the `my-app` project. Test dependencies and other dependencies not required by the `distributionZip` will not be included.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
jobs:
|
jobs:
|
||||||
@@ -707,11 +660,12 @@ jobs:
|
|||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
dependency-graph: generate-and-submit
|
dependency-graph: generate-and-submit
|
||||||
- name: Extract the 'runtimeClasspath' dependencies for 'my-app'
|
- name: Build the distribution Zip for `my-app`
|
||||||
run: ./gradlew :my-app:dependencies --configuration runtimeClasspath
|
run: ./gradlew :my-app:distributionZip
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that the above example will also include `buildSrc` dependencies, since these are resolved as part of running the `dependencies` task.
|
Note that the above example will also include any `buildSrc` dependencies, dependencies resolved when configuring your Gradle build or dependencies resolved while applying plugin. All of these dependencies are resolved in the process of running the `distributionZip` task, and thus will form part of the generated dependency graph.
|
||||||
|
|
||||||
If this isn't desirable, you will still need to use the filtering mechanism described above.
|
If this isn't desirable, you will still need to use the filtering mechanism described above.
|
||||||
|
|
||||||
## Dependency Graphs for pull request workflows
|
## Dependency Graphs for pull request workflows
|
||||||
@@ -824,6 +778,8 @@ To reduce storage costs for these artifacts, you can set the `artifact-retention
|
|||||||
artifact-retention-days: 1
|
artifact-retention-days: 1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Gradle Enterprise plugin injection
|
# Gradle Enterprise plugin injection
|
||||||
|
|
||||||
The `gradle-build-action` provides support for injecting and configuring the Gradle Enterprise Gradle plugin into any Gradle build, without any modification to the project sources.
|
The `gradle-build-action` provides support for injecting and configuring the Gradle Enterprise Gradle plugin into any Gradle build, without any modification to the project sources.
|
||||||
@@ -843,7 +799,7 @@ name: Run build with Gradle Enterprise injection
|
|||||||
env:
|
env:
|
||||||
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
||||||
GRADLE_ENTERPRISE_URL: https://ge.gradle.org
|
GRADLE_ENTERPRISE_URL: https://ge.gradle.org
|
||||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15.1
|
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1
|
||||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_KEY }} # Required to publish scans to ge.gradle.org
|
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_KEY }} # Required to publish scans to ge.gradle.org
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -857,7 +813,7 @@ jobs:
|
|||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
```
|
```
|
||||||
|
|
||||||
This configuration will automatically apply `v3.15.1` of the [Gradle Enterprise Gradle plugin](https://docs.gradle.com/enterprise/gradle-plugin/), and publish build scans to https://ge.gradle.org.
|
This configuration will automatically apply `v3.16.1` of the [Gradle Enterprise Gradle plugin](https://docs.gradle.com/enterprise/gradle-plugin/), and publish build scans to https://ge.gradle.org.
|
||||||
|
|
||||||
Note that the `ge.gradle.org` server requires authentication in order to publish scans. The provided `GRADLE_ENTERPRISE_ACCESS_KEY` isn't required by the Gradle Enterprise injection script,
|
Note that the `ge.gradle.org` server requires authentication in order to publish scans. The provided `GRADLE_ENTERPRISE_ACCESS_KEY` isn't required by the Gradle Enterprise injection script,
|
||||||
but will be used by the GE plugin in order to authenticate with the server.
|
but will be used by the GE plugin in order to authenticate with the server.
|
||||||
|
53
action.yml
53
action.yml
@@ -8,14 +8,6 @@ inputs:
|
|||||||
description: Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle.
|
description: Gradle version to use. If specified, this Gradle version will be downloaded, added to the PATH and used for invoking Gradle.
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
gradle-executable:
|
|
||||||
description: Path to the Gradle executable. If specified, this executable will be added to the PATH and used for invoking Gradle.
|
|
||||||
required: false
|
|
||||||
|
|
||||||
build-root-directory:
|
|
||||||
description: Path to the root directory of the build. Default is the root of the GitHub workspace.
|
|
||||||
required: false
|
|
||||||
|
|
||||||
cache-disabled:
|
cache-disabled:
|
||||||
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
||||||
required: false
|
required: false
|
||||||
@@ -40,6 +32,13 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
|
cache-encryption-key:
|
||||||
|
description: |
|
||||||
|
A base64 encoded AES key used to encrypt the configuration-cache data. The key is exported as 'GRADLE_ENCRYPTION_KEY' for later steps.
|
||||||
|
A suitable key can be generated with `openssl rand -base64 16`.
|
||||||
|
Configuration-cache data will not be saved/restored without an encryption key being provided.
|
||||||
|
required: false
|
||||||
|
|
||||||
gradle-home-cache-includes:
|
gradle-home-cache-includes:
|
||||||
description: Paths within Gradle User Home to cache.
|
description: Paths within Gradle User Home to cache.
|
||||||
required: false
|
required: false
|
||||||
@@ -59,17 +58,18 @@ inputs:
|
|||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
arguments:
|
add-job-summary:
|
||||||
description: Gradle command line arguments (supports multi-line input)
|
description: Specifies when a Job Summary should be inluded in the action results. Valid values are 'never', 'always' (default), and 'on-failure'.
|
||||||
required: false
|
required: false
|
||||||
|
default: 'always'
|
||||||
|
|
||||||
generate-job-summary:
|
add-job-summary-as-pr-comment:
|
||||||
description: When 'false', no Job Summary will be generated for the Job.
|
description: Specifies when each Job Summary should be added as a PR comment. Valid values are 'never' (default), 'always', and 'on-failure'. No action will be taken if the workflow was not triggered from a pull request.
|
||||||
required: false
|
required: false
|
||||||
default: true
|
default: 'never'
|
||||||
|
|
||||||
dependency-graph:
|
dependency-graph:
|
||||||
description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit' and 'download-and-submit'.
|
description: Specifies if a GitHub dependency snapshot should be generated for each Gradle build, and if so, how. Valid values are 'disabled' (default), 'generate', 'generate-and-submit', 'generate-and-upload' and 'download-and-submit'.
|
||||||
required: false
|
required: false
|
||||||
default: 'disabled'
|
default: 'disabled'
|
||||||
|
|
||||||
@@ -77,6 +77,28 @@ inputs:
|
|||||||
description: Specifies the number of days to retain any artifacts generated by the action. If not set, the default retention settings for the repository will apply.
|
description: Specifies the number of days to retain any artifacts generated by the action. If not set, the default retention settings for the repository will apply.
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
|
# DEPRECATED ACTION INPUTS
|
||||||
|
arguments:
|
||||||
|
description: Gradle command line arguments (supports multi-line input)
|
||||||
|
required: false
|
||||||
|
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
|
||||||
|
|
||||||
|
build-root-directory:
|
||||||
|
description: Path to the root directory of the build. Default is the root of the GitHub workspace.
|
||||||
|
required: false
|
||||||
|
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
|
||||||
|
|
||||||
|
gradle-executable:
|
||||||
|
description: Path to the Gradle executable. If specified, this executable will be added to the PATH and used for invoking Gradle.
|
||||||
|
required: false
|
||||||
|
deprecation-message: Using the action to execute Gradle directly is deprecated in favor of using the action to setup Gradle, and executing Gradle in a subsequent Step. See https://github.com/gradle/gradle-build-action?tab=readme-ov-file#use-the-action-to-setup-gradle.
|
||||||
|
|
||||||
|
generate-job-summary:
|
||||||
|
description: When 'false', no Job Summary will be generated for the Job.
|
||||||
|
required: false
|
||||||
|
default: true
|
||||||
|
deprecation-message: Superceded by the new 'add-job-summary' and 'add-job-summary-as-pr-comment' parameters.
|
||||||
|
|
||||||
# EXPERIMENTAL & INTERNAL ACTION INPUTS
|
# EXPERIMENTAL & INTERNAL ACTION INPUTS
|
||||||
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
||||||
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
|
# These properties are experimental and not (yet) designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
|
||||||
@@ -85,6 +107,7 @@ inputs:
|
|||||||
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
workflow-job-context:
|
workflow-job-context:
|
||||||
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
|
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
|
||||||
required: false
|
required: false
|
||||||
@@ -104,7 +127,7 @@ outputs:
|
|||||||
description: Version of Gradle that was setup by the action
|
description: Version of Gradle that was setup by the action
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: 'node16'
|
using: 'node20'
|
||||||
main: 'dist/main/index.js'
|
main: 'dist/main/index.js'
|
||||||
post: 'dist/post/index.js'
|
post: 'dist/post/index.js'
|
||||||
|
|
||||||
|
60602
dist/main/index.js
vendored
60602
dist/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main/index.js.map
vendored
2
dist/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
59757
dist/post/index.js
vendored
59757
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js.map
vendored
2
dist/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
3302
package-lock.json
generated
3302
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -8,11 +8,9 @@
|
|||||||
"format": "prettier --write **/*.ts",
|
"format": "prettier --write **/*.ts",
|
||||||
"format-check": "prettier --check **/*.ts",
|
"format-check": "prettier --check **/*.ts",
|
||||||
"lint": "eslint src/**/*.ts",
|
"lint": "eslint src/**/*.ts",
|
||||||
|
|
||||||
"compile-main": "ncc build src/main.ts --out dist/main --source-map --no-source-map-register",
|
"compile-main": "ncc build src/main.ts --out dist/main --source-map --no-source-map-register",
|
||||||
"compile-post": "ncc build src/post.ts --out dist/post --source-map --no-source-map-register",
|
"compile-post": "ncc build src/post.ts --out dist/post --source-map --no-source-map-register",
|
||||||
"compile": "npm run compile-main && npm run compile-post",
|
"compile": "npm run compile-main && npm run compile-post",
|
||||||
|
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"check": "npm run format && npm run lint",
|
"check": "npm run format && npm run lint",
|
||||||
"build": "npm run check && npm run compile",
|
"build": "npm run check && npm run compile",
|
||||||
@@ -30,33 +28,34 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "1.1.2",
|
"@actions/artifact": "2.0.0",
|
||||||
"@actions/cache": "3.2.2",
|
"@actions/cache": "3.2.2",
|
||||||
"@actions/core": "1.10.1",
|
"@actions/core": "1.10.1",
|
||||||
"@actions/exec": "1.1.1",
|
"@actions/exec": "1.1.1",
|
||||||
"@actions/github": "5.1.1",
|
"@actions/github": "6.0.0",
|
||||||
"@actions/glob": "0.4.0",
|
"@actions/glob": "0.4.0",
|
||||||
"@actions/http-client": "2.2.0",
|
"@actions/http-client": "2.2.0",
|
||||||
"@actions/tool-cache": "2.0.1",
|
"@actions/tool-cache": "2.0.1",
|
||||||
"@octokit/rest": "19.0.13",
|
"@octokit/rest": "19.0.13",
|
||||||
"@octokit/webhooks-types": "7.3.1",
|
"@octokit/webhooks-types": "7.3.1",
|
||||||
|
"semver": "7.5.4",
|
||||||
"string-argv": "0.3.2"
|
"string-argv": "0.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "16.18.38",
|
"@types/jest": "29.5.11",
|
||||||
"@types/jest": "29.5.8",
|
"@types/node": "20.10.0",
|
||||||
"@types/unzipper": "0.10.9",
|
"@types/unzipper": "0.10.9",
|
||||||
"@typescript-eslint/parser": "6.12.0",
|
"@typescript-eslint/parser": "6.17.0",
|
||||||
"@vercel/ncc": "0.38.1",
|
"@vercel/ncc": "0.38.1",
|
||||||
"eslint": "8.54.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-plugin-github": "4.10.1",
|
"eslint-plugin-github": "4.10.1",
|
||||||
"eslint-plugin-jest": "27.6.0",
|
"eslint-plugin-jest": "27.6.1",
|
||||||
"eslint-plugin-prettier": "5.0.1",
|
"eslint-plugin-prettier": "5.1.2",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"js-yaml": "4.1.0",
|
"js-yaml": "4.1.0",
|
||||||
"patch-package": "8.0.0",
|
"patch-package": "8.0.0",
|
||||||
"prettier": "3.1.0",
|
"prettier": "3.1.1",
|
||||||
"ts-jest": "29.1.1",
|
"ts-jest": "29.1.1",
|
||||||
"typescript": "5.3.2"
|
"typescript": "5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,13 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
|
import * as glob from '@actions/glob'
|
||||||
|
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import * as params from './input-params'
|
import * as params from './input-params'
|
||||||
import {CacheListener} from './cache-reporting'
|
import {CacheListener} from './cache-reporting'
|
||||||
import {saveCache, restoreCache, cacheDebug, isCacheDebuggingEnabled, tryDelete, generateCacheKey} from './cache-utils'
|
import {saveCache, restoreCache, cacheDebug, isCacheDebuggingEnabled, tryDelete, generateCacheKey} from './cache-utils'
|
||||||
import {GradleHomeEntryExtractor} from './cache-extract-entries'
|
import {GradleHomeEntryExtractor, ConfigurationCacheEntryExtractor} from './cache-extract-entries'
|
||||||
|
|
||||||
const RESTORED_CACHE_KEY_KEY = 'restored-cache-key'
|
const RESTORED_CACHE_KEY_KEY = 'restored-cache-key'
|
||||||
|
|
||||||
@@ -15,22 +17,24 @@ 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 {
|
||||||
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
this.initializeGradleUserHome()
|
||||||
fs.mkdirSync(actionCacheDir, {recursive: true})
|
|
||||||
|
|
||||||
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
|
// Export the GRADLE_ENCRYPTION_KEY variable if provided
|
||||||
fs.mkdirSync(initScriptsDir, {recursive: true})
|
const encryptionKey = params.getCacheEncryptionKey()
|
||||||
|
if (encryptionKey) {
|
||||||
this.initializeGradleUserHome(this.gradleUserHome, initScriptsDir)
|
core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheOutputExists(): boolean {
|
cacheOutputExists(): boolean {
|
||||||
@@ -79,7 +83,7 @@ export class GradleStateCache {
|
|||||||
async afterRestore(listener: CacheListener): Promise<void> {
|
async afterRestore(listener: CacheListener): Promise<void> {
|
||||||
await this.debugReportGradleUserHomeSize('as restored from cache')
|
await this.debugReportGradleUserHomeSize('as restored from cache')
|
||||||
await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener)
|
await new GradleHomeEntryExtractor(this.gradleUserHome).restore(listener)
|
||||||
// await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener)
|
await new ConfigurationCacheEntryExtractor(this.gradleUserHome).restore(listener)
|
||||||
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
|
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,10 +131,10 @@ export class GradleStateCache {
|
|||||||
*/
|
*/
|
||||||
async beforeSave(listener: CacheListener): Promise<void> {
|
async beforeSave(listener: CacheListener): Promise<void> {
|
||||||
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
||||||
this.deleteExcludedPaths()
|
await this.deleteExcludedPaths()
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener)
|
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener),
|
||||||
// new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
|
new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
|
||||||
])
|
])
|
||||||
await this.debugReportGradleUserHomeSize(
|
await this.debugReportGradleUserHomeSize(
|
||||||
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
|
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
|
||||||
@@ -140,13 +144,21 @@ export class GradleStateCache {
|
|||||||
/**
|
/**
|
||||||
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
|
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
|
||||||
*/
|
*/
|
||||||
private deleteExcludedPaths(): void {
|
private async deleteExcludedPaths(): Promise<void> {
|
||||||
const rawPaths: string[] = params.getCacheExcludes()
|
const rawPaths: string[] = params.getCacheExcludes()
|
||||||
|
rawPaths.push('caches/*/cc-keystore')
|
||||||
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
||||||
|
|
||||||
for (const p of resolvedPaths) {
|
for (const p of resolvedPaths) {
|
||||||
cacheDebug(`Deleting excluded path: ${p}`)
|
cacheDebug(`Removing excluded path: ${p}`)
|
||||||
tryDelete(p)
|
const globber = await glob.create(p, {
|
||||||
|
implicitDescendants: false
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const toDelete of await globber.glob()) {
|
||||||
|
cacheDebug(`Removing excluded file: ${toDelete}`)
|
||||||
|
await tryDelete(toDelete)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,23 +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 {
|
||||||
// Ensure that pre-installed java versions are detected. Only add property if it isn't already defined.
|
// Create a directory for storing action metadata
|
||||||
const gradleProperties = path.resolve(gradleUserHome, 'gradle.properties')
|
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
||||||
const existingGradleProperties = fs.existsSync(gradleProperties)
|
fs.mkdirSync(actionCacheDir, {recursive: true})
|
||||||
? fs.readFileSync(gradleProperties, 'utf8')
|
|
||||||
: ''
|
|
||||||
if (!existingGradleProperties.includes('org.gradle.java.installations.fromEnv=')) {
|
|
||||||
fs.appendFileSync(
|
|
||||||
gradleProperties,
|
|
||||||
`
|
|
||||||
# Auto-detect pre-installed JDKs
|
|
||||||
org.gradle.java.installations.fromEnv=JAVA_HOME_8_X64,JAVA_HOME_11_X64,JAVA_HOME_17_X64
|
|
||||||
`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy init scripts from src/resources
|
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',
|
||||||
@@ -196,15 +206,36 @@ org.gradle.java.installations.fromEnv=JAVA_HOME_8_X64,JAVA_HOME_11_X64,JAVA_HOME
|
|||||||
'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')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,13 +2,14 @@ import path from 'path'
|
|||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as glob from '@actions/glob'
|
import * as glob from '@actions/glob'
|
||||||
|
import * as semver from 'semver'
|
||||||
|
|
||||||
import * as params from './input-params'
|
import * as params from './input-params'
|
||||||
|
|
||||||
import {META_FILE_DIR} from './cache-base'
|
import {META_FILE_DIR} from './cache-base'
|
||||||
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
||||||
import {cacheDebug, getCacheKeyPrefix, hashFileNames, restoreCache, saveCache, tryDelete} from './cache-utils'
|
import {cacheDebug, getCacheKeyPrefix, hashFileNames, restoreCache, saveCache, tryDelete} from './cache-utils'
|
||||||
import {loadBuildResults} from './build-results'
|
import {BuildResult, loadBuildResults} from './build-results'
|
||||||
|
|
||||||
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
|
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ class ExtractedCacheEntryDefinition {
|
|||||||
pattern: string
|
pattern: string
|
||||||
bundle: boolean
|
bundle: boolean
|
||||||
uniqueFileNames = true
|
uniqueFileNames = true
|
||||||
|
notCacheableReason: string | undefined
|
||||||
|
|
||||||
constructor(artifactType: string, pattern: string, bundle: boolean) {
|
constructor(artifactType: string, pattern: string, bundle: boolean) {
|
||||||
this.artifactType = artifactType
|
this.artifactType = artifactType
|
||||||
@@ -53,10 +55,24 @@ class ExtractedCacheEntryDefinition {
|
|||||||
this.bundle = bundle
|
this.bundle = bundle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicate that the file names matching the cache entry pattern are NOT sufficient to uniquely identify the contents.
|
||||||
|
* If the file names are sufficient, then we use a hash of the file names to identify the entry.
|
||||||
|
* With non-unique-file-names, we hash the file contents to identify the cache entry.
|
||||||
|
*/
|
||||||
withNonUniqueFileNames(): ExtractedCacheEntryDefinition {
|
withNonUniqueFileNames(): ExtractedCacheEntryDefinition {
|
||||||
this.uniqueFileNames = false
|
this.uniqueFileNames = false
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify that the cache entry, should not be saved for some reason, even though the contents exist.
|
||||||
|
* This is used to prevent configuration-cache entries being cached when they were generated by Gradle < 8.6,
|
||||||
|
*/
|
||||||
|
notCacheableBecause(reason: string): ExtractedCacheEntryDefinition {
|
||||||
|
this.notCacheableReason = reason
|
||||||
|
return this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,6 +158,11 @@ abstract class AbstractEntryExtractor {
|
|||||||
const artifactType = cacheEntryDefinition.artifactType
|
const artifactType = cacheEntryDefinition.artifactType
|
||||||
const pattern = cacheEntryDefinition.pattern
|
const pattern = cacheEntryDefinition.pattern
|
||||||
|
|
||||||
|
if (cacheEntryDefinition.notCacheableReason) {
|
||||||
|
listener.entry(pattern).markNotSaved(cacheEntryDefinition.notCacheableReason)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
// Find all matching files for this cache entry definition
|
// Find all matching files for this cache entry definition
|
||||||
const globber = await glob.create(pattern, {
|
const globber = await glob.create(pattern, {
|
||||||
implicitDescendants: false
|
implicitDescendants: false
|
||||||
@@ -256,7 +277,7 @@ abstract class AbstractEntryExtractor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
|
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
|
||||||
cacheDebug(`Loaded cache metadata: ${filedata}`)
|
cacheDebug(`Loaded cache metadata for ${this.extractorName}: ${filedata}`)
|
||||||
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
|
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
|
||||||
return extractedCacheEntryMetadata.entries
|
return extractedCacheEntryMetadata.entries
|
||||||
}
|
}
|
||||||
@@ -264,12 +285,12 @@ abstract class AbstractEntryExtractor {
|
|||||||
/**
|
/**
|
||||||
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
|
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
|
||||||
*/
|
*/
|
||||||
private saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
|
protected saveMetadataForCacheResults(results: ExtractedCacheEntry[]): void {
|
||||||
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
|
const extractedCacheEntryMetadata = new ExtractedCacheEntryMetadata()
|
||||||
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
|
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
|
||||||
|
|
||||||
const filedata = JSON.stringify(extractedCacheEntryMetadata)
|
const filedata = JSON.stringify(extractedCacheEntryMetadata)
|
||||||
cacheDebug(`Saving cache metadata: ${filedata}`)
|
cacheDebug(`Saving cache metadata for ${this.extractorName}: ${filedata}`)
|
||||||
|
|
||||||
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
|
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
|
||||||
}
|
}
|
||||||
@@ -351,37 +372,96 @@ export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
|
|||||||
* entry is not reusable.
|
* entry is not reusable.
|
||||||
*/
|
*/
|
||||||
async restore(listener: CacheListener): Promise<void> {
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
if (listener.fullyRestored) {
|
if (!listener.fullyRestored) {
|
||||||
return super.restore(listener)
|
this.markNotRestored(listener, 'Gradle User Home was not fully restored')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
core.info('Not restoring configuration-cache state, as Gradle User Home was not fully restored')
|
if (!params.getCacheEncryptionKey()) {
|
||||||
for (const cacheEntry of this.loadExtractedCacheEntries()) {
|
this.markNotRestored(listener, 'Encryption Key was not provided')
|
||||||
listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED')
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return await super.restore(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private markNotRestored(listener: CacheListener, reason: string): void {
|
||||||
|
const cacheEntries = this.loadExtractedCacheEntries()
|
||||||
|
if (cacheEntries.length > 0) {
|
||||||
|
core.info(`Not restoring configuration-cache state, as ${reason}`)
|
||||||
|
for (const cacheEntry of cacheEntries) {
|
||||||
|
listener.entry(cacheEntry.pattern).markNotRestored(reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the results file based on no entries restored
|
||||||
|
this.saveMetadataForCacheResults([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async extract(listener: CacheListener): Promise<void> {
|
||||||
|
if (!params.getCacheEncryptionKey()) {
|
||||||
|
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
|
||||||
|
if (cacheEntryDefinitions.length > 0) {
|
||||||
|
core.info('Not saving configuration-cache state, as no encryption key was provided')
|
||||||
|
for (const cacheEntry of cacheEntryDefinitions) {
|
||||||
|
listener.entry(cacheEntry.pattern).markNotSaved('No encryption key provided')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await super.extract(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract cache entries for the configuration cache in each project.
|
* Extract cache entries for the configuration cache in each project.
|
||||||
*/
|
*/
|
||||||
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
||||||
return this.getProjectRoots().map(projectRoot => {
|
// Group BuildResult by existing configCacheDir
|
||||||
const configCachePath = path.resolve(projectRoot, '.gradle/configuration-cache')
|
const groupedResults = this.getConfigCacheDirectoriesWithAssociatedBuildResults()
|
||||||
return new ExtractedCacheEntryDefinition(
|
|
||||||
|
return Object.entries(groupedResults).map(([configCachePath, pathResults]) => {
|
||||||
|
// Create a entry definition for each unique configuration cache directory
|
||||||
|
const definition = new ExtractedCacheEntryDefinition(
|
||||||
'configuration-cache',
|
'configuration-cache',
|
||||||
configCachePath,
|
configCachePath,
|
||||||
true
|
true
|
||||||
).withNonUniqueFileNames()
|
).withNonUniqueFileNames()
|
||||||
|
|
||||||
|
// If any associated build result used Gradle < 8.6, then mark it as not cacheable
|
||||||
|
if (
|
||||||
|
pathResults.find(result => {
|
||||||
|
const gradleVersion = semver.coerce(result.gradleVersion)
|
||||||
|
return gradleVersion && semver.lt(gradleVersion, '8.6.0')
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
core.info(
|
||||||
|
`Not saving config-cache data for ${configCachePath}. Configuration cache data is only saved for Gradle 8.6+`
|
||||||
|
)
|
||||||
|
definition.notCacheableBecause('Configuration cache data only saved for Gradle 8.6+')
|
||||||
|
}
|
||||||
|
return definition
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private getConfigCacheDirectoriesWithAssociatedBuildResults(): Record<string, BuildResult[]> {
|
||||||
* For every Gradle invocation, we record the project root directory. This method returns the entire
|
return loadBuildResults().reduce(
|
||||||
* set of project roots, to allow saving of configuration-cache entries for each.
|
(acc, buildResult) => {
|
||||||
*/
|
// For each build result, find the config-cache dir
|
||||||
private getProjectRoots(): string[] {
|
const configCachePath = path.resolve(buildResult.rootProjectDir, '.gradle/configuration-cache')
|
||||||
const buildResults = loadBuildResults()
|
// Ignore case where config-cache dir doesn't exist
|
||||||
const projectRootDirs = buildResults.map(x => x.rootProjectDir)
|
if (!fs.existsSync(configCachePath)) {
|
||||||
return [...new Set(projectRootDirs)] // Remove duplicates
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group by unique config cache directories and collect associated build results
|
||||||
|
if (!acc[configCachePath]) {
|
||||||
|
acc[configCachePath] = []
|
||||||
|
}
|
||||||
|
acc[configCachePath].push(buildResult)
|
||||||
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<string, BuildResult[]>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import * as core from '@actions/core'
|
|
||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,47 +111,36 @@ export class CacheEntryListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeCachingReport(listener: CacheListener): void {
|
export function generateCachingReport(listener: CacheListener): string {
|
||||||
const entries = listener.cacheEntries
|
const entries = listener.cacheEntries
|
||||||
|
|
||||||
core.summary.addRaw(
|
return `
|
||||||
`\n<details><summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>\n`
|
<details>
|
||||||
)
|
<summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>
|
||||||
|
${renderEntryTable(entries)}
|
||||||
|
|
||||||
core.summary.addTable([
|
<h5>Cache Entry Details</h5>
|
||||||
[
|
<pre>
|
||||||
{data: '', header: true},
|
${renderEntryDetails(listener)}
|
||||||
{data: 'Count', header: true},
|
|
||||||
{data: 'Total Size (Mb)', header: true}
|
|
||||||
],
|
|
||||||
['Entries Restored', `${getCount(entries, e => e.restoredSize)}`, `${getSize(entries, e => e.restoredSize)}`],
|
|
||||||
['Entries Saved', `${getCount(entries, e => e.savedSize)}`, `${getSize(entries, e => e.savedSize)}`]
|
|
||||||
])
|
|
||||||
|
|
||||||
core.summary.addHeading('Cache Entry Details', 5)
|
|
||||||
|
|
||||||
const entryDetails = renderEntryDetails(listener)
|
|
||||||
core.summary.addRaw(`<pre>
|
|
||||||
${entryDetails}
|
|
||||||
</pre>
|
</pre>
|
||||||
</details>
|
</details>
|
||||||
`)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function logCachingReport(listener: CacheListener): void {
|
function renderEntryTable(entries: CacheEntryListener[]): string {
|
||||||
const entries = listener.cacheEntries
|
return `
|
||||||
|
<table>
|
||||||
core.startGroup(`Caching for gradle-build-action was ${listener.cacheStatus} - expand for details`)
|
<tr><td></td><th>Count</th><th>Total Size (Mb)</th></tr>
|
||||||
|
<tr><td>Entries Restored</td>
|
||||||
core.info(
|
<td>${getCount(entries, e => e.restoredSize)}</td>
|
||||||
`Entries Restored: ${getCount(entries, e => e.restoredSize)} (${getSize(entries, e => e.restoredSize)} Mb)`
|
<td>${getSize(entries, e => e.restoredSize)}</td>
|
||||||
)
|
</tr>
|
||||||
core.info(`Entries Saved : ${getCount(entries, e => e.savedSize)} (${getSize(entries, e => e.savedSize)} Mb)`)
|
<tr><td>Entries Saved</td>
|
||||||
|
<td>${getCount(entries, e => e.savedSize)}</td>
|
||||||
core.info(`Cache Entry Details`)
|
<td>${getSize(entries, e => e.savedSize)}</td>
|
||||||
core.info(renderEntryDetails(listener))
|
</tr>
|
||||||
|
</table>
|
||||||
core.endGroup()
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEntryDetails(listener: CacheListener): string {
|
function renderEntryDetails(listener: CacheListener): string {
|
||||||
@@ -198,6 +186,9 @@ function getSavedMessage(entry: CacheEntryListener, cacheReadOnly: boolean): str
|
|||||||
if (cacheReadOnly) {
|
if (cacheReadOnly) {
|
||||||
return '(Entry not saved: cache is read-only)'
|
return '(Entry not saved: cache is read-only)'
|
||||||
}
|
}
|
||||||
|
if (entry.notRestored) {
|
||||||
|
return '(Entry not saved: not restored)'
|
||||||
|
}
|
||||||
return '(Entry not saved: reason unknown)'
|
return '(Entry not saved: reason unknown)'
|
||||||
}
|
}
|
||||||
if (entry.savedSize === 0) {
|
if (entry.savedSize === 0) {
|
||||||
|
@@ -11,7 +11,7 @@ import * as params from './input-params'
|
|||||||
|
|
||||||
import {CacheEntryListener} from './cache-reporting'
|
import {CacheEntryListener} from './cache-reporting'
|
||||||
|
|
||||||
const CACHE_PROTOCOL_VERSION = 'v8-'
|
const CACHE_PROTOCOL_VERSION = 'v9-'
|
||||||
|
|
||||||
const CACHE_KEY_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
const CACHE_KEY_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
||||||
const CACHE_KEY_OS_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_ENVIRONMENT'
|
const CACHE_KEY_OS_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_ENVIRONMENT'
|
||||||
@@ -86,10 +86,10 @@ export function generateCacheKey(cacheName: string): CacheKey {
|
|||||||
// At the most general level, share caches for all executions on the same OS
|
// At the most general level, share caches for all executions on the same OS
|
||||||
const cacheKeyForEnvironment = `${cacheKeyBase}|${getCacheKeyEnvironment()}`
|
const cacheKeyForEnvironment = `${cacheKeyBase}|${getCacheKeyEnvironment()}`
|
||||||
|
|
||||||
// Prefer caches that run this job
|
// Then prefer caches that run job with the same ID
|
||||||
const cacheKeyForJob = `${cacheKeyForEnvironment}|${getCacheKeyJob()}`
|
const cacheKeyForJob = `${cacheKeyForEnvironment}|${getCacheKeyJob()}`
|
||||||
|
|
||||||
// Prefer (even more) jobs that run this job with the same context (matrix)
|
// Prefer (even more) jobs that run this job in the same workflow with the same context (matrix)
|
||||||
const cacheKeyForJobContext = `${cacheKeyForJob}[${getCacheKeyJobInstance()}]`
|
const cacheKeyForJobContext = `${cacheKeyForJob}[${getCacheKeyJobInstance()}]`
|
||||||
|
|
||||||
// Exact match on Git SHA
|
// Exact match on Git SHA
|
||||||
@@ -113,12 +113,7 @@ function getCacheKeyEnvironment(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCacheKeyJob(): string {
|
function getCacheKeyJob(): string {
|
||||||
return process.env[CACHE_KEY_JOB_VAR] || getCacheKeyForJob(github.context.workflow, github.context.job)
|
return process.env[CACHE_KEY_JOB_VAR] || github.context.job
|
||||||
}
|
|
||||||
|
|
||||||
export function getCacheKeyForJob(workflowName: string, jobId: string): string {
|
|
||||||
const sanitizedWorkflow = workflowName.replace(/,/g, '').toLowerCase()
|
|
||||||
return `${sanitizedWorkflow}-${jobId}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCacheKeyJobInstance(): string {
|
function getCacheKeyJobInstance(): string {
|
||||||
@@ -127,25 +122,11 @@ function getCacheKeyJobInstance(): string {
|
|||||||
return override
|
return override
|
||||||
}
|
}
|
||||||
|
|
||||||
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
|
// By default, we hash the workflow name and the full `matrix` data for the run, to uniquely identify this job invocation
|
||||||
// The only way we can obtain the `matrix` data is via the `workflow-job-context` parameter in action.yml.
|
// The only way we can obtain the `matrix` data is via the `workflow-job-context` parameter in action.yml.
|
||||||
|
const workflowName = github.context.workflow
|
||||||
const workflowJobContext = params.getJobMatrix()
|
const workflowJobContext = params.getJobMatrix()
|
||||||
return hashStrings([workflowJobContext])
|
return hashStrings([workflowName, workflowJobContext])
|
||||||
}
|
|
||||||
|
|
||||||
export function getUniqueLabelForJobInstance(): string {
|
|
||||||
return getUniqueLabelForJobInstanceValues(github.context.workflow, github.context.job, params.getJobMatrix())
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUniqueLabelForJobInstanceValues(workflow: string, jobId: string, matrixJson: string): string {
|
|
||||||
const matrix = JSON.parse(matrixJson)
|
|
||||||
const matrixString = Object.values(matrix).join('-')
|
|
||||||
const label = matrixString ? `${workflow}-${jobId}-${matrixString}` : `${workflow}-${jobId}`
|
|
||||||
return sanitize(label)
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitize(value: string): string {
|
|
||||||
return value.replace(/[^a-zA-Z0-9_-]/g, '').toLowerCase()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCacheKeyJobExecution(): string {
|
function getCacheKeyJobExecution(): string {
|
||||||
|
@@ -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
|
||||||
@@ -90,10 +91,14 @@ export async function save(
|
|||||||
if (isCacheCleanupEnabled()) {
|
if (isCacheCleanupEnabled()) {
|
||||||
core.info('Forcing cache cleanup.')
|
core.info('Forcing cache cleanup.')
|
||||||
const cacheCleaner = new CacheCleaner(gradleUserHome, process.env['RUNNER_TEMP']!)
|
const cacheCleaner = new CacheCleaner(gradleUserHome, process.env['RUNNER_TEMP']!)
|
||||||
await cacheCleaner.forceCleanup()
|
try {
|
||||||
|
await cacheCleaner.forceCleanup()
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(`Cache cleanup failed. Will continue. ${String(e)}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as artifact from '@actions/artifact'
|
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
import * as glob from '@actions/glob'
|
import * as glob from '@actions/glob'
|
||||||
import * as toolCache from '@actions/tool-cache'
|
import {DefaultArtifactClient} from '@actions/artifact'
|
||||||
import {GitHub} from '@actions/github/lib/utils'
|
import {GitHub} from '@actions/github/lib/utils'
|
||||||
import {RequestError} from '@octokit/request-error'
|
import {RequestError} from '@octokit/request-error'
|
||||||
import type {PullRequestEvent} from '@octokit/webhooks-types'
|
import type {PullRequestEvent} from '@octokit/webhooks-types'
|
||||||
@@ -13,7 +12,7 @@ import fs from 'fs'
|
|||||||
import * as layout from './repository-layout'
|
import * as layout from './repository-layout'
|
||||||
import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params'
|
import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params'
|
||||||
|
|
||||||
const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph'
|
const DEPENDENCY_GRAPH_PREFIX = 'dependency-graph_'
|
||||||
|
|
||||||
export async function setup(option: DependencyGraphOption): Promise<void> {
|
export async function setup(option: DependencyGraphOption): Promise<void> {
|
||||||
if (option === DependencyGraphOption.Disabled) {
|
if (option === DependencyGraphOption.Disabled) {
|
||||||
@@ -39,37 +38,48 @@ export async function setup(option: DependencyGraphOption): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function complete(option: DependencyGraphOption): Promise<void> {
|
export async function complete(option: DependencyGraphOption): Promise<void> {
|
||||||
switch (option) {
|
try {
|
||||||
case DependencyGraphOption.Disabled:
|
switch (option) {
|
||||||
case DependencyGraphOption.DownloadAndSubmit: // Performed in setup
|
case DependencyGraphOption.Disabled:
|
||||||
return
|
case DependencyGraphOption.Generate: // Performed via init-script: nothing to do here
|
||||||
case DependencyGraphOption.Generate:
|
case DependencyGraphOption.DownloadAndSubmit: // Performed in setup
|
||||||
await uploadDependencyGraphs()
|
return
|
||||||
return
|
case DependencyGraphOption.GenerateAndSubmit:
|
||||||
case DependencyGraphOption.GenerateAndSubmit:
|
await submitDependencyGraphs(await findGeneratedDependencyGraphFiles())
|
||||||
await submitDependencyGraphs(await uploadDependencyGraphs())
|
return
|
||||||
return
|
case DependencyGraphOption.GenerateAndUpload:
|
||||||
|
await uploadDependencyGraphs(await findGeneratedDependencyGraphFiles())
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(`Failed to ${option} dependency graph. Will continue. ${String(e)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function uploadDependencyGraphs(): Promise<string[]> {
|
async function findGeneratedDependencyGraphFiles(): Promise<string[]> {
|
||||||
const workspaceDirectory = layout.workspaceDirectory()
|
const workspaceDirectory = layout.workspaceDirectory()
|
||||||
const graphFiles = await findDependencyGraphFiles(workspaceDirectory)
|
return await findDependencyGraphFiles(workspaceDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
const relativeGraphFiles = graphFiles.map(x => getRelativePathFromWorkspace(x))
|
async function uploadDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
|
||||||
core.info(`Uploading dependency graph files: ${relativeGraphFiles}`)
|
const workspaceDirectory = layout.workspaceDirectory()
|
||||||
|
|
||||||
const artifactClient = artifact.create()
|
const artifactClient = new DefaultArtifactClient()
|
||||||
artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, graphFiles, workspaceDirectory, {
|
for (const dependencyGraphFile of dependencyGraphFiles) {
|
||||||
retentionDays: getArtifactRetentionDays()
|
const relativePath = getRelativePathFromWorkspace(dependencyGraphFile)
|
||||||
})
|
core.info(`Uploading dependency graph file: ${relativePath}`)
|
||||||
|
const artifactName = `${DEPENDENCY_GRAPH_PREFIX}${path.basename(dependencyGraphFile)}`
|
||||||
return graphFiles
|
await artifactClient.uploadArtifact(artifactName, [dependencyGraphFile], workspaceDirectory, {
|
||||||
|
retentionDays: getArtifactRetentionDays()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadAndSubmitDependencyGraphs(): Promise<void> {
|
async function downloadAndSubmitDependencyGraphs(): Promise<void> {
|
||||||
const workspaceDirectory = layout.workspaceDirectory()
|
try {
|
||||||
submitDependencyGraphs(await retrieveDependencyGraphs(workspaceDirectory))
|
await submitDependencyGraphs(await downloadDependencyGraphs())
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(`Download and submit dependency graph failed. Will continue. ${String(e)}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
|
async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
|
||||||
@@ -78,12 +88,7 @@ async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<v
|
|||||||
await submitDependencyGraphFile(jsonFile)
|
await submitDependencyGraphFile(jsonFile)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof RequestError) {
|
if (error instanceof RequestError) {
|
||||||
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
|
core.warning(buildWarningMessage(jsonFile, error))
|
||||||
core.warning(
|
|
||||||
`Failed to submit dependency graph ${relativeJsonFile}.\n` +
|
|
||||||
"Please ensure that the 'contents: write' permission is available for the workflow job.\n" +
|
|
||||||
"Note that this permission is never available for a 'pull_request' trigger from a repository fork."
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
@@ -91,6 +96,18 @@ async function submitDependencyGraphs(dependencyGraphFiles: string[]): Promise<v
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildWarningMessage(jsonFile: string, error: RequestError): string {
|
||||||
|
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
|
||||||
|
const mainWarning = `Failed to submit dependency graph ${relativeJsonFile}.\n${String(error)}`
|
||||||
|
if (error.message === 'Resource not accessible by integration') {
|
||||||
|
return `${mainWarning}
|
||||||
|
Please ensure that the 'contents: write' permission is available for the workflow job.
|
||||||
|
Note that this permission is never available for a 'pull_request' trigger from a repository fork.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
return mainWarning
|
||||||
|
}
|
||||||
|
|
||||||
async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
|
async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
|
||||||
const octokit = getOctokit()
|
const octokit = getOctokit()
|
||||||
const jsonContent = fs.readFileSync(jsonFile, 'utf8')
|
const jsonContent = fs.readFileSync(jsonFile, 'utf8')
|
||||||
@@ -104,56 +121,37 @@ async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
|
|||||||
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
|
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function retrieveDependencyGraphs(workspaceDirectory: string): Promise<string[]> {
|
async function downloadDependencyGraphs(): Promise<string[]> {
|
||||||
if (github.context.payload.workflow_run) {
|
const workspaceDirectory = layout.workspaceDirectory()
|
||||||
return await retrieveDependencyGraphsForWorkflowRun(github.context.payload.workflow_run.id, workspaceDirectory)
|
|
||||||
}
|
|
||||||
return retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function retrieveDependencyGraphsForWorkflowRun(runId: number, workspaceDirectory: string): Promise<string[]> {
|
const findBy = github.context.payload.workflow_run
|
||||||
const octokit = getOctokit()
|
? {
|
||||||
|
token: getGithubToken(),
|
||||||
|
workflowRunId: github.context.payload.workflow_run.id,
|
||||||
|
repositoryName: github.context.repo.repo,
|
||||||
|
repositoryOwner: github.context.repo.owner
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
// Find the workflow run artifacts named "dependency-graph"
|
const artifactClient = new DefaultArtifactClient()
|
||||||
const artifacts = await octokit.rest.actions.listWorkflowRunArtifacts({
|
|
||||||
owner: github.context.repo.owner,
|
|
||||||
repo: github.context.repo.repo,
|
|
||||||
run_id: runId
|
|
||||||
})
|
|
||||||
|
|
||||||
const matchArtifact = artifacts.data.artifacts.find(candidate => {
|
|
||||||
return candidate.name === DEPENDENCY_GRAPH_ARTIFACT
|
|
||||||
})
|
|
||||||
|
|
||||||
if (matchArtifact === undefined) {
|
|
||||||
throw new Error(`Dependency graph artifact not found. Has it been generated by workflow run '${runId}'?`)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Download the dependency-graph artifact
|
|
||||||
const download = await octokit.rest.actions.downloadArtifact({
|
|
||||||
owner: github.context.repo.owner,
|
|
||||||
repo: github.context.repo.repo,
|
|
||||||
artifact_id: matchArtifact.id,
|
|
||||||
archive_format: 'zip'
|
|
||||||
})
|
|
||||||
|
|
||||||
const downloadBuffer = download.data as ArrayBuffer
|
|
||||||
const downloadZip = path.resolve(workspaceDirectory, 'dependency-graph.zip')
|
|
||||||
fs.writeFileSync(downloadZip, Buffer.from(downloadBuffer))
|
|
||||||
|
|
||||||
// Expance the dependency-graph zip and locate each dependency-graph JSON file
|
|
||||||
const extractDir = path.resolve(workspaceDirectory, 'dependency-graph')
|
|
||||||
const extracted = await toolCache.extractZip(downloadZip, extractDir)
|
|
||||||
core.info(`Extracted dependency graph artifacts to ${extracted}: ${fs.readdirSync(extracted)}`)
|
|
||||||
|
|
||||||
return findDependencyGraphFiles(extracted)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory: string): Promise<string[]> {
|
|
||||||
const artifactClient = artifact.create()
|
|
||||||
const downloadPath = path.resolve(workspaceDirectory, 'dependency-graph')
|
const downloadPath = path.resolve(workspaceDirectory, 'dependency-graph')
|
||||||
await artifactClient.downloadArtifact(DEPENDENCY_GRAPH_ARTIFACT, downloadPath)
|
|
||||||
return await findDependencyGraphFiles(downloadPath)
|
const dependencyGraphArtifacts = (
|
||||||
|
await artifactClient.listArtifacts({
|
||||||
|
latest: true,
|
||||||
|
findBy
|
||||||
|
})
|
||||||
|
).artifacts.filter(candidate => candidate.name.startsWith(DEPENDENCY_GRAPH_PREFIX))
|
||||||
|
|
||||||
|
for (const artifact of dependencyGraphArtifacts) {
|
||||||
|
const downloadedArtifact = await artifactClient.downloadArtifact(artifact.id, {
|
||||||
|
path: downloadPath,
|
||||||
|
findBy
|
||||||
|
})
|
||||||
|
core.info(`Downloading dependency-graph artifact ${artifact.name} to ${downloadedArtifact.downloadPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return findDependencyGraphFiles(downloadPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findDependencyGraphFiles(dir: string): Promise<string[]> {
|
async function findDependencyGraphFiles(dir: string): Promise<string[]> {
|
||||||
|
@@ -29,6 +29,10 @@ export function isCacheCleanupEnabled(): boolean {
|
|||||||
return getBooleanInput('gradle-home-cache-cleanup')
|
return getBooleanInput('gradle-home-cache-cleanup')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCacheEncryptionKey(): string {
|
||||||
|
return core.getInput('cache-encryption-key')
|
||||||
|
}
|
||||||
|
|
||||||
export function getCacheIncludes(): string[] {
|
export function getCacheIncludes(): string[] {
|
||||||
return core.getMultilineInput('gradle-home-cache-includes')
|
return core.getMultilineInput('gradle-home-cache-includes')
|
||||||
}
|
}
|
||||||
@@ -67,8 +71,25 @@ export function isJobSummaryEnabled(): boolean {
|
|||||||
return getBooleanInput('generate-job-summary', true)
|
return getBooleanInput('generate-job-summary', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDependencyGraphEnabled(): boolean {
|
export function getJobSummaryOption(): JobSummaryOption {
|
||||||
return getBooleanInput('generate-dependency-graph', true)
|
return parseJobSummaryOption('add-job-summary')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPRCommentOption(): JobSummaryOption {
|
||||||
|
return parseJobSummaryOption('add-job-summary-as-pr-comment')
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJobSummaryOption(paramName: string): JobSummaryOption {
|
||||||
|
const val = core.getInput(paramName)
|
||||||
|
switch (val.toLowerCase().trim()) {
|
||||||
|
case 'never':
|
||||||
|
return JobSummaryOption.Never
|
||||||
|
case 'always':
|
||||||
|
return JobSummaryOption.Always
|
||||||
|
case 'on-failure':
|
||||||
|
return JobSummaryOption.OnFailure
|
||||||
|
}
|
||||||
|
throw TypeError(`The value '${val}' is not valid for ${paramName}. Valid values are: [never, always, on-failure].`)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDependencyGraphOption(): DependencyGraphOption {
|
export function getDependencyGraphOption(): DependencyGraphOption {
|
||||||
@@ -80,11 +101,13 @@ export function getDependencyGraphOption(): DependencyGraphOption {
|
|||||||
return DependencyGraphOption.Generate
|
return DependencyGraphOption.Generate
|
||||||
case 'generate-and-submit':
|
case 'generate-and-submit':
|
||||||
return DependencyGraphOption.GenerateAndSubmit
|
return DependencyGraphOption.GenerateAndSubmit
|
||||||
|
case 'generate-and-upload':
|
||||||
|
return DependencyGraphOption.GenerateAndUpload
|
||||||
case 'download-and-submit':
|
case 'download-and-submit':
|
||||||
return DependencyGraphOption.DownloadAndSubmit
|
return DependencyGraphOption.DownloadAndSubmit
|
||||||
}
|
}
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
`The value '${val} is not valid for 'dependency-graph. Valid values are: [disabled, generate-and-upload, generate-and-submit, download-and-submit]. The default value is 'disabled'.`
|
`The value '${val}' is not valid for 'dependency-graph'. Valid values are: [disabled, generate, generate-and-submit, generate-and-upload, download-and-submit]. The default value is 'disabled'.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,8 +142,15 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum DependencyGraphOption {
|
export enum DependencyGraphOption {
|
||||||
Disabled,
|
Disabled = 'disabled',
|
||||||
Generate,
|
Generate = 'generate',
|
||||||
GenerateAndSubmit,
|
GenerateAndSubmit = 'generate-and-submit',
|
||||||
DownloadAndSubmit
|
GenerateAndUpload = 'generate-and-upload',
|
||||||
|
DownloadAndSubmit = 'download-and-submit'
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum JobSummaryOption {
|
||||||
|
Never = 'never',
|
||||||
|
Always = 'always',
|
||||||
|
OnFailure = 'on-failure'
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,94 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
|
import * as github from '@actions/github'
|
||||||
|
import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary'
|
||||||
|
import {RequestError} from '@octokit/request-error'
|
||||||
|
|
||||||
|
import * as params from './input-params'
|
||||||
import {BuildResult} from './build-results'
|
import {BuildResult} from './build-results'
|
||||||
import {writeCachingReport, CacheListener, logCachingReport} from './cache-reporting'
|
import {CacheListener, generateCachingReport} from './cache-reporting'
|
||||||
|
|
||||||
export async function writeJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
export async function generateJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
||||||
core.info('Writing job summary')
|
const summaryTable = renderSummaryTable(buildResults)
|
||||||
|
const cachingReport = generateCachingReport(cacheListener)
|
||||||
|
|
||||||
if (buildResults.length === 0) {
|
if (shouldGenerateJobSummary(buildResults)) {
|
||||||
core.debug('No Gradle build results found. Summary table will not be generated.')
|
core.info('Generating Job Summary')
|
||||||
|
|
||||||
|
core.summary.addRaw(summaryTable)
|
||||||
|
core.summary.addRaw(cachingReport)
|
||||||
|
await core.summary.write()
|
||||||
} else {
|
} else {
|
||||||
writeSummaryTable(buildResults)
|
core.info('============================')
|
||||||
|
core.info(summaryTable)
|
||||||
|
core.info('============================')
|
||||||
|
core.info(cachingReport)
|
||||||
|
core.info('============================')
|
||||||
}
|
}
|
||||||
|
|
||||||
writeCachingReport(cacheListener)
|
if (shouldAddPRComment(buildResults)) {
|
||||||
|
await addPRComment(summaryTable)
|
||||||
await core.summary.write()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function logJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
async function addPRComment(jobSummary: string): Promise<void> {
|
||||||
if (buildResults.length === 0) {
|
const context = github.context
|
||||||
core.debug('No Gradle build results found. Summary table will not be logged.')
|
if (context.payload.pull_request == null) {
|
||||||
} else {
|
core.info('No pull_request trigger: not adding PR comment')
|
||||||
logSummaryTable(buildResults)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
logCachingReport(cacheListener)
|
const pull_request_number = context.payload.pull_request.number
|
||||||
|
core.info(`Adding Job Summary as comment to PR #${pull_request_number}.`)
|
||||||
|
|
||||||
|
const prComment = `<h3>Job Summary for gradle-build-action</h3>
|
||||||
|
<h5>${github.context.workflow} :: <em>${github.context.job}</em></h5>
|
||||||
|
|
||||||
|
${jobSummary}`
|
||||||
|
|
||||||
|
const github_token = params.getGithubToken()
|
||||||
|
const octokit = github.getOctokit(github_token)
|
||||||
|
try {
|
||||||
|
await octokit.rest.issues.createComment({
|
||||||
|
...context.repo,
|
||||||
|
issue_number: pull_request_number,
|
||||||
|
body: prComment
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof RequestError) {
|
||||||
|
core.warning(buildWarningMessage(error))
|
||||||
|
} else {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeSummaryTable(results: BuildResult[]): void {
|
function buildWarningMessage(error: RequestError): string {
|
||||||
core.summary.addHeading('Gradle Builds', 3)
|
const mainWarning = `Failed to generate PR comment.\n${String(error)}`
|
||||||
|
if (error.message === 'Resource not accessible by integration') {
|
||||||
|
return `${mainWarning}
|
||||||
|
Please ensure that the 'pull-requests: write' permission is available for the workflow job.
|
||||||
|
Note that this permission is never available for a workflow triggered from a repository fork.
|
||||||
|
`
|
||||||
|
}
|
||||||
|
return mainWarning
|
||||||
|
}
|
||||||
|
|
||||||
core.summary.addRaw(`
|
function renderSummaryTable(results: BuildResult[]): string {
|
||||||
|
if (results.length === 0) {
|
||||||
|
return 'No Gradle build results detected.'
|
||||||
|
}
|
||||||
|
|
||||||
|
return `
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Root Project</th>
|
<th>Gradle Root Project</th>
|
||||||
<th>Requested Tasks</th>
|
<th>Requested Tasks</th>
|
||||||
<th>Gradle Version</th>
|
<th>Gradle Version</th>
|
||||||
<th>Build Outcome</th>
|
<th>Build Outcome</th>
|
||||||
<th>Build Scan®</th>
|
<th>Build Scan®</th>
|
||||||
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
|
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
|
||||||
</table>
|
</table>
|
||||||
`)
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBuildResultRow(result: BuildResult): string {
|
function renderBuildResultRow(result: BuildResult): string {
|
||||||
@@ -77,18 +126,32 @@ function renderBuildScanBadge(outcomeText: string, outcomeColor: string, targetU
|
|||||||
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
|
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
|
||||||
}
|
}
|
||||||
|
|
||||||
function logSummaryTable(results: BuildResult[]): void {
|
function shouldGenerateJobSummary(buildResults: BuildResult[]): boolean {
|
||||||
core.info('============================')
|
// Check if Job Summary is supported on this platform
|
||||||
core.info('Gradle Builds')
|
if (!process.env[SUMMARY_ENV_VAR]) {
|
||||||
core.info('----------------------------')
|
return false
|
||||||
core.info('Root Project | Requested Tasks | Gradle Version | Build Outcome | Build Scan®')
|
}
|
||||||
core.info('----------------------------')
|
|
||||||
for (const result of results) {
|
// Check if Job Summary is disabled using the deprecated input
|
||||||
core.info(
|
if (!params.isJobSummaryEnabled()) {
|
||||||
`${result.rootProjectName} | ${result.requestedTasks} | ${result.gradleVersion} | ${
|
return false
|
||||||
result.buildFailed ? 'FAILED' : 'SUCCESS'
|
}
|
||||||
} | ${result.buildScanFailed ? 'Publish failed' : result.buildScanUri}`
|
|
||||||
)
|
return shouldAddJobSummary(params.getJobSummaryOption(), buildResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldAddPRComment(buildResults: BuildResult[]): boolean {
|
||||||
|
return shouldAddJobSummary(params.getPRCommentOption(), buildResults)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldAddJobSummary(option: params.JobSummaryOption, buildResults: BuildResult[]): boolean {
|
||||||
|
switch (option) {
|
||||||
|
case params.JobSummaryOption.Always:
|
||||||
|
return true
|
||||||
|
case params.JobSummaryOption.Never:
|
||||||
|
return false
|
||||||
|
case params.JobSummaryOption.OnFailure:
|
||||||
|
core.info(`Got these build results: ${JSON.stringify(buildResults)}`)
|
||||||
|
return buildResults.some(result => result.buildFailed)
|
||||||
}
|
}
|
||||||
core.info('============================')
|
|
||||||
}
|
}
|
||||||
|
@@ -50,12 +50,16 @@ abstract class BuildResultsRecorder implements BuildService<BuildResultsRecorder
|
|||||||
if (!runnerTempDir || !githubActionStep) {
|
if (!runnerTempDir || !githubActionStep) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
try {
|
||||||
buildResultsDir.mkdirs()
|
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
||||||
def buildResultsFile = new File(buildResultsDir, githubActionStep + getParameters().getInvocationId().get() + ".json")
|
buildResultsDir.mkdirs()
|
||||||
if (!buildResultsFile.exists()) {
|
def buildResultsFile = new File(buildResultsDir, githubActionStep + getParameters().getInvocationId().get() + ".json")
|
||||||
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
|
if (!buildResultsFile.exists()) {
|
||||||
|
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
println "\ngradle-build-action failed to write build-results file. Will continue.\n> ${e.getLocalizedMessage()}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -121,18 +121,23 @@ class BuildResults {
|
|||||||
if (!runnerTempDir || !githubActionStep) {
|
if (!runnerTempDir || !githubActionStep) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
|
||||||
buildResultsDir.mkdirs()
|
|
||||||
def buildResultsFile = new File(buildResultsDir, githubActionStep + invocationId + ".json")
|
|
||||||
|
|
||||||
// Overwrite any contents written by buildFinished or build service, since this result is a superset.
|
try {
|
||||||
if (buildResultsFile.exists()) {
|
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
||||||
if (overwrite) {
|
buildResultsDir.mkdirs()
|
||||||
buildResultsFile.text = groovy.json.JsonOutput.toJson(buildResults)
|
def buildResultsFile = new File(buildResultsDir, githubActionStep + invocationId + ".json")
|
||||||
|
|
||||||
|
// Overwrite any contents written by buildFinished or build service, since this result is a superset.
|
||||||
|
if (buildResultsFile.exists()) {
|
||||||
|
if (overwrite) {
|
||||||
|
buildResultsFile.text = groovy.json.JsonOutput.toJson(buildResults)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
|
} catch (Exception e) {
|
||||||
|
println "\ngradle-build-action failed to write build-results file. Will continue.\n> ${e.getLocalizedMessage()}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,6 +1,12 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
|
def getInputParam = { String name ->
|
||||||
|
def envVarName = name.toUpperCase().replace('.', '_').replace('-', '_')
|
||||||
|
return System.getProperty(name) ?: System.getenv(envVarName)
|
||||||
|
}
|
||||||
|
def pluginRepositoryUrl = getInputParam('gradle.plugin-repository.url') ?: 'https://plugins.gradle.org/m2'
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven { url "https://plugins.gradle.org/m2/" }
|
maven { url pluginRepositoryUrl }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "org.gradle:github-dependency-graph-gradle-plugin:1.0.0"
|
classpath "org.gradle:github-dependency-graph-gradle-plugin:1.0.0"
|
||||||
|
@@ -27,7 +27,6 @@ if (isTopLevelBuild) {
|
|||||||
new File(githubOutput) << "dependency-graph-file=${reportFile.absolutePath}\n"
|
new File(githubOutput) << "dependency-graph-file=${reportFile.absolutePath}\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
println "Generating dependency graph into '${reportFile}'"
|
println "Generating dependency graph into '${reportFile}'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -168,7 +168,7 @@ if (GradleVersion.current() < GradleVersion.version('6.0')) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyPluginExternally(PluginManager pluginManager, String pluginClassName) {
|
void applyPluginExternally(def pluginManager, String pluginClassName) {
|
||||||
def externallyApplied = 'gradle.enterprise.externally-applied'
|
def externallyApplied = 'gradle.enterprise.externally-applied'
|
||||||
def oldValue = System.getProperty(externallyApplied)
|
def oldValue = System.getProperty(externallyApplied)
|
||||||
System.setProperty(externallyApplied, 'true')
|
System.setProperty(externallyApplied, 'true')
|
||||||
|
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>
|
@@ -1,23 +1,24 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary'
|
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as caches from './caches'
|
import * as caches from './caches'
|
||||||
import * as layout from './repository-layout'
|
import * as layout from './repository-layout'
|
||||||
import * as params from './input-params'
|
import * as params from './input-params'
|
||||||
import * as dependencyGraph from './dependency-graph'
|
import * as dependencyGraph from './dependency-graph'
|
||||||
|
import * as jobSummary from './job-summary'
|
||||||
|
|
||||||
import {logJobSummary, writeJobSummary} from './job-summary'
|
|
||||||
import {loadBuildResults} from './build-results'
|
import {loadBuildResults} from './build-results'
|
||||||
import {CacheListener} from './cache-reporting'
|
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.
|
||||||
@@ -30,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())
|
||||||
|
|
||||||
@@ -46,23 +48,22 @@ export async function complete(): Promise<void> {
|
|||||||
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.')
|
core.info('Gradle setup post-action only performed for first gradle-build-action step in workflow.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
core.info('In final post-action step, saving state and writing summary')
|
core.info('In post-action step')
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
if (shouldGenerateJobSummary()) {
|
await jobSummary.generateJobSummary(buildResults, cacheListener)
|
||||||
await writeJobSummary(buildResults, cacheListener)
|
|
||||||
} else {
|
|
||||||
logJobSummary(buildResults, cacheListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
await dependencyGraph.complete(params.getDependencyGraphOption())
|
await dependencyGraph.complete(params.getDependencyGraphOption())
|
||||||
|
|
||||||
|
core.info('Completed post-action step')
|
||||||
}
|
}
|
||||||
|
|
||||||
async function determineGradleUserHome(): Promise<string> {
|
async function determineGradleUserHome(): Promise<string> {
|
||||||
@@ -91,12 +92,3 @@ async function determineUserHome(): Promise<string> {
|
|||||||
core.debug(`Determined user.home from java -version output: '${userHome}'`)
|
core.debug(`Determined user.home from java -version output: '${userHome}'`)
|
||||||
return userHome
|
return userHome
|
||||||
}
|
}
|
||||||
|
|
||||||
function shouldGenerateJobSummary(): boolean {
|
|
||||||
// Check if Job Summary is supported on this platform
|
|
||||||
if (!process.env[SUMMARY_ENV_VAR]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return params.isJobSummaryEnabled()
|
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "com.gradle.enterprise" version "3.15.1"
|
id "com.gradle.enterprise" version "3.16.1"
|
||||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12"
|
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
@@ -16,8 +16,8 @@ import java.nio.file.Files
|
|||||||
import java.util.zip.GZIPOutputStream
|
import java.util.zip.GZIPOutputStream
|
||||||
|
|
||||||
class BaseInitScriptTest extends Specification {
|
class BaseInitScriptTest extends Specification {
|
||||||
static final String GE_PLUGIN_VERSION = '3.15.1'
|
static final String GE_PLUGIN_VERSION = '3.16.1'
|
||||||
static final String CCUD_PLUGIN_VERSION = '1.12'
|
static final String CCUD_PLUGIN_VERSION = '1.12.1'
|
||||||
|
|
||||||
static final TestGradleVersion GRADLE_3_X = new TestGradleVersion(GradleVersion.version('3.5.1'), 7, 9)
|
static final TestGradleVersion GRADLE_3_X = new TestGradleVersion(GradleVersion.version('3.5.1'), 7, 9)
|
||||||
static final TestGradleVersion GRADLE_4_X = new TestGradleVersion(GradleVersion.version('4.10.3'), 7, 10)
|
static final TestGradleVersion GRADLE_4_X = new TestGradleVersion(GradleVersion.version('4.10.3'), 7, 10)
|
||||||
@@ -26,7 +26,7 @@ class BaseInitScriptTest extends Specification {
|
|||||||
static final TestGradleVersion GRADLE_6_X = new TestGradleVersion(GradleVersion.version('6.9.4'), 8, 15)
|
static final TestGradleVersion GRADLE_6_X = new TestGradleVersion(GradleVersion.version('6.9.4'), 8, 15)
|
||||||
static final TestGradleVersion GRADLE_7_X = new TestGradleVersion(GradleVersion.version('7.6.2'), 8, 19)
|
static final TestGradleVersion GRADLE_7_X = new TestGradleVersion(GradleVersion.version('7.6.2'), 8, 19)
|
||||||
static final TestGradleVersion GRADLE_8_0 = new TestGradleVersion(GradleVersion.version('8.0.2'), 8, 19)
|
static final TestGradleVersion GRADLE_8_0 = new TestGradleVersion(GradleVersion.version('8.0.2'), 8, 19)
|
||||||
static final TestGradleVersion GRADLE_8_X = new TestGradleVersion(GradleVersion.version('8.4'), 8, 19)
|
static final TestGradleVersion GRADLE_8_X = new TestGradleVersion(GradleVersion.version('8.5'), 8, 19)
|
||||||
|
|
||||||
static final List<TestGradleVersion> ALL_VERSIONS = [
|
static final List<TestGradleVersion> ALL_VERSIONS = [
|
||||||
GRADLE_3_X, // First version where TestKit supports environment variables
|
GRADLE_3_X, // First version where TestKit supports environment variables
|
||||||
|
@@ -148,13 +148,30 @@ class TestBuildResultRecorder extends BaseInitScriptTest {
|
|||||||
testGradleVersion << ALL_VERSIONS
|
testGradleVersion << ALL_VERSIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "produces no build results file when RUNNER_TEMP dir is not a writable directory with #testGradleVersion"() {
|
||||||
|
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
||||||
|
|
||||||
|
when:
|
||||||
|
def invalidDir = new File(testProjectDir, 'invalid-runner-temp')
|
||||||
|
invalidDir.createNewFile()
|
||||||
|
|
||||||
|
run(['help'], initScript, testGradleVersion.gradleVersion, [], [RUNNER_TEMP: invalidDir.absolutePath])
|
||||||
|
|
||||||
|
then:
|
||||||
|
def buildResultsDir = new File(testProjectDir, '.build-results')
|
||||||
|
assert !buildResultsDir.exists()
|
||||||
|
|
||||||
|
where:
|
||||||
|
testGradleVersion << ALL_VERSIONS
|
||||||
|
}
|
||||||
|
|
||||||
def "produces build results file with build scan when GE plugin is applied in settingsEvaluated"() {
|
def "produces build results file with build scan when GE plugin is applied in settingsEvaluated"() {
|
||||||
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
||||||
|
|
||||||
when:
|
when:
|
||||||
settingsFile.text = """
|
settingsFile.text = """
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.gradle.enterprise' version '3.15.1' apply(false)
|
id 'com.gradle.enterprise' version '3.16.1' apply(false)
|
||||||
}
|
}
|
||||||
gradle.settingsEvaluated {
|
gradle.settingsEvaluated {
|
||||||
apply plugin: 'com.gradle.enterprise'
|
apply plugin: 'com.gradle.enterprise'
|
||||||
|
@@ -49,7 +49,7 @@ test('will cleanup unused gradle versions', async () => {
|
|||||||
|
|
||||||
const gradle802 = path.resolve(gradleUserHome, "caches/8.0.2")
|
const gradle802 = path.resolve(gradleUserHome, "caches/8.0.2")
|
||||||
const wrapper802 = path.resolve(gradleUserHome, "wrapper/dists/gradle-8.0.2-bin")
|
const wrapper802 = path.resolve(gradleUserHome, "wrapper/dists/gradle-8.0.2-bin")
|
||||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.4")
|
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.5")
|
||||||
|
|
||||||
expect(fs.existsSync(gradle802)).toBe(true)
|
expect(fs.existsSync(gradle802)).toBe(true)
|
||||||
expect(fs.existsSync(wrapper802)).toBe(true)
|
expect(fs.existsSync(wrapper802)).toBe(true)
|
||||||
|
@@ -17,10 +17,4 @@ describe('cacheUtils-utils', () => {
|
|||||||
expect(posixHash).toBe(windowsHash)
|
expect(posixHash).toBe(windowsHash)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
describe('sanitizes workflow name in cache key', () => {
|
|
||||||
it('with comma', () => {
|
|
||||||
const cacheKey = cacheUtils.getCacheKeyForJob("Workflow, with,commas", "JOB_ID")
|
|
||||||
expect(cacheKey).toBe('workflow withcommas-JOB_ID')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user