mirror of
https://github.com/gradle/actions.git
synced 2026-03-26 14:24:31 +08:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
4
.github/actions/build-dist/action.yml
vendored
4
.github/actions/build-dist/action.yml
vendored
@ -3,7 +3,7 @@ name: 'Build and upload distribution'
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
cache: npm
|
cache: npm
|
||||||
@ -23,7 +23,7 @@ runs:
|
|||||||
cp -r sources/dist .
|
cp -r sources/dist .
|
||||||
|
|
||||||
- name: Upload distribution
|
- name: Upload distribution
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|||||||
2
.github/actions/init-integ-test/action.yml
vendored
2
.github/actions/init-integ-test/action.yml
vendored
@ -23,7 +23,7 @@ runs:
|
|||||||
# Downloads a 'dist' directory artifact that was uploaded in an earlier 'build-dist' step
|
# Downloads a 'dist' directory artifact that was uploaded in an earlier 'build-dist' step
|
||||||
- name: Download dist
|
- name: Download dist
|
||||||
if: ${{ env.SKIP_DIST != 'true' && !env.ACT }}
|
if: ${{ env.SKIP_DIST != 'true' && !env.ACT }}
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
name: dist
|
name: dist
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
2
.github/workflow-samples/groovy-dsl/gradlew
vendored
2
.github/workflow-samples/groovy-dsl/gradlew
vendored
@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
2
.github/workflow-samples/kotlin-dsl/gradlew
vendored
2
.github/workflow-samples/kotlin-dsl/gradlew
vendored
@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
4
.github/workflows/ci-check-and-unit-test.yml
vendored
4
.github/workflows/ci-check-and-unit-test.yml
vendored
@ -19,14 +19,14 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: npm
|
cache: npm
|
||||||
cache-dependency-path: sources/package-lock.json
|
cache-dependency-path: sources/package-lock.json
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
# Use a released version to avoid breakages
|
# Use a released version to avoid breakages
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||||
env:
|
env:
|
||||||
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
||||||
with:
|
with:
|
||||||
|
|||||||
@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Get changed files
|
- name: Get changed files
|
||||||
id: changed-files
|
id: changed-files
|
||||||
uses: tj-actions/changed-files@7dee1b0c1557f278e5c7dc244927139d78c0e22a # v47.0.4
|
uses: tj-actions/changed-files@22103cc46bda19c2b464ffe86db46df6922fd323 # v47.0.5
|
||||||
with:
|
with:
|
||||||
files: |
|
files: |
|
||||||
dist/**
|
dist/**
|
||||||
|
|||||||
4
.github/workflows/ci-codeql.yml
vendored
4
.github/workflows/ci-codeql.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.5
|
uses: github/codeql-action/init@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v3.29.5
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
config: |
|
config: |
|
||||||
@ -43,4 +43,4 @@ jobs:
|
|||||||
- sources/src
|
- sources/src
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.5
|
uses: github/codeql-action/analyze@b1bff81932f5cdfc8695c7752dcee935dcd061c8 # v3.29.5
|
||||||
|
|||||||
2
.github/workflows/ci-init-script-check.yml
vendored
2
.github/workflows/ci-init-script-check.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
|||||||
java-version: 17
|
java-version: 17
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
# Use a released version to avoid breakages
|
# Use a released version to avoid breakages
|
||||||
uses: gradle/actions/setup-gradle@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
uses: gradle/actions/setup-gradle@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||||
env:
|
env:
|
||||||
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
ALLOWED_GRADLE_WRAPPER_CHECKSUMS: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 # Invalid wrapper jar used for testing
|
||||||
- name: Run integration tests
|
- name: Run integration tests
|
||||||
|
|||||||
6
.github/workflows/ci-integ-test-full.yml
vendored
6
.github/workflows/ci-integ-test-full.yml
vendored
@ -5,12 +5,10 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
|
- 'bot/gradle-actions-caching'
|
||||||
paths:
|
paths:
|
||||||
- 'dist/**'
|
- 'dist/**'
|
||||||
|
- 'sources/vendor/gradle-actions-caching/**'
|
||||||
concurrency:
|
|
||||||
group: integ-test
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
10
.github/workflows/ci-integ-test.yml
vendored
10
.github/workflows/ci-integ-test.yml
vendored
@ -11,10 +11,6 @@ on:
|
|||||||
paths-ignore:
|
paths-ignore:
|
||||||
- 'dist/**'
|
- 'dist/**'
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: integ-test
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
@ -31,6 +27,9 @@ jobs:
|
|||||||
caching-integ-tests:
|
caching-integ-tests:
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
uses: ./.github/workflows/suite-integ-test-caching.yml
|
uses: ./.github/workflows/suite-integ-test-caching.yml
|
||||||
|
concurrency:
|
||||||
|
group: CI-integ-test
|
||||||
|
cancel-in-progress: false
|
||||||
with:
|
with:
|
||||||
skip-dist: false
|
skip-dist: false
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
@ -40,6 +39,9 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
needs: build-distribution
|
needs: build-distribution
|
||||||
uses: ./.github/workflows/suite-integ-test-other.yml
|
uses: ./.github/workflows/suite-integ-test-other.yml
|
||||||
|
concurrency:
|
||||||
|
group: CI-integ-test
|
||||||
|
cancel-in-progress: false
|
||||||
with:
|
with:
|
||||||
skip-dist: false
|
skip-dist: false
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|||||||
8
.github/workflows/ci-ossf-scorecard.yml
vendored
8
.github/workflows/ci-ossf-scorecard.yml
vendored
@ -44,14 +44,8 @@ jobs:
|
|||||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||||
# format to the repository Actions tab.
|
# format to the repository Actions tab.
|
||||||
- name: 'Upload artifact'
|
- name: 'Upload artifact'
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
with:
|
with:
|
||||||
name: SARIF file
|
name: SARIF file
|
||||||
path: results.sarif
|
path: results.sarif
|
||||||
retention-days: 5
|
retention-days: 5
|
||||||
|
|
||||||
# Upload the results to GitHub's code scanning dashboard.
|
|
||||||
- name: 'Upload to code-scanning'
|
|
||||||
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v3.29.5
|
|
||||||
with:
|
|
||||||
sarif_file: results.sarif
|
|
||||||
|
|||||||
4
.github/workflows/ci-update-dist.yml
vendored
4
.github/workflows/ci-update-dist.yml
vendored
@ -28,7 +28,7 @@ jobs:
|
|||||||
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
token: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: npm
|
cache: npm
|
||||||
@ -61,7 +61,7 @@ jobs:
|
|||||||
cp -r sources/dist .
|
cp -r sources/dist .
|
||||||
|
|
||||||
- name: Import GPG key to sign commits
|
- 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:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
||||||
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
||||||
|
|||||||
24
.github/workflows/ci-validate-typings.yml
vendored
Normal file
24
.github/workflows/ci-validate-typings.yml
vendored
Normal file
@ -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
|
||||||
2
.github/workflows/ci-validate-wrappers.yml
vendored
2
.github/workflows/ci-validate-wrappers.yml
vendored
@ -12,6 +12,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
- uses: gradle/actions/wrapper-validation@f29f5a9d7b09a7c6b29859002d29d24e1674c884 # v5.0.1
|
- uses: gradle/actions/wrapper-validation@0723195856401067f7a2779048b490ace7a47d7c # v5.0.2
|
||||||
with:
|
with:
|
||||||
allow-checksums: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
allow-checksums: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
||||||
|
|||||||
52
.github/workflows/demo-job-summary.yml
vendored
52
.github/workflows/demo-job-summary.yml
vendored
@ -114,3 +114,55 @@ jobs:
|
|||||||
- name: Build kotlin-dsl project
|
- name: Build kotlin-dsl project
|
||||||
working-directory: .github/workflow-samples/kotlin-dsl
|
working-directory: .github/workflow-samples/kotlin-dsl
|
||||||
run: ./gradlew assemble
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
@ -41,7 +41,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
cache-read-only: false # For testing, allow writing cache entries on non-default branches
|
||||||
- name: Build with 3.1
|
- 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
|
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
|
# Second build will use the cache from the first build, but cleanup should remove unused artifacts
|
||||||
@ -67,7 +67,7 @@ jobs:
|
|||||||
cache-read-only: false
|
cache-read-only: false
|
||||||
cache-cleanup: 'on-success'
|
cache-cleanup: 'on-success'
|
||||||
- name: Build with 3.1.1
|
- 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
|
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
|
# Third build will restore cache entry from second, and verify stale content removed
|
||||||
|
|||||||
@ -178,7 +178,7 @@ jobs:
|
|||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
steps:
|
steps:
|
||||||
- name: Download dependency-graph artifact
|
- name: Download dependency-graph artifact
|
||||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
|
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||||
with:
|
with:
|
||||||
path: downloaded-dependency-graphs
|
path: downloaded-dependency-graphs
|
||||||
pattern: dependency-graph_*dependency-graph-generate-submit-and-upload.json
|
pattern: dependency-graph_*dependency-graph-generate-submit-and-upload.json
|
||||||
|
|||||||
@ -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
|
|
||||||
@ -111,6 +111,12 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
.github
|
||||||
|
dist
|
||||||
|
wrapper-validation
|
||||||
|
|
||||||
- name: Initialize integ-test
|
- name: Initialize integ-test
|
||||||
uses: ./.github/actions/init-integ-test
|
uses: ./.github/actions/init-integ-test
|
||||||
|
|
||||||
@ -118,10 +124,8 @@ jobs:
|
|||||||
id: action-test
|
id: action-test
|
||||||
uses: ./wrapper-validation
|
uses: ./wrapper-validation
|
||||||
with:
|
with:
|
||||||
# to allow the invalid wrapper jar present in test data
|
min-wrapper-count: 7
|
||||||
allow-checksums: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
|
# There are only 6 wrappers in workflow-samples, so expected to fail
|
||||||
min-wrapper-count: 11
|
|
||||||
# Expected to fail; validated below
|
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Check outcome
|
- name: Check outcome
|
||||||
|
|||||||
@ -26,14 +26,6 @@ jobs:
|
|||||||
runner-os: '${{ inputs.runner-os }}'
|
runner-os: '${{ inputs.runner-os }}'
|
||||||
skip-dist: ${{ inputs.skip-dist }}
|
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:
|
restore-containerized-gradle-home:
|
||||||
uses: ./.github/workflows/integ-test-restore-containerized-gradle-home.yml
|
uses: ./.github/workflows/integ-test-restore-containerized-gradle-home.yml
|
||||||
with:
|
with:
|
||||||
|
|||||||
4
.github/workflows/update-checksums-file.yml
vendored
4
.github/workflows/update-checksums-file.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||||
|
|
||||||
- name: Set up Node.js
|
- name: Set up Node.js
|
||||||
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
||||||
with:
|
with:
|
||||||
node-version: 20
|
node-version: 20
|
||||||
cache: npm
|
cache: npm
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
working-directory: sources
|
working-directory: sources
|
||||||
|
|
||||||
- name: Import GPG key to sign commits
|
- 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:
|
with:
|
||||||
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
gpg_private_key: ${{ secrets.GH_BOT_PGP_PRIVATE_KEY }}
|
||||||
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
passphrase: ${{ secrets.GH_BOT_PGP_PASSPHRASE }}
|
||||||
|
|||||||
28
CLAUDE.md
Normal file
28
CLAUDE.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
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.
|
||||||
11
NOTICE
Normal file
11
NOTICE
Normal file
@ -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 bundled `gradle-actions-caching` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
|
||||||
|
The `gradle-actions-caching` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
|
||||||
|
Use of the `gradle-actions-caching` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
If you do not agree to these license terms, do not use the gradle-actions-caching component.
|
||||||
28
README.md
28
README.md
@ -1,9 +1,23 @@
|
|||||||
# GitHub Actions for Gradle builds
|
# 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.
|
This repository contains a set of GitHub Actions that are useful for building Gradle projects on GitHub.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> ## Licensing notice
|
||||||
|
>
|
||||||
|
> The software in this repository 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.
|
||||||
|
> The bundled `gradle-actions-caching` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
>
|
||||||
|
> The `gradle-actions-caching` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
>
|
||||||
|
> Use of the `gradle-actions-caching` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
> If you do not agree to these license terms, do not use the `gradle-actions-caching` component.
|
||||||
|
|
||||||
|
This license notice will be displayed in workflow logs and each job summary. To suppress this message,
|
||||||
|
either [accept the terms of use](docs/setup-gradle.md#publishing-to-scansgradlecom) in your workflow, or [provide a Develocity access key](docs/setup-gradle.md#managing-develocity-access-keys).
|
||||||
|
|
||||||
## The `setup-gradle` action
|
## The `setup-gradle` action
|
||||||
|
|
||||||
The `setup-gradle` action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
The `setup-gradle` action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
||||||
@ -25,9 +39,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -63,9 +77,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -98,7 +112,7 @@ jobs:
|
|||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: gradle/actions/wrapper-validation@v5
|
- uses: gradle/actions/wrapper-validation@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -20,8 +20,9 @@
|
|||||||
- Include a Full changelog link in the format https://github.com/gradle/actions/compare/v2.12.0...v3.0.0
|
- Include a Full changelog link in the format https://github.com/gradle/actions/compare/v2.12.0...v3.0.0
|
||||||
- Publish the release.
|
- 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.
|
- 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.
|
- 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
|
## Post release steps
|
||||||
|
|
||||||
|
|||||||
@ -22,9 +22,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|||||||
146
dependency-submission/action-types.yml
Normal file
146
dependency-submission/action-types.yml
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
# 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-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
|
||||||
519
dist/dependency-submission/main/index.js
vendored
519
dist/dependency-submission/main/index.js
vendored
File diff suppressed because one or more lines are too long
8
dist/dependency-submission/main/index.js.map
vendored
8
dist/dependency-submission/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
485
dist/dependency-submission/post/index.js
vendored
485
dist/dependency-submission/post/index.js
vendored
File diff suppressed because one or more lines are too long
8
dist/dependency-submission/post/index.js.map
vendored
8
dist/dependency-submission/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
517
dist/setup-gradle/main/index.js
vendored
517
dist/setup-gradle/main/index.js
vendored
File diff suppressed because one or more lines are too long
8
dist/setup-gradle/main/index.js.map
vendored
8
dist/setup-gradle/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
564
dist/setup-gradle/post/index.js
vendored
564
dist/setup-gradle/post/index.js
vendored
File diff suppressed because one or more lines are too long
8
dist/setup-gradle/post/index.js.map
vendored
8
dist/setup-gradle/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
353
dist/wrapper-validation/main/index.js
vendored
353
dist/wrapper-validation/main/index.js
vendored
File diff suppressed because one or more lines are too long
8
dist/wrapper-validation/main/index.js.map
vendored
8
dist/wrapper-validation/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
@ -15,6 +15,22 @@ 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.
|
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.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> ## Licensing notice
|
||||||
|
>
|
||||||
|
> The software in this repository 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.
|
||||||
|
> The bundled `gradle-actions-caching` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
>
|
||||||
|
> The `gradle-actions-caching` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
>
|
||||||
|
> Use of the `gradle-actions-caching` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
> If you do not agree to these license terms, do not use the `gradle-actions-caching` component.
|
||||||
|
|
||||||
|
This license notice will be displayed in workflow logs and each job summary. To suppress this message,
|
||||||
|
either [accept the terms of use](setup-gradle.md#publishing-to-scansgradlecom) in your workflow, or [provide a Develocity access key](setup-gradle.md#managing-develocity-access-keys).
|
||||||
|
|
||||||
## General usage
|
## General usage
|
||||||
|
|
||||||
The following workflow will generate a dependency graph for a Gradle project and submit it immediately to the repository via the
|
The following workflow will generate a dependency graph for a Gradle project and submit it immediately to the repository via the
|
||||||
@ -36,8 +52,8 @@ jobs:
|
|||||||
dependency-submission:
|
dependency-submission:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -60,6 +76,13 @@ 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
|
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.
|
additional arguments that will be passed to Gradle on the command-line. See [Configuration Parameters](#configuration-parameters) below.
|
||||||
|
|
||||||
|
### 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
|
### 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`.
|
You can automatically publish a free Develocity Build Scan on every run of `gradle/actions/dependency-submission`.
|
||||||
@ -343,8 +366,8 @@ jobs:
|
|||||||
dependency-submission:
|
dependency-submission:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -405,8 +428,8 @@ jobs:
|
|||||||
dependency-submission:
|
dependency-submission:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -443,7 +466,7 @@ jobs:
|
|||||||
# Gradle version compatibility
|
# Gradle version compatibility
|
||||||
|
|
||||||
Dependency-graph generation is compatible with most versions of Gradle >= `5.2`, and is tested regularly against
|
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.
|
A known exception to this is that Gradle `7.0`, `7.0.1` and `7.0.2` are not supported.
|
||||||
|
|
||||||
@ -454,4 +477,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
|
- 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
|
- 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
|
- Webinar - Gradle at Scale with GitHub and GitHub Actions at Allegro: https://www.youtube.com/watch?v=gV94I28FPos
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,25 @@
|
|||||||
|
|
||||||
This GitHub Action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
This GitHub Action can be used to configure Gradle for optimal execution on any platform supported by GitHub Actions.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> ## Licensing notice
|
||||||
|
>
|
||||||
|
> The software in this repository 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.
|
||||||
|
> The bundled `gradle-actions-caching` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
>
|
||||||
|
> The `gradle-actions-caching` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
>
|
||||||
|
> Use of the `gradle-actions-caching` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
> If you do not agree to these license terms, do not use the `gradle-actions-caching` component.
|
||||||
|
|
||||||
|
This license notice will be displayed in workflow logs and each job summary. To suppress this message,
|
||||||
|
either [accept the terms of use](#publishing-to-scansgradlecom) in your workflow, or [provide a Develocity access key](#managing-develocity-access-keys).
|
||||||
|
|
||||||
## Why use the `setup-gradle` action?
|
## 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:
|
However, the `setup-gradle` action offers a several advantages over this approach:
|
||||||
|
|
||||||
@ -38,8 +54,8 @@ jobs:
|
|||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -90,8 +106,8 @@ jobs:
|
|||||||
gradle-rc:
|
gradle-rc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -212,8 +228,8 @@ jobs:
|
|||||||
gradle-with-configuration-cache:
|
gradle-with-configuration-cache:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -465,8 +481,8 @@ jobs:
|
|||||||
run-gradle-build:
|
run-gradle-build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -502,8 +518,8 @@ jobs:
|
|||||||
gradle:
|
gradle:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -610,8 +626,8 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -662,8 +678,8 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -692,8 +708,8 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-java@v4
|
- uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 17
|
java-version: 17
|
||||||
@ -933,4 +949,3 @@ Each of the plugins is signed by Gradle, and you can simply add the following sn
|
|||||||
</trusted-key>
|
</trusted-key>
|
||||||
</trusted-keys>
|
</trusted-keys>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ jobs:
|
|||||||
name: "Validation"
|
name: "Validation"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: gradle/actions/wrapper-validation@v5
|
- uses: gradle/actions/wrapper-validation@v5
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -120,7 +120,7 @@ restore these Jars on checkout. Without this, only a pointer to the Wrapper Jar
|
|||||||
|
|
||||||
```
|
```
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
lfs: true # gradle-wrapper.jar verification will fail without this
|
lfs: true # gradle-wrapper.jar verification will fail without this
|
||||||
```
|
```
|
||||||
|
|||||||
27
licenses/gradle-actions-caching-license.txt
Normal file
27
licenses/gradle-actions-caching-license.txt
Normal file
@ -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,9 +19,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v5
|
||||||
with:
|
with:
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
java-version: 17
|
java-version: 17
|
||||||
|
|||||||
171
setup-gradle/action-types.yml
Normal file
171
setup-gradle/action-types.yml
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
# Type descriptors based on https://github.com/typesafegithub/github-actions-typing
|
||||||
|
inputs:
|
||||||
|
gradle-version:
|
||||||
|
type: string
|
||||||
|
|
||||||
|
# Cache configuration
|
||||||
|
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
|
||||||
1880
sources/package-lock.json
generated
1880
sources/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -5,7 +5,6 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Execute Gradle Build",
|
"description": "Execute Gradle Build",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "patch-package",
|
|
||||||
"prettier-write": "prettier --write 'src/**/*.ts'",
|
"prettier-write": "prettier --write 'src/**/*.ts'",
|
||||||
"prettier-check": "prettier --check 'src/**/*.ts'",
|
"prettier-check": "prettier --check 'src/**/*.ts'",
|
||||||
"lint": "eslint 'src/**/*.ts'",
|
"lint": "eslint 'src/**/*.ts'",
|
||||||
@ -37,7 +36,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/artifact": "6.1.0",
|
"@actions/artifact": "6.1.0",
|
||||||
"@actions/cache": "4.0.5",
|
"@actions/cache": "6.0.0",
|
||||||
"@actions/core": "3.0.0",
|
"@actions/core": "3.0.0",
|
||||||
"@actions/exec": "3.0.0",
|
"@actions/exec": "3.0.0",
|
||||||
"@actions/github": "9.0.0",
|
"@actions/github": "9.0.0",
|
||||||
@ -52,21 +51,20 @@
|
|||||||
"which": "6.0.1"
|
"which": "6.0.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@jest/globals": "30.2.0",
|
"@jest/globals": "30.3.0",
|
||||||
"@types/jest": "30.0.0",
|
"@types/jest": "30.0.0",
|
||||||
"@types/node": "25.3.0",
|
"@types/node": "25.5.0",
|
||||||
"@types/semver": "7.7.1",
|
"@types/semver": "7.7.1",
|
||||||
"@types/unzipper": "0.10.11",
|
"@types/unzipper": "0.10.11",
|
||||||
"@types/which": "3.0.4",
|
"@types/which": "3.0.4",
|
||||||
"@typescript-eslint/eslint-plugin": "8.56.1",
|
"@typescript-eslint/eslint-plugin": "8.57.1",
|
||||||
"dedent": "1.7.1",
|
"dedent": "1.7.2",
|
||||||
"esbuild": "0.27.3",
|
"esbuild": "0.27.4",
|
||||||
"eslint": "10.0.1",
|
"eslint": "10.0.3",
|
||||||
"globals": "17.3.0",
|
"globals": "17.4.0",
|
||||||
"jest": "30.2.0",
|
"jest": "30.3.0",
|
||||||
"nock": "15.0.0",
|
"nock": "15.0.0",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
"patch-package": "8.0.1",
|
|
||||||
"prettier": "3.8.1",
|
"prettier": "3.8.1",
|
||||||
"ts-jest": "29.4.6",
|
"ts-jest": "29.4.6",
|
||||||
"typescript": "5.9.3"
|
"typescript": "5.9.3"
|
||||||
|
|||||||
@ -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 {parseArgsStringToArgv} from 'string-argv'
|
||||||
import {
|
import {
|
||||||
BuildScanConfig,
|
DevelocityConfig,
|
||||||
CacheConfig,
|
CacheConfig,
|
||||||
DependencyGraphConfig,
|
DependencyGraphConfig,
|
||||||
DependencyGraphOption,
|
DependencyGraphOption,
|
||||||
@ -15,6 +15,7 @@ import {
|
|||||||
} from '../../configuration'
|
} from '../../configuration'
|
||||||
import {saveDeprecationState} from '../../deprecation-collector'
|
import {saveDeprecationState} from '../../deprecation-collector'
|
||||||
import {handleMainActionError} from '../../errors'
|
import {handleMainActionError} from '../../errors'
|
||||||
|
import {forceExit} from '../../force-exit'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main entry point for the action, called by Github Actions for the step.
|
* 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')
|
setActionId('gradle/actions/dependency-submission')
|
||||||
|
|
||||||
// Configure Gradle environment (Gradle User Home)
|
// 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
|
// Capture the enabled state of dependency-graph
|
||||||
const originallyEnabled = process.env['GITHUB_DEPENDENCY_GRAPH_ENABLED']
|
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.
|
// Explicit process.exit() to prevent waiting for hanging promises.
|
||||||
process.exit()
|
await forceExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import * as setupGradle from '../../setup-gradle'
|
|||||||
|
|
||||||
import {CacheConfig, SummaryConfig} from '../../configuration'
|
import {CacheConfig, SummaryConfig} from '../../configuration'
|
||||||
import {handlePostActionError} from '../../errors'
|
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
|
// 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
|
// @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.
|
// Explicit process.exit() to prevent waiting for promises left hanging by `@actions/cache` on save.
|
||||||
process.exit()
|
await forceExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import * as setupGradle from '../../setup-gradle'
|
|||||||
import * as provisioner from '../../execution/provision'
|
import * as provisioner from '../../execution/provision'
|
||||||
import * as dependencyGraph from '../../dependency-graph'
|
import * as dependencyGraph from '../../dependency-graph'
|
||||||
import {
|
import {
|
||||||
BuildScanConfig,
|
DevelocityConfig,
|
||||||
CacheConfig,
|
CacheConfig,
|
||||||
DependencyGraphConfig,
|
DependencyGraphConfig,
|
||||||
GradleExecutionConfig,
|
GradleExecutionConfig,
|
||||||
@ -12,6 +12,7 @@ import {
|
|||||||
} from '../../configuration'
|
} from '../../configuration'
|
||||||
import {failOnUseOfRemovedFeature, saveDeprecationState} from '../../deprecation-collector'
|
import {failOnUseOfRemovedFeature, saveDeprecationState} from '../../deprecation-collector'
|
||||||
import {handleMainActionError} from '../../errors'
|
import {handleMainActionError} from '../../errors'
|
||||||
|
import {forceExit} from '../../force-exit'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main entry point for the action, called by Github Actions for the step.
|
* 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')
|
setActionId('gradle/actions/setup-gradle')
|
||||||
|
|
||||||
// Configure Gradle environment (Gradle User Home)
|
// 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
|
// Configure the dependency graph submission
|
||||||
await dependencyGraph.setup(new DependencyGraphConfig())
|
await dependencyGraph.setup(new DependencyGraphConfig())
|
||||||
@ -42,7 +43,7 @@ export async function run(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Explicit process.exit() to prevent waiting for hanging promises.
|
// Explicit process.exit() to prevent waiting for hanging promises.
|
||||||
process.exit()
|
await forceExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import * as dependencyGraph from '../../dependency-graph'
|
|||||||
import {CacheConfig, DependencyGraphConfig, SummaryConfig} from '../../configuration'
|
import {CacheConfig, DependencyGraphConfig, SummaryConfig} from '../../configuration'
|
||||||
import {handlePostActionError} from '../../errors'
|
import {handlePostActionError} from '../../errors'
|
||||||
import {emitDeprecationWarnings, restoreDeprecationState} from '../../deprecation-collector'
|
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
|
// 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
|
// @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.
|
// Explicit process.exit() to prevent waiting for promises left hanging by `@actions/cache` on save.
|
||||||
process.exit()
|
await forceExit()
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import {versionIsAtLeast} from './execution/gradle'
|
|
||||||
|
|
||||||
export interface BuildResult {
|
export interface BuildResult {
|
||||||
get rootProjectName(): string
|
get rootProjectName(): string
|
||||||
@ -14,47 +13,14 @@ export interface BuildResult {
|
|||||||
get buildScanFailed(): boolean
|
get buildScanFailed(): boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BuildResults {
|
export function loadBuildResults(): BuildResult[] {
|
||||||
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 {
|
|
||||||
const results = getUnprocessedResults().map(filePath => {
|
const results = getUnprocessedResults().map(filePath => {
|
||||||
const content = fs.readFileSync(filePath, 'utf8')
|
const content = fs.readFileSync(filePath, 'utf8')
|
||||||
const buildResult = JSON.parse(content) as BuildResult
|
const buildResult = JSON.parse(content) as BuildResult
|
||||||
addScanResults(filePath, buildResult)
|
addScanResults(filePath, buildResult)
|
||||||
return buildResult
|
return buildResult
|
||||||
})
|
})
|
||||||
return new BuildResults(results)
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
export function markBuildResultsProcessed(): void {
|
export function markBuildResultsProcessed(): void {
|
||||||
|
|||||||
104
sources/src/cache-service-loader.ts
Normal file
104
sources/src/cache-service-loader.ts
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import * as path from 'path'
|
||||||
|
import {pathToFileURL} from 'url'
|
||||||
|
|
||||||
|
import {CacheConfig} from './configuration'
|
||||||
|
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 CACHE_LICENSE_WARNING = `
|
||||||
|
***********************************************************
|
||||||
|
LICENSING NOTICE
|
||||||
|
|
||||||
|
The caching functionality in \`gradle-actions\` has been extracted into \`gradle-actions-caching\`, a proprietary commercial component that is not covered by the MIT License.
|
||||||
|
The bundled \`gradle-actions-caching\` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
|
||||||
|
The \`gradle-actions-caching\` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
|
||||||
|
Use of the \`gradle-actions-caching\` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
If you do not agree to these license terms, do not use the \`gradle-actions-caching\` component.
|
||||||
|
|
||||||
|
You can suppress this message by accepting the terms in your action configuration: see https://github.com/gradle/actions/blob/main/README.md
|
||||||
|
***********************************************************
|
||||||
|
`
|
||||||
|
|
||||||
|
const CACHE_LICENSE_SUMMARY = `
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> #### Licensing notice
|
||||||
|
>
|
||||||
|
> The caching functionality in \`gradle-actions\` has been extracted into \`gradle-actions-caching\`, a proprietary commercial component that is not covered by the MIT License.
|
||||||
|
> The bundled \`gradle-actions-caching\` component is licensed and governed by a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
>
|
||||||
|
> The \`gradle-actions-caching\` component is used only when caching is enabled and is not loaded or used when caching is disabled.
|
||||||
|
>
|
||||||
|
> Use of the \`gradle-actions-caching\` component is subject to a separate license, available at https://gradle.com/legal/terms-of-use/.
|
||||||
|
> If you do not agree to these license terms, do not use the \`gradle-actions-caching\` component.
|
||||||
|
>
|
||||||
|
>You can suppress this message by [accepting the terms in your action configuration](https://github.com/gradle/actions/blob/main/README.md).
|
||||||
|
`
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
constructor(delegate: CacheService) {
|
||||||
|
this.delegate = delegate
|
||||||
|
}
|
||||||
|
|
||||||
|
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${CACHE_LICENSE_SUMMARY}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCacheService(cacheConfig: CacheConfig): Promise<CacheService> {
|
||||||
|
if (cacheConfig.isCacheDisabled()) {
|
||||||
|
return new NoOpCacheService()
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheService = await loadVendoredCacheService()
|
||||||
|
if (cacheConfig.isCacheLicenseAccepted()) {
|
||||||
|
return cacheService
|
||||||
|
}
|
||||||
|
|
||||||
|
await logCacheLicenseWarning()
|
||||||
|
return new LicenseWarningCacheService(cacheService)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 async function logCacheLicenseWarning(): Promise<void> {
|
||||||
|
console.info(CACHE_LICENSE_WARNING)
|
||||||
|
}
|
||||||
18
sources/src/cache-service.ts
Normal file
18
sources/src/cache-service.ts
Normal file
@ -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). 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). 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[]>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,290 +0,0 @@
|
|||||||
import * as core from '@actions/core'
|
|
||||||
import * as exec from '@actions/exec'
|
|
||||||
import * as glob from '@actions/glob'
|
|
||||||
|
|
||||||
import path from 'path'
|
|
||||||
import fs from 'fs'
|
|
||||||
import {generateCacheKey} from './cache-key'
|
|
||||||
import {CacheListener} from './cache-reporting'
|
|
||||||
import {saveCache, restoreCache, cacheDebug, isCacheDebuggingEnabled, tryDelete} from './cache-utils'
|
|
||||||
import {CacheConfig, ACTION_METADATA_DIR} from '../configuration'
|
|
||||||
import {GradleHomeEntryExtractor, ConfigurationCacheEntryExtractor} from './gradle-home-extry-extractor'
|
|
||||||
import {getPredefinedToolchains, mergeToolchainContent, readResourceFileAsString} from './gradle-user-home-utils'
|
|
||||||
|
|
||||||
const RESTORED_CACHE_KEY_KEY = 'restored-cache-key'
|
|
||||||
|
|
||||||
export class GradleUserHomeCache {
|
|
||||||
private readonly cacheName = 'home'
|
|
||||||
private readonly cacheDescription = 'Gradle User Home'
|
|
||||||
|
|
||||||
private readonly userHome: string
|
|
||||||
private readonly gradleUserHome: string
|
|
||||||
private readonly cacheConfig: CacheConfig
|
|
||||||
|
|
||||||
constructor(userHome: string, gradleUserHome: string, cacheConfig: CacheConfig) {
|
|
||||||
this.userHome = userHome
|
|
||||||
this.gradleUserHome = gradleUserHome
|
|
||||||
this.cacheConfig = cacheConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
init(): void {
|
|
||||||
this.initializeGradleUserHome()
|
|
||||||
|
|
||||||
// Export the GRADLE_ENCRYPTION_KEY variable if provided
|
|
||||||
const encryptionKey = this.cacheConfig.getCacheEncryptionKey()
|
|
||||||
if (encryptionKey) {
|
|
||||||
core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cacheOutputExists(): boolean {
|
|
||||||
const cachesDir = path.resolve(this.gradleUserHome, 'caches')
|
|
||||||
if (fs.existsSync(cachesDir)) {
|
|
||||||
cacheDebug(`Cache output exists at ${cachesDir}`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the cache entry, finding the closest match to the currently running job.
|
|
||||||
*/
|
|
||||||
async restore(listener: CacheListener): Promise<void> {
|
|
||||||
const entryListener = listener.entry(this.cacheDescription)
|
|
||||||
|
|
||||||
const cacheKey = generateCacheKey(this.cacheName, this.cacheConfig)
|
|
||||||
|
|
||||||
cacheDebug(
|
|
||||||
`Requesting ${this.cacheDescription} with
|
|
||||||
key:${cacheKey.key}
|
|
||||||
restoreKeys:[${cacheKey.restoreKeys}]`
|
|
||||||
)
|
|
||||||
|
|
||||||
const cachePath = this.getCachePath()
|
|
||||||
const cacheResult = await restoreCache(cachePath, cacheKey.key, cacheKey.restoreKeys, entryListener)
|
|
||||||
if (!cacheResult) {
|
|
||||||
core.info(`${this.cacheDescription} cache not found. Will initialize empty.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
core.saveState(RESTORED_CACHE_KEY_KEY, cacheResult.key)
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.afterRestore(listener)
|
|
||||||
} catch (error) {
|
|
||||||
core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore any extracted cache entries after the main Gradle User Home entry is restored.
|
|
||||||
*/
|
|
||||||
async afterRestore(listener: CacheListener): Promise<void> {
|
|
||||||
await this.debugReportGradleUserHomeSize('as restored from cache')
|
|
||||||
await new GradleHomeEntryExtractor(this.gradleUserHome, this.cacheConfig).restore(listener)
|
|
||||||
await new ConfigurationCacheEntryExtractor(this.gradleUserHome, this.cacheConfig).restore(listener)
|
|
||||||
await this.deleteExcludedPaths()
|
|
||||||
await this.debugReportGradleUserHomeSize('after restoring common artifacts')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the cache entry based on the current cache key unless the cache was restored with the exact key,
|
|
||||||
* in which case we cannot overwrite it.
|
|
||||||
*
|
|
||||||
* If the cache entry was restored with a partial match on a restore key, then
|
|
||||||
* it is saved with the exact key.
|
|
||||||
*/
|
|
||||||
async save(listener: CacheListener): Promise<void> {
|
|
||||||
const cacheKey = generateCacheKey(this.cacheName, this.cacheConfig).key
|
|
||||||
const restoredCacheKey = core.getState(RESTORED_CACHE_KEY_KEY)
|
|
||||||
const gradleHomeEntryListener = listener.entry(this.cacheDescription)
|
|
||||||
|
|
||||||
if (restoredCacheKey && cacheKey === restoredCacheKey) {
|
|
||||||
core.info(`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`)
|
|
||||||
|
|
||||||
for (const entryListener of listener.cacheEntries) {
|
|
||||||
if (entryListener === gradleHomeEntryListener) {
|
|
||||||
entryListener.markNotSaved('cache key not changed')
|
|
||||||
} else {
|
|
||||||
entryListener.markNotSaved(`referencing '${this.cacheDescription}' cache entry not saved`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.beforeSave(listener)
|
|
||||||
} catch (error) {
|
|
||||||
core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const cachePath = this.getCachePath()
|
|
||||||
await saveCache(cachePath, cacheKey, gradleHomeEntryListener)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract and save any defined extracted cache entries prior to the main Gradle User Home entry being saved.
|
|
||||||
*/
|
|
||||||
async beforeSave(listener: CacheListener): Promise<void> {
|
|
||||||
await this.debugReportGradleUserHomeSize('before saving common artifacts')
|
|
||||||
await this.deleteExcludedPaths()
|
|
||||||
await Promise.all([
|
|
||||||
new GradleHomeEntryExtractor(this.gradleUserHome, this.cacheConfig).extract(listener),
|
|
||||||
new ConfigurationCacheEntryExtractor(this.gradleUserHome, this.cacheConfig).extract(listener)
|
|
||||||
])
|
|
||||||
await this.debugReportGradleUserHomeSize(
|
|
||||||
"after extracting common artifacts (only 'caches' and 'notifications' will be stored)"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete any file paths that are excluded by the `gradle-home-cache-excludes` parameter.
|
|
||||||
*/
|
|
||||||
private async deleteExcludedPaths(): Promise<void> {
|
|
||||||
const rawPaths: string[] = this.cacheConfig.getCacheExcludes()
|
|
||||||
rawPaths.push('caches/*/cc-keystore')
|
|
||||||
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
|
||||||
|
|
||||||
for (const p of resolvedPaths) {
|
|
||||||
cacheDebug(`Removing excluded path: ${p}`)
|
|
||||||
const globber = await glob.create(p, {
|
|
||||||
implicitDescendants: false
|
|
||||||
})
|
|
||||||
|
|
||||||
for (const toDelete of await globber.glob()) {
|
|
||||||
cacheDebug(`Removing excluded file: ${toDelete}`)
|
|
||||||
await tryDelete(toDelete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the paths within Gradle User Home to cache.
|
|
||||||
* By default, this is the 'caches' and 'notifications' directories,
|
|
||||||
* but this can be overridden by the `gradle-home-cache-includes` parameter.
|
|
||||||
*/
|
|
||||||
protected getCachePath(): string[] {
|
|
||||||
const rawPaths: string[] = this.cacheConfig.getCacheIncludes()
|
|
||||||
rawPaths.push(ACTION_METADATA_DIR)
|
|
||||||
const resolvedPaths = rawPaths.map(x => this.resolveCachePath(x))
|
|
||||||
cacheDebug(`Using cache paths: ${resolvedPaths}`)
|
|
||||||
return resolvedPaths
|
|
||||||
}
|
|
||||||
|
|
||||||
private resolveCachePath(rawPath: string): string {
|
|
||||||
if (rawPath.startsWith('!')) {
|
|
||||||
const resolved = this.resolveCachePath(rawPath.substring(1))
|
|
||||||
return `!${resolved}`
|
|
||||||
}
|
|
||||||
return path.resolve(this.gradleUserHome, rawPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
private initializeGradleUserHome(): void {
|
|
||||||
// Create a directory for storing action metadata
|
|
||||||
const actionCacheDir = path.resolve(this.gradleUserHome, ACTION_METADATA_DIR)
|
|
||||||
fs.mkdirSync(actionCacheDir, {recursive: true})
|
|
||||||
|
|
||||||
this.copyInitScripts()
|
|
||||||
|
|
||||||
// Copy the default toolchain definitions to `~/.m2/toolchains.xml`
|
|
||||||
this.registerToolchains()
|
|
||||||
|
|
||||||
if (core.isDebug()) {
|
|
||||||
this.configureInfoLogLevel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private copyInitScripts(): void {
|
|
||||||
// Copy init scripts from src/resources to Gradle UserHome
|
|
||||||
const initScriptsDir = path.resolve(this.gradleUserHome, 'init.d')
|
|
||||||
fs.mkdirSync(initScriptsDir, {recursive: true})
|
|
||||||
const initScriptFilenames = [
|
|
||||||
'gradle-actions.build-result-capture.init.gradle',
|
|
||||||
'gradle-actions.build-result-capture-service.plugin.groovy',
|
|
||||||
'gradle-actions.github-dependency-graph.init.gradle',
|
|
||||||
'gradle-actions.github-dependency-graph-gradle-plugin-apply.groovy',
|
|
||||||
'gradle-actions.inject-develocity.init.gradle'
|
|
||||||
]
|
|
||||||
for (const initScriptFilename of initScriptFilenames) {
|
|
||||||
const initScriptContent = readResourceFileAsString('init-scripts', initScriptFilename)
|
|
||||||
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
|
|
||||||
fs.writeFileSync(initScriptPath, initScriptContent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private registerToolchains(): void {
|
|
||||||
const preInstalledToolchains: string | null = getPredefinedToolchains()
|
|
||||||
if (preInstalledToolchains == null) return
|
|
||||||
|
|
||||||
const m2dir = path.resolve(this.userHome, '.m2')
|
|
||||||
const toolchainXmlTarget = path.resolve(m2dir, 'toolchains.xml')
|
|
||||||
if (!fs.existsSync(toolchainXmlTarget)) {
|
|
||||||
// Write a new toolchains.xml file if it doesn't exist
|
|
||||||
fs.mkdirSync(m2dir, {recursive: true})
|
|
||||||
fs.writeFileSync(toolchainXmlTarget, preInstalledToolchains)
|
|
||||||
|
|
||||||
core.info(`Wrote default JDK locations to ${toolchainXmlTarget}`)
|
|
||||||
} else {
|
|
||||||
// Merge into an existing toolchains.xml file
|
|
||||||
const existingToolchainContent = fs.readFileSync(toolchainXmlTarget, 'utf8')
|
|
||||||
const mergedContent = mergeToolchainContent(existingToolchainContent, preInstalledToolchains)
|
|
||||||
|
|
||||||
fs.writeFileSync(toolchainXmlTarget, mergedContent)
|
|
||||||
core.info(`Merged default JDK locations into ${toolchainXmlTarget}`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When the GitHub environment ACTIONS_RUNNER_DEBUG is true, run Gradle with --info and --stacktrace.
|
|
||||||
* see https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging
|
|
||||||
*
|
|
||||||
* @VisibleForTesting
|
|
||||||
*/
|
|
||||||
configureInfoLogLevel(): void {
|
|
||||||
const infoProperties = `org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n`
|
|
||||||
const propertiesFile = path.resolve(this.gradleUserHome, 'gradle.properties')
|
|
||||||
if (fs.existsSync(propertiesFile)) {
|
|
||||||
core.info(`Merged --info and --stacktrace into existing ${propertiesFile} file`)
|
|
||||||
const existingProperties = fs.readFileSync(propertiesFile, 'utf-8')
|
|
||||||
fs.writeFileSync(propertiesFile, `${infoProperties}\n${existingProperties}`)
|
|
||||||
} else {
|
|
||||||
core.info(`Created a new ${propertiesFile} with --info and --stacktrace`)
|
|
||||||
fs.writeFileSync(propertiesFile, infoProperties)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When cache debugging is enabled (or ACTIONS_STEP_DEBUG is on),
|
|
||||||
* this method will give a detailed report of the Gradle User Home contents.
|
|
||||||
*/
|
|
||||||
private async debugReportGradleUserHomeSize(label: string): Promise<void> {
|
|
||||||
if (!isCacheDebuggingEnabled() && !core.isDebug()) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (!fs.existsSync(this.gradleUserHome)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const result = await exec.getExecOutput('du', ['-h', '-c', '-t', '5M'], {
|
|
||||||
cwd: this.gradleUserHome,
|
|
||||||
silent: true,
|
|
||||||
ignoreReturnCode: true
|
|
||||||
})
|
|
||||||
|
|
||||||
core.info(`Gradle User Home (directories >5M): ${label}`)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
result.stdout
|
|
||||||
.trimEnd()
|
|
||||||
.replace(/\t/g, ' ')
|
|
||||||
.split('\n')
|
|
||||||
.map(it => {
|
|
||||||
return ` ${it}`
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
)
|
|
||||||
|
|
||||||
core.info('-----------------------')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import path from 'path'
|
|
||||||
import fs from 'fs'
|
|
||||||
import {fileURLToPath} from 'url'
|
|
||||||
|
|
||||||
export function readResourceFileAsString(...paths: string[]): string {
|
|
||||||
// Resolving relative to __dirname will allow node to find the resource at runtime
|
|
||||||
const moduleDir = path.dirname(fileURLToPath(import.meta.url))
|
|
||||||
const absolutePath = path.resolve(moduleDir, '..', '..', '..', 'sources', 'src', 'resources', ...paths)
|
|
||||||
return fs.readFileSync(absolutePath, 'utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Iterate over all `JAVA_HOME_{version}_{arch}` envs and construct the toolchain.xml.
|
|
||||||
*
|
|
||||||
* @VisibleForTesting
|
|
||||||
*/
|
|
||||||
export function getPredefinedToolchains(): string | null {
|
|
||||||
// Get the version and path for each JAVA_HOME env var
|
|
||||||
const javaHomeEnvs = Object.entries(process.env)
|
|
||||||
.filter(([key]) => key.startsWith('JAVA_HOME_') && process.env[key])
|
|
||||||
.map(([key, value]) => ({
|
|
||||||
jdkVersion: key.match(/JAVA_HOME_(\d+)_/)?.[1] ?? null,
|
|
||||||
jdkPath: value as string
|
|
||||||
}))
|
|
||||||
.filter(env => env.jdkVersion !== null)
|
|
||||||
|
|
||||||
if (javaHomeEnvs.length === 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
// language=XML
|
|
||||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<toolchains>
|
|
||||||
<!-- JDK Toolchains installed by default on GitHub-hosted runners -->
|
|
||||||
${javaHomeEnvs
|
|
||||||
.map(
|
|
||||||
({jdkVersion, jdkPath}) => ` <toolchain>
|
|
||||||
<type>jdk</type>
|
|
||||||
<provides>
|
|
||||||
<version>${jdkVersion}</version>
|
|
||||||
</provides>
|
|
||||||
<configuration>
|
|
||||||
<jdkHome>${jdkPath}</jdkHome>
|
|
||||||
</configuration>
|
|
||||||
</toolchain>`
|
|
||||||
)
|
|
||||||
.join('\n')}
|
|
||||||
</toolchains>\n`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mergeToolchainContent(existingToolchainContent: string, preInstalledToolchains: string): string {
|
|
||||||
const appendedContent = preInstalledToolchains.split('<toolchains>').pop()!
|
|
||||||
return existingToolchainContent.replace('</toolchains>', appendedContent)
|
|
||||||
}
|
|
||||||
@ -132,42 +132,23 @@ export class CacheConfig {
|
|||||||
return getBooleanInput('gradle-home-cache-strict-match')
|
return getBooleanInput('gradle-home-cache-strict-match')
|
||||||
}
|
}
|
||||||
|
|
||||||
isCacheCleanupEnabled(): boolean {
|
getCacheCleanupOption(): string {
|
||||||
if (this.isCacheReadOnly()) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
const cleanupOption = this.getCacheCleanupOption()
|
|
||||||
return cleanupOption === CacheCleanupOption.Always || cleanupOption === CacheCleanupOption.OnSuccess
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldPerformCacheCleanup(hasFailure: boolean): boolean {
|
|
||||||
const cleanupOption = this.getCacheCleanupOption()
|
|
||||||
if (cleanupOption === CacheCleanupOption.Always) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (cleanupOption === CacheCleanupOption.OnSuccess) {
|
|
||||||
return !hasFailure
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCacheCleanupOption(): CacheCleanupOption {
|
|
||||||
const legacyVal = getOptionalBooleanInput('gradle-home-cache-cleanup')
|
const legacyVal = getOptionalBooleanInput('gradle-home-cache-cleanup')
|
||||||
if (legacyVal !== undefined) {
|
if (legacyVal !== undefined) {
|
||||||
deprecator.recordDeprecation(
|
deprecator.recordDeprecation(
|
||||||
'The `gradle-home-cache-cleanup` input parameter has been replaced by `cache-cleanup`'
|
'The `gradle-home-cache-cleanup` input parameter has been replaced by `cache-cleanup`'
|
||||||
)
|
)
|
||||||
return legacyVal ? CacheCleanupOption.Always : CacheCleanupOption.Never
|
return legacyVal ? CacheCleanupOption.Always.toString() : CacheCleanupOption.Never.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
const val = core.getInput('cache-cleanup')
|
const val = core.getInput('cache-cleanup')
|
||||||
switch (val.toLowerCase().trim()) {
|
switch (val.toLowerCase().trim()) {
|
||||||
case 'always':
|
case 'always':
|
||||||
return CacheCleanupOption.Always
|
return CacheCleanupOption.Always.toString()
|
||||||
case 'on-success':
|
case 'on-success':
|
||||||
return CacheCleanupOption.OnSuccess
|
return CacheCleanupOption.OnSuccess.toString()
|
||||||
case 'never':
|
case 'never':
|
||||||
return CacheCleanupOption.Never
|
return CacheCleanupOption.Never.toString()
|
||||||
}
|
}
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
`The value '${val}' is not valid for cache-cleanup. Valid values are: [never, always, on-success].`
|
`The value '${val}' is not valid for cache-cleanup. Valid values are: [never, always, on-success].`
|
||||||
@ -185,6 +166,11 @@ export class CacheConfig {
|
|||||||
getCacheExcludes(): string[] {
|
getCacheExcludes(): string[] {
|
||||||
return core.getMultilineInput('gradle-home-cache-excludes')
|
return core.getMultilineInput('gradle-home-cache-excludes')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCacheLicenseAccepted(): boolean {
|
||||||
|
const dvConfig = new DevelocityConfig()
|
||||||
|
return dvConfig.getDevelocityAccessKey() !== '' || dvConfig.hasTermsOfUseAgreement()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum CacheCleanupOption {
|
export enum CacheCleanupOption {
|
||||||
@ -248,7 +234,7 @@ export enum JobSummaryOption {
|
|||||||
OnFailure = 'on-failure'
|
OnFailure = 'on-failure'
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BuildScanConfig {
|
export class DevelocityConfig {
|
||||||
static DevelocityAccessKeyEnvVar = 'DEVELOCITY_ACCESS_KEY'
|
static DevelocityAccessKeyEnvVar = 'DEVELOCITY_ACCESS_KEY'
|
||||||
static GradleEnterpriseAccessKeyEnvVar = 'GRADLE_ENTERPRISE_ACCESS_KEY'
|
static GradleEnterpriseAccessKeyEnvVar = 'GRADLE_ENTERPRISE_ACCESS_KEY'
|
||||||
|
|
||||||
@ -256,19 +242,19 @@ export class BuildScanConfig {
|
|||||||
return getBooleanInput('build-scan-publish') && this.verifyTermsOfUseAgreement()
|
return getBooleanInput('build-scan-publish') && this.verifyTermsOfUseAgreement()
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuildScanTermsOfUseUrl(): string {
|
getTermsOfUseUrl(): string {
|
||||||
return core.getInput('build-scan-terms-of-use-url')
|
return core.getInput('build-scan-terms-of-use-url')
|
||||||
}
|
}
|
||||||
|
|
||||||
getBuildScanTermsOfUseAgree(): string {
|
getTermsOfUseAgree(): string {
|
||||||
return core.getInput('build-scan-terms-of-use-agree')
|
return core.getInput('build-scan-terms-of-use-agree')
|
||||||
}
|
}
|
||||||
|
|
||||||
getDevelocityAccessKey(): string {
|
getDevelocityAccessKey(): string {
|
||||||
return (
|
return (
|
||||||
core.getInput('develocity-access-key') ||
|
core.getInput('develocity-access-key') ||
|
||||||
process.env[BuildScanConfig.DevelocityAccessKeyEnvVar] ||
|
process.env[DevelocityConfig.DevelocityAccessKeyEnvVar] ||
|
||||||
process.env[BuildScanConfig.GradleEnterpriseAccessKeyEnvVar] ||
|
process.env[DevelocityConfig.GradleEnterpriseAccessKeyEnvVar] ||
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -309,12 +295,17 @@ export class BuildScanConfig {
|
|||||||
return new PluginRepositoryConfig()
|
return new PluginRepositoryConfig()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasTermsOfUseAgreement(): boolean {
|
||||||
|
const develocityAccessKeySet = this.getDevelocityAccessKey() !== ''
|
||||||
|
const termsUrlSet =
|
||||||
|
this.getTermsOfUseUrl() === 'https://gradle.com/terms-of-service' ||
|
||||||
|
this.getTermsOfUseUrl() === 'https://gradle.com/help/legal-terms-of-use'
|
||||||
|
const termsAgreed = this.getTermsOfUseAgree() === 'yes'
|
||||||
|
return develocityAccessKeySet || (termsUrlSet && termsAgreed)
|
||||||
|
}
|
||||||
|
|
||||||
private verifyTermsOfUseAgreement(): boolean {
|
private verifyTermsOfUseAgreement(): boolean {
|
||||||
if (
|
if (!this.hasTermsOfUseAgreement()) {
|
||||||
(this.getBuildScanTermsOfUseUrl() !== 'https://gradle.com/terms-of-service' &&
|
|
||||||
this.getBuildScanTermsOfUseUrl() !== 'https://gradle.com/help/legal-terms-of-use') ||
|
|
||||||
this.getBuildScanTermsOfUseAgree() !== 'yes'
|
|
||||||
) {
|
|
||||||
core.warning(
|
core.warning(
|
||||||
`Terms of use at 'https://gradle.com/help/legal-terms-of-use' must be agreed in order to publish build scans.`
|
`Terms of use at 'https://gradle.com/help/legal-terms-of-use' must be agreed in order to publish build scans.`
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,33 +0,0 @@
|
|||||||
import * as core from '@actions/core'
|
|
||||||
import * as exec from '@actions/exec'
|
|
||||||
import * as fs from 'fs'
|
|
||||||
import * as path from 'path'
|
|
||||||
import {BuildResults} from './build-results'
|
|
||||||
|
|
||||||
export class DaemonController {
|
|
||||||
private readonly gradleHomes
|
|
||||||
|
|
||||||
constructor(buildResults: BuildResults) {
|
|
||||||
this.gradleHomes = buildResults.uniqueGradleHomes()
|
|
||||||
}
|
|
||||||
|
|
||||||
async stopAllDaemons(): Promise<void> {
|
|
||||||
const executions: Promise<number>[] = []
|
|
||||||
const args = ['--stop']
|
|
||||||
|
|
||||||
for (const gradleHome of this.gradleHomes) {
|
|
||||||
const executable = path.resolve(gradleHome, 'bin', 'gradle')
|
|
||||||
if (!fs.existsSync(executable)) {
|
|
||||||
core.warning(`Gradle executable not found at ${executable}. Could not stop Gradle daemons.`)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
core.info(`Stopping Gradle daemons for ${gradleHome}`)
|
|
||||||
executions.push(
|
|
||||||
exec.exec(executable, args, {
|
|
||||||
ignoreReturnCode: true
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
await Promise.all(executions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {BuildScanConfig} from '../configuration'
|
import {DevelocityConfig} from '../configuration'
|
||||||
import {setupToken} from './short-lived-token'
|
import {setupToken} from './short-lived-token'
|
||||||
|
|
||||||
export async function setup(config: BuildScanConfig): Promise<void> {
|
export async function setup(config: DevelocityConfig): Promise<void> {
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_INIT_SCRIPT_NAME', 'gradle-actions.inject-develocity.init.gradle')
|
maybeExportVariable('DEVELOCITY_INJECTION_INIT_SCRIPT_NAME', 'gradle-actions.inject-develocity.init.gradle')
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_CUSTOM_VALUE', 'gradle-actions')
|
maybeExportVariable('DEVELOCITY_INJECTION_CUSTOM_VALUE', 'gradle-actions')
|
||||||
|
|
||||||
@ -36,8 +36,8 @@ export async function setup(config: BuildScanConfig): Promise<void> {
|
|||||||
maybeExportVariable('DEVELOCITY_INJECTION_ENABLED', 'true')
|
maybeExportVariable('DEVELOCITY_INJECTION_ENABLED', 'true')
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_DEVELOCITY_PLUGIN_VERSION', '4.3.2')
|
maybeExportVariable('DEVELOCITY_INJECTION_DEVELOCITY_PLUGIN_VERSION', '4.3.2')
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_CCUD_PLUGIN_VERSION', '2.1')
|
maybeExportVariable('DEVELOCITY_INJECTION_CCUD_PLUGIN_VERSION', '2.1')
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_TERMS_OF_USE_URL', config.getBuildScanTermsOfUseUrl())
|
maybeExportVariable('DEVELOCITY_INJECTION_TERMS_OF_USE_URL', config.getTermsOfUseUrl())
|
||||||
maybeExportVariable('DEVELOCITY_INJECTION_TERMS_OF_USE_AGREE', config.getBuildScanTermsOfUseAgree())
|
maybeExportVariable('DEVELOCITY_INJECTION_TERMS_OF_USE_AGREE', config.getTermsOfUseAgree())
|
||||||
}
|
}
|
||||||
|
|
||||||
return setupToken(
|
return setupToken(
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as httpm from '@actions/http-client'
|
import * as httpm from '@actions/http-client'
|
||||||
import {BuildScanConfig} from '../configuration'
|
import {DevelocityConfig} from '../configuration'
|
||||||
import {recordDeprecation} from '../deprecation-collector'
|
import {recordDeprecation} from '../deprecation-collector'
|
||||||
|
|
||||||
export async function setupToken(
|
export async function setupToken(
|
||||||
@ -28,7 +28,7 @@ export async function setupToken(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exportAccessKeyEnvVars(value: string): void {
|
function exportAccessKeyEnvVars(value: string): void {
|
||||||
;[BuildScanConfig.DevelocityAccessKeyEnvVar, BuildScanConfig.GradleEnterpriseAccessKeyEnvVar].forEach(key =>
|
;[DevelocityConfig.DevelocityAccessKeyEnvVar, DevelocityConfig.GradleEnterpriseAccessKeyEnvVar].forEach(key =>
|
||||||
core.exportVariable(key, value)
|
core.exportVariable(key, value)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -36,12 +36,14 @@ function exportAccessKeyEnvVars(value: string): void {
|
|||||||
function handleMissingAccessToken(): void {
|
function handleMissingAccessToken(): void {
|
||||||
core.warning(`Failed to fetch short-lived token for Develocity`)
|
core.warning(`Failed to fetch short-lived token for Develocity`)
|
||||||
|
|
||||||
if (process.env[BuildScanConfig.GradleEnterpriseAccessKeyEnvVar]) {
|
if (process.env[DevelocityConfig.GradleEnterpriseAccessKeyEnvVar]) {
|
||||||
// We do not clear the GRADLE_ENTERPRISE_ACCESS_KEY env var in v3, to let the users upgrade to DV 2024.1
|
// We do not clear the GRADLE_ENTERPRISE_ACCESS_KEY env var in v3, to let the users upgrade to DV 2024.1
|
||||||
recordDeprecation(`The ${BuildScanConfig.GradleEnterpriseAccessKeyEnvVar} env var is deprecated`)
|
recordDeprecation(`The ${DevelocityConfig.GradleEnterpriseAccessKeyEnvVar} env var is deprecated`)
|
||||||
}
|
}
|
||||||
if (process.env[BuildScanConfig.DevelocityAccessKeyEnvVar]) {
|
if (process.env[DevelocityConfig.DevelocityAccessKeyEnvVar]) {
|
||||||
core.warning(`The ${BuildScanConfig.DevelocityAccessKeyEnvVar} env var should be mapped to a short-lived token`)
|
core.warning(
|
||||||
|
`The ${DevelocityConfig.DevelocityAccessKeyEnvVar} env var should be mapped to a short-lived token`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,9 +6,8 @@ import * as core from '@actions/core'
|
|||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
import * as toolCache from '@actions/tool-cache'
|
import * as toolCache from '@actions/tool-cache'
|
||||||
|
|
||||||
import {determineGradleVersion, findGradleExecutableOnPath, versionIsAtLeast} from './gradle'
|
import {determineGradleVersion, findGradleExecutableOnPath} from './gradle'
|
||||||
import * as gradlew from './gradlew'
|
import * as gradlew from './gradlew'
|
||||||
import {handleCacheFailure} from '../caching/cache-utils'
|
|
||||||
import {CacheConfig} from '../configuration'
|
import {CacheConfig} from '../configuration'
|
||||||
|
|
||||||
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
|
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
|
||||||
@ -109,34 +108,6 @@ async function installGradleVersion(versionInfo: GradleVersionInfo): Promise<str
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Find (or install) a Gradle executable that meets the specified version requirement.
|
|
||||||
* The Gradle version on PATH and all candidates are first checked for version compatibility.
|
|
||||||
* If no existing Gradle version meets the requirement, the required version is installed.
|
|
||||||
* @return Gradle executable with at least the required version.
|
|
||||||
*/
|
|
||||||
export async function provisionGradleWithVersionAtLeast(
|
|
||||||
minimumVersion: string,
|
|
||||||
candidates: string[] = []
|
|
||||||
): Promise<string> {
|
|
||||||
const gradleOnPath = await findGradleExecutableOnPath()
|
|
||||||
const allCandidates = gradleOnPath ? [gradleOnPath, ...candidates] : candidates
|
|
||||||
|
|
||||||
return core.group(`Provision Gradle >= ${minimumVersion}`, async () => {
|
|
||||||
for (const candidate of allCandidates) {
|
|
||||||
const candidateVersion = await determineGradleVersion(candidate)
|
|
||||||
if (candidateVersion && versionIsAtLeast(candidateVersion, minimumVersion)) {
|
|
||||||
core.info(
|
|
||||||
`Gradle version ${candidateVersion} is available at ${candidate} and >= ${minimumVersion}. Not installing.`
|
|
||||||
)
|
|
||||||
return candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return locateGradleAndDownloadIfRequired(await gradleRelease(minimumVersion))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function locateGradleAndDownloadIfRequired(versionInfo: GradleVersionInfo): Promise<string> {
|
async function locateGradleAndDownloadIfRequired(versionInfo: GradleVersionInfo): Promise<string> {
|
||||||
const installsDir = path.join(getProvisionDir(), 'installs')
|
const installsDir = path.join(getProvisionDir(), 'installs')
|
||||||
const installDir = path.join(installsDir, `gradle-${versionInfo.version}`)
|
const installDir = path.join(installsDir, `gradle-${versionInfo.version}`)
|
||||||
@ -222,3 +193,20 @@ interface GradleVersionInfo {
|
|||||||
version: string
|
version: string
|
||||||
downloadUrl: string
|
downloadUrl: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
core.info(error.stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
14
sources/src/force-exit.ts
Normal file
14
sources/src/force-exit.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
const WINDOWS_EXIT_DELAY_MS = 50
|
||||||
|
|
||||||
|
export function getForcedExitDelayMs(platform: NodeJS.Platform = process.platform): number {
|
||||||
|
return platform === 'win32' ? WINDOWS_EXIT_DELAY_MS : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function forceExit(platform: NodeJS.Platform = process.platform): Promise<never> {
|
||||||
|
const exitDelayMs = getForcedExitDelayMs(platform)
|
||||||
|
if (exitDelayMs > 0) {
|
||||||
|
await new Promise(resolve => setTimeout(resolve, exitDelayMs))
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.exit()
|
||||||
|
}
|
||||||
135
sources/src/gradle-user-home.ts
Normal file
135
sources/src/gradle-user-home.ts
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
import {ACTION_METADATA_DIR} from './configuration'
|
||||||
|
|
||||||
|
export function initializeGradleUserHome(userHome: string, gradleUserHome: string, encryptionKey?: string): void {
|
||||||
|
// Create a directory for storing action metadata
|
||||||
|
const actionCacheDir = path.resolve(gradleUserHome, ACTION_METADATA_DIR)
|
||||||
|
fs.mkdirSync(actionCacheDir, {recursive: true})
|
||||||
|
|
||||||
|
copyInitScripts(gradleUserHome)
|
||||||
|
|
||||||
|
// Copy the default toolchain definitions to `~/.m2/toolchains.xml`
|
||||||
|
registerToolchains(userHome)
|
||||||
|
|
||||||
|
if (core.isDebug()) {
|
||||||
|
configureInfoLogLevel(gradleUserHome)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKey) {
|
||||||
|
core.exportVariable('GRADLE_ENCRYPTION_KEY', encryptionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyInitScripts(gradleUserHome: string): void {
|
||||||
|
// Copy init scripts from src/resources to Gradle UserHome
|
||||||
|
const initScriptsDir = path.resolve(gradleUserHome, 'init.d')
|
||||||
|
fs.mkdirSync(initScriptsDir, {recursive: true})
|
||||||
|
const initScriptFilenames = [
|
||||||
|
'gradle-actions.build-result-capture.init.gradle',
|
||||||
|
'gradle-actions.build-result-capture-service.plugin.groovy',
|
||||||
|
'gradle-actions.github-dependency-graph.init.gradle',
|
||||||
|
'gradle-actions.github-dependency-graph-gradle-plugin-apply.groovy',
|
||||||
|
'gradle-actions.inject-develocity.init.gradle'
|
||||||
|
]
|
||||||
|
for (const initScriptFilename of initScriptFilenames) {
|
||||||
|
const initScriptContent = readResourceFileAsString('init-scripts', initScriptFilename)
|
||||||
|
const initScriptPath = path.resolve(initScriptsDir, initScriptFilename)
|
||||||
|
fs.writeFileSync(initScriptPath, initScriptContent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerToolchains(userHome: string): void {
|
||||||
|
const preInstalledToolchains: string | null = getPredefinedToolchains()
|
||||||
|
if (preInstalledToolchains == null) return
|
||||||
|
|
||||||
|
const m2dir = path.resolve(userHome, '.m2')
|
||||||
|
const toolchainXmlTarget = path.resolve(m2dir, 'toolchains.xml')
|
||||||
|
if (!fs.existsSync(toolchainXmlTarget)) {
|
||||||
|
// Write a new toolchains.xml file if it doesn't exist
|
||||||
|
fs.mkdirSync(m2dir, {recursive: true})
|
||||||
|
fs.writeFileSync(toolchainXmlTarget, preInstalledToolchains)
|
||||||
|
|
||||||
|
core.info(`Wrote default JDK locations to ${toolchainXmlTarget}`)
|
||||||
|
} else {
|
||||||
|
// Merge into an existing toolchains.xml file
|
||||||
|
const existingToolchainContent = fs.readFileSync(toolchainXmlTarget, 'utf8')
|
||||||
|
const mergedContent = mergeToolchainContent(existingToolchainContent, preInstalledToolchains)
|
||||||
|
|
||||||
|
fs.writeFileSync(toolchainXmlTarget, mergedContent)
|
||||||
|
core.info(`Merged default JDK locations into ${toolchainXmlTarget}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the GitHub environment ACTIONS_RUNNER_DEBUG is true, run Gradle with --info and --stacktrace.
|
||||||
|
* see https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/enabling-debug-logging
|
||||||
|
*
|
||||||
|
* @VisibleForTesting
|
||||||
|
*/
|
||||||
|
export function configureInfoLogLevel(gradleUserHome: string): void {
|
||||||
|
const infoProperties = `org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n`
|
||||||
|
const propertiesFile = path.resolve(gradleUserHome, 'gradle.properties')
|
||||||
|
if (fs.existsSync(propertiesFile)) {
|
||||||
|
core.info(`Merged --info and --stacktrace into existing ${propertiesFile} file`)
|
||||||
|
const existingProperties = fs.readFileSync(propertiesFile, 'utf-8')
|
||||||
|
fs.writeFileSync(propertiesFile, `${infoProperties}\n${existingProperties}`)
|
||||||
|
} else {
|
||||||
|
core.info(`Created a new ${propertiesFile} with --info and --stacktrace`)
|
||||||
|
fs.writeFileSync(propertiesFile, infoProperties)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readResourceFileAsString(...paths: string[]): string {
|
||||||
|
// Resolving relative to `dist/<action>/main/index.js` will allow node to find the resource at runtime
|
||||||
|
const moduleDir = import.meta.dirname
|
||||||
|
const absolutePath = path.resolve(moduleDir, '..', '..', '..', 'sources', 'src', 'resources', ...paths)
|
||||||
|
return fs.readFileSync(absolutePath, 'utf8')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over all `JAVA_HOME_{version}_{arch}` envs and construct the toolchain.xml.
|
||||||
|
*
|
||||||
|
* @VisibleForTesting
|
||||||
|
*/
|
||||||
|
export function getPredefinedToolchains(): string | null {
|
||||||
|
// Get the version and path for each JAVA_HOME env var
|
||||||
|
const javaHomeEnvs = Object.entries(process.env)
|
||||||
|
.filter(([key]) => key.startsWith('JAVA_HOME_') && process.env[key])
|
||||||
|
.map(([key, value]) => ({
|
||||||
|
jdkVersion: key.match(/JAVA_HOME_(\d+)_/)?.[1] ?? null,
|
||||||
|
jdkPath: value as string
|
||||||
|
}))
|
||||||
|
.filter(env => env.jdkVersion !== null)
|
||||||
|
|
||||||
|
if (javaHomeEnvs.length === 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// language=XML
|
||||||
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<toolchains>
|
||||||
|
<!-- JDK Toolchains installed by default on GitHub-hosted runners -->
|
||||||
|
${javaHomeEnvs
|
||||||
|
.map(
|
||||||
|
({jdkVersion, jdkPath}) => ` <toolchain>
|
||||||
|
<type>jdk</type>
|
||||||
|
<provides>
|
||||||
|
<version>${jdkVersion}</version>
|
||||||
|
</provides>
|
||||||
|
<configuration>
|
||||||
|
<jdkHome>${jdkPath}</jdkHome>
|
||||||
|
</configuration>
|
||||||
|
</toolchain>`
|
||||||
|
)
|
||||||
|
.join('\n')}
|
||||||
|
</toolchains>\n`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mergeToolchainContent(existingToolchainContent: string, preInstalledToolchains: string): string {
|
||||||
|
const appendedContent = preInstalledToolchains.split('<toolchains>').pop()!
|
||||||
|
return existingToolchainContent.replace('</toolchains>', appendedContent)
|
||||||
|
}
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as github from '@actions/github'
|
import * as github from '@actions/github'
|
||||||
|
|
||||||
import {BuildResults, BuildResult} from './build-results'
|
import {BuildResult} from './build-results'
|
||||||
import {SummaryConfig, getActionId, getGithubToken} from './configuration'
|
import {SummaryConfig, getActionId, getGithubToken} from './configuration'
|
||||||
import {Deprecation, getDeprecations, getErrors} from './deprecation-collector'
|
import {Deprecation, getDeprecations, getErrors} from './deprecation-collector'
|
||||||
|
|
||||||
export async function generateJobSummary(
|
export async function generateJobSummary(
|
||||||
buildResults: BuildResults,
|
buildResults: BuildResult[],
|
||||||
cachingReport: string,
|
cachingReport: string,
|
||||||
config: SummaryConfig
|
config: SummaryConfig
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
@ -17,9 +17,8 @@ export async function generateJobSummary(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const summaryTable = renderSummaryTable(buildResults.results)
|
const summaryTable = renderSummaryTable(buildResults)
|
||||||
|
const hasFailure = anyFailed(buildResults)
|
||||||
const hasFailure = buildResults.anyFailed()
|
|
||||||
if (config.shouldGenerateJobSummary(hasFailure)) {
|
if (config.shouldGenerateJobSummary(hasFailure)) {
|
||||||
core.info('Generating Job Summary')
|
core.info('Generating Job Summary')
|
||||||
|
|
||||||
@ -133,6 +132,10 @@ function renderBuildResults(results: BuildResult[]): string {
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function anyFailed(results: BuildResult[]): boolean {
|
||||||
|
return results.some(result => result.buildFailed)
|
||||||
|
}
|
||||||
|
|
||||||
function renderBuildResultRow(result: BuildResult): string {
|
function renderBuildResultRow(result: BuildResult): string {
|
||||||
return `
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@ -3,30 +3,28 @@ import * as exec from '@actions/exec'
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as caches from './caching/caches'
|
|
||||||
import * as jobSummary from './job-summary'
|
import * as jobSummary from './job-summary'
|
||||||
import * as buildScan from './develocity/build-scan'
|
import * as buildScan from './develocity/build-scan'
|
||||||
|
|
||||||
import {loadBuildResults, markBuildResultsProcessed} from './build-results'
|
import {loadBuildResults, markBuildResultsProcessed} from './build-results'
|
||||||
import {CacheListener, generateCachingReport} from './caching/cache-reporting'
|
import {getCacheService} from './cache-service-loader'
|
||||||
import {DaemonController} from './daemon-controller'
|
import {CacheOptions} from './cache-service'
|
||||||
import {
|
import {
|
||||||
BuildScanConfig,
|
DevelocityConfig,
|
||||||
CacheConfig,
|
CacheConfig,
|
||||||
SummaryConfig,
|
SummaryConfig,
|
||||||
WrapperValidationConfig,
|
WrapperValidationConfig,
|
||||||
getWorkspaceDirectory
|
getWorkspaceDirectory
|
||||||
} from './configuration'
|
} from './configuration'
|
||||||
import * as wrapperValidator from './wrapper-validation/wrapper-validator'
|
import * as wrapperValidator from './wrapper-validation/wrapper-validator'
|
||||||
|
import {initializeGradleUserHome} from './gradle-user-home'
|
||||||
|
|
||||||
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
const GRADLE_SETUP_VAR = 'GRADLE_BUILD_ACTION_SETUP_COMPLETED'
|
||||||
const USER_HOME = 'USER_HOME'
|
|
||||||
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
const GRADLE_USER_HOME = 'GRADLE_USER_HOME'
|
||||||
const CACHE_LISTENER = 'CACHE_LISTENER'
|
|
||||||
|
|
||||||
export async function setup(
|
export async function setup(
|
||||||
cacheConfig: CacheConfig,
|
cacheConfig: CacheConfig,
|
||||||
buildScanConfig: BuildScanConfig,
|
develocityConfig: DevelocityConfig,
|
||||||
wrapperValidationConfig: WrapperValidationConfig
|
wrapperValidationConfig: WrapperValidationConfig
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const userHome = await determineUserHome()
|
const userHome = await determineUserHome()
|
||||||
@ -37,23 +35,21 @@ export async function setup(
|
|||||||
core.info('Gradle setup only performed on first gradle/actions step in workflow.')
|
core.info('Gradle setup only performed on first gradle/actions step in workflow.')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
// Record setup complete: visible to all subsequent actions and prevents duplicate setup
|
// Record setup complete: visible to subsequent actions and prevents duplicate setup
|
||||||
core.exportVariable(GRADLE_SETUP_VAR, true)
|
core.exportVariable(GRADLE_SETUP_VAR, true)
|
||||||
// Record setup complete: visible in post-action, to control action completion
|
// Record setup complete: visible in post-action, to control action completion
|
||||||
core.saveState(GRADLE_SETUP_VAR, true)
|
core.saveState(GRADLE_SETUP_VAR, true)
|
||||||
|
// Save the Gradle User Home for use in the post-action step.
|
||||||
// Save the User Home and Gradle User Home for use in the post-action step.
|
|
||||||
core.saveState(USER_HOME, userHome)
|
|
||||||
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
core.saveState(GRADLE_USER_HOME, gradleUserHome)
|
||||||
|
|
||||||
const cacheListener = new CacheListener()
|
initializeGradleUserHome(userHome, gradleUserHome, cacheConfig.getCacheEncryptionKey())
|
||||||
await caches.restore(userHome, gradleUserHome, cacheListener, cacheConfig)
|
|
||||||
|
|
||||||
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
const cacheService = await getCacheService(cacheConfig)
|
||||||
|
await cacheService.restore(gradleUserHome, cacheOptionsFrom(cacheConfig))
|
||||||
|
|
||||||
await wrapperValidator.validateWrappers(wrapperValidationConfig, getWorkspaceDirectory(), gradleUserHome)
|
await wrapperValidator.validateWrappers(wrapperValidationConfig, getWorkspaceDirectory(), gradleUserHome)
|
||||||
|
|
||||||
await buildScan.setup(buildScanConfig)
|
await buildScan.setup(develocityConfig)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -67,14 +63,9 @@ export async function complete(cacheConfig: CacheConfig, summaryConfig: SummaryC
|
|||||||
|
|
||||||
const buildResults = loadBuildResults()
|
const buildResults = loadBuildResults()
|
||||||
|
|
||||||
const userHome = core.getState(USER_HOME)
|
|
||||||
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
const gradleUserHome = core.getState(GRADLE_USER_HOME)
|
||||||
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
const cacheService = await getCacheService(cacheConfig)
|
||||||
|
const cachingReport = await cacheService.save(gradleUserHome, buildResults, cacheOptionsFrom(cacheConfig))
|
||||||
const daemonController = new DaemonController(buildResults)
|
|
||||||
await caches.save(userHome, gradleUserHome, cacheListener, daemonController, buildResults, cacheConfig)
|
|
||||||
|
|
||||||
const cachingReport = generateCachingReport(cacheListener)
|
|
||||||
await jobSummary.generateJobSummary(buildResults, cachingReport, summaryConfig)
|
await jobSummary.generateJobSummary(buildResults, cachingReport, summaryConfig)
|
||||||
|
|
||||||
markBuildResultsProcessed()
|
markBuildResultsProcessed()
|
||||||
@ -84,6 +75,20 @@ export async function complete(cacheConfig: CacheConfig, summaryConfig: SummaryC
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cacheOptionsFrom(config: CacheConfig): CacheOptions {
|
||||||
|
return {
|
||||||
|
disabled: config.isCacheDisabled(),
|
||||||
|
readOnly: config.isCacheReadOnly(),
|
||||||
|
writeOnly: config.isCacheWriteOnly(),
|
||||||
|
overwriteExisting: config.isCacheOverwriteExisting(),
|
||||||
|
strictMatch: config.isCacheStrictMatch(),
|
||||||
|
cleanup: config.getCacheCleanupOption(),
|
||||||
|
encryptionKey: config.getCacheEncryptionKey() || undefined,
|
||||||
|
includes: config.getCacheIncludes(),
|
||||||
|
excludes: config.getCacheExcludes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function determineGradleUserHome(): Promise<string> {
|
async function determineGradleUserHome(): Promise<string> {
|
||||||
const customGradleUserHome = process.env['GRADLE_USER_HOME']
|
const customGradleUserHome = process.env['GRADLE_USER_HOME']
|
||||||
if (customGradleUserHome) {
|
if (customGradleUserHome) {
|
||||||
|
|||||||
@ -1,4 +1,32 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "9.4.1",
|
||||||
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.5.0-milestone-7",
|
||||||
|
"checksum": "7ef3d73bd95c047814d76ec8324f72deefb96593eb9ce87aa06ecdcdaba7ffe8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.4.0",
|
||||||
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.4.0-rc-2",
|
||||||
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.5.0-milestone-6",
|
||||||
|
"checksum": "7ef3d73bd95c047814d76ec8324f72deefb96593eb9ce87aa06ecdcdaba7ffe8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.5.0-milestone-5",
|
||||||
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"version": "9.5.0-milestone-4",
|
||||||
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "9.5.0-milestone-3",
|
"version": "9.5.0-milestone-3",
|
||||||
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
"checksum": "55243ef57851f12b070ad14f7f5bb8302daceeebc5bce5ece5fa6edb23e1145c"
|
||||||
|
|||||||
@ -20,7 +20,7 @@ dependencies {
|
|||||||
testImplementation ('io.ratpack:ratpack-groovy-test:1.9.0') {
|
testImplementation ('io.ratpack:ratpack-groovy-test:1.9.0') {
|
||||||
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
|
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
|
||||||
}
|
}
|
||||||
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.21.1'
|
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-smile:2.21.2'
|
||||||
|
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06
|
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
2
sources/test/init-scripts/gradlew
vendored
2
sources/test/init-scripts/gradlew
vendored
@ -57,7 +57,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (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.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
import * as exec from '@actions/exec'
|
|
||||||
import * as glob from '@actions/glob'
|
|
||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
import {expect, test, jest} from '@jest/globals'
|
|
||||||
|
|
||||||
import {CacheCleaner} from '../../src/caching/cache-cleaner'
|
|
||||||
|
|
||||||
jest.setTimeout(120000)
|
|
||||||
|
|
||||||
test('will cleanup unused dependency jars and build-cache entries', async () => {
|
|
||||||
const projectRoot = prepareTestProject()
|
|
||||||
const gradleUserHome = path.resolve(projectRoot, 'HOME')
|
|
||||||
const tmpDir = path.resolve(projectRoot, 'tmp')
|
|
||||||
const cacheCleaner = new CacheCleaner(gradleUserHome, tmpDir)
|
|
||||||
|
|
||||||
await runGradleBuild(projectRoot, 'build', '3.1')
|
|
||||||
|
|
||||||
const timestamp = await cacheCleaner.prepare()
|
|
||||||
|
|
||||||
await runGradleBuild(projectRoot, 'build', '3.1.1')
|
|
||||||
|
|
||||||
const commonsMath31 = path.resolve(gradleUserHome, "caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.1")
|
|
||||||
const commonsMath311 = path.resolve(gradleUserHome, "caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.1.1")
|
|
||||||
const buildCacheDir = path.resolve(gradleUserHome, "caches/build-cache-1")
|
|
||||||
|
|
||||||
expect(fs.existsSync(commonsMath31)).toBe(true)
|
|
||||||
expect(fs.existsSync(commonsMath311)).toBe(true)
|
|
||||||
expect(fs.readdirSync(buildCacheDir).length).toBe(4) // gc.properties, build-cache-1.lock, and 2 task entries
|
|
||||||
|
|
||||||
await cacheCleaner.forceCleanupFilesOlderThan(timestamp, 'gradle')
|
|
||||||
|
|
||||||
expect(fs.existsSync(commonsMath31)).toBe(false)
|
|
||||||
expect(fs.existsSync(commonsMath311)).toBe(true)
|
|
||||||
expect(fs.readdirSync(buildCacheDir).length).toBe(3) // 1 task entry has been cleaned up
|
|
||||||
})
|
|
||||||
|
|
||||||
test('will cleanup unused gradle versions', async () => {
|
|
||||||
const projectRoot = prepareTestProject()
|
|
||||||
const gradleUserHome = path.resolve(projectRoot, 'HOME')
|
|
||||||
const tmpDir = path.resolve(projectRoot, 'tmp')
|
|
||||||
const cacheCleaner = new CacheCleaner(gradleUserHome, tmpDir)
|
|
||||||
|
|
||||||
// Initialize HOME with 2 different Gradle versions
|
|
||||||
await runGradleWrapperBuild(projectRoot, 'build')
|
|
||||||
await runGradleBuild(projectRoot, 'build')
|
|
||||||
|
|
||||||
const timestamp = await cacheCleaner.prepare()
|
|
||||||
|
|
||||||
// Run with only one of these versions
|
|
||||||
await runGradleBuild(projectRoot, 'build')
|
|
||||||
|
|
||||||
const gradle802 = path.resolve(gradleUserHome, "caches/8.0.2")
|
|
||||||
const transforms3 = path.resolve(gradleUserHome, "caches/transforms-3")
|
|
||||||
const metadata100 = path.resolve(gradleUserHome, "caches/modules-2/metadata-2.100")
|
|
||||||
const wrapper802 = path.resolve(gradleUserHome, "wrapper/dists/gradle-8.0.2-bin")
|
|
||||||
const gradleCurrent = path.resolve(gradleUserHome, "caches/8.14.2")
|
|
||||||
const metadataCurrent = path.resolve(gradleUserHome, "caches/modules-2/metadata-2.107")
|
|
||||||
|
|
||||||
expect(fs.existsSync(gradle802)).toBe(true)
|
|
||||||
expect(fs.existsSync(transforms3)).toBe(true)
|
|
||||||
expect(fs.existsSync(metadata100)).toBe(true)
|
|
||||||
expect(fs.existsSync(wrapper802)).toBe(true)
|
|
||||||
|
|
||||||
expect(fs.existsSync(gradleCurrent)).toBe(true)
|
|
||||||
expect(fs.existsSync(metadataCurrent)).toBe(true)
|
|
||||||
|
|
||||||
// The wrapper won't be removed if it was recently downloaded. Age it.
|
|
||||||
setUtimes(wrapper802, new Date(Date.now() - 48 * 60 * 60 * 1000))
|
|
||||||
|
|
||||||
await cacheCleaner.forceCleanupFilesOlderThan(timestamp, 'gradle')
|
|
||||||
|
|
||||||
expect(fs.existsSync(gradle802)).toBe(false)
|
|
||||||
expect(fs.existsSync(transforms3)).toBe(false)
|
|
||||||
expect(fs.existsSync(metadata100)).toBe(false)
|
|
||||||
expect(fs.existsSync(wrapper802)).toBe(false)
|
|
||||||
|
|
||||||
expect(fs.existsSync(gradleCurrent)).toBe(true)
|
|
||||||
expect(fs.existsSync(metadataCurrent)).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
async function runGradleBuild(projectRoot: string, args: string, version: string = '3.1'): Promise<void> {
|
|
||||||
await exec.exec(`gradle -g HOME --no-daemon --build-cache -Dcommons_math3_version="${version}" ${args}`, [], {
|
|
||||||
cwd: projectRoot
|
|
||||||
})
|
|
||||||
console.log(`Gradle User Home initialized with commons_math3_version=${version} ${args}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runGradleWrapperBuild(projectRoot: string, args: string, version: string = '3.1'): Promise<void> {
|
|
||||||
await exec.exec(`./gradlew -g HOME --no-daemon --build-cache -Dcommons_math3_version="${version}" ${args}`, [], {
|
|
||||||
cwd: projectRoot
|
|
||||||
})
|
|
||||||
console.log(`Gradle User Home initialized with commons_math3_version="${version}" ${args}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareTestProject(): string {
|
|
||||||
const projectRoot = 'test/jest/resources/cache-cleanup'
|
|
||||||
fs.rmSync(path.resolve(projectRoot, 'HOME'), { recursive: true, force: true })
|
|
||||||
fs.rmSync(path.resolve(projectRoot, 'tmp'), { recursive: true, force: true })
|
|
||||||
fs.rmSync(path.resolve(projectRoot, 'build'), { recursive: true, force: true })
|
|
||||||
fs.rmSync(path.resolve(projectRoot, '.gradle'), { recursive: true, force: true })
|
|
||||||
return projectRoot
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setUtimes(pattern: string, timestamp: Date): Promise<void> {
|
|
||||||
const globber = await glob.create(pattern)
|
|
||||||
for await (const file of globber.globGenerator()) {
|
|
||||||
fs.utimesSync(file, timestamp, timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import {CacheEntryListener, CacheListener} from '../../src/caching/cache-reporting'
|
|
||||||
|
|
||||||
describe('caching report', () => {
|
|
||||||
describe('reports not fully restored', () => {
|
|
||||||
it('with one requested entry report', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
report.entry('foo').markRequested('1', ['2'])
|
|
||||||
report.entry('bar').markRequested('3').markRestored('4', 500, 1000)
|
|
||||||
expect(report.fullyRestored).toBe(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('reports fully restored', () => {
|
|
||||||
it('when empty', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
expect(report.fullyRestored).toBe(true)
|
|
||||||
})
|
|
||||||
it('with empty entry reports', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
report.entry('foo')
|
|
||||||
report.entry('bar')
|
|
||||||
expect(report.fullyRestored).toBe(true)
|
|
||||||
})
|
|
||||||
it('with restored entry report', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
report.entry('bar').markRequested('3').markRestored('4', 300, 1000)
|
|
||||||
expect(report.fullyRestored).toBe(true)
|
|
||||||
})
|
|
||||||
it('with multiple restored entry reportss', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
report.entry('foo').markRestored('4', 3300, 111)
|
|
||||||
report.entry('bar').markRequested('3').markRestored('4', 333, 1000)
|
|
||||||
expect(report.fullyRestored).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('can be stringified and rehydrated', () => {
|
|
||||||
it('when empty', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
|
|
||||||
const stringRep = report.stringify()
|
|
||||||
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
|
|
||||||
|
|
||||||
expect(reportClone.cacheEntries).toEqual([])
|
|
||||||
|
|
||||||
// Can call methods on rehydrated
|
|
||||||
expect(reportClone.entry('foo')).toBeInstanceOf(CacheEntryListener)
|
|
||||||
})
|
|
||||||
it('with entry reports', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
report.entry('foo')
|
|
||||||
report.entry('bar')
|
|
||||||
report.entry('baz')
|
|
||||||
|
|
||||||
const stringRep = report.stringify()
|
|
||||||
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
|
|
||||||
|
|
||||||
expect(reportClone.cacheEntries.length).toBe(3)
|
|
||||||
expect(reportClone.cacheEntries[0].entryName).toBe('foo')
|
|
||||||
expect(reportClone.cacheEntries[1].entryName).toBe('bar')
|
|
||||||
expect(reportClone.cacheEntries[2].entryName).toBe('baz')
|
|
||||||
|
|
||||||
expect(reportClone.entry('foo')).toBe(reportClone.cacheEntries[0])
|
|
||||||
})
|
|
||||||
it('with rehydrated entry report', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
const entryReport = report.entry('foo')
|
|
||||||
entryReport.markRequested('1', ['2', '3'])
|
|
||||||
entryReport.markSaved('4', 100, 1000)
|
|
||||||
|
|
||||||
const stringRep = report.stringify()
|
|
||||||
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
|
|
||||||
const entryClone = reportClone.entry('foo')
|
|
||||||
|
|
||||||
expect(entryClone.requestedKey).toBe('1')
|
|
||||||
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
|
|
||||||
expect(entryClone.savedKey).toBe('4')
|
|
||||||
expect(entryClone.savedSize).toBe(100)
|
|
||||||
expect(entryClone.savedTime).toBe(1000)
|
|
||||||
})
|
|
||||||
it('with live entry report', async () => {
|
|
||||||
const report = new CacheListener()
|
|
||||||
const entryReport = report.entry('foo')
|
|
||||||
entryReport.markRequested('1', ['2', '3'])
|
|
||||||
|
|
||||||
const stringRep = report.stringify()
|
|
||||||
const reportClone: CacheListener = CacheListener.rehydrate(stringRep)
|
|
||||||
const entryClone = reportClone.entry('foo')
|
|
||||||
|
|
||||||
// Check type and call method on rehydrated entry report
|
|
||||||
expect(entryClone).toBeInstanceOf(CacheEntryListener)
|
|
||||||
entryClone.markSaved('4', 100, 1000)
|
|
||||||
|
|
||||||
expect(entryClone.requestedKey).toBe('1')
|
|
||||||
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
|
|
||||||
expect(entryClone.savedKey).toBe('4')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import {describe, expect, it} from '@jest/globals'
|
|
||||||
|
|
||||||
import * as cacheUtils from '../../src/caching/cache-utils'
|
|
||||||
|
|
||||||
describe('cacheUtils-utils', () => {
|
|
||||||
describe('can hash', () => {
|
|
||||||
it('a string', async () => {
|
|
||||||
const hash = cacheUtils.hashStrings(['foo'])
|
|
||||||
expect(hash).toBe('acbd18db4cc2f85cedef654fccc4a4d8')
|
|
||||||
})
|
|
||||||
it('multiple strings', async () => {
|
|
||||||
const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
|
|
||||||
expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
|
|
||||||
})
|
|
||||||
it('normalized filenames', async () => {
|
|
||||||
const fileNames = ['/foo/bar/baz.zip', '../boo.html']
|
|
||||||
const posixHash = cacheUtils.hashFileNames(fileNames)
|
|
||||||
const windowsHash = cacheUtils.hashFileNames(fileNames)
|
|
||||||
expect(posixHash).toBe(windowsHash)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
39
sources/test/jest/force-exit.test.ts
Normal file
39
sources/test/jest/force-exit.test.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import {afterEach, describe, expect, it, jest} from '@jest/globals'
|
||||||
|
|
||||||
|
import {forceExit, getForcedExitDelayMs} from '../../src/force-exit'
|
||||||
|
|
||||||
|
describe('forceExit', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
jest.restoreAllMocks()
|
||||||
|
jest.useRealTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('adds a short delay on Windows before exiting', async () => {
|
||||||
|
jest.useFakeTimers()
|
||||||
|
|
||||||
|
const exitSpy = jest.spyOn(process, 'exit').mockImplementation((() => undefined) as never)
|
||||||
|
|
||||||
|
const exitPromise = forceExit('win32')
|
||||||
|
await jest.advanceTimersByTimeAsync(49)
|
||||||
|
|
||||||
|
expect(exitSpy).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
await jest.advanceTimersByTimeAsync(1)
|
||||||
|
await expect(exitPromise).resolves.toBeUndefined()
|
||||||
|
|
||||||
|
expect(exitSpy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exits immediately on non-Windows platforms', async () => {
|
||||||
|
const exitSpy = jest.spyOn(process, 'exit').mockImplementation((() => undefined) as never)
|
||||||
|
|
||||||
|
await expect(forceExit('linux')).resolves.toBeUndefined()
|
||||||
|
expect(exitSpy).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('only delays on Windows', () => {
|
||||||
|
expect(getForcedExitDelayMs('win32')).toBe(50)
|
||||||
|
expect(getForcedExitDelayMs('linux')).toBe(0)
|
||||||
|
expect(getForcedExitDelayMs('darwin')).toBe(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -2,8 +2,7 @@ import * as path from 'path'
|
|||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import {describe, expect, it} from '@jest/globals'
|
import {describe, expect, it} from '@jest/globals'
|
||||||
|
|
||||||
import {GradleUserHomeCache} from "../../src/caching/gradle-user-home-cache"
|
import {configureInfoLogLevel} from '../../src/gradle-user-home'
|
||||||
import {CacheConfig} from "../../src/configuration"
|
|
||||||
|
|
||||||
const testTmp = 'test/jest/tmp'
|
const testTmp = 'test/jest/tmp'
|
||||||
fs.rmSync(testTmp, {recursive: true, force: true})
|
fs.rmSync(testTmp, {recursive: true, force: true})
|
||||||
@ -14,8 +13,7 @@ describe("--info and --stacktrace", () => {
|
|||||||
const emptyGradleHome = `${testTmp}/empty-gradle-home`
|
const emptyGradleHome = `${testTmp}/empty-gradle-home`
|
||||||
fs.mkdirSync(emptyGradleHome, {recursive: true})
|
fs.mkdirSync(emptyGradleHome, {recursive: true})
|
||||||
|
|
||||||
const stateCache = new GradleUserHomeCache("ignored", emptyGradleHome, new CacheConfig())
|
configureInfoLogLevel(emptyGradleHome)
|
||||||
stateCache.configureInfoLogLevel()
|
|
||||||
|
|
||||||
expect(fs.readFileSync(path.resolve(emptyGradleHome, "gradle.properties"), 'utf-8'))
|
expect(fs.readFileSync(path.resolve(emptyGradleHome, "gradle.properties"), 'utf-8'))
|
||||||
.toBe("org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n")
|
.toBe("org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n")
|
||||||
@ -27,8 +25,7 @@ describe("--info and --stacktrace", () => {
|
|||||||
fs.mkdirSync(existingGradleHome, {recursive: true})
|
fs.mkdirSync(existingGradleHome, {recursive: true})
|
||||||
fs.writeFileSync(path.resolve(existingGradleHome, "gradle.properties"), "org.gradle.logging.level=debug\n")
|
fs.writeFileSync(path.resolve(existingGradleHome, "gradle.properties"), "org.gradle.logging.level=debug\n")
|
||||||
|
|
||||||
const stateCache = new GradleUserHomeCache("ignored", existingGradleHome, new CacheConfig())
|
configureInfoLogLevel(existingGradleHome)
|
||||||
stateCache.configureInfoLogLevel()
|
|
||||||
|
|
||||||
expect(fs.readFileSync(path.resolve(existingGradleHome, "gradle.properties"), 'utf-8'))
|
expect(fs.readFileSync(path.resolve(existingGradleHome, "gradle.properties"), 'utf-8'))
|
||||||
.toBe("org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n\norg.gradle.logging.level=debug\n")
|
.toBe("org.gradle.logging.level=info\norg.gradle.logging.stacktrace=all\n\norg.gradle.logging.level=debug\n")
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import {afterAll, describe, expect, it, jest} from '@jest/globals'
|
import {afterAll, describe, expect, it, jest} from '@jest/globals'
|
||||||
|
|
||||||
import {getPredefinedToolchains, mergeToolchainContent} from "../../src/caching/gradle-user-home-utils";
|
import {getPredefinedToolchains, mergeToolchainContent} from '../../src/gradle-user-home'
|
||||||
|
|
||||||
describe('predefined-toolchains', () => {
|
describe('predefined-toolchains', () => {
|
||||||
const OLD_ENV = process.env
|
const OLD_ENV = process.env
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user