mirror of
https://github.com/gradle/gradle-build-action.git
synced 2025-10-19 22:38:58 +08:00
Compare commits
88 Commits
v2.8.1
...
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 | ||
|
87a9a15658 | ||
|
375481748b | ||
|
beff1c573c | ||
|
21a3ebb55f | ||
|
a5be560235 | ||
|
9bca466e27 | ||
|
f757bcfd86 | ||
|
8b6c211905 | ||
|
8db1c7621b | ||
|
6eaacfc06c | ||
|
750905c0ed | ||
|
d5fbcc8361 | ||
|
0e761ca2b4 | ||
|
6b7c087721 | ||
|
0bfe00a136 | ||
|
62cce3c597 | ||
|
842c587ad8 | ||
|
4241e05054 | ||
|
bfa3c0508e | ||
|
c3bdce8205 | ||
|
f92e7c3428 | ||
|
d1b726d8c1 | ||
|
6fcc109efa | ||
|
fde5b4fcde | ||
|
324fbdc804 | ||
|
5658338fb0 | ||
|
87ccc98a2a | ||
|
4441c9f9bf |
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
.github/workflow-samples/gradle-plugin/gradlew
vendored
14
.github/workflow-samples/gradle-plugin/gradlew
vendored
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
.github/workflow-samples/groovy-dsl/gradlew
vendored
14
.github/workflow-samples/groovy-dsl/gradlew
vendored
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.15"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.11.2"
|
||||
id "com.gradle.enterprise" version "3.16.1"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1"
|
||||
}
|
||||
|
||||
gradleEnterprise {
|
||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
.github/workflow-samples/java-toolchain/gradlew
vendored
14
.github/workflow-samples/java-toolchain/gradlew
vendored
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
@@ -8,9 +8,9 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
api("org.apache.commons:commons-math3:3.6.1")
|
||||
implementation("com.google.guava:guava:32.1.2-jre")
|
||||
implementation("com.google.guava:guava:33.0.0-jre")
|
||||
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.10.1")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
|
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=591855b517fc635b9e04de1d05d5e76ada3f89f5fc76f87978d1b245b4f69225
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
|
||||
distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
14
.github/workflow-samples/kotlin-dsl/gradlew
vendored
14
.github/workflow-samples/kotlin-dsl/gradlew
vendored
@@ -145,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
@@ -153,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC3045
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -202,11 +202,11 @@ fi
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
|
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id("com.gradle.enterprise") version "3.15"
|
||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.11.2"
|
||||
id("com.gradle.enterprise") version "3.16.1"
|
||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.12.1"
|
||||
}
|
||||
|
||||
gradleEnterprise {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.build-scan" version "3.15"
|
||||
id "com.gradle.build-scan" version "3.16.1"
|
||||
}
|
||||
|
||||
gradleEnterprise {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.15"
|
||||
id "com.gradle.enterprise" version "3.16.1"
|
||||
}
|
||||
|
||||
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.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# 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).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -67,4 +67,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- 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}}-
|
||||
|
||||
caching-config:
|
||||
uses: ./.github/workflows/integ-test-action-inputs-caching.yml
|
||||
uses: ./.github/workflows/integ-test-caching-config.yml
|
||||
with:
|
||||
cache-key-prefix: ${{github.run_number}}-
|
||||
|
||||
dependency-graph:
|
||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
cache-key-prefix: ${{github.run_number}}-
|
||||
|
||||
@@ -56,10 +58,12 @@ jobs:
|
||||
with:
|
||||
cache-key-prefix: ${{github.run_number}}-
|
||||
|
||||
# restore-configuration-cache:
|
||||
# uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||
# with:
|
||||
# cache-key-prefix: ${{github.run_number}}-
|
||||
restore-configuration-cache:
|
||||
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||
with:
|
||||
cache-key-prefix: ${{github.run_number}}-
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
restore-custom-gradle-home:
|
||||
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
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2.8.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
|
||||
working-directory: test/init-scripts
|
||||
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
|
||||
uses: ./
|
||||
with:
|
||||
gradle-version: 8.3
|
||||
gradle-version: 8.5
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm install
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
caching-config:
|
||||
needs: build-distribution
|
||||
uses: ./.github/workflows/integ-test-action-inputs-caching.yml
|
||||
uses: ./.github/workflows/integ-test-caching-config.yml
|
||||
with:
|
||||
runner-os: '["ubuntu-latest"]'
|
||||
download-dist: true
|
||||
@@ -53,6 +53,8 @@ jobs:
|
||||
dependency-graph:
|
||||
needs: build-distribution
|
||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '["ubuntu-latest"]'
|
||||
download-dist: true
|
||||
@@ -87,12 +89,14 @@ jobs:
|
||||
runner-os: '["ubuntu-latest"]'
|
||||
download-dist: true
|
||||
|
||||
# restore-configuration-cache:
|
||||
# needs: build-distribution
|
||||
# uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||
# with:
|
||||
# runner-os: '["ubuntu-latest"]'
|
||||
# download-dist: true
|
||||
restore-configuration-cache:
|
||||
needs: build-distribution
|
||||
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||
with:
|
||||
runner-os: '["ubuntu-latest"]'
|
||||
download-dist: true
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
restore-containerized-gradle-home:
|
||||
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
|
||||
|
||||
# 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' }}
|
||||
with:
|
||||
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
|
||||
|
||||
jobs:
|
||||
run-gradle-builds:
|
||||
many-gradle-builds:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -42,6 +42,27 @@ jobs:
|
||||
continue-on-error: true
|
||||
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:
|
||||
runs-on: ubuntu-latest
|
||||
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:
|
||||
pull_request:
|
||||
types: [assigned, review_requested]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
gradle:
|
||||
successful-build-with-always-comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: ./
|
||||
with:
|
||||
add-job-summary-as-pr-comment: always
|
||||
- name: Run build with Gradle wrapper
|
||||
id: gradle
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew build --scan
|
||||
- name: "Add Build Scan URL as PR comment"
|
||||
uses: actions/github-script@v6
|
||||
|
||||
successful-build-with-comment-on-failure:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: ./
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'PR ready for review: ${{ steps.gradle.outputs.build-scan-url }}'
|
||||
})
|
||||
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 build --scan
|
||||
|
||||
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:
|
||||
workflow_call:
|
||||
@@ -38,7 +38,8 @@ jobs:
|
||||
enterprise
|
||||
# Exclude build-cache from main cache entry
|
||||
gradle-home-cache-excludes: |
|
||||
caches/build-cache-1
|
||||
caches/build-cache-*
|
||||
caches/*/executionHistory
|
||||
- name: Build using Gradle wrapper
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: ./gradlew test
|
||||
@@ -63,7 +64,8 @@ jobs:
|
||||
caches
|
||||
enterprise
|
||||
gradle-home-cache-excludes: |
|
||||
caches/build-cache-1
|
||||
caches/build-cache-*
|
||||
caches/*/executionHistory
|
||||
cache-read-only: true
|
||||
- name: Execute Gradle build with --offline
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
@@ -90,7 +92,7 @@ jobs:
|
||||
run: gradle help "-DgradleVersionCheck=${{matrix.gradle}}"
|
||||
- name: Check Build Scan url is captured
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
@@ -113,7 +115,7 @@ jobs:
|
||||
run: gradle help "-DgradleVersionCheck=${{matrix.gradle}}"
|
||||
- name: Check Build Scan url is captured
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
@@ -12,6 +12,9 @@ on:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: dependency-graph-${{ inputs.cache-key-prefix }}
|
||||
@@ -31,7 +34,7 @@ jobs:
|
||||
- name: Setup Gradle for dependency-graph generate
|
||||
uses: ./
|
||||
with:
|
||||
dependency-graph: generate
|
||||
dependency-graph: generate-and-upload
|
||||
- name: Run gradle build
|
||||
run: ./gradlew build
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
@@ -55,7 +58,7 @@ jobs:
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
|
||||
submit:
|
||||
needs: [groovy-generate, kotlin-generate]
|
||||
needs: [groovy-generate]
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -80,7 +83,7 @@ jobs:
|
||||
- name: Setup Gradle for dependency-graph generate
|
||||
uses: ./
|
||||
with:
|
||||
dependency-graph: generate
|
||||
dependency-graph: generate-and-submit
|
||||
- id: gradle-assemble
|
||||
run: ./gradlew assemble
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
|
@@ -21,6 +21,7 @@ jobs:
|
||||
# Test that pre-installed runner JDKs are detected
|
||||
pre-installed-toolchains:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -35,7 +36,7 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: |
|
||||
gradle -q javaToolchains > output.txt
|
||||
gradle --info javaToolchains > output.txt
|
||||
cat output.txt
|
||||
- name: Verify detected toolchains
|
||||
shell: bash
|
||||
@@ -44,10 +45,12 @@ jobs:
|
||||
grep -q 'Eclipse Temurin JDK 1.8' output.txt || (echo "::error::Did not detect preinstalled JDK 1.8" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 11' output.txt || (echo "::error::Did not detect preinstalled JDK 11" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 17' output.txt || (echo "::error::Did not detect preinstalled JDK 17" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 21' output.txt || (echo "::error::Did not detect preinstalled JDK 21" && exit 1)
|
||||
|
||||
# Test that JDKs provisioned by setup-java are detected
|
||||
setup-java-installed-toolchain:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -57,12 +60,12 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java 20
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '20'
|
||||
- name: Setup Java 16
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '16'
|
||||
@@ -72,42 +75,19 @@ jobs:
|
||||
shell: bash
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: |
|
||||
gradle -q javaToolchains > output.txt
|
||||
gradle --info javaToolchains > output.txt
|
||||
cat output.txt
|
||||
- name: Verify detected toolchains
|
||||
- name: Verify setup JDKs are detected
|
||||
shell: bash
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: |
|
||||
grep -q 'Eclipse Temurin JDK 16' output.txt || (echo "::error::Did not detect setup-java installed JDK 16" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 20' output.txt || (echo "::error::Did not detect setup-java installed JDK 20" && exit 1)
|
||||
|
||||
# Test that predefined JDK detection property is not overwritten by action
|
||||
check-no-overwrite:
|
||||
strategy:
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Configure java installations env var in Gradle User Home
|
||||
- name: Verify pre-installed toolchains are detected
|
||||
shell: bash
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: |
|
||||
mkdir -p ~/.gradle
|
||||
echo "org.gradle.java.installations.fromEnv=XXXXX" > ~/.gradle/gradle.properties
|
||||
- name: Setup Gradle
|
||||
uses: ./
|
||||
- name: Check gradle.properties
|
||||
shell: bash
|
||||
run: |
|
||||
cat ~/.gradle/gradle.properties
|
||||
if grep -q 'org.gradle.java.installations.fromEnv=JAVA_HOME' ~/.gradle/gradle.properties ; then
|
||||
echo 'Found overwritten fromEnv'
|
||||
exit 1
|
||||
fi
|
||||
if ! grep -q 'org.gradle.java.installations.fromEnv=XXXXX' ~/.gradle/gradle.properties ; then
|
||||
echo 'Did NOT find original fromEnv'
|
||||
exit 1
|
||||
fi
|
||||
grep -q 'Eclipse Temurin JDK 1.8' output.txt || (echo "::error::Did not detect preinstalled JDK 1.8" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 11' output.txt || (echo "::error::Did not detect preinstalled JDK 11" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 17' output.txt || (echo "::error::Did not detect preinstalled JDK 17" && exit 1)
|
||||
grep -q 'Eclipse Temurin JDK 21' output.txt || (echo "::error::Did not detect preinstalled JDK 21" && exit 1)
|
||||
|
4
.github/workflows/integ-test-execution.yml
vendored
4
.github/workflows/integ-test-execution.yml
vendored
@@ -72,7 +72,7 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
arguments: help -DgradleVersionCheck=${{matrix.gradle}}
|
||||
- name: Check Build Scan url
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
|
@@ -21,8 +21,8 @@ env:
|
||||
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
||||
GRADLE_ENTERPRISE_URL: https://ge.solutions-team.gradle.com
|
||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15
|
||||
GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.11.2
|
||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1
|
||||
GRADLE_ENTERPRISE_CCUD_PLUGIN_VERSION: 1.12.1
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
|
||||
|
||||
jobs:
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
run: gradle help
|
||||
- name: Check Build Scan url
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
|
@@ -62,7 +62,7 @@ jobs:
|
||||
gradle-version: current
|
||||
- name: Check current version output parameter
|
||||
if: ${{ !startsWith(steps.gradle-current.outputs.gradle-version , '8.') }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Gradle version parameter not set correctly: value was "${{ steps.gradle-current.outputs.gradle-version }}"')
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 8
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
gradle-version: ${{ matrix.gradle }}
|
||||
- name: Check output parameter
|
||||
if: ${{ steps.setup-gradle.outputs.gradle-version != matrix.gradle }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('Gradle version parameter not set correctly: value was "${{ steps.setup-gradle.outputs.gradle-version }}"')
|
||||
@@ -108,7 +108,7 @@ jobs:
|
||||
run: gradle help "-DgradleVersionCheck=${{matrix.gradle}}"
|
||||
- name: Check Build Scan url
|
||||
if: ${{ !steps.gradle.outputs.build-scan-url }}
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
core.setFailed('No Build Scan detected')
|
||||
|
@@ -11,6 +11,9 @@ on:
|
||||
download-dist:
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY:
|
||||
required: true
|
||||
|
||||
env:
|
||||
DOWNLOAD_DIST: ${{ inputs.download-dist }}
|
||||
@@ -34,9 +37,11 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
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
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: ./gradlew test --configuration-cache
|
||||
run: gradle test --configuration-cache
|
||||
|
||||
verify-build-groovy:
|
||||
env:
|
||||
@@ -55,12 +60,14 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
cache-read-only: true
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
gradle-version: 8.6-rc-1
|
||||
- name: Groovy build with configuration-cache enabled
|
||||
id: execute
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: ./gradlew test --configuration-cache
|
||||
run: gradle test --configuration-cache
|
||||
- name: Check that configuration-cache was used
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
@@ -88,9 +95,11 @@ jobs:
|
||||
GRADLE_BUILD_ACTION_SKIP_RESTORE: "generated-gradle-jars|wrapper-zips|java-toolchains|instrumented-jars|dependencies|kotlin-dsl"
|
||||
with:
|
||||
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)
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: ./gradlew test --configuration-cache
|
||||
run: gradle test --configuration-cache
|
||||
|
||||
seed-build-kotlin:
|
||||
env:
|
||||
@@ -108,9 +117,11 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
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
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew help --configuration-cache
|
||||
run: gradle help --configuration-cache
|
||||
|
||||
modify-build-kotlin:
|
||||
env:
|
||||
@@ -129,9 +140,11 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
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
|
||||
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
|
||||
verify-build-kotlin:
|
||||
@@ -151,12 +164,14 @@ jobs:
|
||||
uses: ./
|
||||
with:
|
||||
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
|
||||
id: execute
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew test --configuration-cache
|
||||
run: gradle test --configuration-cache
|
||||
- name: Check that configuration-cache was used
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs')
|
||||
|
@@ -24,7 +24,7 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: temurin
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
- name: Download distribution if required
|
||||
uses: ./.github/actions/download-dist
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 11
|
||||
distribution: temurin
|
||||
|
@@ -1,3 +1,3 @@
|
||||
# Configuration file for asdf version manager
|
||||
nodejs 16.18.1
|
||||
gradle 8.3
|
||||
nodejs 20.10.0
|
||||
gradle 8.5
|
||||
|
409
README.md
409
README.md
@@ -4,22 +4,22 @@ This GitHub Action can be used to configure Gradle and optionally execute a Grad
|
||||
|
||||
## 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:
|
||||
|
||||
- Easily [configure your workflow to use a specific version of Gradle](#use-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||
- More sophisticated and more efficient caching of Gradle User Home between invocations, compared to `setup-java` and most custom configurations using `actions/cache`. [More details below](#caching).
|
||||
- Easily [configure your workflow to use a specific version of Gradle](#choose-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||
- More sophisticated and more efficient caching of Gradle User Home between invocations, compared to `setup-java` and most custom configurations using `actions/cache`. [More details below](#caching-build-state-between-jobs).
|
||||
- Detailed reporting of cache usage and cache configuration options allow you to [optimize the use of the GitHub actions cache](#optimizing-cache-effectiveness).
|
||||
- [Generate and Submit a GitHub Dependency Graph](#github-dependency-graph-support) for your project, enabling Dependabot security alerts.
|
||||
- [Automatic capture of Build Scan® links](#build-scans) from the build, making these easier to locate for workflow run.
|
||||
- [Automatic capture of Build Scan® links](#build-reporting) from the build, making these easier to locate for workflow run.
|
||||
|
||||
The `gradle-build-action` is designed to provide these benefits with minimal configuration.
|
||||
These features work both when Gradle is executed via the `gradle-build-action` and for any Gradle execution in subsequent steps.
|
||||
|
||||
## Use the action to setup Gradle
|
||||
|
||||
The recommended way to use the `gradle-build-action` is in an initial "Setup Gradle" step, with subsquent steps invoking Gradle directly with a `run` step. This makes the action minimally invasive, and allows a workflow to configure and execute a Gradle execution in any way.
|
||||
The recommended way to use the `gradle-build-action` is in an initial "Setup Gradle" step, with subsequent steps invoking Gradle directly with a `run` step. This makes the action minimally invasive, and allows a workflow to configure and execute a Gradle execution in any way.
|
||||
|
||||
The `gradle-build-action` works by configuring environment variables and by adding a set of Gradle init-scripts to the Gradle User Home. These will apply to all Gradle executions on the runner, no matter how Gradle is invoked.
|
||||
This means that if you have an existing workflow that executes Gradle with a `run` step, you can add an initial "Setup Gradle" Step to benefit from caching, build-scan capture and other features of the gradle-build-action.
|
||||
@@ -35,8 +35,8 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
@@ -84,8 +84,8 @@ jobs:
|
||||
gradle-rc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
@@ -97,7 +97,6 @@ jobs:
|
||||
- run: echo "The release-candidate version was ${{ steps.setup-gradle.outputs.gradle-version }}"
|
||||
```
|
||||
|
||||
|
||||
## 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.
|
||||
@@ -161,6 +160,31 @@ If you want override the default and have the `gradle-build-action` caches overw
|
||||
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
|
||||
|
||||
When using `gradle-build-action` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
||||
@@ -174,7 +198,7 @@ Using either of these mechanisms may interfere with the caching provided by this
|
||||
### Cache debugging and analysis
|
||||
|
||||
A report of all cache entries restored and saved is printed to the Job Summary when saving the cache entries.
|
||||
This report can provide valuable insignt into how much cache space is being used.
|
||||
This report can provide valuable insight into how much cache space is being used.
|
||||
|
||||
It is possible to enable additional debug logging for cache operations. You do via the `GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED` environment variable:
|
||||
|
||||
@@ -213,7 +237,7 @@ In certain cases a particular section of Gradle User Home will be too large to m
|
||||
### Cache keys
|
||||
|
||||
The actual content of the Gradle User Home after a build is the result of many factors, including:
|
||||
- Core Gradle build files (`settngs.gradle[.kts]`, `build.gradle[.kts]`, `gradle.properties`)
|
||||
- Core Gradle build files (`settings.gradle[.kts]`, `build.gradle[.kts]`, `gradle.properties`)
|
||||
- Associated Gradle configuration files (`gradle-wrapper.properties`, `dependencies.toml`, etc)
|
||||
- The entire content of `buildSrc` or any included builds that provide plugins.
|
||||
- The entire content of the repository, in the case of the local build cache.
|
||||
@@ -235,9 +259,9 @@ This allows the most recent state to always be available in the GitHub actions c
|
||||
### 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:
|
||||
- An exact match on OS, workflow, job, 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 and job
|
||||
- An exact match on OS, workflow name, job id, matrix and Git SHA
|
||||
- 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 and job id
|
||||
- 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.
|
||||
@@ -300,7 +324,7 @@ without writing private cache entries that could lead to evicting shared entries
|
||||
|
||||
If you have other long-lived development branches that would benefit from writing to the cache,
|
||||
you can configure these by overriding the `cache-read-only` action parameter.
|
||||
See [Using the caches read-only](#using-the-caches-read-only) for more details.
|
||||
See [Using the cache read-only](#using-the-cache-read-only) for more details.
|
||||
|
||||
Similarly, you could use `cache-read-only` for certain jobs in the workflow, and instead have these jobs reuse the cache content from upstream jobs.
|
||||
|
||||
@@ -344,62 +368,57 @@ gradle-home-cache-cleanup: true
|
||||
|
||||
## Build reporting
|
||||
|
||||
The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, and reports these via
|
||||
a Job Summary, visible in the GitHub Actions UI. For each Gradle execution, details about the invocation are listed, together with
|
||||
a link to any Build Scan® published.
|
||||
The `gradle-build-action` collects information about any Gradle executions that occur in a workflow, including the root project,
|
||||
requested tasks, build outcome and any Build Scan link generated. Details of cache entries read and written are also collected.
|
||||
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
|
||||
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)
|
||||
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.
|
||||
### Adding Job Summary as a Pull Request comment
|
||||
|
||||
### 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
|
||||
# .github/workflows/gradle-build-pr.yml
|
||||
name: Run Gradle on PRs
|
||||
on: pull_request
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
gradle:
|
||||
run-gradle-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- 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()
|
||||
uses: gradle/gradle-build-action@v3
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
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 }}'
|
||||
})
|
||||
add-job-summary-as-pr-comment: on-failure # Valid values are 'never' (default), 'always', and 'on-failure'
|
||||
- run: ./gradlew build --scan
|
||||
```
|
||||
|
||||
### 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.
|
||||
For example:
|
||||
@@ -410,114 +429,26 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Run build with Gradle wrapper
|
||||
run: ./gradlew build --scan
|
||||
- name: Upload build reports
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: 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.
|
||||
|
||||
```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@v3
|
||||
- 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.
|
||||
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.
|
||||
|
||||
## Support for GitHub Enterprise Server (GHES)
|
||||
|
||||
@@ -533,7 +464,7 @@ The `gradle-build-action` has support for submitting a [GitHub Dependency Graph]
|
||||
|
||||
The dependency graph snapshot is generated via integration with the [GitHub Dependency Graph Gradle Plugin](https://plugins.gradle.org/plugin/org.gradle.github-dependency-graph-gradle-plugin), and saved as a workflow artifact. The generated snapshot files can be submitted either in the same job, or in a subsequent job (in the same or a dependent workflow).
|
||||
|
||||
The generated dependency graph snapshot reports all of the dependencies that were resolved during a bulid execution, and is used by GitHub to generate [Dependabot Alerts](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts) for vulnerable dependencies, as well as to populate the [Dependency Graph insights view](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/exploring-the-dependencies-of-a-repository#viewing-the-dependency-graph).
|
||||
The generated dependency graph snapshot reports all of the dependencies that were resolved during a build execution, and is used by GitHub to generate [Dependabot Alerts](https://docs.github.com/en/code-security/dependabot/dependabot-alerts/about-dependabot-alerts) for vulnerable dependencies, as well as to populate the [Dependency Graph insights view](https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/exploring-the-dependencies-of-a-repository#viewing-the-dependency-graph).
|
||||
|
||||
## Enable Dependency Graph generation for a workflow
|
||||
|
||||
@@ -546,11 +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. |
|
||||
| `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. |
|
||||
|
||||
Dependency Graph _submission_ (but not generation) requires the `contents: write` permission, which may need to be explicitly enabled in the workflow file.
|
||||
|
||||
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
|
||||
name: Submit dependency graph
|
||||
name: CI build
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -561,19 +490,89 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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, generating the dependency graph snapshot which will be submitted
|
||||
- name: Run the usual CI build (dependency-graph will be generated and submitted post-job)
|
||||
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.
|
||||
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).
|
||||
|
||||
The above configuration will work for workflows that run as a result of commits to a repository branch, but not when a workflow is triggered by a PR from a repository fork.
|
||||
For a configuration that supports this setup, see [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows).
|
||||
> [!IMPORTANT]
|
||||
> The above configuration will work for workflows that run as a result of commits to a repository branch,
|
||||
> but not when a workflow is triggered by a PR from a repository fork.
|
||||
> This is because the `contents: write` permission is not available when executing a workflow
|
||||
> 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).
|
||||
|
||||
### 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`
|
||||
|
||||
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
|
||||
understand dependency changes (and the security impact of these changes) for a pull request.
|
||||
For the `dependency-review-action` to succeed, it must run _after_ the dependency graph has been submitted for a PR.
|
||||
|
||||
When using `generate-and-submit`, dependency graph files are submitted at the end of the job, after all steps have been
|
||||
executed. For this reason, the `dependency-review-action` must be executed in a dependent job,
|
||||
and not as a subsequent step in the job that generates the dependency graph.
|
||||
|
||||
Example of a pull request workflow that executes a build for a pull request and runs the `dependency-review-action`:
|
||||
|
||||
```yaml
|
||||
name: PR check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
# Note that this permission will not be available if the PR is from a forked repository
|
||||
|
||||
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 and generate the dependency graph which will be submitted post-job
|
||||
run: ./gradlew build
|
||||
|
||||
dependency-review:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
- name: Perform dependency review
|
||||
uses: actions/dependency-review-action@v3
|
||||
```
|
||||
|
||||
See [Dependency Graphs for pull request workflows](#dependency-graphs-for-pull-request-workflows) for a more complex
|
||||
(and less functional) example that will work for pull requests submitted from forked repositories.
|
||||
|
||||
## Limiting the scope of the dependency graph
|
||||
|
||||
@@ -583,7 +582,7 @@ For example, a vulnerability in the tool you use to generate documentation is un
|
||||
There are a number of techniques you can employ to limit the scope of the generated dependency graph:
|
||||
- [Don't generate a dependency graph for all Gradle executions](#choosing-which-gradle-invocations-will-generate-a-dependency-graph)
|
||||
- [For a Gradle execution, filter which Gradle projects and configurations will contribute dependencies](#filtering-which-gradle-configurations-contribute-to-the-dependency-graph)
|
||||
- [Use a separate workflow that only resolves the required dependencies]()
|
||||
- [Use a separate workflow that only resolves the required dependencies](#use-a-dedicated-workflow-for-dependency-graph-generation)
|
||||
|
||||
> [!NOTE]
|
||||
> Ideally, all dependencies involved in building and testing a project will be extracted and reported in a dependency graph.
|
||||
@@ -600,7 +599,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Gradle to generate and submit dependency graphs
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
@@ -632,12 +631,12 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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, generating the dependency graph from 'runtimeClasspath' configurations
|
||||
- name: Run a build, generating the dependency graph from any resolved 'runtimeClasspath' configurations
|
||||
run: ./gradlew build
|
||||
env:
|
||||
DEPENDENCY_GRAPH_INCLUDE_PROJECTS: "^:(?!buildSrc).*"
|
||||
@@ -646,26 +645,27 @@ jobs:
|
||||
|
||||
### 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.
|
||||
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.
|
||||
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 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
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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: Extract the 'runtimeClasspath' dependencies for 'my-app'
|
||||
run: ./gradlew :my-app:dependencies --configuration runtimeClasspath
|
||||
- name: Build the distribution Zip for `my-app`
|
||||
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.
|
||||
|
||||
## Dependency Graphs for pull request workflows
|
||||
@@ -682,11 +682,14 @@ Note: when `download-and-submit` is used in a workflow triggered via [workflow_r
|
||||
```yaml
|
||||
name: run-build-and-generate-dependency-snapshot
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Gradle to generate and submit dependency graphs
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
@@ -705,15 +708,48 @@ on:
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
submit-snapshots:
|
||||
submit-dependency-graph:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Retrieve dependency graph artifact and submit
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Retrieve dependency graph artifact and submit
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
dependency-graph: download-and-submit
|
||||
```
|
||||
|
||||
### Integrating `dependency-review-action` for pull request workflows
|
||||
|
||||
The GitHub [dependency-review-action](https://github.com/actions/dependency-review-action) helps you
|
||||
understand dependency changes (and the security impact of these changes) for a pull request.
|
||||
|
||||
To integrate the `dependency-review-action` into the pull request workflows above, a separate workflow should be added.
|
||||
This workflow will be triggered directly on `pull_request`, but will need to wait until the dependency graph results are
|
||||
submitted before the dependency review can complete. How long to wait is controlled by the `retry-on-snapshot-warnings` input parameters.
|
||||
|
||||
Here's an example of a separate "Dependency Review" workflow that will wait for 10 minutes for the PR check workflow to complete.
|
||||
|
||||
```yaml
|
||||
name: dependency-review
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
with:
|
||||
retry-on-snapshot-warnings: true
|
||||
retry-on-snapshot-warnings-timeout: 600
|
||||
```
|
||||
|
||||
The `retry-on-snapshot-warnings-timeout` (in seconds) needs to be long enough to allow the entire `run-build-and-generate-dependency-snapshot` and `submit-dependency-snapshot` workflows (above) to complete.
|
||||
|
||||
## Gradle version compatibility
|
||||
|
||||
The GitHub Dependency Graph plugin should be compatible with all versions of Gradle >= 5.0, and has been tested against
|
||||
@@ -727,6 +763,23 @@ limited to Gradle "8.1.0" and later:
|
||||
To use this plugin with versions of Gradle older than "8.1.0", you'll need to invoke Gradle with the
|
||||
configuration-cache disabled.
|
||||
|
||||
## Reducing storage costs for saved dependency graph artifacts
|
||||
|
||||
When `generate` or `generate-and-submit` is used with the action, the dependency graph that is generated is stored as a workflow artifact.
|
||||
By default, these artifacts are retained for a period of 30 days (or as configured for the repository).
|
||||
To reduce storage costs for these artifacts, you can set the `artifact-retention-days` value to a lower number.
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- name: Generate dependency graph, but only retain artifact for one day
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
dependency-graph: generate
|
||||
artifact-retention-days: 1
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 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.
|
||||
@@ -746,21 +799,21 @@ name: Run build with Gradle Enterprise injection
|
||||
env:
|
||||
GRADLE_ENTERPRISE_INJECTION_ENABLED: true
|
||||
GRADLE_ENTERPRISE_URL: https://ge.gradle.org
|
||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.15
|
||||
GRADLE_ENTERPRISE_PLUGIN_VERSION: 3.16.1
|
||||
GRADLE_ENTERPRISE_ACCESS_KEY: ${{ secrets.GE_ACCESS_KEY }} # Required to publish scans to ge.gradle.org
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Run a Gradle build with Gradle Enterprise injection enabled
|
||||
run: ./gradlew build
|
||||
```
|
||||
|
||||
This configuration will automatically apply `v3.15` 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,
|
||||
but will be used by the GE plugin in order to authenticate with the server.
|
||||
|
64
action.yml
64
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.
|
||||
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:
|
||||
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
||||
required: false
|
||||
@@ -40,6 +32,13 @@ inputs:
|
||||
required: 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:
|
||||
description: Paths within Gradle User Home to cache.
|
||||
required: false
|
||||
@@ -54,19 +53,51 @@ inputs:
|
||||
# gradle-home-cache-excludes: |
|
||||
# caches/build-cache-1
|
||||
|
||||
gradle-home-cache-cleanup:
|
||||
description: When 'true', the action will attempt to remove any stale/unused entries from the Gradle User Home prior to saving to the GitHub Actions cache.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
add-job-summary:
|
||||
description: Specifies when a Job Summary should be inluded in the action results. Valid values are 'never', 'always' (default), and 'on-failure'.
|
||||
required: false
|
||||
default: 'always'
|
||||
|
||||
add-job-summary-as-pr-comment:
|
||||
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
|
||||
default: 'never'
|
||||
|
||||
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', 'generate-and-upload' and 'download-and-submit'.
|
||||
required: false
|
||||
default: 'disabled'
|
||||
|
||||
artifact-retention-days:
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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'.
|
||||
required: false
|
||||
default: 'disabled'
|
||||
deprecation-message: Superceded by the new 'add-job-summary' and 'add-job-summary-as-pr-comment' parameters.
|
||||
|
||||
# EXPERIMENTAL & INTERNAL ACTION INPUTS
|
||||
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
||||
@@ -76,14 +107,11 @@ inputs:
|
||||
description: When 'true', the action will not attempt to restore the Gradle User Home entries from other Jobs.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
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).
|
||||
required: false
|
||||
default: ${{ toJSON(matrix) }}
|
||||
gradle-home-cache-cleanup:
|
||||
description: When 'true', the action will attempt to remove any stale/unused entries from the Gradle User Home prior to saving to the GitHub Actions cache.
|
||||
required: false
|
||||
default: false
|
||||
|
||||
github-token:
|
||||
description: The GitHub token used to authenticate when submitting via the Dependency Submission API.
|
||||
@@ -99,7 +127,7 @@ outputs:
|
||||
description: Version of Gradle that was setup by the action
|
||||
|
||||
runs:
|
||||
using: 'node16'
|
||||
using: 'node20'
|
||||
main: 'dist/main/index.js'
|
||||
post: 'dist/post/index.js'
|
||||
|
||||
|
82798
dist/main/index.js
vendored
82798
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
81953
dist/post/index.js
vendored
81953
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
3848
package-lock.json
generated
3848
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@@ -8,11 +8,9 @@
|
||||
"format": "prettier --write **/*.ts",
|
||||
"format-check": "prettier --check **/*.ts",
|
||||
"lint": "eslint src/**/*.ts",
|
||||
|
||||
"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": "npm run compile-main && npm run compile-post",
|
||||
|
||||
"test": "jest",
|
||||
"check": "npm run format && npm run lint",
|
||||
"build": "npm run check && npm run compile",
|
||||
@@ -30,32 +28,34 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@actions/artifact": "1.1.2",
|
||||
"@actions/artifact": "2.0.0",
|
||||
"@actions/cache": "3.2.2",
|
||||
"@actions/core": "1.10.1",
|
||||
"@actions/exec": "1.1.1",
|
||||
"@actions/github": "5.1.1",
|
||||
"@actions/github": "6.0.0",
|
||||
"@actions/glob": "0.4.0",
|
||||
"@actions/http-client": "2.1.1",
|
||||
"@actions/http-client": "2.2.0",
|
||||
"@actions/tool-cache": "2.0.1",
|
||||
"@octokit/rest": "19.0.13",
|
||||
"@octokit/webhooks-types": "7.3.1",
|
||||
"semver": "7.5.4",
|
||||
"string-argv": "0.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "16.18.38",
|
||||
"@types/jest": "29.5.5",
|
||||
"@types/unzipper": "0.10.7",
|
||||
"@typescript-eslint/parser": "6.7.2",
|
||||
"@vercel/ncc": "0.38.0",
|
||||
"eslint": "8.49.0",
|
||||
"eslint-plugin-github": "4.10.0",
|
||||
"eslint-plugin-jest": "27.4.0",
|
||||
"eslint-plugin-prettier": "5.0.0",
|
||||
"@types/jest": "29.5.11",
|
||||
"@types/node": "20.10.0",
|
||||
"@types/unzipper": "0.10.9",
|
||||
"@typescript-eslint/parser": "6.17.0",
|
||||
"@vercel/ncc": "0.38.1",
|
||||
"eslint": "8.56.0",
|
||||
"eslint-plugin-github": "4.10.1",
|
||||
"eslint-plugin-jest": "27.6.1",
|
||||
"eslint-plugin-prettier": "5.1.2",
|
||||
"jest": "29.7.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"js-yaml": "4.1.0",
|
||||
"patch-package": "8.0.0",
|
||||
"prettier": "3.0.3",
|
||||
"prettier": "3.1.1",
|
||||
"ts-jest": "29.1.1",
|
||||
"typescript": "5.2.2"
|
||||
"typescript": "5.3.3"
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,13 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as exec from '@actions/exec'
|
||||
import * as glob from '@actions/glob'
|
||||
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import * as params from './input-params'
|
||||
import {CacheListener} from './cache-reporting'
|
||||
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'
|
||||
|
||||
@@ -15,22 +17,24 @@ export class GradleStateCache {
|
||||
private cacheName: string
|
||||
private cacheDescription: string
|
||||
|
||||
protected readonly userHome: string
|
||||
protected readonly gradleUserHome: string
|
||||
|
||||
constructor(gradleUserHome: string) {
|
||||
constructor(userHome: string, gradleUserHome: string) {
|
||||
this.userHome = userHome
|
||||
this.gradleUserHome = gradleUserHome
|
||||
this.cacheName = 'gradle'
|
||||
this.cacheDescription = 'Gradle User Home'
|
||||
}
|
||||
|
||||
init(): void {
|
||||
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
||||
fs.mkdirSync(actionCacheDir, {recursive: true})
|
||||
this.initializeGradleUserHome()
|
||||
|
||||
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
|
||||
fs.mkdirSync(initScriptsDir, {recursive: true})
|
||||
|
||||
this.initializeGradleUserHome(this.gradleUserHome, initScriptsDir)
|
||||
// Export the GRADLE_ENCRYPTION_KEY variable if provided
|
||||
const encryptionKey = params.getCacheEncryptionKey()
|
||||
if (encryptionKey) {
|
||||
core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey)
|
||||
}
|
||||
}
|
||||
|
||||
cacheOutputExists(): boolean {
|
||||
@@ -79,7 +83,7 @@ export class GradleStateCache {
|
||||
async afterRestore(listener: CacheListener): Promise<void> {
|
||||
await this.debugReportGradleUserHomeSize('as restored from cache')
|
||||
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')
|
||||
}
|
||||
|
||||
@@ -127,10 +131,10 @@ export class GradleStateCache {
|
||||
*/
|
||||
async beforeSave(listener: CacheListener): Promise<void> {
|
||||
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
||||
this.deleteExcludedPaths()
|
||||
await this.deleteExcludedPaths()
|
||||
await Promise.all([
|
||||
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener)
|
||||
// new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
|
||||
new GradleHomeEntryExtractor(this.gradleUserHome).extract(listener),
|
||||
new ConfigurationCacheEntryExtractor(this.gradleUserHome).extract(listener)
|
||||
])
|
||||
await this.debugReportGradleUserHomeSize(
|
||||
"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.
|
||||
*/
|
||||
private deleteExcludedPaths(): void {
|
||||
private async deleteExcludedPaths(): Promise<void> {
|
||||
const rawPaths: string[] = params.getCacheExcludes()
|
||||
rawPaths.push('caches/*/cc-keystore')
|
||||
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
||||
|
||||
for (const p of resolvedPaths) {
|
||||
cacheDebug(`Deleting excluded path: ${p}`)
|
||||
tryDelete(p)
|
||||
cacheDebug(`Removing excluded path: ${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)
|
||||
}
|
||||
|
||||
private initializeGradleUserHome(gradleUserHome: string, initScriptsDir: string): void {
|
||||
// Ensure that pre-installed java versions are detected. Only add property if it isn't already defined.
|
||||
const gradleProperties = path.resolve(gradleUserHome, 'gradle.properties')
|
||||
const existingGradleProperties = fs.existsSync(gradleProperties)
|
||||
? 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
|
||||
`
|
||||
)
|
||||
}
|
||||
private initializeGradleUserHome(): void {
|
||||
// Create a directory for storing action metadata
|
||||
const actionCacheDir = path.resolve(this.gradleUserHome, '.gradle-build-action')
|
||||
fs.mkdirSync(actionCacheDir, {recursive: true})
|
||||
|
||||
// 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 = [
|
||||
'gradle-build-action.build-result-capture.init.gradle',
|
||||
'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'
|
||||
]
|
||||
for (const initScriptFilename of initScriptFilenames) {
|
||||
const initScriptContent = this.readInitScriptAsString(initScriptFilename)
|
||||
const initScriptContent = this.readResourceFileAsString('init-scripts', initScriptFilename)
|
||||
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
|
||||
fs.writeFileSync(initScriptPath, initScriptContent)
|
||||
}
|
||||
}
|
||||
|
||||
private readInitScriptAsString(resource: string): string {
|
||||
private registerToolchains(): void {
|
||||
const preInstalledToolchains = this.readResourceFileAsString('toolchains.xml')
|
||||
const m2dir = path.resolve(this.userHome, '.m2')
|
||||
const toolchainXmlTarget = path.resolve(m2dir, 'toolchains.xml')
|
||||
if (!fs.existsSync(toolchainXmlTarget)) {
|
||||
// Write a new toolchains.xml file if it doesn't exist
|
||||
fs.mkdirSync(m2dir, {recursive: true})
|
||||
fs.writeFileSync(toolchainXmlTarget, preInstalledToolchains)
|
||||
|
||||
core.info(`Wrote default JDK locations to ${toolchainXmlTarget}`)
|
||||
} else {
|
||||
// Merge into an existing toolchains.xml file
|
||||
const existingToolchainContent = fs.readFileSync(toolchainXmlTarget, 'utf8')
|
||||
const appendedContent = preInstalledToolchains.split('<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
|
||||
const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', 'init-scripts', resource)
|
||||
const absolutePath = path.resolve(__dirname, '..', '..', 'src', 'resources', ...paths)
|
||||
return fs.readFileSync(absolutePath, 'utf8')
|
||||
}
|
||||
|
||||
|
@@ -42,7 +42,8 @@ export class CacheCleaner {
|
||||
)
|
||||
fs.writeFileSync(path.resolve(cleanupProjectDir, 'build.gradle'), 'task("noop") {}')
|
||||
|
||||
await exec.exec(`gradle -g ${this.gradleUserHome} --no-daemon --build-cache --no-scan --quiet noop`, [], {
|
||||
const gradleCommand = `gradle -g ${this.gradleUserHome} --no-daemon --build-cache --no-scan --quiet -DGITHUB_DEPENDENCY_GRAPH_ENABLED=false noop`
|
||||
await exec.exec(gradleCommand, [], {
|
||||
cwd: cleanupProjectDir
|
||||
})
|
||||
}
|
||||
|
@@ -2,13 +2,14 @@ import path from 'path'
|
||||
import fs from 'fs'
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
import * as semver from 'semver'
|
||||
|
||||
import * as params from './input-params'
|
||||
|
||||
import {META_FILE_DIR} from './cache-base'
|
||||
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
||||
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'
|
||||
|
||||
@@ -46,6 +47,7 @@ class ExtractedCacheEntryDefinition {
|
||||
pattern: string
|
||||
bundle: boolean
|
||||
uniqueFileNames = true
|
||||
notCacheableReason: string | undefined
|
||||
|
||||
constructor(artifactType: string, pattern: string, bundle: boolean) {
|
||||
this.artifactType = artifactType
|
||||
@@ -53,10 +55,24 @@ class ExtractedCacheEntryDefinition {
|
||||
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 {
|
||||
this.uniqueFileNames = false
|
||||
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 pattern = cacheEntryDefinition.pattern
|
||||
|
||||
if (cacheEntryDefinition.notCacheableReason) {
|
||||
listener.entry(pattern).markNotSaved(cacheEntryDefinition.notCacheableReason)
|
||||
continue
|
||||
}
|
||||
|
||||
// Find all matching files for this cache entry definition
|
||||
const globber = await glob.create(pattern, {
|
||||
implicitDescendants: false
|
||||
@@ -256,7 +277,7 @@ abstract class AbstractEntryExtractor {
|
||||
}
|
||||
|
||||
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
|
||||
return extractedCacheEntryMetadata.entries
|
||||
}
|
||||
@@ -264,12 +285,12 @@ abstract class AbstractEntryExtractor {
|
||||
/**
|
||||
* 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()
|
||||
extractedCacheEntryMetadata.entries = results.filter(x => x.cacheKey !== undefined)
|
||||
|
||||
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')
|
||||
}
|
||||
@@ -351,37 +372,96 @@ export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
|
||||
* entry is not reusable.
|
||||
*/
|
||||
async restore(listener: CacheListener): Promise<void> {
|
||||
if (listener.fullyRestored) {
|
||||
return super.restore(listener)
|
||||
if (!listener.fullyRestored) {
|
||||
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')
|
||||
for (const cacheEntry of this.loadExtractedCacheEntries()) {
|
||||
listener.entry(cacheEntry.pattern).markRequested('NOT_RESTORED')
|
||||
if (!params.getCacheEncryptionKey()) {
|
||||
this.markNotRestored(listener, 'Encryption Key was not provided')
|
||||
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.
|
||||
*/
|
||||
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
||||
return this.getProjectRoots().map(projectRoot => {
|
||||
const configCachePath = path.resolve(projectRoot, '.gradle/configuration-cache')
|
||||
return new ExtractedCacheEntryDefinition(
|
||||
// Group BuildResult by existing configCacheDir
|
||||
const groupedResults = this.getConfigCacheDirectoriesWithAssociatedBuildResults()
|
||||
|
||||
return Object.entries(groupedResults).map(([configCachePath, pathResults]) => {
|
||||
// Create a entry definition for each unique configuration cache directory
|
||||
const definition = new ExtractedCacheEntryDefinition(
|
||||
'configuration-cache',
|
||||
configCachePath,
|
||||
true
|
||||
).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
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* For every Gradle invocation, we record the project root directory. This method returns the entire
|
||||
* set of project roots, to allow saving of configuration-cache entries for each.
|
||||
*/
|
||||
private getProjectRoots(): string[] {
|
||||
const buildResults = loadBuildResults()
|
||||
const projectRootDirs = buildResults.map(x => x.rootProjectDir)
|
||||
return [...new Set(projectRootDirs)] // Remove duplicates
|
||||
private getConfigCacheDirectoriesWithAssociatedBuildResults(): Record<string, BuildResult[]> {
|
||||
return loadBuildResults().reduce(
|
||||
(acc, buildResult) => {
|
||||
// For each build result, find the config-cache dir
|
||||
const configCachePath = path.resolve(buildResult.rootProjectDir, '.gradle/configuration-cache')
|
||||
// Ignore case where config-cache dir doesn't exist
|
||||
if (!fs.existsSync(configCachePath)) {
|
||||
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'
|
||||
|
||||
/**
|
||||
@@ -112,47 +111,36 @@ export class CacheEntryListener {
|
||||
}
|
||||
}
|
||||
|
||||
export function writeCachingReport(listener: CacheListener): void {
|
||||
export function generateCachingReport(listener: CacheListener): string {
|
||||
const entries = listener.cacheEntries
|
||||
|
||||
core.summary.addRaw(
|
||||
`\n<details><summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>\n`
|
||||
)
|
||||
return `
|
||||
<details>
|
||||
<summary><h4>Caching for gradle-build-action was ${listener.cacheStatus} - expand for details</h4></summary>
|
||||
${renderEntryTable(entries)}
|
||||
|
||||
core.summary.addTable([
|
||||
[
|
||||
{data: '', header: true},
|
||||
{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}
|
||||
<h5>Cache Entry Details</h5>
|
||||
<pre>
|
||||
${renderEntryDetails(listener)}
|
||||
</pre>
|
||||
</details>
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
export function logCachingReport(listener: CacheListener): void {
|
||||
const entries = listener.cacheEntries
|
||||
|
||||
core.startGroup(`Caching for gradle-build-action was ${listener.cacheStatus} - expand for details`)
|
||||
|
||||
core.info(
|
||||
`Entries Restored: ${getCount(entries, e => e.restoredSize)} (${getSize(entries, e => e.restoredSize)} Mb)`
|
||||
)
|
||||
core.info(`Entries Saved : ${getCount(entries, e => e.savedSize)} (${getSize(entries, e => e.savedSize)} Mb)`)
|
||||
|
||||
core.info(`Cache Entry Details`)
|
||||
core.info(renderEntryDetails(listener))
|
||||
|
||||
core.endGroup()
|
||||
function renderEntryTable(entries: CacheEntryListener[]): string {
|
||||
return `
|
||||
<table>
|
||||
<tr><td></td><th>Count</th><th>Total Size (Mb)</th></tr>
|
||||
<tr><td>Entries Restored</td>
|
||||
<td>${getCount(entries, e => e.restoredSize)}</td>
|
||||
<td>${getSize(entries, e => e.restoredSize)}</td>
|
||||
</tr>
|
||||
<tr><td>Entries Saved</td>
|
||||
<td>${getCount(entries, e => e.savedSize)}</td>
|
||||
<td>${getSize(entries, e => e.savedSize)}</td>
|
||||
</tr>
|
||||
</table>
|
||||
`
|
||||
}
|
||||
|
||||
function renderEntryDetails(listener: CacheListener): string {
|
||||
@@ -198,6 +186,9 @@ function getSavedMessage(entry: CacheEntryListener, cacheReadOnly: boolean): str
|
||||
if (cacheReadOnly) {
|
||||
return '(Entry not saved: cache is read-only)'
|
||||
}
|
||||
if (entry.notRestored) {
|
||||
return '(Entry not saved: not restored)'
|
||||
}
|
||||
return '(Entry not saved: reason unknown)'
|
||||
}
|
||||
if (entry.savedSize === 0) {
|
||||
|
@@ -11,7 +11,7 @@ import * as params from './input-params'
|
||||
|
||||
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_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
|
||||
const cacheKeyForEnvironment = `${cacheKeyBase}|${getCacheKeyEnvironment()}`
|
||||
|
||||
// Prefer caches that run this job
|
||||
// Then prefer caches that run job with the same ID
|
||||
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()}]`
|
||||
|
||||
// Exact match on Git SHA
|
||||
@@ -113,12 +113,7 @@ function getCacheKeyEnvironment(): string {
|
||||
}
|
||||
|
||||
function getCacheKeyJob(): string {
|
||||
return process.env[CACHE_KEY_JOB_VAR] || getCacheKeyForJob(github.context.workflow, github.context.job)
|
||||
}
|
||||
|
||||
export function getCacheKeyForJob(workflowName: string, jobId: string): string {
|
||||
const sanitizedWorkflow = workflowName.replace(/,/g, '').toLowerCase()
|
||||
return `${sanitizedWorkflow}-${jobId}`
|
||||
return process.env[CACHE_KEY_JOB_VAR] || github.context.job
|
||||
}
|
||||
|
||||
function getCacheKeyJobInstance(): string {
|
||||
@@ -127,25 +122,11 @@ function getCacheKeyJobInstance(): string {
|
||||
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.
|
||||
const workflowName = github.context.workflow
|
||||
const workflowJobContext = params.getJobMatrix()
|
||||
return hashStrings([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()
|
||||
return hashStrings([workflowName, workflowJobContext])
|
||||
}
|
||||
|
||||
function getCacheKeyJobExecution(): string {
|
||||
|
@@ -13,7 +13,7 @@ import {CacheCleaner} from './cache-cleaner'
|
||||
|
||||
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
||||
|
||||
export async function restore(gradleUserHome: string, cacheListener: CacheListener): Promise<void> {
|
||||
export async function restore(userHome: string, gradleUserHome: string, cacheListener: CacheListener): Promise<void> {
|
||||
// Bypass restore cache on all but first action step in workflow.
|
||||
if (process.env[CACHE_RESTORED_VAR]) {
|
||||
core.info('Cache only restored on first action step.')
|
||||
@@ -21,7 +21,7 @@ export async function restore(gradleUserHome: string, cacheListener: CacheListen
|
||||
}
|
||||
core.exportVariable(CACHE_RESTORED_VAR, true)
|
||||
|
||||
const gradleStateCache = new GradleStateCache(gradleUserHome)
|
||||
const gradleStateCache = new GradleStateCache(userHome, gradleUserHome)
|
||||
|
||||
if (isCacheDisabled()) {
|
||||
core.info('Cache is disabled: will not restore state from previous builds.')
|
||||
@@ -65,6 +65,7 @@ export async function restore(gradleUserHome: string, cacheListener: CacheListen
|
||||
}
|
||||
|
||||
export async function save(
|
||||
userHome: string,
|
||||
gradleUserHome: string,
|
||||
cacheListener: CacheListener,
|
||||
daemonController: DaemonController
|
||||
@@ -90,10 +91,14 @@ export async function save(
|
||||
if (isCacheCleanupEnabled()) {
|
||||
core.info('Forcing cache cleanup.')
|
||||
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 () => {
|
||||
return new GradleStateCache(gradleUserHome).save(cacheListener)
|
||||
return new GradleStateCache(userHome, gradleUserHome).save(cacheListener)
|
||||
})
|
||||
}
|
||||
|
@@ -1,28 +1,36 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as artifact from '@actions/artifact'
|
||||
import * as github from '@actions/github'
|
||||
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 {RequestError} from '@octokit/request-error'
|
||||
import type {PullRequestEvent} from '@octokit/webhooks-types'
|
||||
|
||||
import * as path from 'path'
|
||||
import fs from 'fs'
|
||||
|
||||
import * as layout from './repository-layout'
|
||||
import {DependencyGraphOption, getJobMatrix} from './input-params'
|
||||
import {DependencyGraphOption, getJobMatrix, getArtifactRetentionDays} from './input-params'
|
||||
|
||||
const DEPENDENCY_GRAPH_ARTIFACT = 'dependency-graph'
|
||||
const DEPENDENCY_GRAPH_PREFIX = 'dependency-graph_'
|
||||
|
||||
export function setup(option: DependencyGraphOption): void {
|
||||
if (option === DependencyGraphOption.Disabled || option === DependencyGraphOption.DownloadAndSubmit) {
|
||||
export async function setup(option: DependencyGraphOption): Promise<void> {
|
||||
if (option === DependencyGraphOption.Disabled) {
|
||||
return
|
||||
}
|
||||
// Download and submit early, for compatability with dependency review.
|
||||
if (option === DependencyGraphOption.DownloadAndSubmit) {
|
||||
await downloadAndSubmitDependencyGraphs()
|
||||
return
|
||||
}
|
||||
|
||||
core.info('Enabling dependency graph generation')
|
||||
const jobCorrelator = getJobCorrelator()
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED', 'true')
|
||||
core.exportVariable('GITHUB_JOB_CORRELATOR', jobCorrelator)
|
||||
core.exportVariable('GITHUB_JOB_ID', github.context.runId)
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR', getJobCorrelator())
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_JOB_ID', github.context.runId)
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_REF', github.context.ref)
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_SHA', getShaFromContext())
|
||||
core.exportVariable('GITHUB_DEPENDENCY_GRAPH_WORKSPACE', layout.workspaceDirectory())
|
||||
core.exportVariable(
|
||||
'DEPENDENCY_GRAPH_REPORT_DIR',
|
||||
path.resolve(layout.workspaceDirectory(), 'dependency-graph-reports')
|
||||
@@ -30,104 +38,120 @@ export function setup(option: DependencyGraphOption): void {
|
||||
}
|
||||
|
||||
export async function complete(option: DependencyGraphOption): Promise<void> {
|
||||
switch (option) {
|
||||
case DependencyGraphOption.Disabled:
|
||||
return
|
||||
case DependencyGraphOption.Generate:
|
||||
await uploadDependencyGraphs()
|
||||
return
|
||||
case DependencyGraphOption.GenerateAndSubmit:
|
||||
await submitDependencyGraphs(await uploadDependencyGraphs())
|
||||
return
|
||||
case DependencyGraphOption.DownloadAndSubmit:
|
||||
await downloadAndSubmitDependencyGraphs()
|
||||
try {
|
||||
switch (option) {
|
||||
case DependencyGraphOption.Disabled:
|
||||
case DependencyGraphOption.Generate: // Performed via init-script: nothing to do here
|
||||
case DependencyGraphOption.DownloadAndSubmit: // Performed in setup
|
||||
return
|
||||
case DependencyGraphOption.GenerateAndSubmit:
|
||||
await submitDependencyGraphs(await findGeneratedDependencyGraphFiles())
|
||||
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 graphFiles = await findDependencyGraphFiles(workspaceDirectory)
|
||||
return await findDependencyGraphFiles(workspaceDirectory)
|
||||
}
|
||||
|
||||
const relativeGraphFiles = graphFiles.map(x => getRelativePathFromWorkspace(x))
|
||||
core.info(`Uploading dependency graph files: ${relativeGraphFiles}`)
|
||||
async function uploadDependencyGraphs(dependencyGraphFiles: string[]): Promise<void> {
|
||||
const workspaceDirectory = layout.workspaceDirectory()
|
||||
|
||||
const artifactClient = artifact.create()
|
||||
artifactClient.uploadArtifact(DEPENDENCY_GRAPH_ARTIFACT, graphFiles, workspaceDirectory)
|
||||
|
||||
return graphFiles
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
for (const dependencyGraphFile of dependencyGraphFiles) {
|
||||
const relativePath = getRelativePathFromWorkspace(dependencyGraphFile)
|
||||
core.info(`Uploading dependency graph file: ${relativePath}`)
|
||||
const artifactName = `${DEPENDENCY_GRAPH_PREFIX}${path.basename(dependencyGraphFile)}`
|
||||
await artifactClient.uploadArtifact(artifactName, [dependencyGraphFile], workspaceDirectory, {
|
||||
retentionDays: getArtifactRetentionDays()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadAndSubmitDependencyGraphs(): Promise<void> {
|
||||
const workspaceDirectory = layout.workspaceDirectory()
|
||||
submitDependencyGraphs(await retrieveDependencyGraphs(workspaceDirectory))
|
||||
try {
|
||||
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> {
|
||||
const octokit = getOctokit()
|
||||
|
||||
for (const jsonFile of dependencyGraphFiles) {
|
||||
const jsonContent = fs.readFileSync(jsonFile, 'utf8')
|
||||
|
||||
const jsonObject = JSON.parse(jsonContent)
|
||||
jsonObject.owner = github.context.repo.owner
|
||||
jsonObject.repo = github.context.repo.repo
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject)
|
||||
|
||||
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
|
||||
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
|
||||
try {
|
||||
await submitDependencyGraphFile(jsonFile)
|
||||
} catch (error) {
|
||||
if (error instanceof RequestError) {
|
||||
core.warning(buildWarningMessage(jsonFile, error))
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function retrieveDependencyGraphs(workspaceDirectory: string): Promise<string[]> {
|
||||
if (github.context.payload.workflow_run) {
|
||||
return await retrieveDependencyGraphsForWorkflowRun(github.context.payload.workflow_run.id, workspaceDirectory)
|
||||
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 retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory)
|
||||
return mainWarning
|
||||
}
|
||||
|
||||
async function retrieveDependencyGraphsForWorkflowRun(runId: number, workspaceDirectory: string): Promise<string[]> {
|
||||
async function submitDependencyGraphFile(jsonFile: string): Promise<void> {
|
||||
const octokit = getOctokit()
|
||||
const jsonContent = fs.readFileSync(jsonFile, 'utf8')
|
||||
|
||||
// Find the workflow run artifacts named "dependency-graph"
|
||||
const artifacts = await octokit.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
run_id: runId
|
||||
})
|
||||
const jsonObject = JSON.parse(jsonContent)
|
||||
jsonObject.owner = github.context.repo.owner
|
||||
jsonObject.repo = github.context.repo.repo
|
||||
const response = await octokit.request('POST /repos/{owner}/{repo}/dependency-graph/snapshots', jsonObject)
|
||||
|
||||
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)
|
||||
const relativeJsonFile = getRelativePathFromWorkspace(jsonFile)
|
||||
core.notice(`Submitted ${relativeJsonFile}: ${response.data.message}`)
|
||||
}
|
||||
|
||||
async function retrieveDependencyGraphsForCurrentWorkflow(workspaceDirectory: string): Promise<string[]> {
|
||||
const artifactClient = artifact.create()
|
||||
async function downloadDependencyGraphs(): Promise<string[]> {
|
||||
const workspaceDirectory = layout.workspaceDirectory()
|
||||
|
||||
const findBy = github.context.payload.workflow_run
|
||||
? {
|
||||
token: getGithubToken(),
|
||||
workflowRunId: github.context.payload.workflow_run.id,
|
||||
repositoryName: github.context.repo.repo,
|
||||
repositoryOwner: github.context.repo.owner
|
||||
}
|
||||
: undefined
|
||||
|
||||
const artifactClient = new DefaultArtifactClient()
|
||||
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[]> {
|
||||
@@ -149,7 +173,26 @@ function getRelativePathFromWorkspace(file: string): string {
|
||||
return path.relative(workspaceDirectory, file)
|
||||
}
|
||||
|
||||
export function getJobCorrelator(): string {
|
||||
function getShaFromContext(): string {
|
||||
const context = github.context
|
||||
const pullRequestEvents = [
|
||||
'pull_request',
|
||||
'pull_request_comment',
|
||||
'pull_request_review',
|
||||
'pull_request_review_comment'
|
||||
// Note that pull_request_target is omitted here.
|
||||
// That event runs in the context of the base commit of the PR,
|
||||
// so the snapshot should not be associated with the head commit.
|
||||
]
|
||||
if (pullRequestEvents.includes(context.eventName)) {
|
||||
const pr = (context.payload as PullRequestEvent).pull_request
|
||||
return pr.head.sha
|
||||
} else {
|
||||
return context.sha
|
||||
}
|
||||
}
|
||||
|
||||
function getJobCorrelator(): string {
|
||||
return constructJobCorrelator(github.context.workflow, github.context.job, getJobMatrix())
|
||||
}
|
||||
|
||||
|
@@ -29,6 +29,10 @@ export function isCacheCleanupEnabled(): boolean {
|
||||
return getBooleanInput('gradle-home-cache-cleanup')
|
||||
}
|
||||
|
||||
export function getCacheEncryptionKey(): string {
|
||||
return core.getInput('cache-encryption-key')
|
||||
}
|
||||
|
||||
export function getCacheIncludes(): string[] {
|
||||
return core.getMultilineInput('gradle-home-cache-includes')
|
||||
}
|
||||
@@ -67,8 +71,25 @@ export function isJobSummaryEnabled(): boolean {
|
||||
return getBooleanInput('generate-job-summary', true)
|
||||
}
|
||||
|
||||
export function isDependencyGraphEnabled(): boolean {
|
||||
return getBooleanInput('generate-dependency-graph', true)
|
||||
export function getJobSummaryOption(): JobSummaryOption {
|
||||
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 {
|
||||
@@ -80,14 +101,33 @@ export function getDependencyGraphOption(): DependencyGraphOption {
|
||||
return DependencyGraphOption.Generate
|
||||
case 'generate-and-submit':
|
||||
return DependencyGraphOption.GenerateAndSubmit
|
||||
case 'generate-and-upload':
|
||||
return DependencyGraphOption.GenerateAndUpload
|
||||
case 'download-and-submit':
|
||||
return DependencyGraphOption.DownloadAndSubmit
|
||||
}
|
||||
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'.`
|
||||
)
|
||||
}
|
||||
|
||||
export function getArtifactRetentionDays(): number {
|
||||
const val = core.getInput('artifact-retention-days')
|
||||
return parseNumericInput('artifact-retention-days', val, 0)
|
||||
// Zero indicates that the default repository settings should be used
|
||||
}
|
||||
|
||||
export function parseNumericInput(paramName: string, paramValue: string, paramDefault: number): number {
|
||||
if (paramValue.length === 0) {
|
||||
return paramDefault
|
||||
}
|
||||
const numericValue = parseInt(paramValue)
|
||||
if (isNaN(numericValue)) {
|
||||
throw TypeError(`The value '${paramValue}' is not a valid numeric value for '${paramName}'.`)
|
||||
}
|
||||
return numericValue
|
||||
}
|
||||
|
||||
function getBooleanInput(paramName: string, paramDefault = false): boolean {
|
||||
const paramValue = core.getInput(paramName)
|
||||
switch (paramValue.toLowerCase().trim()) {
|
||||
@@ -102,8 +142,15 @@ function getBooleanInput(paramName: string, paramDefault = false): boolean {
|
||||
}
|
||||
|
||||
export enum DependencyGraphOption {
|
||||
Disabled,
|
||||
Generate,
|
||||
GenerateAndSubmit,
|
||||
DownloadAndSubmit
|
||||
Disabled = 'disabled',
|
||||
Generate = 'generate',
|
||||
GenerateAndSubmit = 'generate-and-submit',
|
||||
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 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 {writeCachingReport, CacheListener, logCachingReport} from './cache-reporting'
|
||||
import {CacheListener, generateCachingReport} from './cache-reporting'
|
||||
|
||||
export async function writeJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
||||
core.info('Writing job summary')
|
||||
export async function generateJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
||||
const summaryTable = renderSummaryTable(buildResults)
|
||||
const cachingReport = generateCachingReport(cacheListener)
|
||||
|
||||
if (buildResults.length === 0) {
|
||||
core.debug('No Gradle build results found. Summary table will not be generated.')
|
||||
if (shouldGenerateJobSummary(buildResults)) {
|
||||
core.info('Generating Job Summary')
|
||||
|
||||
core.summary.addRaw(summaryTable)
|
||||
core.summary.addRaw(cachingReport)
|
||||
await core.summary.write()
|
||||
} else {
|
||||
writeSummaryTable(buildResults)
|
||||
core.info('============================')
|
||||
core.info(summaryTable)
|
||||
core.info('============================')
|
||||
core.info(cachingReport)
|
||||
core.info('============================')
|
||||
}
|
||||
|
||||
writeCachingReport(cacheListener)
|
||||
|
||||
await core.summary.write()
|
||||
if (shouldAddPRComment(buildResults)) {
|
||||
await addPRComment(summaryTable)
|
||||
}
|
||||
}
|
||||
|
||||
export async function logJobSummary(buildResults: BuildResult[], cacheListener: CacheListener): Promise<void> {
|
||||
if (buildResults.length === 0) {
|
||||
core.debug('No Gradle build results found. Summary table will not be logged.')
|
||||
} else {
|
||||
logSummaryTable(buildResults)
|
||||
async function addPRComment(jobSummary: string): Promise<void> {
|
||||
const context = github.context
|
||||
if (context.payload.pull_request == null) {
|
||||
core.info('No pull_request trigger: not adding PR comment')
|
||||
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 {
|
||||
core.summary.addHeading('Gradle Builds', 3)
|
||||
function buildWarningMessage(error: RequestError): string {
|
||||
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>
|
||||
<tr>
|
||||
<th>Root Project</th>
|
||||
<th>Gradle Root Project</th>
|
||||
<th>Requested Tasks</th>
|
||||
<th>Gradle Version</th>
|
||||
<th>Build Outcome</th>
|
||||
<th>Build Scan®</th>
|
||||
</tr>${results.map(result => renderBuildResultRow(result)).join('')}
|
||||
</table>
|
||||
`)
|
||||
`
|
||||
}
|
||||
|
||||
function renderBuildResultRow(result: BuildResult): string {
|
||||
@@ -77,18 +126,32 @@ function renderBuildScanBadge(outcomeText: string, outcomeColor: string, targetU
|
||||
return `<a href="${targetUrl}" rel="nofollow">${badgeHtml}</a>`
|
||||
}
|
||||
|
||||
function logSummaryTable(results: BuildResult[]): void {
|
||||
core.info('============================')
|
||||
core.info('Gradle Builds')
|
||||
core.info('----------------------------')
|
||||
core.info('Root Project | Requested Tasks | Gradle Version | Build Outcome | Build Scan®')
|
||||
core.info('----------------------------')
|
||||
for (const result of results) {
|
||||
core.info(
|
||||
`${result.rootProjectName} | ${result.requestedTasks} | ${result.gradleVersion} | ${
|
||||
result.buildFailed ? 'FAILED' : 'SUCCESS'
|
||||
} | ${result.buildScanFailed ? 'Publish failed' : result.buildScanUri}`
|
||||
)
|
||||
function shouldGenerateJobSummary(buildResults: BuildResult[]): boolean {
|
||||
// Check if Job Summary is supported on this platform
|
||||
if (!process.env[SUMMARY_ENV_VAR]) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if Job Summary is disabled using the deprecated input
|
||||
if (!params.isJobSummaryEnabled()) {
|
||||
return false
|
||||
}
|
||||
|
||||
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) {
|
||||
return
|
||||
}
|
||||
|
||||
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
||||
buildResultsDir.mkdirs()
|
||||
def buildResultsFile = new File(buildResultsDir, githubActionStep + getParameters().getInvocationId().get() + ".json")
|
||||
if (!buildResultsFile.exists()) {
|
||||
buildResultsFile << groovy.json.JsonOutput.toJson(buildResults)
|
||||
|
||||
try {
|
||||
def buildResultsDir = new File(runnerTempDir, ".build-results")
|
||||
buildResultsDir.mkdirs()
|
||||
def buildResultsFile = new File(buildResultsDir, githubActionStep + getParameters().getInvocationId().get() + ".json")
|
||||
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) {
|
||||
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.
|
||||
if (buildResultsFile.exists()) {
|
||||
if (overwrite) {
|
||||
buildResultsFile.text = groovy.json.JsonOutput.toJson(buildResults)
|
||||
try {
|
||||
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.
|
||||
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,9 +1,15 @@
|
||||
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 {
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
maven { url pluginRepositoryUrl }
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.gradle:github-dependency-graph-gradle-plugin:0.2.0"
|
||||
classpath "org.gradle:github-dependency-graph-gradle-plugin:1.0.0"
|
||||
}
|
||||
}
|
||||
apply plugin: org.gradle.github.GitHubDependencyGraphPlugin
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import org.gradle.util.GradleVersion
|
||||
|
||||
// Only run when dependency graph is explicitly enabled
|
||||
if (System.env.GITHUB_DEPENDENCY_GRAPH_ENABLED != "true") {
|
||||
if (getVariable('GITHUB_DEPENDENCY_GRAPH_ENABLED') != "true") {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@ if (GradleVersion.current().baseVersion < GradleVersion.version("5.0")) {
|
||||
// This is only required for top-level builds
|
||||
def isTopLevelBuild = gradle.getParent() == null
|
||||
if (isTopLevelBuild) {
|
||||
def reportFile = getUniqueReportFile(System.env.GITHUB_JOB_CORRELATOR)
|
||||
def reportFile = getUniqueReportFile(getVariable('GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR'))
|
||||
|
||||
if (reportFile == null) {
|
||||
println "::warning::No dependency snapshot generated for step. Could not determine unique job correlator - specify GITHUB_JOB_CORRELATOR var for this step."
|
||||
println "::warning::No dependency snapshot generated for step. Could not determine unique job correlator - specify GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR var for this step."
|
||||
return
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ if (isTopLevelBuild) {
|
||||
new File(githubOutput) << "dependency-graph-file=${reportFile.absolutePath}\n"
|
||||
}
|
||||
|
||||
|
||||
println "Generating dependency graph into '${reportFile}'"
|
||||
}
|
||||
|
||||
@@ -40,7 +39,7 @@ apply from: 'gradle-build-action.github-dependency-graph-gradle-plugin-apply.gro
|
||||
* - When found, this value is set as a System property override.
|
||||
*/
|
||||
File getUniqueReportFile(String jobCorrelator) {
|
||||
def reportDir = System.env.DEPENDENCY_GRAPH_REPORT_DIR
|
||||
def reportDir = getVariable('DEPENDENCY_GRAPH_REPORT_DIR')
|
||||
def reportFile = new File(reportDir, jobCorrelator + ".json")
|
||||
if (!reportFile.exists()) return reportFile
|
||||
|
||||
@@ -49,7 +48,7 @@ File getUniqueReportFile(String jobCorrelator) {
|
||||
def candidateCorrelator = jobCorrelator + "-" + i
|
||||
def candidateFile = new File(reportDir, candidateCorrelator + ".json")
|
||||
if (!candidateFile.exists()) {
|
||||
System.properties['GITHUB_JOB_CORRELATOR'] = candidateCorrelator
|
||||
System.properties['GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR'] = candidateCorrelator
|
||||
return candidateFile
|
||||
}
|
||||
}
|
||||
@@ -57,3 +56,10 @@ File getUniqueReportFile(String jobCorrelator) {
|
||||
// Could not determine unique job correlator
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the environment variable value, or equivalent system property (if set)
|
||||
*/
|
||||
String getVariable(String name) {
|
||||
return System.properties[name] ?: System.getenv(name)
|
||||
}
|
||||
|
@@ -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 oldValue = System.getProperty(externallyApplied)
|
||||
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 exec from '@actions/exec'
|
||||
import {SUMMARY_ENV_VAR} from '@actions/core/lib/summary'
|
||||
import * as path from 'path'
|
||||
import * as os from 'os'
|
||||
import * as caches from './caches'
|
||||
import * as layout from './repository-layout'
|
||||
import * as params from './input-params'
|
||||
import * as dependencyGraph from './dependency-graph'
|
||||
import * as jobSummary from './job-summary'
|
||||
|
||||
import {logJobSummary, writeJobSummary} from './job-summary'
|
||||
import {loadBuildResults} from './build-results'
|
||||
import {CacheListener} from './cache-reporting'
|
||||
import {DaemonController} from './daemon-controller'
|
||||
|
||||
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
||||
const USER_HOME = 'USER_HOME'
|
||||
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
||||
const CACHE_LISTENER = 'CACHE_LISTENER'
|
||||
|
||||
export async function setup(): Promise<void> {
|
||||
const userHome = await determineUserHome()
|
||||
const gradleUserHome = await determineGradleUserHome()
|
||||
|
||||
// Bypass setup on all but first action step in workflow.
|
||||
@@ -30,15 +31,16 @@ export async function setup(): Promise<void> {
|
||||
// Record setup complete: visible in post-action, to control action completion
|
||||
core.saveState(GRADLE_SETUP_VAR, true)
|
||||
|
||||
// Save the Gradle User Home for use in the post-action step.
|
||||
// Save the User Home and Gradle User Home for use in the post-action step.
|
||||
core.saveState(USER_HOME, userHome)
|
||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||
|
||||
const cacheListener = new CacheListener()
|
||||
await caches.restore(gradleUserHome, cacheListener)
|
||||
await caches.restore(userHome, gradleUserHome, cacheListener)
|
||||
|
||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||
|
||||
dependencyGraph.setup(params.getDependencyGraphOption())
|
||||
await dependencyGraph.setup(params.getDependencyGraphOption())
|
||||
}
|
||||
|
||||
export async function complete(): Promise<void> {
|
||||
@@ -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.')
|
||||
return
|
||||
}
|
||||
core.info('In final post-action step, saving state and writing summary')
|
||||
core.info('In post-action step')
|
||||
|
||||
const buildResults = loadBuildResults()
|
||||
|
||||
const userHome = core.getState(USER_HOME)
|
||||
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
||||
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
||||
const daemonController = new DaemonController(buildResults)
|
||||
|
||||
await caches.save(gradleUserHome, cacheListener, daemonController)
|
||||
await caches.save(userHome, gradleUserHome, cacheListener, daemonController)
|
||||
|
||||
if (shouldGenerateJobSummary()) {
|
||||
await writeJobSummary(buildResults, cacheListener)
|
||||
} else {
|
||||
logJobSummary(buildResults, cacheListener)
|
||||
}
|
||||
await jobSummary.generateJobSummary(buildResults, cacheListener)
|
||||
|
||||
dependencyGraph.complete(params.getDependencyGraphOption())
|
||||
await dependencyGraph.complete(params.getDependencyGraphOption())
|
||||
|
||||
core.info('Completed post-action step')
|
||||
}
|
||||
|
||||
async function determineGradleUserHome(): Promise<string> {
|
||||
@@ -91,12 +92,3 @@ async function determineUserHome(): Promise<string> {
|
||||
core.debug(`Determined user.home from java -version output: '${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()
|
||||
}
|
||||
|
@@ -20,7 +20,7 @@ dependencies {
|
||||
testImplementation ('io.ratpack:ratpack-groovy-test:1.9.0') {
|
||||
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
|
||||
}
|
||||
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.15.2'
|
||||
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.16.0'
|
||||
}
|
||||
|
||||
test {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
plugins {
|
||||
id "com.gradle.enterprise" version "3.15"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.11.2"
|
||||
id "com.gradle.enterprise" version "3.16.1"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "1.12.1"
|
||||
}
|
||||
|
||||
gradleEnterprise {
|
||||
|
@@ -16,8 +16,8 @@ import java.nio.file.Files
|
||||
import java.util.zip.GZIPOutputStream
|
||||
|
||||
class BaseInitScriptTest extends Specification {
|
||||
static final String GE_PLUGIN_VERSION = '3.15'
|
||||
static final String CCUD_PLUGIN_VERSION = '1.11.2'
|
||||
static final String GE_PLUGIN_VERSION = '3.16.1'
|
||||
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_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_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_X = new TestGradleVersion(GradleVersion.version('8.3'), 8, 19)
|
||||
static final TestGradleVersion GRADLE_8_X = new TestGradleVersion(GradleVersion.version('8.5'), 8, 19)
|
||||
|
||||
static final List<TestGradleVersion> ALL_VERSIONS = [
|
||||
GRADLE_3_X, // First version where TestKit supports environment variables
|
||||
|
@@ -148,13 +148,30 @@ class TestBuildResultRecorder extends BaseInitScriptTest {
|
||||
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"() {
|
||||
assumeTrue testGradleVersion.compatibleWithCurrentJvm
|
||||
|
||||
when:
|
||||
settingsFile.text = """
|
||||
plugins {
|
||||
id 'com.gradle.enterprise' version '3.15' apply(false)
|
||||
id 'com.gradle.enterprise' version '3.16.1' apply(false)
|
||||
}
|
||||
gradle.settingsEvaluated {
|
||||
apply plugin: 'com.gradle.enterprise'
|
||||
|
@@ -110,11 +110,11 @@ class TestDependencyGraph extends BaseInitScriptTest {
|
||||
def getEnvVars() {
|
||||
return [
|
||||
GITHUB_DEPENDENCY_GRAPH_ENABLED: "true",
|
||||
GITHUB_JOB_CORRELATOR: "CORRELATOR",
|
||||
GITHUB_JOB_ID: "1",
|
||||
GITHUB_REF: "main",
|
||||
GITHUB_SHA: "123456",
|
||||
GITHUB_WORKSPACE: testProjectDir.absolutePath,
|
||||
GITHUB_DEPENDENCY_GRAPH_JOB_CORRELATOR: "CORRELATOR",
|
||||
GITHUB_DEPENDENCY_GRAPH_JOB_ID: "1",
|
||||
GITHUB_DEPENDENCY_GRAPH_REF: "main",
|
||||
GITHUB_DEPENDENCY_GRAPH_SHA: "123456",
|
||||
GITHUB_DEPENDENCY_GRAPH_WORKSPACE: testProjectDir.absolutePath,
|
||||
DEPENDENCY_GRAPH_REPORT_DIR: reportsDir.absolutePath,
|
||||
GITHUB_OUTPUT: gitHubOutputFile.absolutePath
|
||||
]
|
||||
|
@@ -49,7 +49,7 @@ test('will cleanup unused gradle versions', async () => {
|
||||
|
||||
const gradle802 = path.resolve(gradleUserHome, "caches/8.0.2")
|
||||
const wrapper802 = path.resolve(gradleUserHome, "wrapper/dists/gradle-8.0.2-bin")
|
||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.3")
|
||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.5")
|
||||
|
||||
expect(fs.existsSync(gradle802)).toBe(true)
|
||||
expect(fs.existsSync(wrapper802)).toBe(true)
|
||||
|
@@ -17,10 +17,4 @@ describe('cacheUtils-utils', () => {
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
22
test/jest/input-params.test.ts
Normal file
22
test/jest/input-params.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as inputParams from '../../src/input-params'
|
||||
|
||||
describe('input params', () => {
|
||||
describe('parses numeric input', () => {
|
||||
it('uses default value', () => {
|
||||
const val = inputParams.parseNumericInput('param-name', '', 88)
|
||||
expect(val).toBe(88)
|
||||
})
|
||||
it('parses numeric input', () => {
|
||||
const val = inputParams.parseNumericInput('param-name', '34', 88)
|
||||
expect(val).toBe(34)
|
||||
})
|
||||
it('fails on non-numeric input', () => {
|
||||
const t = () => {
|
||||
inputParams.parseNumericInput('param-name', 'xyz', 88)
|
||||
};
|
||||
|
||||
expect(t).toThrow(TypeError)
|
||||
expect(t).toThrow("The value 'xyz' is not a valid numeric value for 'param-name'.")
|
||||
})
|
||||
})
|
||||
})
|
Reference in New Issue
Block a user