mirror of
https://github.com/gradle/actions.git
synced 2026-05-31 17:11:57 +00:00
Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 255b544613 | |||
| 11d4d83c63 | |||
| 50e97c2cd7 | |||
| f2e6298504 | |||
| b294b1e2db | |||
| 83d3189aae | |||
| 1d5db06d26 | |||
| 1c80961550 | |||
| 9e99920b14 | |||
| bb8aaaf9d5 | |||
| f5dfb43fc8 | |||
| ff9ae24c39 | |||
| ac396bf1a8 | |||
| f333b2d34b | |||
| d341a58140 | |||
| f8858e0208 | |||
| c841461734 | |||
| 6ac57d8041 | |||
| b93d9dccdc | |||
| d63f73fa0d | |||
| 8d29df0507 | |||
| 5f8d454165 | |||
| 5636af687b | |||
| 00c71e737e | |||
| 43b3895d27 | |||
| 0b8fa27eb1 | |||
| 263d8fe18e | |||
| 39e147cb9d | |||
| 14ac3d6351 | |||
| 81fec7a823 | |||
| 4ac5b012ea | |||
| f64284c333 | |||
| c2457a7fb2 | |||
| 8205114447 | |||
| 6710000013 | |||
| 3d0e2a88da | |||
| f663ed9f3d | |||
| 9e6b5adc6e | |||
| 310f24cf39 | |||
| 0f4528296b | |||
| 6c6dec06f4 | |||
| dd0829a494 | |||
| fd60153b4a | |||
| 9c9963b8af | |||
| 22c3ef2608 | |||
| 51a7f13c39 | |||
| 2c1f55600d | |||
| d0745b9c76 | |||
| 8bfa39f571 | |||
| db39ac7061 | |||
| 8f7f5a5ed8 | |||
| 25454f526a | |||
| 2cab5e3c71 | |||
| 75dee00288 | |||
| 56227c19fe | |||
| b99ed96fc6 | |||
| 9becd650e8 | |||
| 73536630a5 | |||
| a0ee12f71e | |||
| c999154b1f | |||
| 03ee1e1693 | |||
| 0ec8ab4e61 | |||
| e9f7b35003 | |||
| 092fa96ff7 | |||
| 39bfbcf1a9 | |||
| 9b2e353921 | |||
| 41b9789f1f | |||
| 5e8b335505 | |||
| a3dd0763dc | |||
| b8b4104aef | |||
| b84bcb3369 | |||
| 342e94ec9a | |||
| 9a834d769a | |||
| 626cd96bc6 | |||
| 0723195856 | |||
| 63603d9aff | |||
| a5a91d4119 | |||
| 30aed73792 | |||
| e66cf857e9 | |||
| 0a06264b90 | |||
| a51c1c2093 | |||
| f7783ee9b0 | |||
| fa3616072f | |||
| 78f7dee633 | |||
| aa108b0a12 | |||
| 1f0a4bb10f | |||
| 9bbab15bc5 | |||
| 2b9247147a | |||
| d9d9739be8 | |||
| 1c1f38bee9 | |||
| bc2dd73ee2 | |||
| 88d177f2ef | |||
| 7f124f7142 | |||
| 7d36419f83 | |||
| 9d5155435d | |||
| 49ec880f92 |
@@ -3,7 +3,7 @@ name: 'Build and upload distribution'
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 24
|
||||
cache: npm
|
||||
@@ -23,7 +23,7 @@ runs:
|
||||
cp -r sources/dist .
|
||||
|
||||
- name: Upload distribution
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
@@ -10,7 +10,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@f2beeb24e141e01a676f977032f5a29d81c9e27e # v5.1.0
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ inputs.java-version }}
|
||||
@@ -23,7 +23,7 @@ runs:
|
||||
# Downloads a 'dist' directory artifact that was uploaded in an earlier 'build-dist' step
|
||||
- name: Download dist
|
||||
if: ${{ env.SKIP_DIST != 'true' && !env.ACT }}
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
name: dist
|
||||
path: dist/
|
||||
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "4.3.1"
|
||||
id "com.gradle.develocity" version "4.4.0"
|
||||
id "com.gradle.common-custom-user-data-gradle-plugin" version "2.4.0"
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,8 +1,10 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
distributionSha256Sum=553c78f50dafcd54d65b9a444649057857469edf836431389695608536d6b746
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.0-bin.zip
|
||||
networkTimeout=10000
|
||||
retries=0
|
||||
retryBackOffMs=500
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
@rem Set local scope for the variables, and ensure extensions are enabled
|
||||
setlocal EnableExtensions
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@@ -51,7 +51,7 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
@@ -65,7 +65,7 @@ echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
"%COMSPEC%" /c exit 1
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
@@ -73,21 +73,10 @@ goto fail
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||
@rem endlocal doesn't take effect until after the line is parsed and variables are expanded
|
||||
@rem which allows us to clear the local environment before executing the java command
|
||||
endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
:exitWithErrorLevel
|
||||
@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts
|
||||
"%COMSPEC%" /c exit %ERRORLEVEL%
|
||||
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionSha256Sum=0d585f69da091fc5b2beced877feab55a3064d43b8a1d46aeb07996b0915e0e0
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip
|
||||
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/2d6327017519d23b96af35865dc997fcb544fb40/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id("com.gradle.develocity") version "4.3.1"
|
||||
id("com.gradle.develocity") version "4.4.0"
|
||||
id("com.gradle.common-custom-user-data-gradle-plugin") version "2.4.0"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "4.3.1"
|
||||
id "com.gradle.develocity" version "4.4.0"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "4.3.1"
|
||||
id "com.gradle.develocity" version "4.4.0"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
id "com.gradle.develocity" version "4.3.1"
|
||||
id "com.gradle.develocity" version "4.4.0"
|
||||
}
|
||||
|
||||
develocity {
|
||||
|
||||
@@ -19,23 +19,30 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: sources/package-lock.json
|
||||
- name: Setup Gradle
|
||||
# Use a released version to avoid breakages
|
||||
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
|
||||
uses: gradle/actions/setup-gradle@39e147cb9de83bb9910b8ef8bd7fff0ee20fcd6f # v6.0.1
|
||||
env:
|
||||
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
||||
with:
|
||||
gradle-version: '8.14.2'
|
||||
|
||||
- name: Install Develocity npm agent
|
||||
run: |
|
||||
npm exec -y -- pacote extract @gradle-tech/develocity-agent@3.0.1 ~/.node_libraries/@gradle-tech/develocity-agent
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm clean-install
|
||||
working-directory: sources
|
||||
env:
|
||||
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
|
||||
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
|
||||
|
||||
- name: Check formatting and compile
|
||||
run: |
|
||||
@@ -44,7 +51,6 @@ jobs:
|
||||
working-directory: sources
|
||||
env:
|
||||
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
|
||||
DEVELOCITY_URL: 'https://ge.solutions-team.gradle.com'
|
||||
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
|
||||
|
||||
- name: Run unit tests
|
||||
@@ -53,5 +59,4 @@ jobs:
|
||||
working-directory: sources
|
||||
env:
|
||||
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
|
||||
DEVELOCITY_URL: 'https://ge.solutions-team.gradle.com'
|
||||
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- name: Get changed files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@e0021407031f5be11a464abee9a0776171c79891 # v47.0.1
|
||||
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||
with:
|
||||
files: |
|
||||
dist/**
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
|
||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config: |
|
||||
@@ -43,4 +43,4 @@ jobs:
|
||||
- sources/src
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
|
||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v3.29.5
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
# Use a released version to avoid breakages
|
||||
uses: gradle/actions/setup-gradle@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
|
||||
uses: gradle/actions/setup-gradle@39e147cb9de83bb9910b8ef8bd7fff0ee20fcd6f # v6.0.1
|
||||
env:
|
||||
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
||||
- name: Run integration tests
|
||||
|
||||
@@ -5,12 +5,10 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'bot/gradle-actions-caching'
|
||||
paths:
|
||||
- 'dist/**'
|
||||
|
||||
concurrency:
|
||||
group: integ-test
|
||||
cancel-in-progress: false
|
||||
- 'sources/vendor/gradle-actions-caching/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -19,21 +17,32 @@ jobs:
|
||||
caching-integ-tests:
|
||||
uses: ./.github/workflows/suite-integ-test-caching.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-full
|
||||
group: CI-integ-test-caching
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
runner-os: '["ubuntu-latest", "windows-latest", "macos-latest"]'
|
||||
skip-dist: true
|
||||
secrets: inherit
|
||||
|
||||
other-integ-tests:
|
||||
permissions:
|
||||
contents: write
|
||||
needs: caching-integ-tests
|
||||
uses: ./.github/workflows/suite-integ-test-other.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-full
|
||||
group: CI-integ-test-other
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
skip-dist: true
|
||||
secrets: inherit
|
||||
|
||||
dependency-submission-integ-tests:
|
||||
permissions:
|
||||
contents: write
|
||||
needs: other-integ-tests
|
||||
uses: ./.github/workflows/suite-integ-test-dependency-submission.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-dependency-submission
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
runner-os: '["ubuntu-latest", "windows-latest", "macos-latest"]'
|
||||
skip-dist: true
|
||||
secrets: inherit
|
||||
|
||||
@@ -11,10 +11,6 @@ on:
|
||||
paths-ignore:
|
||||
- 'dist/**'
|
||||
|
||||
concurrency:
|
||||
group: integ-test
|
||||
cancel-in-progress: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -31,6 +27,9 @@ jobs:
|
||||
caching-integ-tests:
|
||||
needs: build-distribution
|
||||
uses: ./.github/workflows/suite-integ-test-caching.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-caching
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
skip-dist: false
|
||||
secrets: inherit
|
||||
@@ -38,8 +37,23 @@ jobs:
|
||||
other-integ-tests:
|
||||
permissions:
|
||||
contents: write
|
||||
needs: build-distribution
|
||||
needs: caching-integ-tests
|
||||
uses: ./.github/workflows/suite-integ-test-other.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-other
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
skip-dist: false
|
||||
secrets: inherit
|
||||
|
||||
dependency-submission-integ-tests:
|
||||
permissions:
|
||||
contents: write
|
||||
needs: other-integ-tests
|
||||
uses: ./.github/workflows/suite-integ-test-dependency-submission.yml
|
||||
concurrency:
|
||||
group: CI-integ-test-dependency-submission
|
||||
cancel-in-progress: false
|
||||
with:
|
||||
skip-dist: false
|
||||
secrets: inherit
|
||||
|
||||
@@ -44,14 +44,8 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: 'Upload artifact'
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v3.29.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -28,16 +28,23 @@ jobs:
|
||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: sources/package-lock.json
|
||||
|
||||
- name: Install Develocity npm agent
|
||||
run: |
|
||||
npm exec -y -- pacote extract @gradle-tech/develocity-agent@3.0.1 ~/.node_libraries/@gradle-tech/develocity-agent
|
||||
|
||||
- name: Install npm dependencies
|
||||
run: |
|
||||
npm clean-install
|
||||
working-directory: sources
|
||||
env:
|
||||
NODE_OPTIONS: '-r @gradle-tech/develocity-agent/preload'
|
||||
DEVELOCITY_ACCESS_KEY: '${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}'
|
||||
|
||||
- name: Build distribution
|
||||
run: |
|
||||
@@ -54,7 +61,7 @@ jobs:
|
||||
cp -r sources/dist .
|
||||
|
||||
- name: Import GPG key to sign commits
|
||||
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
|
||||
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
name: ci-validate-typings.yml
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release/**'
|
||||
paths-ignore:
|
||||
- 'dist/**'
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
validate-typings:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: typesafegithub/github-actions-typing@9ddf35b71a482be7d8922b28e8d00df16b77e315 # v2.2.2
|
||||
with:
|
||||
ignored-action-files: |
|
||||
.github/actions/build-dist/action.yml
|
||||
.github/actions/init-integ-test/action.yml
|
||||
action.yml
|
||||
@@ -12,6 +12,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: gradle/actions/wrapper-validation@4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2 # v5.0.0
|
||||
- uses: gradle/actions/wrapper-validation@39e147cb9de83bb9910b8ef8bd7fff0ee20fcd6f # v6.0.1
|
||||
with:
|
||||
allow-checksums: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
|
||||
@@ -114,3 +114,72 @@ jobs:
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew assemble
|
||||
|
||||
cache-disabled:
|
||||
needs: build-distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-disabled: true
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew assemble
|
||||
|
||||
basic-caching:
|
||||
needs: build-distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-provider: basic
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew assemble
|
||||
|
||||
terms-of-use-accepted:
|
||||
needs: build-distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
build-scan-terms-of-use-url: https://gradle.com/help/legal-terms-of-use
|
||||
build-scan-terms-of-use-agree: yes
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew assemble
|
||||
|
||||
develocity-access-key-set:
|
||||
needs: build-distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.DV_SOLUTIONS_ACCESS_KEY }}
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew assemble
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
name: Test basic cache provider
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cache-key-prefix:
|
||||
type: string
|
||||
default: '0'
|
||||
runner-os:
|
||||
type: string
|
||||
default: '["ubuntu-latest"]'
|
||||
skip-dist:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: basic-cache-provider-${{ inputs.cache-key-prefix }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
basic-cache-seed-build:
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle with basic cache provider
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-provider: basic
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew build
|
||||
|
||||
basic-cache-verify-build:
|
||||
needs: basic-cache-seed-build
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle with basic cache provider
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-provider: basic
|
||||
cache-read-only: true
|
||||
- name: Build kotlin-dsl project
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: ./gradlew build --offline
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
- name: Build with 3.1
|
||||
working-directory: sources/test/jest/resources/cache-cleanup
|
||||
working-directory: .github/workflow-samples/cache-cleanup
|
||||
run: ./gradlew --no-daemon --build-cache -Dcommons_math3_version="3.1" build
|
||||
|
||||
# Second build will use the cache from the first build, but cleanup should remove unused artifacts
|
||||
@@ -67,12 +67,13 @@ jobs:
|
||||
cache-read-only: false
|
||||
cache-cleanup: 'on-success'
|
||||
- name: Build with 3.1.1
|
||||
working-directory: sources/test/jest/resources/cache-cleanup
|
||||
working-directory: .github/workflow-samples/cache-cleanup
|
||||
run: ./gradlew --no-daemon --build-cache -Dcommons_math3_version="3.1.1" build
|
||||
|
||||
# Third build will restore cache entry from second, and verify stale content removed
|
||||
cache-cleanup-check-clean-cache:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_2
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_1
|
||||
needs: cache-cleanup-assemble-build
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- name: Download dependency-graph artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
path: downloaded-dependency-graphs
|
||||
pattern: dependency-graph_*dependency-graph-generate-submit-and-upload.json
|
||||
|
||||
@@ -394,7 +394,7 @@ jobs:
|
||||
dependency-graph-report-dir: '${{ github.workspace }}/custom/report-dir'
|
||||
build-root-directory: .github/workflow-samples/groovy-dsl
|
||||
|
||||
custom-report-dir-download-and-submit:
|
||||
dependency-submission-custom-report-dir-download-and-submit:
|
||||
permissions:
|
||||
contents: write
|
||||
needs: [dependency-submission-custom-report-dir-upload]
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
matrix:
|
||||
gradle: ['current', '8.14.3', '7.6.2', '6.9.4', '5.6.4']
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: ['3.16.2', '4.3.1']
|
||||
plugin-version: ['3.16.2', '4.4.0']
|
||||
include:
|
||||
- java-version: 17
|
||||
- gradle: '8.14.3'
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
java-version: 11
|
||||
- plugin-version: '3.16.2'
|
||||
accessKeyEnv: GRADLE_ENTERPRISE_ACCESS_KEY
|
||||
- plugin-version: '4.3.1'
|
||||
- plugin-version: '4.4.0'
|
||||
accessKeyEnv: DEVELOCITY_ACCESS_KEY
|
||||
runs-on: ${{ matrix.os }}
|
||||
env:
|
||||
@@ -92,7 +92,7 @@ jobs:
|
||||
matrix:
|
||||
gradle: ['current', '8.14.3', '7.6.2', '6.9.4', '5.6.4']
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: ['3.16.2', '4.3.1']
|
||||
plugin-version: ['3.16.2', '4.4.0']
|
||||
include:
|
||||
- java-version: 17
|
||||
- gradle: '8.14.3'
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
matrix:
|
||||
gradle: ['current', '8.14.3', '7.6.2', '6.9.4', '5.6.4']
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [ '3.16.2', '4.3.1' ]
|
||||
plugin-version: [ '3.16.2', '4.4.0' ]
|
||||
include:
|
||||
- java-version: 17
|
||||
- gradle: '8.14.3'
|
||||
@@ -191,7 +191,7 @@ jobs:
|
||||
matrix:
|
||||
gradle: ['current', '8.14.3', '7.6.2', '6.9.4', '5.6.4']
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
plugin-version: [ '3.16.2', '4.3.1' ]
|
||||
plugin-version: [ '3.16.2', '4.4.0' ]
|
||||
include:
|
||||
- java-version: 17
|
||||
- gradle: '8.14.3'
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
name: Test restore configuration-cache
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
cache-key-prefix:
|
||||
type: string
|
||||
default: '0'
|
||||
runner-os:
|
||||
type: string
|
||||
default: '["ubuntu-latest"]'
|
||||
skip-dist:
|
||||
type: boolean
|
||||
default: false
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY:
|
||||
required: true
|
||||
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: restore-configuration-cache-${{ inputs.cache-key-prefix }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
restore-cc-seed-build-groovy:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
cache-write-only: true # Ensure we start with a clean cache entry
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Groovy build with configuration-cache enabled
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: gradle test --configuration-cache
|
||||
|
||||
restore-cc-verify-build-groovy:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_1
|
||||
needs: restore-cc-seed-build-groovy
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false
|
||||
cache-cleanup: on-success
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Groovy build with configuration-cache enabled
|
||||
id: execute
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: gradle test --configuration-cache
|
||||
- name: Verify configuration-cache hit
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -e ".github/workflow-samples/groovy-dsl/task-configured.txt" ]; then
|
||||
echo "Configuration cache was not used - task was configured unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure that cache-cleanup doesn't remove all necessary files
|
||||
restore-cc-verify-no-cache-cleanup-groovy:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_2
|
||||
needs: restore-cc-verify-build-groovy
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: true
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Groovy build with configuration-cache enabled
|
||||
id: execute
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: gradle test --configuration-cache
|
||||
- name: Verify configuration-cache hit
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -e ".github/workflow-samples/groovy-dsl/task-configured.txt" ]; then
|
||||
echo "Configuration cache was not used - task was configured unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that the build can run when no extracted cache entries are restored
|
||||
restore-cc-gradle-user-home-not-fully-restored:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-groovy
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_x
|
||||
needs: restore-cc-seed-build-groovy
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle with no extracted cache entries restored
|
||||
uses: ./setup-gradle
|
||||
env:
|
||||
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 }}
|
||||
- name: Check execute Gradle build with configuration cache enabled (but not restored)
|
||||
working-directory: .github/workflow-samples/groovy-dsl
|
||||
run: gradle test --configuration-cache
|
||||
|
||||
restore-cc-seed-build-kotlin:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
cache-write-only: true # Ensure we start with a clean cache entry
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Execute 'help' with configuration-cache enabled
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: gradle help --configuration-cache
|
||||
|
||||
restore-cc-modify-build-kotlin:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_1
|
||||
needs: restore-cc-seed-build-kotlin
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Execute 'test' with configuration-cache enabled
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: gradle test --configuration-cache
|
||||
|
||||
# Test restore configuration-cache from the third build invocation
|
||||
restore-cc-verify-build-kotlin:
|
||||
env:
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB: restore-cc-kotlin
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION: ${{github.sha}}_2
|
||||
needs: restore-cc-modify-build-kotlin
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: ${{fromJSON(inputs.runner-os)}}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: ./setup-gradle
|
||||
with:
|
||||
cache-read-only: true
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- name: Execute 'test' again with configuration-cache enabled
|
||||
id: execute
|
||||
working-directory: .github/workflow-samples/kotlin-dsl
|
||||
run: gradle test --configuration-cache
|
||||
- name: Verify configuration-cache hit
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -e ".github/workflow-samples/kotlin-dsl/task-configured.txt" ]; then
|
||||
echo "Configuration cache was not used - task was configured unexpectedly"
|
||||
exit 1
|
||||
fi
|
||||
@@ -12,6 +12,7 @@ on:
|
||||
|
||||
env:
|
||||
SKIP_DIST: ${{ inputs.skip-dist }}
|
||||
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: wrapper-validation- # Avoid loading cache entries from other tests, that may have cached allowed wrapper checksums
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -111,6 +112,12 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
sparse-checkout: |
|
||||
.github
|
||||
dist
|
||||
wrapper-validation
|
||||
|
||||
- name: Initialize integ-test
|
||||
uses: ./.github/actions/init-integ-test
|
||||
|
||||
@@ -118,10 +125,8 @@ jobs:
|
||||
id: action-test
|
||||
uses: ./wrapper-validation
|
||||
with:
|
||||
# to allow the invalid wrapper jar present in test data
|
||||
allow-checksums: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||
min-wrapper-count: 11
|
||||
# Expected to fail; validated below
|
||||
min-wrapper-count: 7
|
||||
# There are only 6 wrappers in workflow-samples, so expected to fail
|
||||
continue-on-error: true
|
||||
|
||||
- name: Check outcome
|
||||
|
||||
@@ -26,14 +26,6 @@ jobs:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
restore-configuration-cache:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-restore-configuration-cache.yml
|
||||
with:
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
secrets:
|
||||
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
|
||||
restore-containerized-gradle-home:
|
||||
uses: ./.github/workflows/integ-test-restore-containerized-gradle-home.yml
|
||||
with:
|
||||
@@ -53,3 +45,21 @@ jobs:
|
||||
uses: ./.github/workflows/integ-test-restore-java-toolchain.yml
|
||||
with:
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
sample-kotlin-dsl:
|
||||
uses: ./.github/workflows/integ-test-sample-kotlin-dsl.yml
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
sample-gradle-plugin:
|
||||
uses: ./.github/workflows/integ-test-sample-gradle-plugin.yml
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
basic-cache-provider:
|
||||
uses: ./.github/workflows/integ-test-basic-cache-provider.yml
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: suite-integ-test-dependency-submission
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
runner-os:
|
||||
type: string
|
||||
default: '["ubuntu-latest"]'
|
||||
skip-dist:
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-graph:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
dependency-submission:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-submission.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
dependency-submission-failures:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-submission-failures.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
@@ -20,33 +20,6 @@ jobs:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
dependency-graph:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-graph.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
dependency-submission:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-submission.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
dependency-submission-failures:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-dependency-submission-failures.yml
|
||||
permissions:
|
||||
contents: write
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
develocity-injection:
|
||||
if: ${{ ! github.event.pull_request.head.repo.fork }}
|
||||
uses: ./.github/workflows/integ-test-inject-develocity.yml
|
||||
@@ -61,18 +34,6 @@ jobs:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
sample-kotlin-dsl:
|
||||
uses: ./.github/workflows/integ-test-sample-kotlin-dsl.yml
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
sample-gradle-plugin:
|
||||
uses: ./.github/workflows/integ-test-sample-gradle-plugin.yml
|
||||
with:
|
||||
runner-os: '${{ inputs.runner-os }}'
|
||||
skip-dist: ${{ inputs.skip-dist }}
|
||||
|
||||
toolchain-detection:
|
||||
uses: ./.github/workflows/integ-test-detect-toolchains.yml
|
||||
with:
|
||||
|
||||
@@ -22,7 +22,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
||||
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
working-directory: sources
|
||||
|
||||
- name: Import GPG key to sign commits
|
||||
uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0
|
||||
uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7.0.0
|
||||
with:
|
||||
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
||||
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
.git
|
||||
.vscode
|
||||
.claude
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Configuration file for asdf version manager
|
||||
nodejs 24.3.0
|
||||
gradle 8.14.2
|
||||
java liberica-11.0.26+9
|
||||
@@ -0,0 +1,34 @@
|
||||
# Workspace Instructions
|
||||
|
||||
## Vendored Library Flow
|
||||
|
||||
This repository uses a proprietary caching library: `gradle-actions-caching`.
|
||||
|
||||
- The vendored copy lives at `sources/vendor/gradle-actions-caching`
|
||||
- The source code is at `../actions-caching` and https://github.com/gradle/actions-caching
|
||||
|
||||
When a task involves building, updating, validating, or testing the vendored `gradle-actions-caching` library, use this sequence:
|
||||
|
||||
1. Run `npm run build` in `actions-caching`.
|
||||
2. Copy (overwrite) the contents of `actions-caching/dist/` onto `sources/vendor/gradle-actions-caching/`. (No need to rm the existing contents)
|
||||
3. Then continue with any build, test, or validation steps in this repository.
|
||||
|
||||
Do not treat `actions/sources/vendor/gradle-actions-caching` as the source of truth. The source of truth is `actions-caching`, and the vendor directory must be refreshed from its `dist/` output after rebuilding.
|
||||
|
||||
## Building
|
||||
|
||||
Before running any build or npm commands, initialize the PATH:
|
||||
|
||||
```sh
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
To build this repository, run the `build` script at the root of that repository with no arguments:
|
||||
|
||||
```sh
|
||||
./build
|
||||
```
|
||||
|
||||
## dist directory
|
||||
|
||||
Never make direct changes to the 'dist' directory. Building with npm will populate 'sources/dist' which is enough. There is a CI workflow that will update the 'dist' directory when required.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Distribution and Licensing of gradle/actions
|
||||
|
||||
This document provides a clear breakdown of the components within the `gradle/actions` repository, their respective licenses, and how we handle data and privacy. Our goal is to provide the best possible experience for running Gradle on GitHub Actions while maintaining our commitment to the open-source community.
|
||||
|
||||
## 1. Component Map
|
||||
The `gradle/actions` project consists of three primary components:
|
||||
|
||||
| Component | License | Source | Description |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Action Runner** | **[MIT](LICENSE)** | Open | The core action logic: configures a local Gradle installation, performs wrapper validation, and reports on Gradle build execution. |
|
||||
| **[Basic Caching](docs/setup-gradle.md#basic-caching)** | **[MIT](LICENSE)** | Open | Configures basic Gradle User Home caching using 'actions/cache'. |
|
||||
| **[Enhanced Caching](docs/setup-gradle.md#enhanced-caching)** | **[Proprietary](licenses/gradle-actions-caching-license.txt)** | Closed | Uses the 'gradle-actions-caching' library to provide fine-grained caching of Gradle User Home, intelligent cache cleanup and other advanced features. |
|
||||
|
||||
By default, **Enhanced Caching** is enabled to provide the best experience.
|
||||
|
||||
## 2. The "Safe Harbor" Clause (Data Privacy)
|
||||
The proprietary components of this action are governed by the **[Gradle Technologies Terms of Use](https://gradle.com/legal/gradle-technologies-terms-of-use/)**. We have updated these terms to include a specific safe harbor for users of `gradle-actions-caching`.
|
||||
|
||||
> **Plain English Summary:** Gradle does not claim ownership of any code, build artifacts, or other content processed by the caching library. These remain your sole property. We only use metadata (like cache keys) to facilitate the caching service.
|
||||
|
||||
## 3. Usage Tiers
|
||||
To support the development of high-performance CI tooling, we offer the following usage model:
|
||||
|
||||
* **Public Repositories:**
|
||||
* Both **Basic** and **Enhanced** caching are free forever. We are committed to supporting the open-source ecosystem at no cost.
|
||||
* **Private Repositories:**
|
||||
* **Basic Caching** is free forever under the MIT license.
|
||||
* **Enhanced Caching** is currently in a **Free Preview** phase. We plan to introduce usage-based pricing for large-scale commercial organizations in the future.
|
||||
|
||||
While we haven't finalized specific usage thresholds yet, our intent is to keep Enhanced Caching free for individual developers and small teams. We are currently in a learning phase to ensure that future restrictions are focused on large-scale commercial users of the product. No matter how these limits evolve, the open-source Basic Caching provider will always remain a free, functional alternative for everyone.
|
||||
|
||||
## 4. Your Choice: Basic vs. Enhanced
|
||||
We believe in user autonomy. If you do not wish to use proprietary code or accept the Gradle Technologies Terms of Use, you can opt-out of enhanced caching with a single line of configuration:
|
||||
|
||||
```yaml
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-provider: basic # Switches to the MIT-licensed open-source implementation
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> ## Licensing notice
|
||||
>
|
||||
> The software in this repository, except for the bundled `gradle-actions-caching` component, is licensed under the [MIT License](LICENSE).
|
||||
>
|
||||
> The caching functionality in this project has been extracted into `gradle-actions-caching`, a proprietary commercial component that is not covered by the MIT License for this repository.
|
||||
>
|
||||
> Use of the `gradle-actions-caching` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||
>
|
||||
> The `gradle-actions-caching` component is used only when enhanced caching is enabled and is not loaded or used with basic caching or when caching is disabled.
|
||||
@@ -1,3 +1,14 @@
|
||||
Note on Repository Contents:
|
||||
|
||||
The software in this repository is primarily licensed under the MIT License (see below).
|
||||
However, this repository includes a vendor-included component, 'gradle-actions-caching',
|
||||
which is Proprietary and distributed under a different license:
|
||||
see licenses/gradle-actions-caching-license.txt.
|
||||
|
||||
For a detailed breakdown of which components are Open Source (MIT) and which are
|
||||
Proprietary, please refer to DISTRIBUTION.md and NOTICE.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
@@ -19,4 +30,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
NOTICE
|
||||
|
||||
The software in this repository, except for the bundled `gradle-actions-caching` component, is licensed under the MIT License.
|
||||
|
||||
The caching functionality in this project has been extracted into `gradle-actions-caching`, a proprietary commercial component that is not covered by the MIT License for this repository.
|
||||
|
||||
The `gradle-actions-caching` component is used only when enhanced caching is enabled and is not loaded or used with basic caching or when caching is disabled.
|
||||
|
||||
Use the `gradle-actions-caching` component is subject to a separate license, available at license/gradle-actions-caching-license.txt.
|
||||
|
||||
For a detailed breakdown of components, licensing tiers, and data privacy terms, see DISTRIBUTION.md.
|
||||
@@ -1,9 +1,16 @@
|
||||
# GitHub Actions for Gradle builds
|
||||
|
||||
[](https://scorecard.dev/viewer/?uri=github.com/gradle/actions)
|
||||
|
||||
This repository contains a set of GitHub Actions that are useful for building Gradle projects on GitHub.
|
||||
|
||||
> [!NOTE]
|
||||
> ### ⚡️ Choice of caching providers in v6
|
||||
> To provide the fastest possible build experience this action includes **Enhanced Caching** via `gradle-actions-caching`, an optimized provider powered by proprietary technology. This feature is **free for all public repositories** and is currently available as a **Free Preview** for private repositories.
|
||||
>
|
||||
> **Prefer a 100% Open Source (MIT) path?**
|
||||
> We also provide a **Basic Caching** provider as a thin wrapper over `actions/cache`. This provider is **free for all repositories** (public and private) and can be enabled at any time by setting `cache-provider: basic`.
|
||||
>
|
||||
> For a full breakdown of the components, usage tiers, and our **Safe Harbor** data privacy commitment, see our [Distribution & Licensing Guide](./DISTRIBUTION.md).
|
||||
|
||||
## The `setup-gradle` action
|
||||
|
||||
The `setup-gradle` action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
||||
@@ -25,14 +32,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
```
|
||||
@@ -63,14 +70,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
```
|
||||
|
||||
See the [full action documentation](docs/dependency-submission.md) for more advanced usage scenarios.
|
||||
@@ -98,8 +105,8 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: gradle/actions/wrapper-validation@v6
|
||||
```
|
||||
|
||||
See the [full action documentation](docs/wrapper-validation.md) for more advanced usage scenarios.
|
||||
|
||||
+2
-1
@@ -20,8 +20,9 @@
|
||||
- Include a Full changelog link in the format https://github.com/gradle/actions/compare/v2.12.0...v3.0.0
|
||||
- Publish the release.
|
||||
- Force push the `v5` tag (or current major version) to point to the new release. It is conventional for users to bind to a major release version using this tag.
|
||||
- From CLI: `git tag -f -s -a -m "v5.0.0" v5 v5.0.0 && git push -f --tags`
|
||||
- From CLI: `git tag -f -s -a -m "v5.0.0" v5 && git push -f --tags`
|
||||
- Note that we sign the tag and set the commit message for the tag to the newly released version.
|
||||
- Your HEAD must point at the commit to be tagged.
|
||||
|
||||
## Post release steps
|
||||
|
||||
|
||||
@@ -2,12 +2,6 @@
|
||||
|
||||
cd sources
|
||||
|
||||
if [[ -f ~/.gradle/develocity/keys.properties ]]; then
|
||||
export NODE_OPTIONS='-r @gradle-tech/develocity-agent/preload'
|
||||
export DEVELOCITY_URL=https://ge.solutions-team.gradle.com
|
||||
export DEVELOCITY_ACCESS_KEY=$(paste -sd ';' ~/.gradle/develocity/keys.properties)
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
all)
|
||||
npm run all
|
||||
|
||||
@@ -22,14 +22,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
```
|
||||
|
||||
See the [full action documentation](../docs/dependency-submission.md) for more advanced usage scenarios.
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
# Type descriptors based on https://github.com/typesafegithub/github-actions-typing
|
||||
inputs:
|
||||
# Gradle execution configuration
|
||||
gradle-version:
|
||||
type: string
|
||||
|
||||
build-root-directory:
|
||||
type: string
|
||||
|
||||
dependency-resolution-task:
|
||||
type: string
|
||||
|
||||
additional-arguments:
|
||||
type: string
|
||||
|
||||
# Cache configuration
|
||||
cache-provider:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- basic
|
||||
- enhanced
|
||||
|
||||
cache-disabled:
|
||||
type: boolean
|
||||
|
||||
cache-read-only:
|
||||
type: boolean
|
||||
|
||||
cache-write-only:
|
||||
type: boolean
|
||||
|
||||
cache-overwrite-existing:
|
||||
type: boolean
|
||||
|
||||
cache-encryption-key:
|
||||
type: string
|
||||
|
||||
cache-cleanup:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- on-success
|
||||
- always
|
||||
|
||||
gradle-home-cache-cleanup:
|
||||
type: boolean
|
||||
|
||||
gradle-home-cache-includes:
|
||||
type: list
|
||||
separator: '\n'
|
||||
list-item:
|
||||
type: string
|
||||
|
||||
gradle-home-cache-excludes:
|
||||
type: list
|
||||
separator: '\n'
|
||||
list-item:
|
||||
type: string
|
||||
|
||||
# Job summary configuration
|
||||
add-job-summary:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- always
|
||||
- on-failure
|
||||
|
||||
add-job-summary-as-pr-comment:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- always
|
||||
- on-failure
|
||||
|
||||
# Dependency Graph configuration
|
||||
dependency-graph:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- generate-and-submit
|
||||
- generate-submit-and-upload
|
||||
- generate-and-upload
|
||||
- download-and-submit
|
||||
|
||||
dependency-graph-report-dir:
|
||||
type: string
|
||||
|
||||
dependency-graph-continue-on-failure:
|
||||
type: boolean
|
||||
|
||||
dependency-graph-exclude-projects:
|
||||
type: string
|
||||
|
||||
dependency-graph-include-projects:
|
||||
type: string
|
||||
|
||||
dependency-graph-exclude-configurations:
|
||||
type: string
|
||||
|
||||
dependency-graph-include-configurations:
|
||||
type: string
|
||||
|
||||
artifact-retention-days:
|
||||
type: integer
|
||||
|
||||
# Build Scan configuration
|
||||
build-scan-publish:
|
||||
type: boolean
|
||||
|
||||
build-scan-terms-of-use-url:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- https://gradle.com/terms-of-service
|
||||
- https://gradle.com/help/legal-terms-of-use
|
||||
|
||||
build-scan-terms-of-use-agree:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- 'yes'
|
||||
|
||||
develocity-access-key:
|
||||
type: string
|
||||
|
||||
develocity-token-expiry:
|
||||
type: integer
|
||||
|
||||
# Wrapper validation configuration
|
||||
validate-wrappers:
|
||||
type: boolean
|
||||
|
||||
allow-snapshot-wrappers:
|
||||
type: boolean
|
||||
|
||||
# Experimental action inputs
|
||||
gradle-home-cache-strict-match:
|
||||
type: boolean
|
||||
|
||||
# Internal action inputs
|
||||
workflow-job-context:
|
||||
type: string
|
||||
|
||||
github-token:
|
||||
type: string
|
||||
|
||||
outputs:
|
||||
build-scan-url:
|
||||
type: string
|
||||
|
||||
dependency-graph-file:
|
||||
type: string
|
||||
|
||||
gradle-version:
|
||||
type: string
|
||||
@@ -26,6 +26,14 @@ inputs:
|
||||
required: false
|
||||
|
||||
# Cache configuration
|
||||
cache-provider:
|
||||
description: |
|
||||
Specifies the caching implementation to use.
|
||||
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
||||
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
||||
required: false
|
||||
default: 'enhanced'
|
||||
|
||||
cache-disabled:
|
||||
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
||||
required: false
|
||||
|
||||
+348
-211308
File diff suppressed because one or more lines are too long
+7
-1
File diff suppressed because one or more lines are too long
+220
-162120
File diff suppressed because one or more lines are too long
+7
-1
File diff suppressed because one or more lines are too long
Vendored
+326
-211224
File diff suppressed because one or more lines are too long
Vendored
+7
-1
File diff suppressed because one or more lines are too long
Vendored
+340
-211222
File diff suppressed because one or more lines are too long
Vendored
+7
-1
File diff suppressed because one or more lines are too long
+157
-149245
File diff suppressed because one or more lines are too long
+7
-1
File diff suppressed because one or more lines are too long
@@ -15,6 +15,15 @@ for vulnerable dependencies, as well as to populate the
|
||||
|
||||
If you're confused by the behaviour you're seeing or have specific questions, please check out [the FAQ](dependency-submission-faq.md) before raising an issue.
|
||||
|
||||
> [!NOTE]
|
||||
> ### ⚡️ Choice of caching providers in v6
|
||||
> To provide the fastest possible build experience this action includes **Enhanced Caching** via `gradle-actions-caching`, an optimized provider powered by proprietary technology. This feature is **free for all public repositories** and is currently available as a **Free Preview** for private repositories.
|
||||
>
|
||||
> **Prefer a 100% Open Source (MIT) path?**
|
||||
> We also provide a **Basic Caching** provider as a thin wrapper over `actions/cache`. This provider is **free for all repositories** (public and private) and can be enabled at any time by setting `cache-provider: basic`.
|
||||
>
|
||||
> For a full breakdown of the components, usage tiers, and our **Safe Harbor** data privacy commitment, see our [Distribution & Licensing Guide](../DISTRIBUTION.md).
|
||||
|
||||
## General usage
|
||||
|
||||
The following workflow will generate a dependency graph for a Gradle project and submit it immediately to the repository via the
|
||||
@@ -36,14 +45,14 @@ jobs:
|
||||
dependency-submission:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
```
|
||||
|
||||
### Gradle execution
|
||||
@@ -60,6 +69,17 @@ on the command-line will be used.
|
||||
The action provides the ability to override the Gradle version and task to execute, as well as provide
|
||||
additional arguments that will be passed to Gradle on the command-line. See [Configuration Parameters](#configuration-parameters) below.
|
||||
|
||||
### Selecting a cache provider
|
||||
|
||||
See [Selecting a cache provider](setup-gradle.md#selecting-a-cache-provider) for details on choosing between the `enhanced` (default) and `basic` cache providers via the `cache-provider` input.
|
||||
|
||||
### Disabling caching
|
||||
|
||||
Caching is enabled by default. You can disable caching for the action as follows:
|
||||
```yaml
|
||||
cache-disabled: true
|
||||
```
|
||||
|
||||
### Publishing a Develocity Build Scan® from your dependency submission workflow
|
||||
|
||||
You can automatically publish a free Develocity Build Scan on every run of `gradle/actions/dependency-submission`.
|
||||
@@ -68,7 +88,7 @@ Three input parameters are required, one to enable publishing and two more to ac
|
||||
|
||||
```yaml
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
build-scan-publish: true
|
||||
build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
|
||||
@@ -83,7 +103,7 @@ In some cases, the default action configuration will not be sufficient, and addi
|
||||
|
||||
```yaml
|
||||
- name: Generate and save dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
# Use a particular Gradle version instead of the configured wrapper.
|
||||
gradle-version: '8.6'
|
||||
@@ -130,7 +150,7 @@ To reduce storage costs for these artifacts, you can:
|
||||
|
||||
```yaml
|
||||
- name: Generate dependency graph but only store workflow artifacts for 1 day
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
artifact-retention-days: 1 # Default is 30 days or as configured for repository
|
||||
```
|
||||
@@ -139,7 +159,7 @@ To reduce storage costs for these artifacts, you can:
|
||||
|
||||
```yaml
|
||||
- name: Generate and submit dependency graph but do not store as workflow artifact
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
dependency-graph: 'generate-and-submit' # Default value is 'generate-submit-and-upload'
|
||||
```
|
||||
@@ -299,7 +319,7 @@ For example, if you want to exclude dependencies resolved by the `buildSrc` proj
|
||||
|
||||
```yaml
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
# Exclude all dependencies that originate solely in the 'buildSrc' project
|
||||
dependency-graph-exclude-projects: ':buildSrc'
|
||||
@@ -343,14 +363,14 @@ jobs:
|
||||
dependency-submission:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Generate and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
```
|
||||
|
||||
#### 2. Add a dedicated Dependency Review workflow
|
||||
@@ -405,14 +425,14 @@ jobs:
|
||||
dependency-submission:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Generate and save dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
dependency-graph: generate-and-upload
|
||||
```
|
||||
@@ -435,7 +455,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download and submit dependency graph
|
||||
uses: gradle/actions/dependency-submission@v5
|
||||
uses: gradle/actions/dependency-submission@v6
|
||||
with:
|
||||
dependency-graph: download-and-submit # Download saved dependency-graph and submit
|
||||
```
|
||||
@@ -443,7 +463,7 @@ jobs:
|
||||
# Gradle version compatibility
|
||||
|
||||
Dependency-graph generation is compatible with most versions of Gradle >= `5.2`, and is tested regularly against
|
||||
Gradle versions `5.2.1`, `5.6.4`, `6.0.1`, `6.9.4`, `7.1.1` and `7.6.3`, as well as all patched versions of Gradle 8.x.
|
||||
Gradle versions `5.2.1`, `5.6.4`, `6.0.1`, `6.9.4`, `7.1.1`, `7.6.6`, `8.0.2` and `8.14.4`, as well as all patched versions of Gradle 9.x.
|
||||
|
||||
A known exception to this is that Gradle `7.0`, `7.0.1` and `7.0.2` are not supported.
|
||||
|
||||
@@ -454,4 +474,3 @@ See [here](https://github.com/gradle/github-dependency-graph-gradle-plugin?tab=r
|
||||
- Dependency Submission Demo repository: https://github.com/gradle/github-dependency-submission-demo
|
||||
- GitHub Dependency Graph Gradle Plugin: https://github.com/gradle/github-dependency-graph-gradle-plugin
|
||||
- Webinar - Gradle at Scale with GitHub and GitHub Actions at Allegro: https://www.youtube.com/watch?v=gV94I28FPos
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ To convert your workflows, simply replace:
|
||||
```
|
||||
with
|
||||
```
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
```
|
||||
|
||||
## The action `gradle/wrapper-validation-action` has been replaced by `gradle/actions/wrapper-validation`
|
||||
@@ -40,7 +40,7 @@ To convert your workflows, simply replace:
|
||||
```
|
||||
with
|
||||
```
|
||||
uses: gradle/actions/wrapper-validation@v5
|
||||
uses: gradle/actions/wrapper-validation@v6
|
||||
```
|
||||
|
||||
## Using the action to execute Gradle via the `arguments` parameter is deprecated
|
||||
@@ -82,7 +82,7 @@ The exact syntax depends on whether or not your project is configured with the [
|
||||
|
||||
```
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Assemble the project
|
||||
run: ./gradlew assemble
|
||||
@@ -99,7 +99,7 @@ The exact syntax depends on whether or not your project is configured with the [
|
||||
|
||||
```
|
||||
- name: Setup Gradle for a non-wrapper project
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
gradle-version: '8.11'
|
||||
|
||||
|
||||
+233
-165
@@ -2,13 +2,23 @@
|
||||
|
||||
This GitHub Action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
||||
|
||||
> [!NOTE]
|
||||
> ### ⚡️ Choice of caching providers in v6
|
||||
> To provide the fastest possible build experience this action includes **Enhanced Caching** via `gradle-actions-caching`, an optimized provider powered by proprietary technology. This feature is **free for all public repositories** and is currently available as a **Free Preview** for private repositories.
|
||||
>
|
||||
> **Prefer a 100% Open Source (MIT) path?**
|
||||
> We also provide a **Basic Caching** provider as a thin wrapper over `actions/cache`. This provider is **free for all repositories** (public and private) and can be enabled at any time by setting `cache-provider: basic`.
|
||||
>
|
||||
> For a full breakdown of the components, usage tiers, and our **Safe Harbor** data privacy commitment, see our [Distribution & Licensing Guide](../DISTRIBUTION.md).
|
||||
|
||||
## Why use the `setup-gradle` action?
|
||||
|
||||
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.
|
||||
It is possible to directly invoke Gradle in your workflow, and the `actions/setup-java@v5` action provides a simple way to cache Gradle dependencies.
|
||||
|
||||
However, the `setup-gradle` action offers a several advantages over this approach:
|
||||
|
||||
- Easily [configure your workflow to use a specific version of Gradle](#build-with-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||
- [Gradle Wrapper files are automatically validated](#gradle-wrapper-validation) against known checksums
|
||||
- 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.
|
||||
@@ -38,14 +48,14 @@ jobs:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Execute Gradle build
|
||||
run: ./gradlew build
|
||||
@@ -58,7 +68,7 @@ Downloaded Gradle versions are stored in the GitHub Actions cache, to avoid havi
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle 8.10
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
gradle-version: '8.10' # Quotes required to prevent YAML converting to number
|
||||
- name: Build with Gradle 8.10
|
||||
@@ -90,13 +100,13 @@ jobs:
|
||||
gradle-rc:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
id: setup-gradle
|
||||
with:
|
||||
gradle-version: release-candidate
|
||||
@@ -112,24 +122,44 @@ The cached state includes:
|
||||
- Any distributions downloaded to satisfy a `gradle-version` parameter.
|
||||
- A subset of the Gradle User Home directory, including downloaded dependencies, wrapper distributions, and the local build cache.
|
||||
|
||||
To reduce the space required for caching, this action attempts to reduce duplication in cache entries on a best effort basis.
|
||||
|
||||
The state will be restored from the cache during the first `setup-gradle` step for any workflow job, and cache entries will be written back to the cache at the end of the job after all Gradle executions have been completed.
|
||||
|
||||
### Disabling caching
|
||||
### Selecting a cache provider
|
||||
|
||||
The `setup-gradle` action offers two caching providers, offered under different license terms and with different features.
|
||||
See [DISTRIBUTION.md](../DISTRIBUTION.md) for more details.
|
||||
|
||||
You choose which provider to use via the `cache-provider` input:
|
||||
|
||||
- **`enhanced`** (default): Uses the full-featured commercial `gradle-actions-caching` library. Provides advanced features like fine-grained cache entries, intelligent cache cleanup, and deduplication. See [Enhanced Caching](#enhanced-caching) for details.
|
||||
- **`basic`**: A fully open-source (MIT) caching implementation built on the standard GitHub Actions cache (`@actions/cache`). Uses the same caching strategy as `actions/setup-java` with `cache: gradle`. See [Basic Caching](#basic-caching) for details.
|
||||
|
||||
```yaml
|
||||
# Use the open-source basic cache provider
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
id: setup-gradle
|
||||
with:
|
||||
cache-provider: basic
|
||||
```
|
||||
|
||||
### Common cache configuration
|
||||
|
||||
The following options apply to both the `enhanced` and `basic` cache providers.
|
||||
|
||||
#### Disabling caching
|
||||
|
||||
Caching is enabled by default. You can disable caching for the action as follows:
|
||||
```yaml
|
||||
cache-disabled: true
|
||||
```
|
||||
|
||||
### Using the cache read-only
|
||||
#### Using the cache read-only
|
||||
|
||||
By default, The `setup-gradle` action will only write to the cache from Jobs on the default (`main`/`master`) branch.
|
||||
Jobs on other branches will read entries from the cache but will not write updated entries.
|
||||
|
||||
This setup is designed around [GitHub imposed restrictions on cache access](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache) and should work well in most scenarios.
|
||||
See [Optimizing cache effectiveness](#select-which-branches-should-write-to-the-cache) for a more detailed explanation.
|
||||
See [Optimizing cache effectiveness](#optimizing-cache-effectiveness) for a more detailed explanation.
|
||||
|
||||
In some circumstances, it makes sense to change this default and configure a workflow Job to read existing cache entries but not to write changes back.
|
||||
|
||||
@@ -147,93 +177,7 @@ You can also configure read-only caching only for certain branches:
|
||||
cache-read-only: ${{ github.ref != 'refs/heads/main' && github.ref != 'refs/heads/release' }}
|
||||
```
|
||||
|
||||
### Using the cache write-only
|
||||
|
||||
In certain circumstances it may be desirable to start with a clean Gradle User Home state, but to save the state at the end of a workflow Job:
|
||||
|
||||
```yaml
|
||||
cache-write-only: true
|
||||
```
|
||||
|
||||
### Configuring cache cleanup
|
||||
|
||||
The Gradle User Home directory tends to grow over time. When you switch to a new Gradle wrapper version
|
||||
or upgrade a dependency version the old files are not automatically and immediately removed.
|
||||
While this can make sense in a local environment, in a GitHub Actions environment
|
||||
it can lead to ever-larger Gradle User Home cache entries being saved and restored.
|
||||
|
||||
To avoid this situation, the `setup-gradle` and `dependency-submission` actions will perform "cache-cleanup",
|
||||
purging any unused files from the Gradle User Home before saving it to the GitHub Actions cache.
|
||||
Cache cleanup will attempt to remove any files that are initially restored to the Gradle User Home directory
|
||||
but that are not used used by Gradle during the GitHub Actions Workflow.
|
||||
|
||||
If a Gradle build fails when running the Job, then it is possible that some required files and dependencies
|
||||
will not be touched during the Job. To prevent these files from being purged, the default behavior is for
|
||||
cache cleanup to run only when all Gradle builds in the Job are successful.
|
||||
|
||||
Gradle Home cache cleanup is enabled by default, and can be controlled by the `cache-cleanup` parameter as follows:
|
||||
- `cache-cleanup: always`: Always run cache cleanup, even when a Gradle build fails in the Job.
|
||||
- `cache-cleanup: on-success` (default): Run cache cleanup when the Job contains no failing Gradle builds.
|
||||
- `cache-cleanup: never`: Disable cache cleanup for the Job.
|
||||
|
||||
Cache cleanup will never run when the cache is configured as read-only or disabled.
|
||||
|
||||
### Overwriting an existing Gradle User Home
|
||||
|
||||
When the action detects that the Gradle User Home caches directory already exists (`$GRADLE_USER_HOME/caches`), then by default it will not overwrite the existing content of this directory.
|
||||
This can occur when a prior action initializes this directory, or when using a self-hosted runner that retains this directory between uses.
|
||||
|
||||
In this case, the Job Summary will display a message like:
|
||||
> Caching for Gradle actions was disabled due to pre-existing Gradle User Home
|
||||
|
||||
If you want to override the default and have the caches of the `setup-gradle` action overwrite existing content in the Gradle User Home, you can set the `cache-overwrite-existing` parameter to `true`:
|
||||
|
||||
```yaml
|
||||
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 to be safely stored in the GitHub Actions cache.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> To avoid potentially leaking secrets in the configuration-cache entry, the action will only save or restore configuration-cache data if the `cache-encryption-key` parameter is set.
|
||||
|
||||
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/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: actions/setup-java@v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
with:
|
||||
gradle-version: '8.6'
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
- run: gradle build --configuration-cache
|
||||
```
|
||||
|
||||
Even with everything correctly configured, you may find that the configuration-cache entry is not reused in your workflow.
|
||||
This is often due to a known issue: [Included builds containing build logic prevent configuration-cache reuse](https://github.com/gradle/actions/issues/21). Refer to the issue for more details.
|
||||
|
||||
> [!NOTE]
|
||||
> The configuration cache cannot be saved or restored in workflows triggered by a pull requests from a repository fork.
|
||||
> This is because [GitHub secrets are not passed to workflows triggered by PRs from forks](https://docs.github.com/en/actions/security-guides/using-secrets-in-github-actions#using-secrets-in-a-workflow).
|
||||
> This prevents a malicious PR from reading the configuration-cache data, which may encode secrets read by Gradle.
|
||||
|
||||
### Incompatibility with other caching mechanisms
|
||||
#### Incompatibility with other caching mechanisms
|
||||
|
||||
When using `setup-gradle` we recommend that you avoid using other mechanisms to save and restore the Gradle User Home.
|
||||
|
||||
@@ -243,18 +187,22 @@ Specifically:
|
||||
|
||||
Using either of these mechanisms may interfere with the caching provided by this action. If you choose to use a different mechanism to save and restore the Gradle User Home, you should disable the caching provided by this action, as described above.
|
||||
|
||||
## How Gradle User Home caching works
|
||||
## Enhanced Caching
|
||||
|
||||
### Properties of the GitHub Actions cache
|
||||
Enhanced Caching is the default cache provider. It uses the proprietary `gradle-actions-caching` library to provide advanced caching features including fine-grained cache entries, intelligent cache cleanup, and deduplication of cached content. This reduces cache storage requirements and improves restore times compared to basic caching.
|
||||
|
||||
### How Enhanced Caching works
|
||||
|
||||
#### Properties of the GitHub Actions cache
|
||||
|
||||
The GitHub Actions cache has some properties that present problems for efficient caching of the Gradle User Home.
|
||||
- Immutable entries: once a cache entry is written for a key, it cannot be overwritten or changed.
|
||||
- Branch scope: cache entries written for a Git branch are not visible from actions running against different branches or tags. Entries written for the default branch are visible to all. https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache
|
||||
- Restore keys: if no exact match is found, a set of partial keys can be provided that will match by cache key prefix. https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#matching-a-cache-key
|
||||
|
||||
Each of these properties has influenced the design and implementation of the caching in `setup-gradle`, as described below.
|
||||
Each of these properties has influenced the design and implementation of Enhanced Caching, as described below.
|
||||
|
||||
### Which content is cached
|
||||
#### Which content is cached
|
||||
|
||||
Using experiments and observations, we have attempted to identify which Gradle User Home content is worth saving and restoring between build invocations. We considered both the respective size of the content and the impact this content has on build times. As well as the obvious candidates like downloaded dependencies, we saw that compiled build scripts, transformed Jar files, and other content can also have a significant impact.
|
||||
|
||||
@@ -266,9 +214,9 @@ In the end, we opted to save and restore as much content as is practical, includ
|
||||
- `caches/jars-9`: Jar files that have been processed/instrumented by Gradle
|
||||
- `caches/build-cache-1`: The local build cache
|
||||
|
||||
In certain cases, a particular section of Gradle User Home will be too large to make caching effective. In these cases, particular subdirectories can be excluded from caching. See [Exclude content from Gradle User Home cache](#exclude-content-from-gradle-user-home-cache).
|
||||
In certain cases, a particular section of Gradle User Home will be too large to make caching effective. In these cases, particular subdirectories can be excluded from caching. See [Include/exclude content from Gradle User Home](#includeexclude-content-from-gradle-user-home).
|
||||
|
||||
### Cache keys
|
||||
#### Cache keys
|
||||
|
||||
The actual content of the Gradle User Home after a build is the result of many factors, including:
|
||||
- Core Gradle build files (`settings.gradle[.kts]`, `build.gradle[.kts]`, `gradle.properties`)
|
||||
@@ -290,7 +238,7 @@ Specifically, the cache key is: `${cache-protocol}-gradle|${runner-os}|${job-id}
|
||||
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
||||
This allows the most recent state to always be available in the GitHub actions cache.
|
||||
|
||||
### Finding a matching cache entry
|
||||
#### Finding a matching cache entry
|
||||
|
||||
In most cases, no exact match will exist for the cache key. Instead, the Gradle User Home will be restored for the closest matching cache entry, using a set of "restore keys". The entries will be matched with the following precedence:
|
||||
- An exact match on OS, job id, workflow name, matrix, and Git SHA
|
||||
@@ -304,9 +252,9 @@ After the Job is complete, the current Gradle User Home state will be collected
|
||||
|
||||
Note that while effective, this mechanism is not inherently efficient. It requires the entire Gradle User Home directory to be stored separately for each branch, for every OS+Job+Matrix combination. In addition, it writes a new cache entry on every GitHub Actions run.
|
||||
|
||||
This inefficiency is effectively mitigated by [Deduplication of Gradle User Home cache entries](#deduplication-of-gradle-user-home-cache-entries) and can be further optimized for a workflow using the techniques described in [Optimizing cache effectiveness](#optimizing-cache-effectiveness).
|
||||
This inefficiency is effectively mitigated by [Deduplication of cache entries](#deduplication-of-cache-entries) and can be further optimized for a workflow using the techniques described in [Optimizing cache effectiveness](#optimizing-cache-effectiveness).
|
||||
|
||||
### Deduplication of Gradle User Home cache entries
|
||||
#### Deduplication of cache entries
|
||||
|
||||
To reduce duplication between cache entries, certain artifacts in Gradle User Home are extracted and cached independently based on their identity. This allows each Gradle User Home cache entry to be relatively small, sharing common elements between them without duplication.
|
||||
|
||||
@@ -318,53 +266,78 @@ Artifacts that are cached independently include:
|
||||
|
||||
For example, this means that all jobs executing a particular version of the Gradle wrapper will share a single common entry for this wrapper distribution and one for each of the generated Gradle API jars.
|
||||
|
||||
### Stopping the Gradle daemon
|
||||
#### Stopping the Gradle daemon
|
||||
|
||||
By default, the action will stop all running Gradle daemons in the post-action step, before saving the Gradle User Home state.
|
||||
This allows for any Gradle User Home cleanup to occur, and avoid file-locking issues on Windows.
|
||||
|
||||
If caching is disabled or the cache is in read-only mode, the daemon will not be stopped and will continue running after the job is completed.
|
||||
|
||||
## Optimizing cache effectiveness
|
||||
### Enhanced Caching configuration options
|
||||
|
||||
Cache storage space for GitHub actions is limited, and writing new cache entries can trigger the deletion of existing entries.
|
||||
Eviction of shared cache entries can reduce cache effectiveness, slowing down your `setup-gradle` steps.
|
||||
The following configuration options only apply when using the `enhanced` cache provider.
|
||||
|
||||
There are a several actions you can take if your cache use is less effective due to entry eviction.
|
||||
#### Using the cache write-only
|
||||
|
||||
At the end of a Job, The `setup-gradle` action will write a summary of the Gradle builds executed, together with a detailed report of the cache entries that were read and written during the Job. This report can provide valuable insights that may help to determine the right way to optimize the cache usage for your workflow.
|
||||
In certain circumstances it may be desirable to start with a clean Gradle User Home state, but to save the state at the end of a workflow Job:
|
||||
|
||||
### Select which jobs should write to the cache
|
||||
```yaml
|
||||
cache-write-only: true
|
||||
```
|
||||
|
||||
Consider a workflow that first runs a Job "compile-and-unit-test" to compile the code and run some basic unit tests, which is followed by a matrix of parallel "integration-test" jobs that each run a set of integration tests for the repository. Each "integration test" Job requires all of the dependencies required by "compile-and-unit-test", and possibly one or 2 additional dependencies.
|
||||
#### Overwriting an existing Gradle User Home
|
||||
|
||||
By default, a new cache entry will be written on completion of each integration test job. If no additional dependencies were downloaded then this cache entry will share the "dependencies" entry with the "compile-and-unit-test" job, but if a single dependency was downloaded then an entirely new "dependencies" entry would be written. (The `setup-gradle` action does not _yet_ support a layered cache that could do this more efficiently). If each of these "integration-test" entries with their different "dependencies" entries is too large, then it could result in other important entries being evicted from the GitHub Actions cache.
|
||||
When the action detects that the Gradle User Home caches directory already exists (`$GRADLE_USER_HOME/caches`), then by default it will not overwrite the existing content of this directory.
|
||||
This can occur when a prior action initializes this directory, or when using a self-hosted runner that retains this directory between uses.
|
||||
|
||||
Some techniques can be used to avoid/mitigate this issue:
|
||||
- Configure the "integration-test" jobs with `cache-read-only: true`, meaning that the Job will use the entry written by the "compile-and-unit-test" job. This will avoid the overhead of cache entries for each of these jobs, at the expense of re-downloading any additional dependencies required by "integration-test".
|
||||
- Add a step to the "compile-and-unit-test" job which downloads all dependencies required by the integration-test jobs but does not execute the tests. This will allow the "dependencies" entry for "compile-and-unit-test" to be shared among all cache entries for "integration-test". The resulting "integration-test" entries should be much smaller, reducing the potential for eviction.
|
||||
- Combine the above 2 techniques, so that no cache entry is written by "integration-test" jobs, but all required dependencies are already present from the restored "compile-and-unit-test" entry.
|
||||
In this case, the Job Summary will display a message like:
|
||||
> Caching for Gradle actions was disabled due to pre-existing Gradle User Home
|
||||
|
||||
### Select which branches should write to the cache
|
||||
If you want to override the default and have the caches of the `setup-gradle` action overwrite existing content in the Gradle User Home, you can set the `cache-overwrite-existing` parameter to `true`:
|
||||
|
||||
GitHub cache entries are not shared between builds on different branches or tags.
|
||||
Workflow runs can _only_ restore caches created in either the same branch or the default branch (usually `main`).
|
||||
This means that each branch will have its own Gradle User Home cache scope, and will not benefit from cache entries written for other (non-default) branches.
|
||||
```yaml
|
||||
cache-overwrite-existing: true
|
||||
```
|
||||
|
||||
By default, The `setup-gradle` action will only _write_ to the cache for builds run on the default (`master`/`main`) branch.
|
||||
Jobs running on other branches will only read from the cache. In most cases, this is the desired behavior.
|
||||
This is because Jobs running on other branches will benefit from the cached Gradle User Home from `main`,
|
||||
without writing private cache entries which could lead to evicting these shared entries.
|
||||
#### Configuring cache cleanup
|
||||
|
||||
If you have other long-lived development branches that would benefit from writing to the cache,
|
||||
you can configure this by disabling the `cache-read-only` action parameter for these branches.
|
||||
See [Using the cache read-only](#using-the-cache-read-only) for more details.
|
||||
The Gradle User Home directory tends to grow over time. When you switch to a new Gradle wrapper version
|
||||
or upgrade a dependency version the old files are not automatically and immediately removed.
|
||||
While this can make sense in a local environment, in a GitHub Actions environment
|
||||
it can lead to ever-larger Gradle User Home cache entries being saved and restored.
|
||||
|
||||
Note there are some cases where writing cache entries is typically unhelpful (these are disabled by default):
|
||||
- For `pull_request` triggered runs, the cache scope is limited to the merge ref (`refs/pull/.../merge`) and can only be restored by re-runs of the same pull request.
|
||||
- For `merge_group` triggered runs, the cache scope is limited to a temporary branch with a special prefix created to validate pull request changes, and won't be available on subsequent Merge Queue executions.
|
||||
To avoid this situation, the `setup-gradle` and `dependency-submission` actions will perform "cache-cleanup",
|
||||
purging any unused files from the Gradle User Home before saving it to the GitHub Actions cache.
|
||||
Cache cleanup will attempt to remove any files that are initially restored to the Gradle User Home directory
|
||||
but that are not used by Gradle during the GitHub Actions Workflow.
|
||||
|
||||
### Exclude content from Gradle User Home cache
|
||||
If a Gradle build fails when running the Job, then it is possible that some required files and dependencies
|
||||
will not be touched during the Job. To prevent these files from being purged, the default behavior is for
|
||||
cache cleanup to run only when all Gradle builds in the Job are successful.
|
||||
|
||||
Gradle Home cache cleanup is enabled by default, and can be controlled by the `cache-cleanup` parameter as follows:
|
||||
- `cache-cleanup: always`: Always run cache cleanup, even when a Gradle build fails in the Job.
|
||||
- `cache-cleanup: on-success` (default): Run cache cleanup when the Job contains no failing Gradle builds.
|
||||
- `cache-cleanup: never`: Disable cache cleanup for the Job.
|
||||
|
||||
Cache cleanup will never run when the cache is configured as read-only or disabled.
|
||||
|
||||
#### Cache encryption key
|
||||
|
||||
The `cache-encryption-key` parameter allows you to provide a base64-encoded AES key to encrypt configuration-cache data stored in the GitHub Actions cache. This is useful when your configuration-cache entries contain sensitive information.
|
||||
|
||||
```yaml
|
||||
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
|
||||
```
|
||||
|
||||
A suitable key can be generated with:
|
||||
```
|
||||
openssl rand -base64 16
|
||||
```
|
||||
|
||||
The key is exported as the `GRADLE_ENCRYPTION_KEY` environment variable for use by subsequent steps.
|
||||
|
||||
#### Include/exclude content from Gradle User Home
|
||||
|
||||
As well as any wrapper distributions, the action will attempt to save and restore the `caches` and `notifications` directories from Gradle User Home.
|
||||
|
||||
@@ -387,7 +360,57 @@ gradle-home-cache-excludes: |
|
||||
You can specify any number of fixed paths or patterns to include or exclude.
|
||||
File pattern support is documented at https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#patterns-to-match-file-paths.
|
||||
|
||||
### Disable local build-cache when remote build-cache is available
|
||||
#### Strict cache matching
|
||||
|
||||
By default, the Enhanced Caching provider will attempt to restore the Gradle User Home from cache entries written by other Jobs in the same workflow (using restore keys). Setting `gradle-home-cache-strict-match` to `true` disables this behavior, so that cache entries are only restored from the same Job.
|
||||
|
||||
```yaml
|
||||
gradle-home-cache-strict-match: true
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This is an experimental feature that may be useful if you find that cache entries from other Jobs are causing issues.
|
||||
|
||||
### Optimizing cache effectiveness
|
||||
|
||||
Cache storage space for GitHub actions is limited, and writing new cache entries can trigger the deletion of existing entries.
|
||||
Eviction of shared cache entries can reduce cache effectiveness, slowing down your `setup-gradle` steps.
|
||||
|
||||
There are a several actions you can take if your cache use is less effective due to entry eviction.
|
||||
|
||||
At the end of a Job, The `setup-gradle` action will write a summary of the Gradle builds executed, together with a detailed report of the cache entries that were read and written during the Job. This report can provide valuable insights that may help to determine the right way to optimize the cache usage for your workflow.
|
||||
|
||||
#### Select which jobs should write to the cache
|
||||
|
||||
Consider a workflow that first runs a Job "compile-and-unit-test" to compile the code and run some basic unit tests, which is followed by a matrix of parallel "integration-test" jobs that each run a set of integration tests for the repository. Each "integration test" Job requires all of the dependencies required by "compile-and-unit-test", and possibly one or 2 additional dependencies.
|
||||
|
||||
By default, a new cache entry will be written on completion of each integration test job. If no additional dependencies were downloaded then this cache entry will share the "dependencies" entry with the "compile-and-unit-test" job, but if a single dependency was downloaded then an entirely new "dependencies" entry would be written. (The `setup-gradle` action does not _yet_ support a layered cache that could do this more efficiently). If each of these "integration-test" entries with their different "dependencies" entries is too large, then it could result in other important entries being evicted from the GitHub Actions cache.
|
||||
|
||||
Some techniques can be used to avoid/mitigate this issue:
|
||||
- Configure the "integration-test" jobs with `cache-read-only: true`, meaning that the Job will use the entry written by the "compile-and-unit-test" job. This will avoid the overhead of cache entries for each of these jobs, at the expense of re-downloading any additional dependencies required by "integration-test".
|
||||
- Add a step to the "compile-and-unit-test" job which downloads all dependencies required by the integration-test jobs but does not execute the tests. This will allow the "dependencies" entry for "compile-and-unit-test" to be shared among all cache entries for "integration-test". The resulting "integration-test" entries should be much smaller, reducing the potential for eviction.
|
||||
- Combine the above 2 techniques, so that no cache entry is written by "integration-test" jobs, but all required dependencies are already present from the restored "compile-and-unit-test" entry.
|
||||
|
||||
#### Select which branches should write to the cache
|
||||
|
||||
GitHub cache entries are not shared between builds on different branches or tags.
|
||||
Workflow runs can _only_ restore caches created in either the same branch or the default branch (usually `main`).
|
||||
This means that each branch will have its own Gradle User Home cache scope, and will not benefit from cache entries written for other (non-default) branches.
|
||||
|
||||
By default, The `setup-gradle` action will only _write_ to the cache for builds run on the default (`master`/`main`) branch.
|
||||
Jobs running on other branches will only read from the cache. In most cases, this is the desired behavior.
|
||||
This is because Jobs running on other branches will benefit from the cached Gradle User Home from `main`,
|
||||
without writing private cache entries which could lead to evicting these shared entries.
|
||||
|
||||
If you have other long-lived development branches that would benefit from writing to the cache,
|
||||
you can configure this by disabling the `cache-read-only` action parameter for these branches.
|
||||
See [Using the cache read-only](#using-the-cache-read-only) for more details.
|
||||
|
||||
Note there are some cases where writing cache entries is typically unhelpful (these are disabled by default):
|
||||
- For `pull_request` triggered runs, the cache scope is limited to the merge ref (`refs/pull/.../merge`) and can only be restored by re-runs of the same pull request.
|
||||
- For `merge_group` triggered runs, the cache scope is limited to a temporary branch with a special prefix created to validate pull request changes, and won't be available on subsequent Merge Queue executions.
|
||||
|
||||
#### Disable local build-cache when remote build-cache is available
|
||||
|
||||
If you have a remote build-cache available for your build, then it is recommended to do the following:
|
||||
- Enable [remote build-cache push](https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_use_cases) for your GitHub Actions builds
|
||||
@@ -397,6 +420,52 @@ As well as reducing the content that needs to be saved to the GitHub Actions cac
|
||||
this setup will ensure that your CI builds populate the remote cache and keep the cache entries fresh by reading these entries.
|
||||
Local builds can then benefit from the remote cache.
|
||||
|
||||
## Basic Caching
|
||||
|
||||
Basic Caching is an open-source (MIT) alternative that uses `@actions/cache` to provide straightforward, path-based caching of Gradle dependencies. It uses the same caching strategy as `actions/setup-java` with `cache: gradle`.
|
||||
|
||||
To enable Basic Caching:
|
||||
|
||||
```yaml
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
cache-provider: basic
|
||||
```
|
||||
|
||||
### What Basic Caching stores
|
||||
|
||||
The basic provider saves and restores two directories from the Gradle User Home:
|
||||
- `~/.gradle/caches` — downloaded dependencies, compiled build scripts, transforms, and the local build cache
|
||||
- `~/.gradle/wrapper` — wrapper distributions
|
||||
|
||||
### Cache key strategy
|
||||
|
||||
The cache key is computed from the contents of your Gradle build files, using the format:
|
||||
|
||||
`setup-java-{RUNNER_OS}-{arch}-gradle-{hash}`
|
||||
|
||||
The hash is derived from the following file patterns:
|
||||
- `**/*.gradle*`
|
||||
- `**/gradle-wrapper.properties`
|
||||
- `buildSrc/**/Versions.kt`
|
||||
- `buildSrc/**/Dependencies.kt`
|
||||
- `gradle/*.versions.toml`
|
||||
- `**/versions.properties`
|
||||
|
||||
Unlike the Enhanced Caching provider, Basic Caching does not use restore keys. If no exact cache key match is found, the cache starts empty. This means that when you update a dependency or change a build file, the next build will re-download all dependencies and write a fresh cache entry.
|
||||
|
||||
### Limitations
|
||||
|
||||
The basic provider does not support the following Enhanced Caching features:
|
||||
- **Cache cleanup** (`cache-cleanup`): Stale entries are not automatically removed.
|
||||
- **Deduplication**: The entire `caches` and `wrapper` directories are stored as a single cache entry, without deduplication of individual artifacts.
|
||||
- **Include/exclude paths** (`gradle-home-cache-includes` / `gradle-home-cache-excludes`): The cached paths are fixed.
|
||||
- **Write-only mode** (`cache-write-only`): Not available with basic caching.
|
||||
- **Overwrite existing** (`cache-overwrite-existing`): Not available with basic caching.
|
||||
- **Strict cache matching** (`gradle-home-cache-strict-match`): Not applicable since restore keys are not used.
|
||||
|
||||
If you need these features, switch to the default [Enhanced Caching](#enhanced-caching) provider.
|
||||
|
||||
## Debugging and Troubleshooting
|
||||
|
||||
To debug a failed job, it can be useful to run with [debug logging enabled](https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging).
|
||||
@@ -465,14 +534,14 @@ jobs:
|
||||
run-gradle-build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
add-job-summary-as-pr-comment: 'on-failure' # Valid values are 'never' (default), 'always', and 'on-failure'
|
||||
|
||||
@@ -502,14 +571,14 @@ jobs:
|
||||
gradle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Run build with Gradle wrapper
|
||||
run: ./gradlew build --scan
|
||||
@@ -540,7 +609,7 @@ If you do not want wrapper-validation to occur automatically, you can disable it
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
validate-wrappers: false
|
||||
```
|
||||
@@ -552,7 +621,7 @@ These are not allowed by default.
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
validate-wrappers: true
|
||||
allow-snapshot-wrappers: true
|
||||
@@ -610,14 +679,14 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle to generate and submit dependency graphs
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
dependency-graph: generate-and-submit
|
||||
- name: Run the usual CI build (dependency-graph will be generated and submitted post-job)
|
||||
@@ -644,7 +713,7 @@ graph cannot be generated or submitted. You can enable this behavior with the `d
|
||||
|
||||
```yaml
|
||||
# Ensure that the workflow Job will fail if the dependency graph cannot be submitted
|
||||
- uses: gradle/actions/setup-gradle@v5
|
||||
- uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
dependency-graph: generate-and-submit
|
||||
dependency-graph-continue-on-failure: false
|
||||
@@ -662,14 +731,14 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle to generate and submit dependency graphs
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
dependency-graph: generate-and-submit
|
||||
- name: Run a build, resolving the 'dependency-graph' plugin from the plugin portal proxy
|
||||
@@ -692,14 +761,14 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 17
|
||||
|
||||
- name: Setup Gradle to generate and submit dependency graphs
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
dependency-graph: generate-and-submit
|
||||
- name: Build the app, generating a graph of dependencies required
|
||||
@@ -743,7 +812,7 @@ To publish to https://scans.gradle.com, you must specify in your workflow that y
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle to publish build scans
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
build-scan-publish: true
|
||||
build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service'
|
||||
@@ -765,7 +834,7 @@ The short-lived access token will then be used wherever a Develocity access key
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.MY_DEVELOCITY_ACCESS_KEY }} # Long-lived access key, visiblility is restricted to this step.
|
||||
|
||||
@@ -783,7 +852,7 @@ To avoid this, use the `develocity-token-expiry` parameter to specify a differen
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.MY_DEVELOCITY_ACCESS_KEY }}
|
||||
develocity-token-expiry: '8' # The number of hours that the access token should remain valid (max 24).
|
||||
@@ -805,7 +874,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
# The build will automatically use a short-lived access token to authenticate with Develocity
|
||||
- name: Run a Gradle build that is configured to publish to Develocity.
|
||||
@@ -837,7 +906,7 @@ Here's a minimal example:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
develocity-injection-enabled: true
|
||||
develocity-url: 'https://develocity.your-server.com'
|
||||
@@ -847,14 +916,14 @@ Here's a minimal example:
|
||||
run: ./gradlew build
|
||||
```
|
||||
|
||||
This configuration will automatically apply `v4.3.1` of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/), and publish build scans to https://develocity.your-server.com.
|
||||
This configuration will automatically apply `v4.4.0` of the [Develocity Gradle plugin](https://docs.gradle.com/develocity/gradle-plugin/), and publish build scans to https://develocity.your-server.com.
|
||||
|
||||
This example assumes that the `develocity.your-server.com` server allows anonymous publishing of build scans.
|
||||
In the likely scenario that your Develocity server requires authentication, you will also need to pass a valid [Develocity access key](https://docs.gradle.com/develocity/gradle-plugin/#via_environment_variable) taken from a secret:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
with:
|
||||
develocity-access-key: ${{ secrets.MY_DEVELOCITY_ACCESS_KEY }}
|
||||
|
||||
@@ -905,7 +974,7 @@ Here's an example using the env vars:
|
||||
|
||||
```yaml
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
|
||||
- name: Run a Gradle build with Develocity injection enabled with environment variables
|
||||
run: ./gradlew build
|
||||
@@ -933,4 +1002,3 @@ Each of the plugins is signed by Gradle, and you can simply add the following sn
|
||||
</trusted-key>
|
||||
</trusted-keys>
|
||||
```
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ We created an example [Homoglyph attack PR here](https://github.com/JLLeitschuh/
|
||||
Simply add this action to your workflow **after** having checked out your source tree and **before** running any Gradle build:
|
||||
|
||||
```yaml
|
||||
uses: gradle/actions/wrapper-validation@v5
|
||||
uses: gradle/actions/wrapper-validation@v6
|
||||
```
|
||||
|
||||
This action step should precede any step using `gradle/gradle-build-action` or `gradle/actions/setup-gradle`.
|
||||
@@ -72,8 +72,8 @@ jobs:
|
||||
name: "Validation"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/actions/wrapper-validation@v5
|
||||
- uses: actions/checkout@v6
|
||||
- uses: gradle/actions/wrapper-validation@v6
|
||||
```
|
||||
|
||||
## Contributing to an external GitHub Repository
|
||||
@@ -120,7 +120,7 @@ restore these Jars on checkout. Without this, only a pointer to the Wrapper Jar
|
||||
|
||||
```
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
lfs: true # gradle-wrapper.jar verification will fail without this
|
||||
```
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
Software License Agreement Copyright (c) 2026 Gradle, Inc. All rights
|
||||
reserved.
|
||||
|
||||
1. PROPRIETARY AND CONFIDENTIAL. This software and its source code are the
|
||||
proprietary and confidential property of Gradle, Inc.
|
||||
|
||||
2. LICENSE GRANT AND TERMS OF USE. Subject to your compliance with the Terms
|
||||
of Use referenced below, Gradle, Inc. grants you a non-exclusive,
|
||||
non-transferable license to use this library solely for internal CI/CD
|
||||
purposes. Your use, installation, and distribution of this software are
|
||||
strictly governed by the Gradle, Inc. Terms of Use, which can be found at the
|
||||
following URL: https://gradle.com/legal/terms-of-use/. By downloading,
|
||||
installing, or using this software, you agree to be bound by the terms and
|
||||
conditions set forth in the link above.
|
||||
|
||||
3. RESTRICTIONS. Unless expressly permitted in the Terms of Use referenced
|
||||
above, you may not: (a) Modify, decompile, or reverse engineer this software.
|
||||
(b) Redistribute the source code or binaries without prior written consent.
|
||||
(c) Use this software for any purpose not authorized by the Terms of Use.
|
||||
|
||||
4. NO WARRANTY. AS SET FORTH IN THE GRADLE, INC. TERMS OF USE, THIS SOFTWARE IS
|
||||
PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -19,14 +19,14 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v5
|
||||
uses: gradle/actions/setup-gradle@v6
|
||||
- name: Build with Gradle
|
||||
run: ./gradlew build
|
||||
```
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
# Type descriptors based on https://github.com/typesafegithub/github-actions-typing
|
||||
inputs:
|
||||
gradle-version:
|
||||
type: string
|
||||
|
||||
# Cache configuration
|
||||
cache-provider:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- basic
|
||||
- enhanced
|
||||
|
||||
cache-disabled:
|
||||
type: boolean
|
||||
|
||||
cache-read-only:
|
||||
type: boolean
|
||||
|
||||
cache-write-only:
|
||||
type: boolean
|
||||
|
||||
cache-overwrite-existing:
|
||||
type: boolean
|
||||
|
||||
cache-encryption-key:
|
||||
type: string
|
||||
|
||||
cache-cleanup:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- on-success
|
||||
- always
|
||||
|
||||
gradle-home-cache-cleanup:
|
||||
type: boolean
|
||||
|
||||
gradle-home-cache-includes:
|
||||
type: list
|
||||
separator: '\n'
|
||||
list-item:
|
||||
type: string
|
||||
|
||||
gradle-home-cache-excludes:
|
||||
type: list
|
||||
separator: '\n'
|
||||
list-item:
|
||||
type: string
|
||||
|
||||
# Job summary configuration
|
||||
add-job-summary:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- always
|
||||
- on-failure
|
||||
|
||||
add-job-summary-as-pr-comment:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- never
|
||||
- always
|
||||
- on-failure
|
||||
|
||||
# Dependency Graph configuration
|
||||
dependency-graph:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- disabled
|
||||
- generate
|
||||
- generate-and-submit
|
||||
- generate-and-upload
|
||||
- download-and-submit
|
||||
|
||||
dependency-graph-report-dir:
|
||||
type: string
|
||||
|
||||
dependency-graph-continue-on-failure:
|
||||
type: boolean
|
||||
|
||||
dependency-graph-exclude-projects:
|
||||
type: string
|
||||
|
||||
dependency-graph-include-projects:
|
||||
type: string
|
||||
|
||||
dependency-graph-exclude-configurations:
|
||||
type: string
|
||||
|
||||
dependency-graph-include-configurations:
|
||||
type: string
|
||||
|
||||
artifact-retention-days:
|
||||
type: integer
|
||||
|
||||
# Build Scan configuration
|
||||
build-scan-publish:
|
||||
type: boolean
|
||||
|
||||
build-scan-terms-of-use-url:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- https://gradle.com/terms-of-service
|
||||
- https://gradle.com/help/legal-terms-of-use
|
||||
|
||||
build-scan-terms-of-use-agree:
|
||||
type: enum
|
||||
allowed-values:
|
||||
- 'yes'
|
||||
|
||||
develocity-access-key:
|
||||
type: string
|
||||
|
||||
develocity-token-expiry:
|
||||
type: integer
|
||||
|
||||
develocity-injection-enabled:
|
||||
type: boolean
|
||||
|
||||
develocity-url:
|
||||
type: string
|
||||
|
||||
develocity-allow-untrusted-server:
|
||||
type: boolean
|
||||
|
||||
develocity-capture-file-fingerprints:
|
||||
type: boolean
|
||||
|
||||
develocity-enforce-url:
|
||||
type: boolean
|
||||
|
||||
develocity-plugin-version:
|
||||
type: string
|
||||
|
||||
develocity-ccud-plugin-version:
|
||||
type: string
|
||||
|
||||
gradle-plugin-repository-url:
|
||||
type: string
|
||||
|
||||
gradle-plugin-repository-username:
|
||||
type: string
|
||||
|
||||
gradle-plugin-repository-password:
|
||||
type: string
|
||||
|
||||
# Wrapper validation configuration
|
||||
validate-wrappers:
|
||||
type: boolean
|
||||
|
||||
allow-snapshot-wrappers:
|
||||
type: boolean
|
||||
|
||||
# Deprecated action inputs
|
||||
arguments:
|
||||
type: string
|
||||
|
||||
# Experimental action inputs
|
||||
gradle-home-cache-strict-match:
|
||||
type: boolean
|
||||
|
||||
# Internal action inputs
|
||||
workflow-job-context:
|
||||
type: string
|
||||
|
||||
github-token:
|
||||
type: string
|
||||
|
||||
outputs:
|
||||
build-scan-url:
|
||||
type: string
|
||||
|
||||
dependency-graph-file:
|
||||
type: string
|
||||
|
||||
gradle-version:
|
||||
type: string
|
||||
@@ -9,6 +9,14 @@ inputs:
|
||||
required: false
|
||||
|
||||
# Cache configuration
|
||||
cache-provider:
|
||||
description: |
|
||||
Specifies the caching implementation to use.
|
||||
'enhanced' (default) uses the full-featured commercial caching service (gradle-actions-caching).
|
||||
'basic' uses a simple open-source caching implementation based on GitHub Actions cache.
|
||||
required: false
|
||||
default: 'enhanced'
|
||||
|
||||
cache-disabled:
|
||||
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
||||
required: false
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
const { fromPropertiesFile, inGradleUserHome } = require('@gradle-tech/develocity-agent/api/config');
|
||||
|
||||
module.exports = {
|
||||
server: {
|
||||
url: 'https://ge.solutions-team.gradle.com/',
|
||||
accessKey: fromPropertiesFile(inGradleUserHome())
|
||||
},
|
||||
}
|
||||
+10
-4
@@ -1,14 +1,20 @@
|
||||
module.exports = {
|
||||
import {createRequire} from 'node:module'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const develocityReporter = require.resolve('@gradle-tech/develocity-agent/jest-reporter')
|
||||
|
||||
export default {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts', 'json'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
extensionsToTreatAsEsm: ['.ts'],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
'^.+\\.ts$': ['ts-jest', { useESM: true }]
|
||||
},
|
||||
reporters: [
|
||||
'default',
|
||||
'@gradle-tech/develocity-agent/jest-reporter',
|
||||
'default',
|
||||
develocityReporter
|
||||
],
|
||||
verbose: true
|
||||
}
|
||||
|
||||
Generated
+1527
-1622
File diff suppressed because it is too large
Load Diff
+28
-30
@@ -2,21 +2,21 @@
|
||||
"name": "gradle-actions",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"description": "Execute Gradle Build",
|
||||
"scripts": {
|
||||
"postinstall": "patch-package",
|
||||
"prettier-write": "prettier --write 'src/**/*.ts'",
|
||||
"prettier-check": "prettier --check 'src/**/*.ts'",
|
||||
"lint": "eslint 'src/**/*.ts'",
|
||||
"compile-dependency-submission-main": "ncc build src/actions/dependency-submission/main.ts --out dist/dependency-submission/main --source-map --no-source-map-register",
|
||||
"compile-dependency-submission-post": "ncc build src/actions/dependency-submission/post.ts --out dist/dependency-submission/post --source-map --no-source-map-register",
|
||||
"compile-setup-gradle-main": "ncc build src/actions/setup-gradle/main.ts --out dist/setup-gradle/main --source-map --no-source-map-register",
|
||||
"compile-setup-gradle-post": "ncc build src/actions/setup-gradle/post.ts --out dist/setup-gradle/post --source-map --no-source-map-register",
|
||||
"compile-wrapper-validation-main": "ncc build src/actions/wrapper-validation/main.ts --out dist/wrapper-validation/main --source-map --no-source-map-register",
|
||||
"compile-dependency-submission-main": "esbuild src/actions/dependency-submission/main.ts --bundle --platform=node --target=node24 --format=esm --banner:js=\"import {createRequire} from 'module';const require=createRequire(import.meta.url);\" --outfile=dist/dependency-submission/main/index.js --sourcemap --minify",
|
||||
"compile-dependency-submission-post": "esbuild src/actions/dependency-submission/post.ts --bundle --platform=node --target=node24 --format=esm --banner:js=\"import {createRequire} from 'module';const require=createRequire(import.meta.url);\" --outfile=dist/dependency-submission/post/index.js --sourcemap --minify",
|
||||
"compile-setup-gradle-main": "esbuild src/actions/setup-gradle/main.ts --bundle --platform=node --target=node24 --format=esm --banner:js=\"import {createRequire} from 'module';const require=createRequire(import.meta.url);\" --outfile=dist/setup-gradle/main/index.js --sourcemap --minify",
|
||||
"compile-setup-gradle-post": "esbuild src/actions/setup-gradle/post.ts --bundle --platform=node --target=node24 --format=esm --banner:js=\"import {createRequire} from 'module';const require=createRequire(import.meta.url);\" --outfile=dist/setup-gradle/post/index.js --sourcemap --minify",
|
||||
"compile-wrapper-validation-main": "esbuild src/actions/wrapper-validation/main.ts --bundle --platform=node --target=node24 --format=esm --banner:js=\"import {createRequire} from 'module';const require=createRequire(import.meta.url);\" --outfile=dist/wrapper-validation/main/index.js --sourcemap --minify",
|
||||
"compile": "npm-run-all --parallel compile-*",
|
||||
"check": "npm-run-all --parallel prettier-check lint",
|
||||
"format": "npm-run-all --parallel prettier-write lint",
|
||||
"test": "jest",
|
||||
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
||||
"build": "npm run format && npm run compile",
|
||||
"all": "npm run build && npm test"
|
||||
},
|
||||
@@ -35,40 +35,38 @@
|
||||
"node": ">=24.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/artifact": "4.0.0",
|
||||
"@actions/cache": "4.0.5",
|
||||
"@actions/core": "2.0.2",
|
||||
"@actions/exec": "2.0.0",
|
||||
"@actions/github": "7.0.0",
|
||||
"@actions/glob": "0.5.0",
|
||||
"@actions/http-client": "3.0.1",
|
||||
"@actions/tool-cache": "3.0.0",
|
||||
"@actions/artifact": "6.1.0",
|
||||
"@actions/cache": "6.0.0",
|
||||
"@actions/core": "3.0.0",
|
||||
"@actions/exec": "3.0.0",
|
||||
"@actions/github": "9.0.0",
|
||||
"@actions/glob": "0.6.1",
|
||||
"@actions/http-client": "4.0.0",
|
||||
"@actions/tool-cache": "4.0.0",
|
||||
"@octokit/webhooks-types": "7.6.1",
|
||||
"cheerio": "1.1.2",
|
||||
"semver": "7.7.3",
|
||||
"cheerio": "1.2.0",
|
||||
"semver": "7.7.4",
|
||||
"string-argv": "0.3.2",
|
||||
"unhomoglyph": "1.0.6",
|
||||
"which": "6.0.0"
|
||||
"which": "6.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gradle-tech/develocity-agent": "3.0.1",
|
||||
"@jest/globals": "30.2.0",
|
||||
"@jest/globals": "30.3.0",
|
||||
"@types/jest": "30.0.0",
|
||||
"@types/node": "25.0.9",
|
||||
"@types/node": "25.5.0",
|
||||
"@types/semver": "7.7.1",
|
||||
"@types/unzipper": "0.10.11",
|
||||
"@types/which": "3.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.53.0",
|
||||
"@vercel/ncc": "0.38.4",
|
||||
"dedent": "1.7.1",
|
||||
"eslint": "9.24.0",
|
||||
"globals": "17.0.0",
|
||||
"jest": "30.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.58.0",
|
||||
"dedent": "1.7.2",
|
||||
"esbuild": "0.28.0",
|
||||
"eslint": "10.1.0",
|
||||
"globals": "17.4.0",
|
||||
"jest": "30.3.0",
|
||||
"nock": "15.0.0",
|
||||
"npm-run-all": "4.1.5",
|
||||
"patch-package": "8.0.1",
|
||||
"prettier": "3.8.0",
|
||||
"ts-jest": "29.4.6",
|
||||
"prettier": "3.8.1",
|
||||
"ts-jest": "29.4.9",
|
||||
"typescript": "5.9.3"
|
||||
},
|
||||
"overrides": {
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
diff --git a/node_modules/@actions/cache/lib/cache.d.ts b/node_modules/@actions/cache/lib/cache.d.ts
|
||||
index ef0928b..d06e675 100644
|
||||
--- a/node_modules/@actions/cache/lib/cache.d.ts
|
||||
+++ b/node_modules/@actions/cache/lib/cache.d.ts
|
||||
@@ -21,7 +21,8 @@ export declare function isFeatureAvailable(): boolean;
|
||||
* @param enableCrossOsArchive an optional boolean enabled to restore on windows any cache created on any platform
|
||||
* @returns string returns the key for the cache hit, otherwise returns undefined
|
||||
*/
|
||||
-export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions, enableCrossOsArchive?: boolean): Promise<string | undefined>;
|
||||
+export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions, enableCrossOsArchive?: boolean): Promise<CacheEntry | undefined>;
|
||||
+
|
||||
/**
|
||||
* Saves a list of files with the specified key
|
||||
*
|
||||
@@ -31,4 +32,12 @@ export declare function restoreCache(paths: string[], primaryKey: string, restor
|
||||
* @param options cache upload options
|
||||
* @returns number returns cacheId if the cache was saved successfully and throws an error if save fails
|
||||
*/
|
||||
-export declare function saveCache(paths: string[], key: string, options?: UploadOptions, enableCrossOsArchive?: boolean): Promise<number>;
|
||||
+export declare function saveCache(paths: string[], key: string, options?: UploadOptions, enableCrossOsArchive?: boolean): Promise<CacheEntry>;
|
||||
+
|
||||
+// PATCHED: Add `CacheEntry` as return type for save/restore functions
|
||||
+// This allows us to track and report on cache entry sizes.
|
||||
+export declare class CacheEntry {
|
||||
+ key: string;
|
||||
+ size?: number;
|
||||
+ constructor(key: string, size?: number);
|
||||
+}
|
||||
diff --git a/node_modules/@actions/cache/lib/cache.js b/node_modules/@actions/cache/lib/cache.js
|
||||
index 41f2a37..2fe1600 100644
|
||||
--- a/node_modules/@actions/cache/lib/cache.js
|
||||
+++ b/node_modules/@actions/cache/lib/cache.js
|
||||
@@ -165,26 +165,29 @@ function restoreCacheV1(paths, primaryKey, restoreKeys, options, enableCrossOsAr
|
||||
core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`);
|
||||
yield (0, tar_1.extractTar)(archivePath, compressionMethod);
|
||||
core.info('Cache restored successfully');
|
||||
- return cacheEntry.cacheKey;
|
||||
- }
|
||||
- catch (error) {
|
||||
- const typedError = error;
|
||||
- if (typedError.name === ValidationError.name) {
|
||||
- throw error;
|
||||
- }
|
||||
- else {
|
||||
- // warn on cache restore failure and continue build
|
||||
- // Log server errors (5xx) as errors, all other errors as warnings
|
||||
- if (typedError instanceof http_client_1.HttpClientError &&
|
||||
- typeof typedError.statusCode === 'number' &&
|
||||
- typedError.statusCode >= 500) {
|
||||
- core.error(`Failed to restore: ${error.message}`);
|
||||
- }
|
||||
- else {
|
||||
- core.warning(`Failed to restore: ${error.message}`);
|
||||
- }
|
||||
- }
|
||||
+
|
||||
+ // PATCHED - Include size of restored entry
|
||||
+ return new CacheEntry(cacheEntry.cacheKey, archiveFileSize);
|
||||
}
|
||||
+ // PATCHED - propagate errors
|
||||
+ // catch (error) {
|
||||
+ // const typedError = error;
|
||||
+ // if (typedError.name === ValidationError.name) {
|
||||
+ // throw error;
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // // warn on cache restore failure and continue build
|
||||
+ // // Log server errors (5xx) as errors, all other errors as warnings
|
||||
+ // if (typedError instanceof http_client_1.HttpClientError &&
|
||||
+ // typeof typedError.statusCode === 'number' &&
|
||||
+ // typedError.statusCode >= 500) {
|
||||
+ // core.error(`Failed to restore: ${error.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // core.warning(`Failed to restore: ${error.message}`);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
finally {
|
||||
// Try to delete the archive to save space
|
||||
try {
|
||||
@@ -257,26 +260,29 @@ function restoreCacheV2(paths, primaryKey, restoreKeys, options, enableCrossOsAr
|
||||
}
|
||||
yield (0, tar_1.extractTar)(archivePath, compressionMethod);
|
||||
core.info('Cache restored successfully');
|
||||
- return response.matchedKey;
|
||||
- }
|
||||
- catch (error) {
|
||||
- const typedError = error;
|
||||
- if (typedError.name === ValidationError.name) {
|
||||
- throw error;
|
||||
- }
|
||||
- else {
|
||||
- // Supress all non-validation cache related errors because caching should be optional
|
||||
- // Log server errors (5xx) as errors, all other errors as warnings
|
||||
- if (typedError instanceof http_client_1.HttpClientError &&
|
||||
- typeof typedError.statusCode === 'number' &&
|
||||
- typedError.statusCode >= 500) {
|
||||
- core.error(`Failed to restore: ${error.message}`);
|
||||
- }
|
||||
- else {
|
||||
- core.warning(`Failed to restore: ${error.message}`);
|
||||
- }
|
||||
- }
|
||||
+
|
||||
+ // PATCHED - Include size of restored entry
|
||||
+ return new CacheEntry(response.matchedKey, archiveFileSize);
|
||||
}
|
||||
+ // PATCHED - propagate errors
|
||||
+ // catch (error) {
|
||||
+ // const typedError = error;
|
||||
+ // if (typedError.name === ValidationError.name) {
|
||||
+ // throw error;
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // // Supress all non-validation cache related errors because caching should be optional
|
||||
+ // // Log server errors (5xx) as errors, all other errors as warnings
|
||||
+ // if (typedError instanceof http_client_1.HttpClientError &&
|
||||
+ // typeof typedError.statusCode === 'number' &&
|
||||
+ // typedError.statusCode >= 500) {
|
||||
+ // core.error(`Failed to restore: ${error.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // core.warning(`Failed to restore: ${error.message}`);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
finally {
|
||||
try {
|
||||
if (archivePath) {
|
||||
@@ -367,27 +373,31 @@ function saveCacheV1(paths, key, options, enableCrossOsArchive = false) {
|
||||
}
|
||||
core.debug(`Saving Cache (ID: ${cacheId})`);
|
||||
yield cacheHttpClient.saveCache(cacheId, archivePath, '', options);
|
||||
+
|
||||
+ // PATCHED - Include size of saved entry
|
||||
+ return new CacheEntry(key, archiveFileSize);
|
||||
}
|
||||
- catch (error) {
|
||||
- const typedError = error;
|
||||
- if (typedError.name === ValidationError.name) {
|
||||
- throw error;
|
||||
- }
|
||||
- else if (typedError.name === ReserveCacheError.name) {
|
||||
- core.info(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- else {
|
||||
- // Log server errors (5xx) as errors, all other errors as warnings
|
||||
- if (typedError instanceof http_client_1.HttpClientError &&
|
||||
- typeof typedError.statusCode === 'number' &&
|
||||
- typedError.statusCode >= 500) {
|
||||
- core.error(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- else {
|
||||
- core.warning(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
+ // PATCHED - propagate errors
|
||||
+ //catch (error) {
|
||||
+ // const typedError = error;
|
||||
+ // if (typedError.name === ValidationError.name) {
|
||||
+ // throw error;
|
||||
+ // }
|
||||
+ // else if (typedError.name === ReserveCacheError.name) {
|
||||
+ // core.info(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // // Log server errors (5xx) as errors, all other errors as warnings
|
||||
+ // if (typedError instanceof http_client_1.HttpClientError &&
|
||||
+ // typeof typedError.statusCode === 'number' &&
|
||||
+ // typedError.statusCode >= 500) {
|
||||
+ // core.error(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // core.warning(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
finally {
|
||||
// Try to delete the archive to save space
|
||||
try {
|
||||
@@ -471,27 +481,31 @@ function saveCacheV2(paths, key, options, enableCrossOsArchive = false) {
|
||||
throw new Error(`Unable to finalize cache with key ${key}, another job may be finalizing this cache.`);
|
||||
}
|
||||
cacheId = parseInt(finalizeResponse.entryId);
|
||||
+
|
||||
+ // PATCHED - Include size of saved entry
|
||||
+ return new CacheEntry(key, archiveFileSize);
|
||||
}
|
||||
- catch (error) {
|
||||
- const typedError = error;
|
||||
- if (typedError.name === ValidationError.name) {
|
||||
- throw error;
|
||||
- }
|
||||
- else if (typedError.name === ReserveCacheError.name) {
|
||||
- core.info(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- else {
|
||||
- // Log server errors (5xx) as errors, all other errors as warnings
|
||||
- if (typedError instanceof http_client_1.HttpClientError &&
|
||||
- typeof typedError.statusCode === 'number' &&
|
||||
- typedError.statusCode >= 500) {
|
||||
- core.error(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- else {
|
||||
- core.warning(`Failed to save: ${typedError.message}`);
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
+ // PATCHED - propagate errors
|
||||
+ //catch (error) {
|
||||
+ // const typedError = error;
|
||||
+ // if (typedError.name === ValidationError.name) {
|
||||
+ // throw error;
|
||||
+ // }
|
||||
+ // else if (typedError.name === ReserveCacheError.name) {
|
||||
+ // core.info(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // // Log server errors (5xx) as errors, all other errors as warnings
|
||||
+ // if (typedError instanceof http_client_1.HttpClientError &&
|
||||
+ // typeof typedError.statusCode === 'number' &&
|
||||
+ // typedError.statusCode >= 500) {
|
||||
+ // core.error(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // else {
|
||||
+ // core.warning(`Failed to save: ${typedError.message}`);
|
||||
+ // }
|
||||
+ // }
|
||||
+ //}
|
||||
finally {
|
||||
// Try to delete the archive to save space
|
||||
try {
|
||||
@@ -504,4 +518,12 @@ function saveCacheV2(paths, key, options, enableCrossOsArchive = false) {
|
||||
return cacheId;
|
||||
});
|
||||
}
|
||||
+// PATCHED - CacheEntry class
|
||||
+class CacheEntry {
|
||||
+ constructor(key, size) {
|
||||
+ this.key = key;
|
||||
+ this.size = size;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
//# sourceMappingURL=cache.js.map
|
||||
\ No newline at end of file
|
||||
@@ -5,7 +5,7 @@ import * as dependencyGraph from '../../dependency-graph'
|
||||
|
||||
import {parseArgsStringToArgv} from 'string-argv'
|
||||
import {
|
||||
BuildScanConfig,
|
||||
DevelocityConfig,
|
||||
CacheConfig,
|
||||
DependencyGraphConfig,
|
||||
DependencyGraphOption,
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
} from '../../configuration'
|
||||
import {saveDeprecationState} from '../../deprecation-collector'
|
||||
import {handleMainActionError} from '../../errors'
|
||||
import {forceExit} from '../../force-exit'
|
||||
|
||||
/**
|
||||
* The main entry point for the action, called by Github Actions for the step.
|
||||
@@ -24,7 +25,7 @@ export async function run(): Promise<void> {
|
||||
setActionId('gradle/actions/dependency-submission')
|
||||
|
||||
// Configure Gradle environment (Gradle User Home)
|
||||
await setupGradle.setup(new CacheConfig(), new BuildScanConfig(), new WrapperValidationConfig())
|
||||
await setupGradle.setup(new CacheConfig(), new DevelocityConfig(), new WrapperValidationConfig())
|
||||
|
||||
// Capture the enabled state of dependency-graph
|
||||
const originallyEnabled = process.env['GITHUB_DEPENDENCY_GRAPH_ENABLED']
|
||||
@@ -67,7 +68,7 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
// Explicit process.exit() to prevent waiting for hanging promises.
|
||||
process.exit()
|
||||
await forceExit()
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -2,6 +2,7 @@ import * as setupGradle from '../../setup-gradle'
|
||||
|
||||
import {CacheConfig, SummaryConfig} from '../../configuration'
|
||||
import {handlePostActionError} from '../../errors'
|
||||
import {forceExit} from '../../force-exit'
|
||||
|
||||
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
||||
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
||||
@@ -19,7 +20,7 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
// Explicit process.exit() to prevent waiting for promises left hanging by `@actions/cache` on save.
|
||||
process.exit()
|
||||
await forceExit()
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as setupGradle from '../../setup-gradle'
|
||||
import * as provisioner from '../../execution/provision'
|
||||
import * as dependencyGraph from '../../dependency-graph'
|
||||
import {
|
||||
BuildScanConfig,
|
||||
DevelocityConfig,
|
||||
CacheConfig,
|
||||
DependencyGraphConfig,
|
||||
GradleExecutionConfig,
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from '../../configuration'
|
||||
import {failOnUseOfRemovedFeature, saveDeprecationState} from '../../deprecation-collector'
|
||||
import {handleMainActionError} from '../../errors'
|
||||
import {forceExit} from '../../force-exit'
|
||||
|
||||
/**
|
||||
* The main entry point for the action, called by Github Actions for the step.
|
||||
@@ -27,7 +28,7 @@ export async function run(): Promise<void> {
|
||||
setActionId('gradle/actions/setup-gradle')
|
||||
|
||||
// Configure Gradle environment (Gradle User Home)
|
||||
await setupGradle.setup(new CacheConfig(), new BuildScanConfig(), new WrapperValidationConfig())
|
||||
await setupGradle.setup(new CacheConfig(), new DevelocityConfig(), new WrapperValidationConfig())
|
||||
|
||||
// Configure the dependency graph submission
|
||||
await dependencyGraph.setup(new DependencyGraphConfig())
|
||||
@@ -42,7 +43,7 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
// Explicit process.exit() to prevent waiting for hanging promises.
|
||||
process.exit()
|
||||
await forceExit()
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -4,6 +4,7 @@ import * as dependencyGraph from '../../dependency-graph'
|
||||
import {CacheConfig, DependencyGraphConfig, SummaryConfig} from '../../configuration'
|
||||
import {handlePostActionError} from '../../errors'
|
||||
import {emitDeprecationWarnings, restoreDeprecationState} from '../../deprecation-collector'
|
||||
import {forceExit} from '../../force-exit'
|
||||
|
||||
// Catch and log any unhandled exceptions. These exceptions can leak out of the uploadChunk method in
|
||||
// @actions/toolkit when a failed upload closes the file descriptor causing any in-process reads to
|
||||
@@ -27,7 +28,7 @@ export async function run(): Promise<void> {
|
||||
}
|
||||
|
||||
// Explicit process.exit() to prevent waiting for promises left hanging by `@actions/cache` on save.
|
||||
process.exit()
|
||||
await forceExit()
|
||||
}
|
||||
|
||||
run()
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {versionIsAtLeast} from './execution/gradle'
|
||||
|
||||
export interface BuildResult {
|
||||
get rootProjectName(): string
|
||||
@@ -14,47 +13,14 @@ export interface BuildResult {
|
||||
get buildScanFailed(): boolean
|
||||
}
|
||||
|
||||
export class BuildResults {
|
||||
results: BuildResult[]
|
||||
|
||||
constructor(results: BuildResult[]) {
|
||||
this.results = results
|
||||
}
|
||||
|
||||
anyFailed(): boolean {
|
||||
return this.results.some(result => result.buildFailed)
|
||||
}
|
||||
|
||||
anyConfigCacheHit(): boolean {
|
||||
return this.results.some(result => result.configCacheHit)
|
||||
}
|
||||
|
||||
uniqueGradleHomes(): string[] {
|
||||
const allHomes = this.results.map(buildResult => buildResult.gradleHomeDir)
|
||||
return Array.from(new Set(allHomes))
|
||||
}
|
||||
|
||||
highestGradleVersion(): string | null {
|
||||
if (this.results.length === 0) {
|
||||
return null
|
||||
}
|
||||
return this.results
|
||||
.map(result => result.gradleVersion)
|
||||
.reduce((maxVersion: string, currentVersion: string) => {
|
||||
if (!maxVersion) return currentVersion
|
||||
return versionIsAtLeast(currentVersion, maxVersion) ? currentVersion : maxVersion
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function loadBuildResults(): BuildResults {
|
||||
export function loadBuildResults(): BuildResult[] {
|
||||
const results = getUnprocessedResults().map(filePath => {
|
||||
const content = fs.readFileSync(filePath, 'utf8')
|
||||
const buildResult = JSON.parse(content) as BuildResult
|
||||
addScanResults(filePath, buildResult)
|
||||
return buildResult
|
||||
})
|
||||
return new BuildResults(results)
|
||||
return results
|
||||
}
|
||||
|
||||
export function markBuildResultsProcessed(): void {
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import * as cache from '@actions/cache'
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
import * as path from 'path'
|
||||
|
||||
import {BuildResult} from './build-results'
|
||||
import {CacheOptions, CacheService} from './cache-service'
|
||||
|
||||
const PRIMARY_KEY_STATE = 'BASIC_CACHE_PRIMARY_KEY'
|
||||
const RESTORED_KEY_STATE = 'BASIC_CACHE_RESTORED_KEY'
|
||||
const CACHE_KEY_PREFIX = 'setup-java'
|
||||
|
||||
const GRADLE_BUILD_FILE_PATTERNS = [
|
||||
'**/*.gradle*',
|
||||
'**/gradle-wrapper.properties',
|
||||
'buildSrc/**/Versions.kt',
|
||||
'buildSrc/**/Dependencies.kt',
|
||||
'gradle/*.versions.toml',
|
||||
'**/versions.properties'
|
||||
]
|
||||
|
||||
export class BasicCacheService implements CacheService {
|
||||
async restore(gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
||||
const cachePaths = getCachePaths(gradleUserHome)
|
||||
const primaryKey = await computeCacheKey()
|
||||
core.saveState(PRIMARY_KEY_STATE, primaryKey)
|
||||
|
||||
// No "restoreKeys" is set, to start with a clear cache after dependency update
|
||||
// See https://github.com/actions/setup-java/issues/269
|
||||
try {
|
||||
const restoredKey = await cache.restoreCache(cachePaths, primaryKey)
|
||||
if (restoredKey) {
|
||||
core.saveState(RESTORED_KEY_STATE, restoredKey)
|
||||
core.info(`Basic caching restored from cache key: ${restoredKey}`)
|
||||
} else {
|
||||
core.info('Basic caching did not find an entry to restore. Will start with empty state.')
|
||||
}
|
||||
} catch (error) {
|
||||
core.warning(`Basic caching failed to restore from cache: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
async save(gradleUserHome: string, _buildResults: BuildResult[], cacheOptions: CacheOptions): Promise<string> {
|
||||
if (cacheOptions.readOnly) {
|
||||
const restoredKey = core.getState(RESTORED_KEY_STATE)
|
||||
if (restoredKey) {
|
||||
return `Basic caching was read-only. Restored from cache key \`${restoredKey}\`.`
|
||||
}
|
||||
return 'Basic caching was read-only. No cache entry was found to restore.'
|
||||
}
|
||||
|
||||
const primaryKey = core.getState(PRIMARY_KEY_STATE)
|
||||
const restoredKey = core.getState(RESTORED_KEY_STATE)
|
||||
|
||||
if (restoredKey === primaryKey) {
|
||||
core.info(`Basic caching restored entry with key \`${primaryKey}\`. Save was skipped.`)
|
||||
return `Basic caching restored entry with key \`${primaryKey}\`. Save was skipped.`
|
||||
}
|
||||
|
||||
const cachePaths = getCachePaths(gradleUserHome)
|
||||
|
||||
try {
|
||||
await cache.saveCache(cachePaths, primaryKey)
|
||||
core.info(`Basic caching saved entry with key: ${primaryKey}`)
|
||||
return `Basic caching saved entry with key \`${primaryKey}\`.`
|
||||
} catch (error) {
|
||||
core.warning(`Basic caching failed to save entry with key \`${primaryKey}\`: ${error}`)
|
||||
return `Basic caching save failed: ${error}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getCachePaths(gradleUserHome: string): string[] {
|
||||
return [path.join(gradleUserHome, 'caches'), path.join(gradleUserHome, 'wrapper')]
|
||||
}
|
||||
|
||||
async function computeCacheKey(): Promise<string> {
|
||||
const fileHash = await glob.hashFiles(GRADLE_BUILD_FILE_PATTERNS.join('\n'))
|
||||
if (!fileHash) {
|
||||
throw new Error(
|
||||
`No file in ${process.cwd()} matched to [${GRADLE_BUILD_FILE_PATTERNS}], make sure you have checked out the target repository`
|
||||
)
|
||||
}
|
||||
return `${CACHE_KEY_PREFIX}-${process.env['RUNNER_OS']}-${process.arch}-gradle-${fileHash}`
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
import {pathToFileURL} from 'url'
|
||||
|
||||
import {CacheConfig, CacheProvider} from './configuration'
|
||||
import {BasicCacheService} from './cache-service-basic'
|
||||
import {BuildResult} from './build-results'
|
||||
import {CacheOptions, CacheService} from './cache-service'
|
||||
|
||||
const NOOP_CACHING_REPORT = `
|
||||
[Cache was disabled](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#disabling-caching). Gradle User Home was not restored from or saved to the cache.
|
||||
`
|
||||
|
||||
const ENHANCED_CACHE_MESSAGE = `Enhanced Caching: This build is using the proprietary 'gradle-actions-caching' provider for optimized caching support. See https://github.com/gradle/actions/blob/main/DISTRIBUTION.md for terms of use and opt-out instructions.`
|
||||
|
||||
const ENHANCED_CACHE_SUMMARY = `
|
||||
> [!NOTE]
|
||||
> ### ⚡️ Enhanced Caching enabled
|
||||
> This build provides optimized caching support via the proprietary **gradle-actions-caching** provider.
|
||||
> See [DISTRIBUTION.md](https://github.com/gradle/actions/blob/main/DISTRIBUTION.md) for terms of use and opt-out instructions.
|
||||
`
|
||||
|
||||
const BASIC_CACHE_MESSAGE = `Basic Caching: This build uses the open-source caching provider for reliable, path-based caching of Gradle dependencies. Upgrade available: for faster builds and advanced features, consider switching to the Enhanced Caching provider. See https://github.com/gradle/actions/blob/main/DISTRIBUTION.md for details.`
|
||||
|
||||
const BASIC_CACHE_SUMMARY = `
|
||||
> [!NOTE]
|
||||
> ### 🛡️ Basic Caching enabled
|
||||
> This build uses the open-source caching provider for reliable, path-based caching of Gradle dependencies.
|
||||
>
|
||||
> **Upgrade Available:** For faster builds and advanced features, consider switching to the **Enhanced Caching** provider.
|
||||
> See [DISTRIBUTION.md](https://github.com/gradle/actions/blob/main/DISTRIBUTION.md) for details.`
|
||||
|
||||
class NoOpCacheService implements CacheService {
|
||||
async restore(_gradleUserHome: string, _cacheOptions: CacheOptions): Promise<void> {
|
||||
return
|
||||
}
|
||||
|
||||
async save(_gradleUserHome: string, _buildResults: BuildResult[], _cacheOptions: CacheOptions): Promise<string> {
|
||||
return NOOP_CACHING_REPORT
|
||||
}
|
||||
}
|
||||
|
||||
class LicenseWarningCacheService implements CacheService {
|
||||
private delegate: CacheService
|
||||
private summary: string
|
||||
|
||||
constructor(delegate: CacheService, summary: string) {
|
||||
this.delegate = delegate
|
||||
this.summary = summary
|
||||
}
|
||||
|
||||
async restore(gradleUserHome: string, cacheOptions: CacheOptions): Promise<void> {
|
||||
await this.delegate.restore(gradleUserHome, cacheOptions)
|
||||
}
|
||||
|
||||
async save(gradleUserHome: string, buildResults: BuildResult[], cacheOptions: CacheOptions): Promise<string> {
|
||||
const cachingReport = await this.delegate.save(gradleUserHome, buildResults, cacheOptions)
|
||||
return `${cachingReport}\n${this.summary}`
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheService> {
|
||||
if (cacheConfig.isCacheDisabled()) {
|
||||
logCacheMessage('Cache is disabled: will not restore state from previous builds.')
|
||||
return new NoOpCacheService()
|
||||
}
|
||||
|
||||
if (cacheConfig.getCacheProvider() === CacheProvider.Basic) {
|
||||
logCacheMessage(BASIC_CACHE_MESSAGE)
|
||||
return new LicenseWarningCacheService(new BasicCacheService(), BASIC_CACHE_SUMMARY)
|
||||
}
|
||||
|
||||
logCacheMessage(ENHANCED_CACHE_MESSAGE)
|
||||
const cacheService = await loadVendoredCacheService()
|
||||
if (cacheConfig.isCacheLicenseAccepted()) {
|
||||
return cacheService
|
||||
}
|
||||
|
||||
return new LicenseWarningCacheService(cacheService, ENHANCED_CACHE_SUMMARY)
|
||||
}
|
||||
|
||||
export async function loadVendoredCacheService(): Promise<CacheService> {
|
||||
const vendoredLibraryPath = findVendoredLibraryPath()
|
||||
const moduleUrl = pathToFileURL(vendoredLibraryPath).href
|
||||
return (await import(moduleUrl)) as CacheService
|
||||
}
|
||||
|
||||
function findVendoredLibraryPath(): string {
|
||||
const moduleDir = import.meta.dirname
|
||||
const absolutePath = path.resolve(moduleDir, '../../../sources/vendor/gradle-actions-caching/index.js')
|
||||
|
||||
if (fs.existsSync(absolutePath)) {
|
||||
return absolutePath
|
||||
}
|
||||
|
||||
throw new Error(`Unable to locate vendored cache library at ${absolutePath}.`)
|
||||
}
|
||||
|
||||
export function logCacheMessage(message: string): void {
|
||||
console.info(message)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import {BuildResult} from './build-results'
|
||||
|
||||
export interface CacheOptions {
|
||||
disabled: boolean
|
||||
readOnly: boolean
|
||||
writeOnly: boolean
|
||||
overwriteExisting: boolean
|
||||
strictMatch: boolean
|
||||
cleanup: string
|
||||
encryptionKey?: string
|
||||
includes: string[]
|
||||
excludes: string[]
|
||||
}
|
||||
|
||||
export interface CacheService {
|
||||
restore(gradleUserHome: string, cacheOptions: CacheOptions): Promise<void>
|
||||
save(gradleUserHome: string, buildResults: BuildResult[], cacheOptions: CacheOptions): Promise<string>
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as exec from '@actions/exec'
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import * as provisioner from '../execution/provision'
|
||||
import {BuildResult, BuildResults} from '../build-results'
|
||||
import {versionIsAtLeast} from '../execution/gradle'
|
||||
import {gradleWrapperScript} from '../execution/gradlew'
|
||||
|
||||
export class CacheCleaner {
|
||||
private readonly gradleUserHome: string
|
||||
private readonly tmpDir: string
|
||||
|
||||
constructor(gradleUserHome: string, tmpDir: string) {
|
||||
this.gradleUserHome = gradleUserHome
|
||||
this.tmpDir = tmpDir
|
||||
}
|
||||
|
||||
async prepare(): Promise<string> {
|
||||
// Save the current timestamp
|
||||
const timestamp = Date.now().toString()
|
||||
core.saveState('clean-timestamp', timestamp)
|
||||
return timestamp
|
||||
}
|
||||
|
||||
async forceCleanup(buildResults: BuildResults): Promise<void> {
|
||||
const executable = await this.gradleExecutableForCleanup(buildResults)
|
||||
const cleanTimestamp = core.getState('clean-timestamp')
|
||||
await this.forceCleanupFilesOlderThan(cleanTimestamp, executable)
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to use the newest Gradle version that was used to run a build, at least 8.11.
|
||||
*
|
||||
* This will avoid the need to provision a Gradle version for the cleanup when not necessary.
|
||||
*/
|
||||
private async gradleExecutableForCleanup(buildResults: BuildResults): Promise<string> {
|
||||
const preferredVersion = buildResults.highestGradleVersion()
|
||||
if (preferredVersion && versionIsAtLeast(preferredVersion, '8.11')) {
|
||||
try {
|
||||
const wrapperScripts = buildResults.results
|
||||
.map(result => this.findGradleWrapperScript(result))
|
||||
.filter(Boolean) as string[]
|
||||
|
||||
return await provisioner.provisionGradleWithVersionAtLeast(preferredVersion, wrapperScripts)
|
||||
} catch (_) {
|
||||
// Ignore the case where the preferred version cannot be located in https://services.gradle.org/versions/all.
|
||||
// This can happen for snapshot Gradle versions.
|
||||
core.info(
|
||||
`Failed to provision Gradle ${preferredVersion} for cache cleanup. Falling back to default version.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to the minimum version required for cache-cleanup
|
||||
return await provisioner.provisionGradleWithVersionAtLeast('8.11')
|
||||
}
|
||||
|
||||
private findGradleWrapperScript(result: BuildResult): string | null {
|
||||
try {
|
||||
const wrapperScript = gradleWrapperScript(result.rootProjectDir)
|
||||
return path.resolve(result.rootProjectDir, wrapperScript)
|
||||
} catch (error) {
|
||||
core.debug(`No Gradle Wrapper found for ${result.rootProjectName}: ${error}`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// Visible for testing
|
||||
async forceCleanupFilesOlderThan(cleanTimestamp: string, executable: string): Promise<void> {
|
||||
// Run a dummy Gradle build to trigger cache cleanup
|
||||
const cleanupProjectDir = path.resolve(this.tmpDir, 'dummy-cleanup-project')
|
||||
fs.mkdirSync(cleanupProjectDir, {recursive: true})
|
||||
fs.writeFileSync(
|
||||
path.resolve(cleanupProjectDir, 'settings.gradle'),
|
||||
'rootProject.name = "dummy-cleanup-project"'
|
||||
)
|
||||
fs.writeFileSync(
|
||||
path.resolve(cleanupProjectDir, 'init.gradle'),
|
||||
`
|
||||
beforeSettings { settings ->
|
||||
def cleanupTime = ${cleanTimestamp}
|
||||
|
||||
settings.caches {
|
||||
cleanup = Cleanup.ALWAYS
|
||||
|
||||
releasedWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
||||
snapshotWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
||||
downloadedResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
||||
createdResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
||||
buildCache.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
fs.writeFileSync(path.resolve(cleanupProjectDir, 'build.gradle'), 'task("noop") {}')
|
||||
|
||||
await core.group('Executing Gradle to clean up caches', async () => {
|
||||
core.info(`Cleaning up caches last used before ${cleanTimestamp}`)
|
||||
await this.executeCleanupBuild(executable, cleanupProjectDir)
|
||||
})
|
||||
}
|
||||
|
||||
private async executeCleanupBuild(executable: string, cleanupProjectDir: string): Promise<void> {
|
||||
const args = [
|
||||
'-g',
|
||||
this.gradleUserHome,
|
||||
'-I',
|
||||
'init.gradle',
|
||||
'--info',
|
||||
'--no-daemon',
|
||||
'--no-scan',
|
||||
'--build-cache',
|
||||
'-DGITHUB_DEPENDENCY_GRAPH_ENABLED=false',
|
||||
'-DGRADLE_ACTIONS_SKIP_BUILD_RESULT_CAPTURE=true',
|
||||
'noop'
|
||||
]
|
||||
|
||||
await exec.exec(executable, args, {
|
||||
cwd: cleanupProjectDir
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
import * as github from '@actions/github'
|
||||
|
||||
import {CacheConfig, getJobMatrix} from '../configuration'
|
||||
import {hashStrings} from './cache-utils'
|
||||
|
||||
const CACHE_PROTOCOL_VERSION = 'v1'
|
||||
|
||||
const CACHE_KEY_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
||||
const CACHE_KEY_OS_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_ENVIRONMENT'
|
||||
const CACHE_KEY_JOB_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB'
|
||||
const CACHE_KEY_JOB_INSTANCE_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB_INSTANCE'
|
||||
const CACHE_KEY_JOB_EXECUTION_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_JOB_EXECUTION'
|
||||
|
||||
/**
|
||||
* Represents a key used to restore a cache entry.
|
||||
* The Github Actions cache will first try for an exact match on the key.
|
||||
* If that fails, it will try for a prefix match on any of the restoreKeys.
|
||||
*/
|
||||
export class CacheKey {
|
||||
key: string
|
||||
restoreKeys: string[]
|
||||
|
||||
constructor(key: string, restoreKeys: string[]) {
|
||||
this.key = key
|
||||
this.restoreKeys = restoreKeys
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a cache key specific to the current job execution.
|
||||
* The key is constructed from the following inputs (with some user overrides):
|
||||
* - The cache key prefix: defaults to 'gradle-' but can be overridden by the user
|
||||
* - The cache protocol version
|
||||
* - The runner operating system
|
||||
* - The name of the workflow and Job being executed
|
||||
* - The matrix values for the Job being executed (job context)
|
||||
* - The SHA of the commit being executed
|
||||
*
|
||||
* Caches are restored by trying to match the these key prefixes in order:
|
||||
* - The full key with SHA
|
||||
* - A previous key for this Job + matrix
|
||||
* - Any previous key for this Job (any matrix)
|
||||
* - Any previous key for this cache on the current OS
|
||||
*/
|
||||
export function generateCacheKey(cacheName: string, config: CacheConfig): CacheKey {
|
||||
const prefix = process.env[CACHE_KEY_PREFIX_VAR] || ''
|
||||
|
||||
const cacheKeyBase = `${prefix}${getCacheKeyBase(cacheName, CACHE_PROTOCOL_VERSION)}`
|
||||
|
||||
// At the most general level, share caches for all executions on the same OS
|
||||
const cacheKeyForEnvironment = `${cacheKeyBase}|${getCacheKeyEnvironment()}`
|
||||
|
||||
// Then prefer caches that run job with the same ID
|
||||
const cacheKeyForJob = `${cacheKeyForEnvironment}|${getCacheKeyJob()}`
|
||||
|
||||
// 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
|
||||
const cacheKey = `${cacheKeyForJobContext}-${getCacheKeyJobExecution()}`
|
||||
|
||||
if (config.isCacheStrictMatch()) {
|
||||
return new CacheKey(cacheKey, [cacheKeyForJobContext])
|
||||
}
|
||||
|
||||
return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForEnvironment])
|
||||
}
|
||||
|
||||
export function getCacheKeyBase(cacheName: string, cacheProtocolVersion: string): string {
|
||||
// Prefix can be used to force change all cache keys (defaults to cache protocol version)
|
||||
return `gradle-${cacheName}-${cacheProtocolVersion}`
|
||||
}
|
||||
|
||||
function getCacheKeyEnvironment(): string {
|
||||
const runnerOs = process.env['RUNNER_OS'] || ''
|
||||
const runnerArch = process.env['RUNNER_ARCH'] || ''
|
||||
return process.env[CACHE_KEY_OS_VAR] || `${runnerOs}-${runnerArch}`
|
||||
}
|
||||
|
||||
function getCacheKeyJob(): string {
|
||||
return process.env[CACHE_KEY_JOB_VAR] || github.context.job
|
||||
}
|
||||
|
||||
function getCacheKeyJobInstance(): string {
|
||||
const override = process.env[CACHE_KEY_JOB_INSTANCE_VAR]
|
||||
if (override) {
|
||||
return override
|
||||
}
|
||||
|
||||
// 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 = getJobMatrix()
|
||||
return hashStrings([workflowName, workflowJobContext])
|
||||
}
|
||||
|
||||
function getCacheKeyJobExecution(): string {
|
||||
// Used to associate a cache key with a particular execution (default is bound to the git commit sha)
|
||||
return process.env[CACHE_KEY_JOB_EXECUTION_VAR] || github.context.sha
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
import * as cache from '@actions/cache'
|
||||
|
||||
export const DEFAULT_CACHE_ENABLED_REASON = `[Cache was enabled](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#caching-build-state-between-jobs). Action attempted to both restore and save the Gradle User Home.`
|
||||
|
||||
export const DEFAULT_READONLY_REASON = `[Cache was read-only](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#using-the-cache-read-only). By default, the action will only write to the cache for Jobs running on the default branch.`
|
||||
|
||||
export const DEFAULT_DISABLED_REASON = `[Cache was disabled](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#disabling-caching) via action configuration. Gradle User Home was not restored from or saved to the cache.`
|
||||
|
||||
export const DEFAULT_WRITEONLY_REASON = `[Cache was set to write-only](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#using-the-cache-write-only) via action configuration. Gradle User Home was not restored from cache.`
|
||||
|
||||
export const EXISTING_GRADLE_HOME = `[Cache was disabled to avoid overwriting a pre-existing Gradle User Home](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#overwriting-an-existing-gradle-user-home). Gradle User Home was not restored from or saved to the cache.`
|
||||
|
||||
export const CLEANUP_DISABLED_READONLY = `[Cache cleanup](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-cache-cleanup) is always disabled when cache is read-only or disabled.`
|
||||
|
||||
export const DEFAULT_CLEANUP_ENABLED_REASON = `[Cache cleanup](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-cache-cleanup) was enabled. Stale files in Gradle User Home were purged before saving to the cache.`
|
||||
|
||||
export const DEFAULT_CLEANUP_DISABLED_REASON = `[Cache cleanup](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-cache-cleanup) was disabled via action parameter. No cleanup of Gradle User Home was performed.`
|
||||
|
||||
export const CLEANUP_DISABLED_DUE_TO_FAILURE =
|
||||
'[Cache cleanup was disabled due to build failure](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-cache-cleanup). Use `cache-cleanup: always` to override this behavior.'
|
||||
|
||||
export const CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT =
|
||||
'[Cache cleanup was disabled due to configuration-cache reuse](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#configuring-cache-cleanup). This is expected.'
|
||||
|
||||
/**
|
||||
* Collects information on what entries were saved and restored during the action.
|
||||
* This information is used to generate a summary of the cache usage.
|
||||
*/
|
||||
export class CacheListener {
|
||||
cacheEntries: CacheEntryListener[] = []
|
||||
cacheReadOnly = false
|
||||
cacheWriteOnly = false
|
||||
cacheDisabled = false
|
||||
cacheStatusReason: string = DEFAULT_CACHE_ENABLED_REASON
|
||||
cacheCleanupMessage: string = DEFAULT_CLEANUP_DISABLED_REASON
|
||||
|
||||
get fullyRestored(): boolean {
|
||||
return this.cacheEntries.every(x => !x.wasRequestedButNotRestored())
|
||||
}
|
||||
|
||||
get cacheStatus(): string {
|
||||
if (!cache.isFeatureAvailable()) return 'not available'
|
||||
if (this.cacheDisabled) return 'disabled'
|
||||
if (this.cacheWriteOnly) return 'write-only'
|
||||
if (this.cacheReadOnly) return 'read-only'
|
||||
return 'enabled'
|
||||
}
|
||||
|
||||
setReadOnly(reason: string = DEFAULT_READONLY_REASON): void {
|
||||
this.cacheReadOnly = true
|
||||
this.cacheStatusReason = reason
|
||||
this.cacheCleanupMessage = CLEANUP_DISABLED_READONLY
|
||||
}
|
||||
|
||||
setDisabled(reason: string = DEFAULT_DISABLED_REASON): void {
|
||||
this.cacheDisabled = true
|
||||
this.cacheStatusReason = reason
|
||||
this.cacheCleanupMessage = CLEANUP_DISABLED_READONLY
|
||||
}
|
||||
|
||||
setWriteOnly(reason: string = DEFAULT_WRITEONLY_REASON): void {
|
||||
this.cacheWriteOnly = true
|
||||
this.cacheStatusReason = reason
|
||||
}
|
||||
|
||||
setCacheCleanupEnabled(): void {
|
||||
this.cacheCleanupMessage = DEFAULT_CLEANUP_ENABLED_REASON
|
||||
}
|
||||
|
||||
setCacheCleanupDisabled(reason: string = DEFAULT_CLEANUP_DISABLED_REASON): void {
|
||||
this.cacheCleanupMessage = reason
|
||||
}
|
||||
|
||||
entry(name: string): CacheEntryListener {
|
||||
for (const entry of this.cacheEntries) {
|
||||
if (entry.entryName === name) {
|
||||
return entry
|
||||
}
|
||||
}
|
||||
|
||||
const newEntry = new CacheEntryListener(name)
|
||||
this.cacheEntries.push(newEntry)
|
||||
return newEntry
|
||||
}
|
||||
|
||||
stringify(): string {
|
||||
return JSON.stringify(this)
|
||||
}
|
||||
|
||||
static rehydrate(stringRep: string): CacheListener {
|
||||
if (stringRep === '') {
|
||||
return new CacheListener()
|
||||
}
|
||||
const rehydrated: CacheListener = Object.assign(new CacheListener(), JSON.parse(stringRep))
|
||||
const entries = rehydrated.cacheEntries
|
||||
for (let index = 0; index < entries.length; index++) {
|
||||
const rawEntry = entries[index]
|
||||
entries[index] = Object.assign(new CacheEntryListener(rawEntry.entryName), rawEntry)
|
||||
}
|
||||
return rehydrated
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects information on the state of a single cache entry.
|
||||
*/
|
||||
export class CacheEntryListener {
|
||||
entryName: string
|
||||
requestedKey: string | undefined
|
||||
requestedRestoreKeys: string[] | undefined
|
||||
restoredKey: string | undefined
|
||||
restoredSize: number | undefined
|
||||
restoredTime: number | undefined
|
||||
notRestored: string | undefined
|
||||
|
||||
savedKey: string | undefined
|
||||
savedSize: number | undefined
|
||||
savedTime: number | undefined
|
||||
notSaved: string | undefined
|
||||
|
||||
constructor(entryName: string) {
|
||||
this.entryName = entryName
|
||||
}
|
||||
|
||||
wasRequestedButNotRestored(): boolean {
|
||||
return this.requestedKey !== undefined && this.restoredKey === undefined
|
||||
}
|
||||
|
||||
markRequested(key: string, restoreKeys: string[] = []): CacheEntryListener {
|
||||
this.requestedKey = key
|
||||
this.requestedRestoreKeys = restoreKeys
|
||||
return this
|
||||
}
|
||||
|
||||
markRestored(key: string, size: number | undefined, time: number): CacheEntryListener {
|
||||
this.restoredKey = key
|
||||
this.restoredSize = size
|
||||
this.restoredTime = time
|
||||
return this
|
||||
}
|
||||
|
||||
markNotRestored(message: string): CacheEntryListener {
|
||||
this.notRestored = message
|
||||
return this
|
||||
}
|
||||
|
||||
markSaved(key: string, size: number | undefined, time: number): CacheEntryListener {
|
||||
this.savedKey = key
|
||||
this.savedSize = size
|
||||
this.savedTime = time
|
||||
return this
|
||||
}
|
||||
|
||||
markAlreadyExists(key: string): CacheEntryListener {
|
||||
this.savedKey = key
|
||||
this.savedSize = 0
|
||||
return this
|
||||
}
|
||||
|
||||
markNotSaved(message: string): CacheEntryListener {
|
||||
this.notSaved = message
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
export function generateCachingReport(listener: CacheListener): string {
|
||||
const entries = listener.cacheEntries
|
||||
|
||||
return `
|
||||
<details>
|
||||
<summary><h4>Caching for Gradle actions was ${listener.cacheStatus} - expand for details</h4></summary>
|
||||
|
||||
- ${listener.cacheStatusReason}
|
||||
- ${listener.cacheCleanupMessage}
|
||||
|
||||
${renderEntryTable(entries)}
|
||||
|
||||
<h5>Cache Entry Details</h5>
|
||||
<pre>
|
||||
${renderEntryDetails(listener)}
|
||||
</pre>
|
||||
</details>
|
||||
`
|
||||
}
|
||||
|
||||
function renderEntryTable(entries: CacheEntryListener[]): string {
|
||||
return `
|
||||
<table>
|
||||
<tr><td></td><th>Count</th><th>Total Size (Mb)</th><th>Total Time (ms)</tr>
|
||||
<tr><td>Entries Restored</td>
|
||||
<td>${getCount(entries, e => e.restoredSize)}</td>
|
||||
<td>${getSize(entries, e => e.restoredSize)}</td>
|
||||
<td>${getTime(entries, e => e.restoredTime)}</td>
|
||||
</tr>
|
||||
<tr><td>Entries Saved</td>
|
||||
<td>${getCount(entries, e => e.savedSize)}</td>
|
||||
<td>${getSize(entries, e => e.savedSize)}</td>
|
||||
<td>${getTime(entries, e => e.savedTime)}</td>
|
||||
</tr>
|
||||
</table>
|
||||
`
|
||||
}
|
||||
|
||||
function renderEntryDetails(listener: CacheListener): string {
|
||||
return listener.cacheEntries
|
||||
.map(
|
||||
entry => `Entry: ${entry.entryName}
|
||||
Requested Key : ${entry.requestedKey ?? ''}
|
||||
Restored Key : ${entry.restoredKey ?? ''}
|
||||
Size: ${formatSize(entry.restoredSize)}
|
||||
Time: ${formatTime(entry.restoredTime)}
|
||||
${getRestoredMessage(entry, listener.cacheWriteOnly)}
|
||||
Saved Key : ${entry.savedKey ?? ''}
|
||||
Size: ${formatSize(entry.savedSize)}
|
||||
Time: ${formatTime(entry.savedTime)}
|
||||
${getSavedMessage(entry, listener.cacheReadOnly)}
|
||||
`
|
||||
)
|
||||
.join('---\n')
|
||||
}
|
||||
|
||||
function getRestoredMessage(entry: CacheEntryListener, cacheWriteOnly: boolean): string {
|
||||
if (entry.notRestored) {
|
||||
return `(Entry not restored: ${entry.notRestored})`
|
||||
}
|
||||
if (cacheWriteOnly) {
|
||||
return '(Entry not restored: cache is write-only)'
|
||||
}
|
||||
if (entry.requestedKey === undefined) {
|
||||
return '(Entry not restored: not requested)'
|
||||
}
|
||||
if (entry.restoredKey === undefined) {
|
||||
return '(Entry not restored: no match found)'
|
||||
}
|
||||
if (entry.restoredKey === entry.requestedKey) {
|
||||
return '(Entry restored: exact match found)'
|
||||
}
|
||||
return '(Entry restored: partial match found)'
|
||||
}
|
||||
|
||||
function getSavedMessage(entry: CacheEntryListener, cacheReadOnly: boolean): string {
|
||||
if (entry.notSaved) {
|
||||
return `(Entry not saved: ${entry.notSaved})`
|
||||
}
|
||||
if (entry.savedKey === undefined) {
|
||||
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) {
|
||||
return '(Entry not saved: entry with key already exists)'
|
||||
}
|
||||
return '(Entry saved)'
|
||||
}
|
||||
|
||||
function getCount(
|
||||
cacheEntries: CacheEntryListener[],
|
||||
predicate: (value: CacheEntryListener) => number | undefined
|
||||
): number {
|
||||
return cacheEntries.filter(e => predicate(e)).length
|
||||
}
|
||||
|
||||
function getSize(
|
||||
cacheEntries: CacheEntryListener[],
|
||||
predicate: (value: CacheEntryListener) => number | undefined
|
||||
): number {
|
||||
const bytes = cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0)
|
||||
return Math.round(bytes / (1024 * 1024))
|
||||
}
|
||||
|
||||
function getTime(
|
||||
cacheEntries: CacheEntryListener[],
|
||||
predicate: (value: CacheEntryListener) => number | undefined
|
||||
): number {
|
||||
return cacheEntries.map(e => predicate(e) ?? 0).reduce((p, v) => p + v, 0)
|
||||
}
|
||||
|
||||
function formatSize(bytes: number | undefined): string {
|
||||
if (bytes === undefined || bytes === 0) {
|
||||
return ''
|
||||
}
|
||||
return `${Math.round(bytes / (1024 * 1024))} MB (${bytes} B)`
|
||||
}
|
||||
|
||||
function formatTime(ms: number | undefined): string {
|
||||
if (ms === undefined || ms === 0) {
|
||||
return ''
|
||||
}
|
||||
return `${ms} ms`
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
import * as core from '@actions/core'
|
||||
import * as cache from '@actions/cache'
|
||||
import * as exec from '@actions/exec'
|
||||
|
||||
import * as crypto from 'crypto'
|
||||
import * as path from 'path'
|
||||
import * as fs from 'fs'
|
||||
|
||||
import {CacheEntryListener} from './cache-reporting'
|
||||
|
||||
const SEGMENT_DOWNLOAD_TIMEOUT_VAR = 'SEGMENT_DOWNLOAD_TIMEOUT_MINS'
|
||||
const SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT = 10 * 60 * 1000 // 10 minutes
|
||||
|
||||
export function isCacheDebuggingEnabled(): boolean {
|
||||
if (core.isDebug()) {
|
||||
return true
|
||||
}
|
||||
return process.env['GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED'] ? true : false
|
||||
}
|
||||
|
||||
export function hashFileNames(fileNames: string[]): string {
|
||||
return hashStrings(fileNames.map(x => x.replace(new RegExp(`\\${path.sep}`, 'g'), '/')))
|
||||
}
|
||||
|
||||
export function hashStrings(values: string[]): string {
|
||||
const hash = crypto.createHash('md5')
|
||||
for (const value of values) {
|
||||
hash.update(value)
|
||||
}
|
||||
return hash.digest('hex')
|
||||
}
|
||||
|
||||
export async function restoreCache(
|
||||
cachePath: string[],
|
||||
cacheKey: string,
|
||||
cacheRestoreKeys: string[],
|
||||
listener: CacheEntryListener
|
||||
): Promise<cache.CacheEntry | undefined> {
|
||||
listener.markRequested(cacheKey, cacheRestoreKeys)
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
// Only override the read timeout if the SEGMENT_DOWNLOAD_TIMEOUT_MINS env var has NOT been set
|
||||
const cacheRestoreOptions = process.env[SEGMENT_DOWNLOAD_TIMEOUT_VAR]
|
||||
? {}
|
||||
: {segmentTimeoutInMs: SEGMENT_DOWNLOAD_TIMEOUT_DEFAULT}
|
||||
const restoredEntry = await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys, cacheRestoreOptions)
|
||||
if (restoredEntry !== undefined) {
|
||||
const restoreTime = Date.now() - startTime
|
||||
listener.markRestored(restoredEntry.key, restoredEntry.size, restoreTime)
|
||||
core.info(`Restored cache entry with key ${cacheKey} to ${cachePath.join()} in ${restoreTime}ms`)
|
||||
}
|
||||
return restoredEntry
|
||||
} catch (error) {
|
||||
listener.markNotRestored((error as Error).message)
|
||||
handleCacheFailure(error, `Failed to restore ${cacheKey}`)
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveCache(cachePath: string[], cacheKey: string, listener: CacheEntryListener): Promise<void> {
|
||||
try {
|
||||
const startTime = Date.now()
|
||||
const savedEntry = await cache.saveCache(cachePath, cacheKey)
|
||||
const saveTime = Date.now() - startTime
|
||||
listener.markSaved(savedEntry.key, savedEntry.size, saveTime)
|
||||
core.info(`Saved cache entry with key ${cacheKey} from ${cachePath.join()} in ${saveTime}ms`)
|
||||
} catch (error) {
|
||||
if (error instanceof cache.ReserveCacheError) {
|
||||
listener.markAlreadyExists(cacheKey)
|
||||
} else {
|
||||
listener.markNotSaved((error as Error).message)
|
||||
}
|
||||
handleCacheFailure(error, `Failed to save cache entry with path '${cachePath}' and key: ${cacheKey}`)
|
||||
}
|
||||
}
|
||||
|
||||
export function cacheDebug(message: string): void {
|
||||
if (isCacheDebuggingEnabled()) {
|
||||
core.info(message)
|
||||
} else {
|
||||
core.debug(message)
|
||||
}
|
||||
}
|
||||
|
||||
export function handleCacheFailure(error: unknown, message: string): void {
|
||||
if (error instanceof cache.ValidationError) {
|
||||
// Fail on cache validation errors
|
||||
throw error
|
||||
}
|
||||
if (error instanceof cache.ReserveCacheError) {
|
||||
// Reserve cache errors are expected if the artifact has been previously cached
|
||||
core.info(`${message}: ${error}`)
|
||||
} else {
|
||||
// Warn on all other errors
|
||||
core.warning(`${message}: ${error}`)
|
||||
if (error instanceof Error && error.stack) {
|
||||
cacheDebug(error.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to delete a file or directory, waiting to allow locks to be released
|
||||
*/
|
||||
export async function tryDelete(file: string): Promise<void> {
|
||||
const maxAttempts = 5
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
if (!fs.existsSync(file)) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const stat = fs.lstatSync(file)
|
||||
if (stat.isDirectory()) {
|
||||
fs.rmSync(file, {recursive: true})
|
||||
} else {
|
||||
fs.unlinkSync(file)
|
||||
}
|
||||
return
|
||||
} catch (error) {
|
||||
if (attempt === maxAttempts) {
|
||||
core.warning(`Failed to delete ${file}, which will impact caching.
|
||||
It is likely locked by another process. Output of 'jps -ml':
|
||||
${await getJavaProcesses()}`)
|
||||
throw error
|
||||
} else {
|
||||
cacheDebug(`Attempt to delete ${file} failed. Will try again.`)
|
||||
await delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function delay(ms: number): Promise<void> {
|
||||
return new Promise(resolve => setTimeout(resolve, ms))
|
||||
}
|
||||
|
||||
async function getJavaProcesses(): Promise<string> {
|
||||
const jpsOutput = await exec.getExecOutput('jps', ['-lm'])
|
||||
return jpsOutput.stdout
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
import * as core from '@actions/core'
|
||||
import {
|
||||
CacheListener,
|
||||
EXISTING_GRADLE_HOME,
|
||||
CLEANUP_DISABLED_DUE_TO_FAILURE,
|
||||
CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT
|
||||
} from './cache-reporting'
|
||||
import {GradleUserHomeCache} from './gradle-user-home-cache'
|
||||
import {CacheCleaner} from './cache-cleaner'
|
||||
import {DaemonController} from '../daemon-controller'
|
||||
import {CacheConfig} from '../configuration'
|
||||
import {BuildResults} from '../build-results'
|
||||
|
||||
const CACHE_RESTORED_VAR = 'GRADLE_BUILD_ACTION_CACHE_RESTORED'
|
||||
|
||||
export async function restore(
|
||||
userHome: string,
|
||||
gradleUserHome: string,
|
||||
cacheListener: CacheListener,
|
||||
cacheConfig: CacheConfig
|
||||
): 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.')
|
||||
return
|
||||
}
|
||||
core.exportVariable(CACHE_RESTORED_VAR, true)
|
||||
|
||||
const gradleStateCache = new GradleUserHomeCache(userHome, gradleUserHome, cacheConfig)
|
||||
|
||||
if (cacheConfig.isCacheDisabled()) {
|
||||
core.info('Cache is disabled: will not restore state from previous builds.')
|
||||
// Initialize the Gradle User Home even when caching is disabled.
|
||||
gradleStateCache.init()
|
||||
cacheListener.setDisabled()
|
||||
return
|
||||
}
|
||||
|
||||
if (gradleStateCache.cacheOutputExists()) {
|
||||
if (!cacheConfig.isCacheOverwriteExisting()) {
|
||||
core.info('Gradle User Home already exists: will not restore from cache.')
|
||||
// Initialize pre-existing Gradle User Home.
|
||||
gradleStateCache.init()
|
||||
cacheListener.setDisabled(EXISTING_GRADLE_HOME)
|
||||
return
|
||||
}
|
||||
core.info('Gradle User Home already exists: will overwrite with cached contents.')
|
||||
}
|
||||
|
||||
gradleStateCache.init()
|
||||
// Mark the state as restored so that post-action will perform save.
|
||||
core.saveState(CACHE_RESTORED_VAR, true)
|
||||
|
||||
if (cacheConfig.isCacheCleanupEnabled()) {
|
||||
core.info('Preparing cache for cleanup.')
|
||||
const cacheCleaner = new CacheCleaner(gradleUserHome, process.env['RUNNER_TEMP']!)
|
||||
await cacheCleaner.prepare()
|
||||
}
|
||||
|
||||
if (cacheConfig.isCacheWriteOnly()) {
|
||||
core.info('Cache is write-only: will not restore from cache.')
|
||||
cacheListener.setWriteOnly()
|
||||
return
|
||||
}
|
||||
|
||||
await core.group('Restore Gradle state from cache', async () => {
|
||||
await gradleStateCache.restore(cacheListener)
|
||||
})
|
||||
}
|
||||
|
||||
export async function save(
|
||||
userHome: string,
|
||||
gradleUserHome: string,
|
||||
cacheListener: CacheListener,
|
||||
daemonController: DaemonController,
|
||||
buildResults: BuildResults,
|
||||
cacheConfig: CacheConfig
|
||||
): Promise<void> {
|
||||
if (cacheConfig.isCacheDisabled()) {
|
||||
core.info('Cache is disabled: will not save state for later builds.')
|
||||
return
|
||||
}
|
||||
|
||||
if (!core.getState(CACHE_RESTORED_VAR)) {
|
||||
core.info('Cache will not be saved: not restored in main action step.')
|
||||
return
|
||||
}
|
||||
|
||||
if (cacheConfig.isCacheReadOnly()) {
|
||||
core.info('Cache is read-only: will not save state for use in subsequent builds.')
|
||||
cacheListener.setReadOnly()
|
||||
return
|
||||
}
|
||||
|
||||
await core.group('Stopping Gradle daemons', async () => {
|
||||
await daemonController.stopAllDaemons()
|
||||
})
|
||||
|
||||
if (cacheConfig.isCacheCleanupEnabled()) {
|
||||
if (buildResults.anyConfigCacheHit()) {
|
||||
core.info('Not performing cache-cleanup due to config-cache reuse')
|
||||
cacheListener.setCacheCleanupDisabled(CLEANUP_DISABLED_DUE_TO_CONFIG_CACHE_HIT)
|
||||
} else if (cacheConfig.shouldPerformCacheCleanup(buildResults.anyFailed())) {
|
||||
cacheListener.setCacheCleanupEnabled()
|
||||
await performCacheCleanup(gradleUserHome, buildResults)
|
||||
} else {
|
||||
core.info('Not performing cache-cleanup due to build failure')
|
||||
cacheListener.setCacheCleanupDisabled(CLEANUP_DISABLED_DUE_TO_FAILURE)
|
||||
}
|
||||
}
|
||||
|
||||
await core.group('Caching Gradle state', async () => {
|
||||
return new GradleUserHomeCache(userHome, gradleUserHome, cacheConfig).save(cacheListener)
|
||||
})
|
||||
}
|
||||
|
||||
async function performCacheCleanup(gradleUserHome: string, buildResults: BuildResults): Promise<void> {
|
||||
const cacheCleaner = new CacheCleaner(gradleUserHome, process.env['RUNNER_TEMP']!)
|
||||
try {
|
||||
await cacheCleaner.forceCleanup(buildResults)
|
||||
} catch (e) {
|
||||
core.warning(`Cache cleanup failed. Will continue. ${String(e)}`)
|
||||
}
|
||||
}
|
||||
@@ -1,467 +0,0 @@
|
||||
import path from 'path'
|
||||
import fs from 'fs'
|
||||
import * as core from '@actions/core'
|
||||
import * as glob from '@actions/glob'
|
||||
|
||||
import {CacheEntryListener, CacheListener} from './cache-reporting'
|
||||
import {cacheDebug, hashFileNames, isCacheDebuggingEnabled, restoreCache, saveCache, tryDelete} from './cache-utils'
|
||||
|
||||
import {BuildResult, loadBuildResults} from '../build-results'
|
||||
import {CacheConfig, ACTION_METADATA_DIR} from '../configuration'
|
||||
import {getCacheKeyBase} from './cache-key'
|
||||
import {versionIsAtLeast} from '../execution/gradle'
|
||||
|
||||
const SKIP_RESTORE_VAR = 'GRADLE_BUILD_ACTION_SKIP_RESTORE'
|
||||
const CACHE_PROTOCOL_VERSION = 'v1'
|
||||
|
||||
/**
|
||||
* Represents the result of attempting to load or store an extracted cache entry.
|
||||
* An undefined cacheKey indicates that the operation did not succeed.
|
||||
* The collected results are then used to populate the `cache-metadata.json` file for later use.
|
||||
*/
|
||||
class ExtractedCacheEntry {
|
||||
artifactType: string
|
||||
pattern: string
|
||||
cacheKey: string | undefined
|
||||
|
||||
constructor(artifactType: string, pattern: string, cacheKey: string | undefined) {
|
||||
this.artifactType = artifactType
|
||||
this.pattern = pattern
|
||||
this.cacheKey = cacheKey
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Representation of all of the extracted cache entries for this Gradle User Home.
|
||||
* This object is persisted to JSON file in the Gradle User Home directory for storing,
|
||||
* and subsequently used to restore the Gradle User Home.
|
||||
*/
|
||||
class ExtractedCacheEntryMetadata {
|
||||
entries: ExtractedCacheEntry[] = []
|
||||
}
|
||||
|
||||
/**
|
||||
* The specification for a type of extracted cache entry.
|
||||
*/
|
||||
class ExtractedCacheEntryDefinition {
|
||||
artifactType: string
|
||||
pattern: string
|
||||
bundle: boolean
|
||||
uniqueFileNames = true
|
||||
notCacheableReason: string | undefined
|
||||
|
||||
constructor(artifactType: string, pattern: string, bundle: boolean) {
|
||||
this.artifactType = artifactType
|
||||
this.pattern = pattern
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches and restores the entire Gradle User Home directory, extracting entries containing common artifacts
|
||||
* for more efficient storage.
|
||||
*/
|
||||
abstract class AbstractEntryExtractor {
|
||||
protected readonly cacheConfig: CacheConfig
|
||||
protected readonly gradleUserHome: string
|
||||
private extractorName: string
|
||||
|
||||
constructor(gradleUserHome: string, extractorName: string, cacheConfig: CacheConfig) {
|
||||
this.gradleUserHome = gradleUserHome
|
||||
this.extractorName = extractorName
|
||||
this.cacheConfig = cacheConfig
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores any artifacts that were cached separately, based on the information in the `cache-metadata.json` file.
|
||||
* Each extracted cache entry is restored in parallel, except when debugging is enabled.
|
||||
*/
|
||||
async restore(listener: CacheListener): Promise<void> {
|
||||
const previouslyExtractedCacheEntries = this.loadExtractedCacheEntries()
|
||||
|
||||
const processes: Promise<ExtractedCacheEntry>[] = []
|
||||
|
||||
for (const cacheEntry of previouslyExtractedCacheEntries) {
|
||||
const artifactType = cacheEntry.artifactType
|
||||
const entryListener = listener.entry(cacheEntry.pattern)
|
||||
|
||||
// Handle case where the extracted-cache-entry definitions have been changed
|
||||
const skipRestore = process.env[SKIP_RESTORE_VAR] || ''
|
||||
if (skipRestore.includes(artifactType)) {
|
||||
core.info(`Not restoring extracted cache entry for ${artifactType}`)
|
||||
entryListener.markRequested('SKIP_RESTORE')
|
||||
} else {
|
||||
processes.push(
|
||||
this.awaitForDebugging(
|
||||
this.restoreExtractedCacheEntry(
|
||||
artifactType,
|
||||
cacheEntry.cacheKey!,
|
||||
cacheEntry.pattern,
|
||||
entryListener
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
this.saveMetadataForCacheResults(await Promise.all(processes))
|
||||
}
|
||||
|
||||
private async restoreExtractedCacheEntry(
|
||||
artifactType: string,
|
||||
cacheKey: string,
|
||||
pattern: string,
|
||||
listener: CacheEntryListener
|
||||
): Promise<ExtractedCacheEntry> {
|
||||
const restoredEntry = await restoreCache(pattern.split('\n'), cacheKey, [], listener)
|
||||
if (restoredEntry) {
|
||||
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
||||
} else {
|
||||
core.info(`Did not restore ${artifactType} with key ${cacheKey} to ${pattern}`)
|
||||
return new ExtractedCacheEntry(artifactType, pattern, undefined)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any artifacts that are configured to be cached separately, based on the extracted cache entry definitions.
|
||||
* Each entry is extracted and saved in parallel, except when debugging is enabled.
|
||||
*/
|
||||
async extract(listener: CacheListener): Promise<void> {
|
||||
// Load the cache entry definitions (from config) and the previously restored entries (from persisted metadata file)
|
||||
const cacheEntryDefinitions = this.getExtractedCacheEntryDefinitions()
|
||||
cacheDebug(
|
||||
`Extracting cache entries for ${this.extractorName}: ${JSON.stringify(cacheEntryDefinitions, null, 2)}`
|
||||
)
|
||||
|
||||
const previouslyRestoredEntries = this.loadExtractedCacheEntries()
|
||||
const cacheActions: Promise<ExtractedCacheEntry>[] = []
|
||||
|
||||
// For each cache entry definition, determine if it has already been restored, and if not, extract it
|
||||
for (const cacheEntryDefinition of cacheEntryDefinitions) {
|
||||
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
|
||||
})
|
||||
const matchingFiles = await globber.glob()
|
||||
|
||||
if (matchingFiles.length === 0) {
|
||||
cacheDebug(`No files found to cache for ${artifactType}`)
|
||||
continue
|
||||
}
|
||||
|
||||
if (cacheEntryDefinition.bundle) {
|
||||
// For an extracted "bundle", use the defined pattern and cache all matching files in a single entry.
|
||||
cacheActions.push(
|
||||
this.awaitForDebugging(
|
||||
this.saveExtractedCacheEntry(
|
||||
matchingFiles,
|
||||
artifactType,
|
||||
pattern,
|
||||
cacheEntryDefinition.uniqueFileNames,
|
||||
previouslyRestoredEntries,
|
||||
listener.entry(pattern)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
// Otherwise cache each matching file in a separate entry, using the complete file path as the cache pattern.
|
||||
for (const cacheFile of matchingFiles) {
|
||||
cacheActions.push(
|
||||
this.awaitForDebugging(
|
||||
this.saveExtractedCacheEntry(
|
||||
[cacheFile],
|
||||
artifactType,
|
||||
cacheFile,
|
||||
cacheEntryDefinition.uniqueFileNames,
|
||||
previouslyRestoredEntries,
|
||||
listener.entry(cacheFile)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.saveMetadataForCacheResults(await Promise.all(cacheActions))
|
||||
}
|
||||
|
||||
private async saveExtractedCacheEntry(
|
||||
matchingFiles: string[],
|
||||
artifactType: string,
|
||||
pattern: string,
|
||||
uniqueFileNames: boolean,
|
||||
previouslyRestoredEntries: ExtractedCacheEntry[],
|
||||
entryListener: CacheEntryListener
|
||||
): Promise<ExtractedCacheEntry> {
|
||||
const cacheKey = uniqueFileNames
|
||||
? this.createCacheKeyFromFileNames(artifactType, matchingFiles)
|
||||
: await this.createCacheKeyFromFileContents(artifactType, pattern)
|
||||
const previouslyRestoredKey = previouslyRestoredEntries.find(
|
||||
x => x.artifactType === artifactType && x.pattern === pattern
|
||||
)?.cacheKey
|
||||
|
||||
if (previouslyRestoredKey === cacheKey) {
|
||||
cacheDebug(`No change to previously restored ${artifactType}. Not saving.`)
|
||||
entryListener.markNotSaved('contents unchanged')
|
||||
} else {
|
||||
await saveCache(pattern.split('\n'), cacheKey, entryListener)
|
||||
}
|
||||
|
||||
for (const file of matchingFiles) {
|
||||
tryDelete(file)
|
||||
}
|
||||
|
||||
return new ExtractedCacheEntry(artifactType, pattern, cacheKey)
|
||||
}
|
||||
|
||||
protected createCacheKeyFromFileNames(artifactType: string, files: string[]): string {
|
||||
const relativeFiles = files.map(x => path.relative(this.gradleUserHome, x))
|
||||
const key = hashFileNames(relativeFiles)
|
||||
|
||||
cacheDebug(`Generating cache key for ${artifactType} from file names: ${relativeFiles}`)
|
||||
|
||||
return `${getCacheKeyBase(artifactType, CACHE_PROTOCOL_VERSION)}-${key}`
|
||||
}
|
||||
|
||||
protected async createCacheKeyFromFileContents(artifactType: string, pattern: string): Promise<string> {
|
||||
const key = await glob.hashFiles(pattern)
|
||||
|
||||
cacheDebug(`Generating cache key for ${artifactType} from files matching: ${pattern}`)
|
||||
|
||||
return `${getCacheKeyBase(artifactType, CACHE_PROTOCOL_VERSION)}-${key}`
|
||||
}
|
||||
|
||||
// Run actions sequentially if debugging is enabled
|
||||
private async awaitForDebugging(p: Promise<ExtractedCacheEntry>): Promise<ExtractedCacheEntry> {
|
||||
if (isCacheDebuggingEnabled()) {
|
||||
await p
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Load information about the extracted cache entries previously restored/saved. This is loaded from the 'cache-metadata.json' file.
|
||||
*/
|
||||
protected loadExtractedCacheEntries(): ExtractedCacheEntry[] {
|
||||
const cacheMetadataFile = this.getCacheMetadataFile()
|
||||
if (!fs.existsSync(cacheMetadataFile)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const filedata = fs.readFileSync(cacheMetadataFile, 'utf-8')
|
||||
cacheDebug(`Loaded cache metadata for ${this.extractorName}: ${filedata}`)
|
||||
const extractedCacheEntryMetadata = JSON.parse(filedata) as ExtractedCacheEntryMetadata
|
||||
return extractedCacheEntryMetadata.entries
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves information about the extracted cache entries into the 'cache-metadata.json' file.
|
||||
*/
|
||||
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 for ${this.extractorName}: ${filedata}`)
|
||||
|
||||
fs.writeFileSync(this.getCacheMetadataFile(), filedata, 'utf-8')
|
||||
}
|
||||
|
||||
private getCacheMetadataFile(): string {
|
||||
const actionMetadataDirectory = path.resolve(this.gradleUserHome, ACTION_METADATA_DIR)
|
||||
fs.mkdirSync(actionMetadataDirectory, {recursive: true})
|
||||
|
||||
return path.resolve(actionMetadataDirectory, `${this.extractorName}-entry-metadata.json`)
|
||||
}
|
||||
|
||||
protected abstract getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[]
|
||||
}
|
||||
|
||||
export class GradleHomeEntryExtractor extends AbstractEntryExtractor {
|
||||
constructor(gradleUserHome: string, cacheConfig: CacheConfig) {
|
||||
super(gradleUserHome, 'gradle-home', cacheConfig)
|
||||
}
|
||||
|
||||
async extract(listener: CacheListener): Promise<void> {
|
||||
await this.deleteWrapperZips()
|
||||
return super.extract(listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete any downloaded wrapper zip files that are not needed after extraction.
|
||||
* These files are cleaned up by Gradle >= 7.5, but for older versions we remove them manually.
|
||||
*/
|
||||
private async deleteWrapperZips(): Promise<void> {
|
||||
const wrapperZips = path.resolve(this.gradleUserHome, 'wrapper/dists/*/*/*.zip')
|
||||
const globber = await glob.create(wrapperZips, {
|
||||
implicitDescendants: false
|
||||
})
|
||||
|
||||
for (const wrapperZip of await globber.glob()) {
|
||||
cacheDebug(`Deleting wrapper zip: ${wrapperZip}`)
|
||||
await tryDelete(wrapperZip)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the extracted cache entry definitions, which determine which artifacts will be cached
|
||||
* separately from the rest of the Gradle User Home cache entry.
|
||||
*/
|
||||
protected getExtractedCacheEntryDefinitions(): ExtractedCacheEntryDefinition[] {
|
||||
const entryDefinition = (
|
||||
artifactType: string,
|
||||
patterns: string[],
|
||||
bundle: boolean
|
||||
): ExtractedCacheEntryDefinition => {
|
||||
const resolvedPatterns = patterns
|
||||
.map(x => {
|
||||
const isDir = x.endsWith('/')
|
||||
const resolved = path.resolve(this.gradleUserHome, x)
|
||||
return isDir ? `${resolved}/` : resolved // Restore trailing '/' removed by path.resolve()
|
||||
})
|
||||
.join('\n')
|
||||
return new ExtractedCacheEntryDefinition(artifactType, resolvedPatterns, bundle)
|
||||
}
|
||||
|
||||
return [
|
||||
entryDefinition('generated-gradle-jars', ['caches/*/generated-gradle-jars/*.jar'], false),
|
||||
entryDefinition('wrapper-zips', ['wrapper/dists/*/*/'], false), // Each wrapper directory cached separately
|
||||
entryDefinition('java-toolchains', ['jdks/*/'], false), // Each extracted JDK cached separately
|
||||
entryDefinition('dependencies', ['caches/modules-*/files-*/*/*/*/*'], true),
|
||||
entryDefinition('instrumented-jars', ['caches/jars-*/*/'], true),
|
||||
entryDefinition('kotlin-dsl', ['caches/*/kotlin-dsl/accessors/*/', 'caches/*/kotlin-dsl/scripts/*/'], true),
|
||||
entryDefinition('groovy-dsl', ['caches/*/groovy-dsl/*/'], true),
|
||||
entryDefinition('transforms', ['caches/transforms-4/*/', 'caches/*/transforms/*/'], true)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurationCacheEntryExtractor extends AbstractEntryExtractor {
|
||||
constructor(gradleUserHome: string, cacheConfig: CacheConfig) {
|
||||
super(gradleUserHome, 'configuration-cache', cacheConfig)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the case where Gradle User Home has not been fully restored, so that the configuration-cache
|
||||
* entry is not reusable.
|
||||
*/
|
||||
async restore(listener: CacheListener): Promise<void> {
|
||||
if (!listener.fullyRestored) {
|
||||
this.markNotRestored(listener, 'Gradle User Home was not fully restored')
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.cacheConfig.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 (!this.cacheConfig.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[] {
|
||||
// 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 => {
|
||||
return !versionIsAtLeast(result.gradleVersion, '8.6.0')
|
||||
})
|
||||
) {
|
||||
core.info(
|
||||
`Not saving config-cache data for ${configCachePath}. Configuration cache data is only saved for Gradle 8.6+`
|
||||
)
|
||||
definition.notCacheableBecause('Configuration cache data only saved for Gradle 8.6+')
|
||||
}
|
||||
return definition
|
||||
})
|
||||
}
|
||||
|
||||
private getConfigCacheDirectoriesWithAssociatedBuildResults(): Record<string, BuildResult[]> {
|
||||
return loadBuildResults().results.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[]>
|
||||
)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user