mirror of
https://github.com/gradle/gradle-build-action.git
synced 2025-10-20 07:08:56 +08:00
Compare commits
70 Commits
v2.0-beta.
...
v2.0-rc.3
Author | SHA1 | Date | |
---|---|---|---|
|
4e899835b3 | ||
|
3812292b26 | ||
|
c12283ec57 | ||
|
f2dc0d8256 | ||
|
717db318c1 | ||
|
2a57ddf74a | ||
|
230fd6b47f | ||
|
472ac8a356 | ||
|
3ba05ede1f | ||
|
d785346c8c | ||
|
6ca4d4ade2 | ||
|
75cec40e58 | ||
|
c317ccac62 | ||
|
a74bb0fad6 | ||
|
6ff2065a12 | ||
|
727b4612ba | ||
|
613f4ec588 | ||
|
db6202adcd | ||
|
f0f68e07c3 | ||
|
8ba5a0033b | ||
|
9edc2a11bd | ||
|
079e4844d6 | ||
|
4ebd000afd | ||
|
063fc6a872 | ||
|
e3ada7e5c2 | ||
|
d61e5be06a | ||
|
db2b34260f | ||
|
c031dc946b | ||
|
0eb881f067 | ||
|
27f2dc276c | ||
|
cba1833dde | ||
|
39db90e99b | ||
|
947a893558 | ||
|
b99e9f0bc3 | ||
|
4cf255df10 | ||
|
614d8770a4 | ||
|
69453dbfc5 | ||
|
1113cb87cb | ||
|
9c95294209 | ||
|
f901ec9c20 | ||
|
a94b9252d5 | ||
|
25672bf196 | ||
|
cb6a0acca4 | ||
|
aa2ed2e033 | ||
|
263f84178a | ||
|
0eb5996567 | ||
|
fe55bf4667 | ||
|
709ded51a5 | ||
|
8b1f1a3817 | ||
|
7abf13ee48 | ||
|
da64595ccc | ||
|
29b14c7fca | ||
|
d1ab42cddf | ||
|
422726cec5 | ||
|
4bc52c85c3 | ||
|
e7b5fd0b28 | ||
|
53ccc3e0d7 | ||
|
8ab7c9d8dd | ||
|
0cf00ed767 | ||
|
aedc5fc8f9 | ||
|
78e25cd233 | ||
|
29894757f3 | ||
|
5328161026 | ||
|
4968d2280b | ||
|
c000a0b58f | ||
|
6ff498182a | ||
|
60b1ffac6b | ||
|
9b7c81f8f6 | ||
|
b650771559 | ||
|
17f624cb5b |
@@ -11,7 +11,7 @@
|
|||||||
"eslint-comments/no-use": "off",
|
"eslint-comments/no-use": "off",
|
||||||
"import/no-namespace": "off",
|
"import/no-namespace": "off",
|
||||||
"no-unused-vars": "off",
|
"no-unused-vars": "off",
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
||||||
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
||||||
"@typescript-eslint/no-require-imports": "error",
|
"@typescript-eslint/no-require-imports": "error",
|
||||||
"@typescript-eslint/array-type": "error",
|
"@typescript-eslint/array-type": "error",
|
||||||
|
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
70
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ main ]
|
||||||
|
schedule:
|
||||||
|
- cron: '25 23 * * 2'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'javascript' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v1
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v1
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v1
|
2
.github/workflows/failure-cases.yml
vendored
2
.github/workflows/failure-cases.yml
vendored
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
|
30
.github/workflows/integTest-action-inputs.yml
vendored
Normal file
30
.github/workflows/integTest-action-inputs.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
name: Test different action inputs
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
action-inputs:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Invoke with multi-line arguments
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: |
|
||||||
|
--configuration-cache
|
||||||
|
--build-cache
|
||||||
|
-DsystemProperty=FOO
|
||||||
|
-PgradleProperty=BAR
|
||||||
|
test
|
||||||
|
jar
|
75
.github/workflows/integTest-caching-config.yml
vendored
Normal file
75
.github/workflows/integTest-caching-config.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
name: Test caching configuration
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Run initial Gradle builds to push initial cache entries
|
||||||
|
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
|
||||||
|
seed-build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build using Gradle wrapper
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test
|
||||||
|
# Add "wrapper" to main cache entry and remove 'wrapper-zips' bundle
|
||||||
|
# Exclude build-cache from main cache entry
|
||||||
|
gradle-home-cache-includes: |
|
||||||
|
caches
|
||||||
|
notifications
|
||||||
|
wrapper
|
||||||
|
gradle-home-cache-excludes: |
|
||||||
|
caches/build-cache-1
|
||||||
|
gradle-home-cache-artifact-bundles: |
|
||||||
|
[
|
||||||
|
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
||||||
|
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
||||||
|
["instrumented-jars", "caches/jars-*/*/"],
|
||||||
|
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
||||||
|
]
|
||||||
|
|
||||||
|
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
||||||
|
verify-build:
|
||||||
|
needs: seed-build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Execute Gradle build with --offline
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test --offline
|
||||||
|
cache-read-only: true
|
||||||
|
# Need the same configuration when restoring state from cache
|
||||||
|
gradle-home-cache-includes: |
|
||||||
|
caches
|
||||||
|
notifications
|
||||||
|
wrapper
|
||||||
|
gradle-home-cache-excludes: |
|
||||||
|
caches/build-cache-1
|
||||||
|
gradle-home-cache-artifact-bundles: |
|
||||||
|
[
|
||||||
|
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
||||||
|
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
||||||
|
["instrumented-jars", "caches/jars-*/*/"],
|
||||||
|
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
65
.github/workflows/integTest-caching-configuration-cache.yml
vendored
Normal file
65
.github/workflows/integTest-caching-configuration-cache.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Test save/restore configuration-cache state
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Run initial Gradle builds to push initial cache entries
|
||||||
|
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
|
||||||
|
seed-build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Build with configuration-cache enabled
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test --configuration-cache
|
||||||
|
|
||||||
|
# Test that the project-dot-gradle cache will cache and restore configuration-cache
|
||||||
|
configuration-cache:
|
||||||
|
needs: seed-build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Execute Gradle build and verify cached configuration
|
||||||
|
uses: ./
|
||||||
|
env:
|
||||||
|
VERIFY_CACHED_CONFIGURATION: true
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test --configuration-cache
|
||||||
|
cache-read-only: true
|
||||||
|
|
||||||
|
# Check that the build can run when no bundles are restored
|
||||||
|
no-bundles-restored:
|
||||||
|
needs: seed-build
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Execute Gradle build with no cache artifact bundles restored
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test --configuration-cache
|
||||||
|
cache-read-only: true
|
||||||
|
gradle-home-cache-artifact-bundles: '[]'
|
||||||
|
|
@@ -1,4 +1,4 @@
|
|||||||
name: Test caching
|
name: Test save/restore Gradle Home directory
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Run initial Gradle builds to push initial cache entries
|
# Run initial Gradle builds to push initial cache entries
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
seed-build:
|
seed-build:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
@@ -22,20 +22,15 @@ jobs:
|
|||||||
- name: Build using Gradle wrapper
|
- name: Build using Gradle wrapper
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test
|
arguments: test
|
||||||
- name: Build with configuration-cache enabled
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
build-root-directory: __tests__/samples/basic
|
|
||||||
arguments: test --configuration-cache
|
|
||||||
|
|
||||||
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
||||||
dependencies-cache:
|
dependencies-cache:
|
||||||
needs: seed-build
|
needs: seed-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
@@ -43,7 +38,7 @@ jobs:
|
|||||||
- name: Execute Gradle build with --offline
|
- name: Execute Gradle build with --offline
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test --offline
|
arguments: test --offline
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
|
||||||
@@ -52,7 +47,7 @@ jobs:
|
|||||||
needs: seed-build
|
needs: seed-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
@@ -60,25 +55,25 @@ jobs:
|
|||||||
- name: Execute Gradle build and verify tasks from cache
|
- name: Execute Gradle build and verify tasks from cache
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test -DverifyCachedBuild=true
|
arguments: test -DverifyCachedBuild=true
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
|
||||||
# Test that the project-dot-gradle cache will cache and restore configuration-cache
|
# Check that the build can run when no bundles are restored
|
||||||
configuration-cache:
|
no-bundles-restored:
|
||||||
needs: seed-build
|
needs: seed-build
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Execute Gradle build and verify cached configuration
|
- name: Execute Gradle build with no cache artifact bundles restored
|
||||||
uses: ./
|
uses: ./
|
||||||
env:
|
|
||||||
VERIFY_CACHED_CONFIGURATION: true
|
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test --configuration-cache
|
arguments: test
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
gradle-home-cache-artifact-bundles: '[]'
|
||||||
|
|
75
.github/workflows/integTest-execution.yml
vendored
75
.github/workflows/integTest-execution.yml
vendored
@@ -6,7 +6,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Tests for executing with different Gradle versions.
|
# Tests for executing with different Gradle versions.
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
gradle-execution:
|
gradle-execution:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
script-suffix: '.bat'
|
script-suffix: '.bat'
|
||||||
@@ -33,10 +33,75 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
gradle-version: release-candidate
|
gradle-version: release-candidate
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
arguments: help -DgradleVersionCheck=7.2
|
arguments: help
|
||||||
- name: Test use defined Gradle executable
|
- name: Test use defined Gradle executable
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
gradle-executable: __tests__/samples/basic/gradlew${{ matrix.script-suffix }}
|
gradle-executable: __tests__/samples/groovy-dsl/gradlew${{ matrix.script-suffix }}
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
arguments: help -DgradleVersionCheck=7.1.1
|
arguments: help -DgradleVersionCheck=7.1.1
|
||||||
|
|
||||||
|
gradle-versions:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
include:
|
||||||
|
- os: windows-latest
|
||||||
|
script-suffix: '.bat'
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Test Gradle 7
|
||||||
|
uses: ./
|
||||||
|
id: gradle7
|
||||||
|
with:
|
||||||
|
gradle-version: 7.2
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
|
arguments: help -DgradleVersionCheck=7.2
|
||||||
|
- name: Check Gradle 7 scan
|
||||||
|
if: ${{ !steps.gradle7.outputs.build-scan-url }}
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('No build scan detected')
|
||||||
|
- name: Test Gradle 6
|
||||||
|
uses: ./
|
||||||
|
id: gradle6
|
||||||
|
with:
|
||||||
|
gradle-version: 6.9
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
|
arguments: help -DgradleVersionCheck=6.9
|
||||||
|
- name: Check Gradle 6 scan
|
||||||
|
if: ${{ !steps.gradle6.outputs.build-scan-url }}
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('No build scan detected')
|
||||||
|
- name: Test Gradle 5
|
||||||
|
uses: ./
|
||||||
|
id: gradle5
|
||||||
|
with:
|
||||||
|
gradle-version: 5.6.4
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper-gradle-5
|
||||||
|
arguments: help -DgradleVersionCheck=5.6.4
|
||||||
|
- name: Check Gradle 5 scan
|
||||||
|
if: ${{ !steps.gradle5.outputs.build-scan-url }}
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('No build scan detected')
|
||||||
|
- name: Test Gradle 4
|
||||||
|
uses: ./
|
||||||
|
id: gradle4
|
||||||
|
with:
|
||||||
|
gradle-version: 4.10.3
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper-gradle-4
|
||||||
|
arguments: help -DgradleVersionCheck=4.10.3
|
||||||
|
- name: Check Gradle 4 scan
|
||||||
|
if: ${{ !steps.gradle4.outputs.build-scan-url }}
|
||||||
|
uses: actions/github-script@v3
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
core.setFailed('No build scan detected')
|
||||||
|
|
||||||
|
23
.github/workflows/integTest-gradle-user-home.yml
vendored
23
.github/workflows/integTest-gradle-user-home.yml
vendored
@@ -6,47 +6,56 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
GRADLE_USER_HOME: custom/gradle/home
|
GRADLE_USER_HOME: custom/gradle/home
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Run initial Gradle builds to push initial cache entries
|
# Run initial Gradle builds to push initial cache entries
|
||||||
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
|
# These builds should start fresh without cache hits, due to the seed injected into the cache key above.
|
||||||
seed-build:
|
seed-build:
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Build using Gradle wrapper
|
- name: Build using Gradle wrapper
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test
|
arguments: test
|
||||||
|
|
||||||
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
||||||
dependencies-cache:
|
dependencies-cache:
|
||||||
needs: seed-build
|
needs: seed-build
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Execute Gradle build with --offline
|
- name: Execute Gradle build with --offline
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test --offline
|
arguments: test --offline
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
|
||||||
# Test that the gradle-user-home cache will cache and restore local build-cache
|
# Test that the gradle-user-home cache will cache and restore local build-cache
|
||||||
build-cache:
|
build-cache:
|
||||||
needs: seed-build
|
needs: seed-build
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Execute Gradle build and verify tasks from cache
|
- name: Execute Gradle build and verify tasks from cache
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test -DverifyCachedBuild=true
|
arguments: test -DverifyCachedBuild=true
|
||||||
cache-read-only: true
|
cache-read-only: true
|
||||||
|
14
.github/workflows/integTest-kotlin-dsl.yml
vendored
14
.github/workflows/integTest-kotlin-dsl.yml
vendored
@@ -6,13 +6,16 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
CACHE_DEBUG_ENABLED: true
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# Use kotlin-dsl project to verify caching of generated jars and compiled scripts
|
# Use kotlin-dsl project to verify caching of generated jars and compiled scripts
|
||||||
seed-build:
|
seed-build:
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@@ -25,7 +28,10 @@ jobs:
|
|||||||
# Check that the build can run --offline
|
# Check that the build can run --offline
|
||||||
verify-build:
|
verify-build:
|
||||||
needs: seed-build
|
needs: seed-build
|
||||||
runs-on: ubuntu-latest
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"printWidth": 80,
|
"printWidth": 120,
|
||||||
"tabWidth": 4,
|
"tabWidth": 4,
|
||||||
"useTabs": false,
|
"useTabs": false,
|
||||||
"semi": false,
|
"semi": false,
|
||||||
|
93
README.md
93
README.md
@@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
This GitHub Action can be used to execute a Gradle build on any platform supported by GitHub Actions.
|
This GitHub Action can be used to execute a Gradle build on any platform supported by GitHub Actions.
|
||||||
|
|
||||||
**Note:** The following documentation is for `gradle/gradle-build-action@v2`, currently in Beta release.
|
|
||||||
You can view the documentation for the latest stable release (v1.5.1) [on the GitHub Marketplace](https://github.com/marketplace/actions/gradle-build-action?version=v1.5.1).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The following workflow will run `./gradlew build` on ubuntu, macos and windows.
|
The following workflow will run `./gradlew build` on ubuntu, macos and windows.
|
||||||
@@ -42,11 +39,25 @@ Each invocation will start its run with the filesystem state remaining from the
|
|||||||
arguments: check
|
arguments: check
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Why is this better than running Gradle directly?
|
||||||
|
|
||||||
|
It is possible to directly invoke Gradle in your workflow, and the `setup-java` action provides a simple way to cache Gradle dependencies.
|
||||||
|
|
||||||
|
However, the `gradle-build-action` offers a number of advantages over this approach:
|
||||||
|
|
||||||
|
- Easily [run the build with different versions of Gradle](#download-install-and-use-a-specific-gradle-version) using the `gradle-version` parameter. Gradle distributions are automatically downloaded and cached.
|
||||||
|
- More sophisticated and more efficient caching of Gradle User Home between invocations, compared to `setup-java` and most custom configurations using `actions/cache`. [More details below](#caching).
|
||||||
|
- Detailed reporting of cache usage and cache configuration options allow you to [optimize the use of the GitHub actions cache](#optimizing-cache-effectiveness).
|
||||||
|
- [Automatic capture of build scan links](#build-scans) from the build, making these easier to locate for workflow run.
|
||||||
|
|
||||||
|
The `gradle-build-action` is designed to provide these benefits with minimal configuration.
|
||||||
|
|
||||||
## Gradle Execution
|
## Gradle Execution
|
||||||
|
|
||||||
### Command-line arguments
|
### Command-line arguments
|
||||||
|
|
||||||
The `arguments` input can used to pass arbitrary arguments to the `gradle` command line.
|
The `arguments` input can used to pass arbitrary arguments to the `gradle` command line.
|
||||||
|
Arguments can be supplied in a single line, or as a multi-line input.
|
||||||
|
|
||||||
Here are some valid examples:
|
Here are some valid examples:
|
||||||
```yaml
|
```yaml
|
||||||
@@ -54,8 +65,11 @@ arguments: build
|
|||||||
arguments: check --scan
|
arguments: check --scan
|
||||||
arguments: some arbitrary tasks
|
arguments: some arbitrary tasks
|
||||||
arguments: build -PgradleProperty=foo
|
arguments: build -PgradleProperty=foo
|
||||||
arguments: build -DsystemProperty=bar
|
arguments: |
|
||||||
....
|
build
|
||||||
|
--scan
|
||||||
|
-PgradleProperty=foo
|
||||||
|
-DsystemProperty=bar
|
||||||
```
|
```
|
||||||
|
|
||||||
See `gradle --help` for more information.
|
See `gradle --help` for more information.
|
||||||
@@ -155,12 +169,9 @@ Caching is enabled by default. You can disable caching for the action as follows
|
|||||||
cache-disabled: true
|
cache-disabled: true
|
||||||
```
|
```
|
||||||
|
|
||||||
At this time it is not possible to fine-tune the caching performed by this action.
|
|
||||||
If you have a legitimate use case for fine-grained caching or restricting which files are cached, please raise an issue.
|
|
||||||
|
|
||||||
### Cache keys
|
### Cache keys
|
||||||
|
|
||||||
For cached distributions, the cache key is unique to the downloaded distribution. This will not change over time.
|
For distributions downloaded to satisfy a `gradle-version` parametere are stored outside of Gradle User Home and cached separately. The cache key is unique to the downloaded distribution and will not change over time.
|
||||||
|
|
||||||
The state of the Gradle User Home and configuration-cache are highly dependent on the Gradle execution, so the cache key is composed of the current commit hash and the GitHub actions job id.
|
The state of the Gradle User Home and configuration-cache are highly dependent on the Gradle execution, so the cache key is composed of the current commit hash and the GitHub actions job id.
|
||||||
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
||||||
@@ -172,16 +183,76 @@ For example, this means that all jobs executing a particular version of the Grad
|
|||||||
|
|
||||||
### Using the caches read-only
|
### Using the caches read-only
|
||||||
|
|
||||||
Cache storage space is limited for GitHub actions, and writing new cache entries can trigger the deletion of exising entries.
|
|
||||||
In some circumstances, it makes sense for a Gradle invocation to read any existing cache entries but not to write changes back.
|
In some circumstances, it makes sense for a Gradle invocation to read any existing cache entries but not to write changes back.
|
||||||
For example, you may want to write cache entries for builds on your `main` branch, but not for any PR build invocations.
|
For example, you may want to write cache entries for builds on your `main` branch, but not for any PR build invocations.
|
||||||
|
|
||||||
You can enable read-only caching for any of the caches as follows:
|
You can enable read-only caching for any of the caches as follows:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
cache-read-only: true
|
# Only write to the cache for builds on the 'main' branch.
|
||||||
|
# Builds on other branches will only read existing entries from the cache.
|
||||||
|
cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Gradle User Home cache tuning
|
||||||
|
|
||||||
|
As well as any wrapper distributions, the action will attempt to save and restore the `caches` and `notifications` directories from Gradle User Home.
|
||||||
|
|
||||||
|
The contents to be cached can be fine tuned by including and excluding certain paths with Gradle User Home.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Cache downloaded JDKs in addition to the default directories.
|
||||||
|
gradle-home-cache-includes: |
|
||||||
|
caches
|
||||||
|
notifications
|
||||||
|
jdks
|
||||||
|
# Exclude the local build-cache from the directories cached.
|
||||||
|
gradle-home-cache-excludes: |
|
||||||
|
caches/build-cache-1
|
||||||
|
```
|
||||||
|
|
||||||
|
You can specify any number of fixed paths or patterns to include or exclude.
|
||||||
|
File pattern support is documented at https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions#patterns-to-match-file-paths.
|
||||||
|
|
||||||
|
### Cache debugging and analysis
|
||||||
|
|
||||||
|
Gradle User Home state will be restored from the cache during the first `gradle-build-action` step for any workflow job.
|
||||||
|
This state will be saved back to the cache at the end of the job, after all Gradle executions have completed.
|
||||||
|
A report of all cache entries restored and saved is printed to the action log when saving the cache entries.
|
||||||
|
This report can provide valuable insignt into how much cache space is being used.
|
||||||
|
|
||||||
|
It is possible to enable additional debug logging for cache operations. You do via the `GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED` environment variable:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this setting will also prevent certain cache operations from running in parallel, further assisting with debugging.
|
||||||
|
|
||||||
|
### Optimizing cache effectiveness
|
||||||
|
|
||||||
|
Cache storage space for GitHub actions is limited, and writing new cache entries can trigger the deletion of exising entries.
|
||||||
|
Eviction of shared cache entries can reduce cache effectiveness, slowing down your `gradle-build-action` steps.
|
||||||
|
|
||||||
|
There are a number of actions you can take if your cache use is less effective due to entry eviction.
|
||||||
|
|
||||||
|
#### Only write to the cache from the default branch
|
||||||
|
|
||||||
|
GitHub cache entries are not shared between builds on different branches. This means that identical cache entries will be stored separately for different branches.
|
||||||
|
The exception to the is cache entries for the default (`master`/`main`) branch can be read by actions invoked for other branches.
|
||||||
|
|
||||||
|
An easy way to reduce cache usage when you run builds on many different branches is to only permit your default branch to write to the cache,
|
||||||
|
with all other branch builds using `cache-read-only`. See [Using the caches read-only](#using-the-caches-read-only) for more details.
|
||||||
|
|
||||||
|
Similarly, you could use `cache-read-only` for certain jobs in the workflow, and instead have these jobs reuse the cache content from upstream jobs.
|
||||||
|
|
||||||
|
#### Exclude content from Gradle User Home cache
|
||||||
|
|
||||||
|
Each build is different, and some builds produce more Gradle User Home content than others.
|
||||||
|
[Cache debugging ](#cache-debugging-and-analysis) can provide insight into which cache entries are the largest,
|
||||||
|
and you can selectively [exclude content using `gradle-home-cache-exclude`](#gradle-user-home-cache-tuning).
|
||||||
|
|
||||||
## Build scans
|
## Build scans
|
||||||
|
|
||||||
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will:
|
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will:
|
||||||
|
95
__tests__/cache-base.test.ts
Normal file
95
__tests__/cache-base.test.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import {CacheEntryListener, CacheListener} from '../src/cache-base'
|
||||||
|
|
||||||
|
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)
|
||||||
|
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)
|
||||||
|
expect(report.fullyRestored).toBe(true)
|
||||||
|
})
|
||||||
|
it('with multiple restored entry reportss', async () => {
|
||||||
|
const report = new CacheListener()
|
||||||
|
report.entry('foo').markRestored('4', 3300)
|
||||||
|
report.entry('bar').markRequested('3').markRestored('4', 333)
|
||||||
|
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)
|
||||||
|
|
||||||
|
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')
|
||||||
|
})
|
||||||
|
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)
|
||||||
|
|
||||||
|
expect(entryClone.requestedKey).toBe('1')
|
||||||
|
expect(entryClone.requestedRestoreKeys).toEqual(['2', '3'])
|
||||||
|
expect(entryClone.savedKey).toBe('4')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@@ -1,5 +1,4 @@
|
|||||||
import * as cacheUtils from '../src/cache-utils'
|
import * as cacheUtils from '../src/cache-utils'
|
||||||
import * as path from 'path'
|
|
||||||
|
|
||||||
describe('cacheUtils-utils', () => {
|
describe('cacheUtils-utils', () => {
|
||||||
describe('can hash', () => {
|
describe('can hash', () => {
|
||||||
|
6
__tests__/samples/basic/.gitattributes
vendored
6
__tests__/samples/basic/.gitattributes
vendored
@@ -1,6 +0,0 @@
|
|||||||
#
|
|
||||||
# https://help.github.com/articles/dealing-with-line-endings/
|
|
||||||
#
|
|
||||||
# These are explicitly windows files and should use crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
|
|
6
__tests__/samples/kotlin-dsl/.gitattributes
vendored
6
__tests__/samples/kotlin-dsl/.gitattributes
vendored
@@ -1,6 +0,0 @@
|
|||||||
#
|
|
||||||
# https://help.github.com/articles/dealing-with-line-endings/
|
|
||||||
#
|
|
||||||
# These are explicitly windows files and should use crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
|
|
5
__tests__/samples/kotlin-dsl/.gitignore
vendored
5
__tests__/samples/kotlin-dsl/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
# Ignore Gradle project-specific cache directory
|
|
||||||
.gradle
|
|
||||||
|
|
||||||
# Ignore Gradle build output directory
|
|
||||||
build
|
|
10
__tests__/samples/no-wrapper-gradle-4/build.gradle
Normal file
10
__tests__/samples/no-wrapper-gradle-4/build.gradle
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
plugins {
|
||||||
|
id "com.gradle.build-scan" version "1.16"
|
||||||
|
}
|
||||||
|
|
||||||
|
buildScan {
|
||||||
|
termsOfServiceUrl = "https://gradle.com/terms-of-service"
|
||||||
|
termsOfServiceAgree = "yes"
|
||||||
|
publishAlways()
|
||||||
|
}
|
||||||
|
|
8
__tests__/samples/no-wrapper-gradle-4/settings.gradle
Normal file
8
__tests__/samples/no-wrapper-gradle-4/settings.gradle
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
rootProject.name = 'no-wrapper'
|
||||||
|
|
||||||
|
println "Using Gradle version: ${gradle.gradleVersion}"
|
||||||
|
|
||||||
|
def gradleVersionCheck = System.properties.gradleVersionCheck
|
||||||
|
if (gradleVersionCheck && gradle.gradleVersion != gradleVersionCheck) {
|
||||||
|
throw new RuntimeException("Got the wrong version: expected ${gradleVersionCheck} but was ${gradle.gradleVersion}")
|
||||||
|
}
|
12
__tests__/samples/no-wrapper-gradle-5/build.gradle
Normal file
12
__tests__/samples/no-wrapper-gradle-5/build.gradle
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.gradle.build-scan") version("3.7")
|
||||||
|
}
|
||||||
|
|
||||||
|
gradleEnterprise {
|
||||||
|
buildScan {
|
||||||
|
termsOfServiceUrl = "https://gradle.com/terms-of-service"
|
||||||
|
termsOfServiceAgree = "yes"
|
||||||
|
publishAlways()
|
||||||
|
uploadInBackground = false
|
||||||
|
}
|
||||||
|
}
|
8
__tests__/samples/no-wrapper-gradle-5/settings.gradle
Normal file
8
__tests__/samples/no-wrapper-gradle-5/settings.gradle
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
rootProject.name = 'no-wrapper'
|
||||||
|
|
||||||
|
println "Using Gradle version: ${gradle.gradleVersion}"
|
||||||
|
|
||||||
|
def gradleVersionCheck = System.properties.gradleVersionCheck
|
||||||
|
if (gradleVersionCheck && gradle.gradleVersion != gradleVersionCheck) {
|
||||||
|
throw new RuntimeException("Got the wrong version: expected ${gradleVersionCheck} but was ${gradle.gradleVersion}")
|
||||||
|
}
|
6
__tests__/samples/no-wrapper/.gitattributes
vendored
6
__tests__/samples/no-wrapper/.gitattributes
vendored
@@ -1,6 +0,0 @@
|
|||||||
#
|
|
||||||
# https://help.github.com/articles/dealing-with-line-endings/
|
|
||||||
#
|
|
||||||
# These are explicitly windows files and should use crlf
|
|
||||||
*.bat text eol=crlf
|
|
||||||
|
|
5
__tests__/samples/no-wrapper/.gitignore
vendored
5
__tests__/samples/no-wrapper/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
# Ignore Gradle project-specific cache directory
|
|
||||||
.gradle
|
|
||||||
|
|
||||||
# Ignore Gradle build output directory
|
|
||||||
build
|
|
44
action.yml
44
action.yml
@@ -7,29 +7,65 @@ inputs:
|
|||||||
gradle-executable:
|
gradle-executable:
|
||||||
description: Path to the Gradle executable
|
description: Path to the Gradle executable
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
gradle-version:
|
gradle-version:
|
||||||
description: Gradle version to use
|
description: Gradle version to use
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
build-root-directory:
|
build-root-directory:
|
||||||
description: Path to the root directory of the build
|
description: Path to the root directory of the build
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
arguments:
|
arguments:
|
||||||
description: Gradle command line arguments, see gradle --help
|
description: Gradle command line arguments (supports multi-line input)
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
cache-disabled:
|
cache-disabled:
|
||||||
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
description: When 'true', all caching is disabled. No entries will be written to or read from the cache.
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
|
|
||||||
cache-read-only:
|
cache-read-only:
|
||||||
description: When 'true', existing entries will be read from the cache but no entries will be written
|
description: When 'true', existing entries will be read from the cache but no entries will be written.
|
||||||
required: false
|
required: false
|
||||||
# TODO: It might be useful to default to read-only for PRs, or non-main branch.
|
|
||||||
default: false
|
default: false
|
||||||
|
# e.g. Use the following setting to only write cache entries from your 'main' branch
|
||||||
|
# cache-read-only: ${{ github.ref != 'refs/heads/main' }}
|
||||||
|
|
||||||
|
gradle-home-cache-includes:
|
||||||
|
description: Paths within Gradle User Home to cache.
|
||||||
|
required: false
|
||||||
|
default: |
|
||||||
|
caches
|
||||||
|
notifications
|
||||||
|
|
||||||
|
gradle-home-cache-excludes:
|
||||||
|
description: Paths within Gradle User Home to exclude from cache.
|
||||||
|
required: false
|
||||||
|
|
||||||
|
# e.g. Use the following setting to prevent the local build cache from being saved/restored
|
||||||
|
# gradle-home-cache-excludes: |
|
||||||
|
# ["caches/build-cache-1"]
|
||||||
|
|
||||||
|
# EXPERIMENTAL & INTERNAL CONFIGURATION PROPERTIES
|
||||||
|
# The following action properties allow fine-grained tweaking of the action caching behaviour.
|
||||||
|
# These properties are not designed for production use, and may change without notice in a subsequent release of `gradle-build-action`.
|
||||||
|
# Use at your own risk!
|
||||||
workflow-job-context:
|
workflow-job-context:
|
||||||
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users.
|
description: Used to uniquely identify the current job invocation. Defaults to the matrix values for this job; this should not be overridden by users (INTERNAL).
|
||||||
required: false
|
required: false
|
||||||
default: ${{ toJSON(matrix) }}
|
default: ${{ toJSON(matrix) }}
|
||||||
|
gradle-home-cache-artifact-bundles:
|
||||||
|
description: Names and patterns of artifact bundles to cache separately. (EXPERIMENTAL - may be changed/removed without notice)
|
||||||
|
required: false
|
||||||
|
default: |
|
||||||
|
[
|
||||||
|
["generated-gradle-jars", "caches/*/generated-gradle-jars/*.jar"],
|
||||||
|
["wrapper-zips", "wrapper/dists/*/*/*.zip"],
|
||||||
|
["dependencies", "caches/modules-*/files-*/*/*/*/*/"],
|
||||||
|
["instrumented-jars", "caches/jars-*/*/"],
|
||||||
|
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
||||||
|
]
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
build-scan-url:
|
build-scan-url:
|
||||||
|
2
dist/main/index.js
vendored
2
dist/main/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/main/index.js.map
vendored
2
dist/main/index.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js
vendored
2
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js.map
vendored
2
dist/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
7370
package-lock.json
generated
7370
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
@@ -4,6 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"description": "Execute Gradle Build",
|
"description": "Execute Gradle Build",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"postinstall": "patch-package",
|
||||||
"format": "prettier --write **/*.ts",
|
"format": "prettier --write **/*.ts",
|
||||||
"format-check": "prettier --check **/*.ts",
|
"format-check": "prettier --check **/*.ts",
|
||||||
"lint": "eslint src/**/*.ts",
|
"lint": "eslint src/**/*.ts",
|
||||||
@@ -21,7 +22,6 @@
|
|||||||
"github-actions",
|
"github-actions",
|
||||||
"gradle"
|
"gradle"
|
||||||
],
|
],
|
||||||
"author": "Paul Merlin <paul@nosphere.org>",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/cache": "1.0.7",
|
"@actions/cache": "1.0.7",
|
||||||
@@ -31,10 +31,11 @@
|
|||||||
"@actions/glob": "0.2.0",
|
"@actions/glob": "0.2.0",
|
||||||
"@actions/http-client": "1.0.11",
|
"@actions/http-client": "1.0.11",
|
||||||
"@actions/tool-cache": "1.7.1",
|
"@actions/tool-cache": "1.7.1",
|
||||||
|
"patch-package": "6.4.7",
|
||||||
"string-argv": "0.3.1"
|
"string-argv": "0.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "26.0.23",
|
"@types/jest": "27.0.2",
|
||||||
"@types/node": "14.17.3",
|
"@types/node": "14.17.3",
|
||||||
"@types/unzipper": "0.10.4",
|
"@types/unzipper": "0.10.4",
|
||||||
"@typescript-eslint/parser": "4.28.2",
|
"@typescript-eslint/parser": "4.28.2",
|
||||||
@@ -42,11 +43,11 @@
|
|||||||
"eslint": "7.30.0",
|
"eslint": "7.30.0",
|
||||||
"eslint-plugin-github": "4.1.3",
|
"eslint-plugin-github": "4.1.3",
|
||||||
"eslint-plugin-jest": "24.3.6",
|
"eslint-plugin-jest": "24.3.6",
|
||||||
"jest": "26.6.3",
|
"jest": "27.3.1",
|
||||||
"jest-circus": "26.6.3",
|
"jest-circus": "27.3.1",
|
||||||
"js-yaml": "3.14.1",
|
"js-yaml": "3.14.1",
|
||||||
"prettier": "2.3.2",
|
"prettier": "2.3.2",
|
||||||
"ts-jest": "26.5.6",
|
"ts-jest": "27.0.7",
|
||||||
"typescript": "4.3.5"
|
"typescript": "4.3.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
92
patches/@actions+cache+1.0.7.patch
Normal file
92
patches/@actions+cache+1.0.7.patch
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
diff --git a/node_modules/@actions/cache/lib/cache.d.ts b/node_modules/@actions/cache/lib/cache.d.ts
|
||||||
|
index 805a8e5..d3ab419 100644
|
||||||
|
--- a/node_modules/@actions/cache/lib/cache.d.ts
|
||||||
|
+++ b/node_modules/@actions/cache/lib/cache.d.ts
|
||||||
|
@@ -5,6 +5,11 @@ export declare class ValidationError extends Error {
|
||||||
|
export declare class ReserveCacheError extends Error {
|
||||||
|
constructor(message: string);
|
||||||
|
}
|
||||||
|
+export declare class CacheEntry {
|
||||||
|
+ key: string;
|
||||||
|
+ size?: number;
|
||||||
|
+ constructor(key: string, size?: number);
|
||||||
|
+}
|
||||||
|
/**
|
||||||
|
* Restores cache from keys
|
||||||
|
*
|
||||||
|
@@ -14,7 +19,7 @@ export declare class ReserveCacheError extends Error {
|
||||||
|
* @param downloadOptions cache download options
|
||||||
|
* @returns string returns the key for the cache hit, otherwise returns undefined
|
||||||
|
*/
|
||||||
|
-export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions): Promise<string | undefined>;
|
||||||
|
+export declare function restoreCache(paths: string[], primaryKey: string, restoreKeys?: string[], options?: DownloadOptions): Promise<CacheEntry | undefined>;
|
||||||
|
/**
|
||||||
|
* Saves a list of files with the specified key
|
||||||
|
*
|
||||||
|
@@ -23,4 +28,4 @@ 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): Promise<number>;
|
||||||
|
+export declare function saveCache(paths: string[], key: string, options?: UploadOptions): Promise<CacheEntry>;
|
||||||
|
diff --git a/node_modules/@actions/cache/lib/cache.js b/node_modules/@actions/cache/lib/cache.js
|
||||||
|
index df78fe0..540114f 100644
|
||||||
|
--- a/node_modules/@actions/cache/lib/cache.js
|
||||||
|
+++ b/node_modules/@actions/cache/lib/cache.js
|
||||||
|
@@ -37,6 +37,13 @@ class ReserveCacheError extends Error {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exports.ReserveCacheError = ReserveCacheError;
|
||||||
|
+class CacheEntry {
|
||||||
|
+ constructor(key, size) {
|
||||||
|
+ this.key = key;
|
||||||
|
+ this.size = size;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+exports.CacheEntry = CacheEntry;
|
||||||
|
function checkPaths(paths) {
|
||||||
|
if (!paths || paths.length === 0) {
|
||||||
|
throw new ValidationError(`Path Validation Error: At least one directory or file path is required`);
|
||||||
|
@@ -84,6 +91,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
|
||||||
|
}
|
||||||
|
const archivePath = path.join(yield utils.createTempDirectory(), utils.getCacheFileName(compressionMethod));
|
||||||
|
core.debug(`Archive Path: ${archivePath}`);
|
||||||
|
+ const restoredEntry = new CacheEntry(cacheEntry.cacheKey);
|
||||||
|
try {
|
||||||
|
// Download the cache from the cache entry
|
||||||
|
yield cacheHttpClient.downloadCache(cacheEntry.archiveLocation, archivePath, options);
|
||||||
|
@@ -91,6 +99,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
|
||||||
|
yield tar_1.listTar(archivePath, compressionMethod);
|
||||||
|
}
|
||||||
|
const archiveFileSize = utils.getArchiveFileSizeIsBytes(archivePath);
|
||||||
|
+ restoredEntry.size = archiveFileSize;
|
||||||
|
core.info(`Cache Size: ~${Math.round(archiveFileSize / (1024 * 1024))} MB (${archiveFileSize} B)`);
|
||||||
|
yield tar_1.extractTar(archivePath, compressionMethod);
|
||||||
|
core.info('Cache restored successfully');
|
||||||
|
@@ -104,7 +113,7 @@ function restoreCache(paths, primaryKey, restoreKeys, options) {
|
||||||
|
core.debug(`Failed to delete archive: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- return cacheEntry.cacheKey;
|
||||||
|
+ return restoredEntry;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.restoreCache = restoreCache;
|
||||||
|
@@ -147,7 +156,7 @@ function saveCache(paths, key, options) {
|
||||||
|
}
|
||||||
|
core.debug(`Saving Cache (ID: ${cacheId})`);
|
||||||
|
yield cacheHttpClient.saveCache(cacheId, archivePath, options);
|
||||||
|
- return cacheId;
|
||||||
|
+ return new CacheEntry(key, archiveFileSize);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports.saveCache = saveCache;
|
||||||
|
diff --git a/node_modules/@actions/cache/lib/cache.js.map b/node_modules/@actions/cache/lib/cache.js.map
|
||||||
|
index 05fc369..41b9189 100644
|
||||||
|
--- a/node_modules/@actions/cache/lib/cache.js.map
|
||||||
|
+++ b/node_modules/@actions/cache/lib/cache.js.map
|
||||||
|
@@ -1 +1 @@
|
||||||
|
-{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
||||||
|
\ No newline at end of file
|
||||||
|
+{"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
|
||||||
|
\ No newline at end of file
|
@@ -18,31 +18,29 @@ import org.gradle.util.GradleVersion
|
|||||||
def isTopLevelBuild = gradle.getParent() == null
|
def isTopLevelBuild = gradle.getParent() == null
|
||||||
if (isTopLevelBuild) {
|
if (isTopLevelBuild) {
|
||||||
def version = GradleVersion.current().baseVersion
|
def version = GradleVersion.current().baseVersion
|
||||||
def atLeastGradle5 = version >= GradleVersion.version("5.0")
|
def atLeastGradle4 = version >= GradleVersion.version("4.0")
|
||||||
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
||||||
|
|
||||||
if (atLeastGradle6) {
|
if (atLeastGradle6) {
|
||||||
settingsEvaluated { settings ->
|
settingsEvaluated { settings ->
|
||||||
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
|
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
|
||||||
registerCallbacks(settings.extensions["gradleEnterprise"], settings.rootProject.name)
|
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (atLeastGradle5) {
|
} else if (atLeastGradle4) {
|
||||||
projectsEvaluated { gradle ->
|
projectsEvaluated { gradle ->
|
||||||
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
|
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
|
||||||
registerCallbacks(gradle.rootProject.extensions["gradleEnterprise"], gradle.rootProject.name)
|
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def registerCallbacks(gradleEnterprise, rootProjectName) {
|
def registerCallbacks(buildScanExtension, rootProjectName) {
|
||||||
gradleEnterprise.with {
|
buildScanExtension.with {
|
||||||
buildScan {
|
def scanFile = new File("gradle-build-scan.txt")
|
||||||
def scanFile = new File("gradle-build-scan.txt")
|
buildScanPublished { buildScan ->
|
||||||
buildScanPublished { buildScan ->
|
scanFile.text = buildScan.buildScanUri
|
||||||
scanFile.text = buildScan.buildScanUri
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
244
src/cache-base.ts
Normal file
244
src/cache-base.ts
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as cache from '@actions/cache'
|
||||||
|
import * as github from '@actions/github'
|
||||||
|
import {isCacheDebuggingEnabled, getCacheKeyPrefix, hashStrings, handleCacheFailure} from './cache-utils'
|
||||||
|
|
||||||
|
const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
|
||||||
|
|
||||||
|
function generateCacheKey(cacheName: string): CacheKey {
|
||||||
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
|
|
||||||
|
// At the most general level, share caches for all executions on the same OS
|
||||||
|
const runnerOs = process.env['RUNNER_OS'] || ''
|
||||||
|
const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}`
|
||||||
|
|
||||||
|
// Prefer caches that run this job
|
||||||
|
const cacheKeyForJob = `${cacheKeyForOs}|${github.context.job}`
|
||||||
|
|
||||||
|
// Prefer (even more) jobs that run this job with the same context (matrix)
|
||||||
|
const cacheKeyForJobContext = `${cacheKeyForJob}[${determineJobContext()}]`
|
||||||
|
|
||||||
|
// Exact match on Git SHA
|
||||||
|
const cacheKey = `${cacheKeyForJobContext}-${github.context.sha}`
|
||||||
|
|
||||||
|
return new CacheKey(cacheKey, [cacheKeyForJobContext, cacheKeyForJob, cacheKeyForOs])
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineJobContext(): string {
|
||||||
|
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
|
||||||
|
const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER)
|
||||||
|
return hashStrings([workflowJobContext])
|
||||||
|
}
|
||||||
|
|
||||||
|
class CacheKey {
|
||||||
|
key: string
|
||||||
|
restoreKeys: string[]
|
||||||
|
|
||||||
|
constructor(key: string, restoreKeys: string[]) {
|
||||||
|
this.key = key
|
||||||
|
this.restoreKeys = restoreKeys
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CacheListener {
|
||||||
|
cacheEntries: CacheEntryListener[] = []
|
||||||
|
|
||||||
|
get fullyRestored(): boolean {
|
||||||
|
return this.cacheEntries.every(x => !x.wasRequestedButNotRestored())
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CacheEntryListener {
|
||||||
|
entryName: string
|
||||||
|
requestedKey: string | undefined
|
||||||
|
requestedRestoreKeys: string[] | undefined
|
||||||
|
restoredKey: string | undefined
|
||||||
|
restoredSize: number | undefined
|
||||||
|
|
||||||
|
savedKey: string | undefined
|
||||||
|
savedSize: number | 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): CacheEntryListener {
|
||||||
|
this.restoredKey = key
|
||||||
|
this.restoredSize = size
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
markSaved(key: string, size: number | undefined): CacheEntryListener {
|
||||||
|
this.savedKey = key
|
||||||
|
this.savedSize = size
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class AbstractCache {
|
||||||
|
private cacheName: string
|
||||||
|
private cacheDescription: string
|
||||||
|
private cacheKeyStateKey: string
|
||||||
|
private cacheResultStateKey: string
|
||||||
|
|
||||||
|
protected readonly cacheDebuggingEnabled: boolean
|
||||||
|
|
||||||
|
constructor(cacheName: string, cacheDescription: string) {
|
||||||
|
this.cacheName = cacheName
|
||||||
|
this.cacheDescription = cacheDescription
|
||||||
|
this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
|
||||||
|
this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
|
||||||
|
this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
|
||||||
|
}
|
||||||
|
|
||||||
|
async restore(listener: CacheListener): Promise<void> {
|
||||||
|
if (this.cacheOutputExists()) {
|
||||||
|
core.info(`${this.cacheDescription} already exists. Not restoring from cache.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheKey = this.prepareCacheKey()
|
||||||
|
const entryReport = listener.entry(this.cacheDescription)
|
||||||
|
entryReport.markRequested(cacheKey.key, cacheKey.restoreKeys)
|
||||||
|
|
||||||
|
this.debug(
|
||||||
|
`Requesting ${this.cacheDescription} with
|
||||||
|
key:${cacheKey.key}
|
||||||
|
restoreKeys:[${cacheKey.restoreKeys}]`
|
||||||
|
)
|
||||||
|
|
||||||
|
const cacheResult = await this.restoreCache(this.getCachePath(), cacheKey.key, cacheKey.restoreKeys)
|
||||||
|
|
||||||
|
if (!cacheResult) {
|
||||||
|
core.info(`${this.cacheDescription} cache not found. Will start with empty.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.saveState(this.cacheResultStateKey, cacheResult.key)
|
||||||
|
entryReport.markRestored(cacheResult.key, cacheResult.size)
|
||||||
|
core.info(`Restored ${this.cacheDescription} from cache key: ${cacheResult.key}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.afterRestore(listener)
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prepareCacheKey(): CacheKey {
|
||||||
|
const cacheKey = generateCacheKey(this.cacheName)
|
||||||
|
|
||||||
|
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
||||||
|
return cacheKey
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async restoreCache(
|
||||||
|
cachePath: string[],
|
||||||
|
cacheKey: string,
|
||||||
|
cacheRestoreKeys: string[] = []
|
||||||
|
): Promise<cache.CacheEntry | undefined> {
|
||||||
|
try {
|
||||||
|
return await cache.restoreCache(cachePath, cacheKey, cacheRestoreKeys)
|
||||||
|
} catch (error) {
|
||||||
|
handleCacheFailure(error, `Failed to restore ${cacheKey}`)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async afterRestore(_listener: CacheListener): Promise<void> {}
|
||||||
|
|
||||||
|
async save(listener: CacheListener): Promise<void> {
|
||||||
|
if (!this.cacheOutputExists()) {
|
||||||
|
this.debug(`No ${this.cacheDescription} to cache.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const cacheKey = core.getState(this.cacheKeyStateKey)
|
||||||
|
const cacheResult = core.getState(this.cacheResultStateKey)
|
||||||
|
|
||||||
|
if (!cacheKey) {
|
||||||
|
this.debug(`${this.cacheDescription} existed prior to cache restore. Not saving.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cacheResult && cacheKey === cacheResult) {
|
||||||
|
core.info(`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.beforeSave(listener)
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.info(`Caching ${this.cacheDescription} with cache key: ${cacheKey}`)
|
||||||
|
const cachePath = this.getCachePath()
|
||||||
|
const savedEntry = await this.saveCache(cachePath, cacheKey)
|
||||||
|
|
||||||
|
if (savedEntry) {
|
||||||
|
listener.entry(this.cacheDescription).markSaved(savedEntry.key, savedEntry.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async beforeSave(_listener: CacheListener): Promise<void> {}
|
||||||
|
|
||||||
|
protected async saveCache(cachePath: string[], cacheKey: string): Promise<cache.CacheEntry | undefined> {
|
||||||
|
try {
|
||||||
|
return await cache.saveCache(cachePath, cacheKey)
|
||||||
|
} catch (error) {
|
||||||
|
handleCacheFailure(error, `Failed to save cache entry ${cacheKey}`)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
protected debug(message: string): void {
|
||||||
|
if (this.cacheDebuggingEnabled) {
|
||||||
|
core.info(message)
|
||||||
|
} else {
|
||||||
|
core.debug(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract cacheOutputExists(): boolean
|
||||||
|
protected abstract getCachePath(): string[]
|
||||||
|
}
|
@@ -5,17 +5,14 @@ import * as core from '@actions/core'
|
|||||||
import * as glob from '@actions/glob'
|
import * as glob from '@actions/glob'
|
||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
|
|
||||||
import {AbstractCache, hashFileNames} from './cache-utils'
|
import {AbstractCache, CacheEntryListener, CacheListener} from './cache-base'
|
||||||
|
import {getCacheKeyPrefix, hashFileNames, tryDelete} from './cache-utils'
|
||||||
|
|
||||||
// Which paths under Gradle User Home should be cached
|
const META_FILE_DIR = '.gradle-build-action'
|
||||||
const CACHE_PATH = ['caches', 'notifications']
|
|
||||||
|
|
||||||
const COMMON_ARTIFACT_CACHES = new Map([
|
const INCLUDE_PATHS_PARAMETER = 'gradle-home-cache-includes'
|
||||||
['generated-gradle-jars', 'caches/*/generated-gradle-jars/*.jar'],
|
const EXCLUDE_PATHS_PARAMETER = 'gradle-home-cache-excludes'
|
||||||
['wrapper-zips', 'wrapper/dists/*/*/*.zip'],
|
const ARTIFACT_BUNDLES_PARAMETER = 'gradle-home-cache-artifact-bundles'
|
||||||
['dependency-jars', 'caches/modules-*/files-*/**/*.jar'],
|
|
||||||
['instrumented-jars', 'caches/jars-*/*/*.jar']
|
|
||||||
])
|
|
||||||
|
|
||||||
export class GradleUserHomeCache extends AbstractCache {
|
export class GradleUserHomeCache extends AbstractCache {
|
||||||
private gradleUserHome: string
|
private gradleUserHome: string
|
||||||
@@ -25,99 +22,97 @@ export class GradleUserHomeCache extends AbstractCache {
|
|||||||
this.gradleUserHome = this.determineGradleUserHome(rootDir)
|
this.gradleUserHome = this.determineGradleUserHome(rootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterRestore(): Promise<void> {
|
async afterRestore(listener: CacheListener): Promise<void> {
|
||||||
await this.reportCacheEntrySize('as restored from cache')
|
await this.reportGradleUserHomeSize('as restored from cache')
|
||||||
await this.restoreCommonArtifacts()
|
await this.restoreArtifactBundles(listener)
|
||||||
await this.reportCacheEntrySize('after restoring common artifacts')
|
await this.reportGradleUserHomeSize('after restoring common artifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restoreCommonArtifacts(): Promise<void> {
|
private async restoreArtifactBundles(listener: CacheListener): Promise<void> {
|
||||||
const processes: Promise<void>[] = []
|
const processes: Promise<void>[] = []
|
||||||
for (const [bundle, pattern] of this.getCommonArtifactPaths()) {
|
|
||||||
const p = this.restoreCommonArtifactBundle(bundle, pattern)
|
|
||||||
// Run sequentially when debugging enabled
|
|
||||||
if (this.cacheDebuggingEnabled) {
|
|
||||||
await p
|
|
||||||
}
|
|
||||||
processes.push(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(processes)
|
const bundleMetaFiles = await this.getBundleMetaFiles()
|
||||||
}
|
const bundlePatterns = this.getArtifactBundles()
|
||||||
|
|
||||||
private async restoreCommonArtifactBundle(
|
// Iterate over all bundle meta files and try to restore
|
||||||
bundle: string,
|
for (const bundleMetaFile of bundleMetaFiles) {
|
||||||
artifactPath: string
|
const bundle = path.basename(bundleMetaFile, '.cache')
|
||||||
): Promise<void> {
|
const entryListener = listener.entry(bundle)
|
||||||
const cacheMetaFile = this.getCacheMetaFile(bundle)
|
const bundlePattern = bundlePatterns.get(bundle)
|
||||||
if (fs.existsSync(cacheMetaFile)) {
|
|
||||||
const cacheKey = fs.readFileSync(cacheMetaFile, 'utf-8').trim()
|
// Handle case where the 'artifactBundlePatterns' have been changed
|
||||||
const restoreKey = await this.restoreCache([artifactPath], cacheKey)
|
if (bundlePattern === undefined) {
|
||||||
if (restoreKey) {
|
core.info(`Found bundle metafile for ${bundle} but no such bundle defined`)
|
||||||
core.info(
|
entryListener.markRequested('BUNDLE_NOT_CONFIGURED')
|
||||||
`Restored ${bundle} with key ${cacheKey} to ${artifactPath}`
|
tryDelete(bundleMetaFile)
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
this.debug(
|
const p = this.restoreArtifactBundle(bundle, bundlePattern, bundleMetaFile, entryListener)
|
||||||
`Failed to restore ${bundle} with key ${cacheKey} to ${artifactPath}`
|
// Run sequentially when debugging enabled
|
||||||
)
|
if (this.cacheDebuggingEnabled) {
|
||||||
|
await p
|
||||||
|
}
|
||||||
|
processes.push(p)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(processes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async restoreArtifactBundle(
|
||||||
|
bundle: string,
|
||||||
|
bundlePattern: string,
|
||||||
|
bundleMetaFile: string,
|
||||||
|
listener: CacheEntryListener
|
||||||
|
): Promise<void> {
|
||||||
|
const cacheKey = fs.readFileSync(bundleMetaFile, 'utf-8').trim()
|
||||||
|
listener.markRequested(cacheKey)
|
||||||
|
|
||||||
|
const restoredEntry = await this.restoreCache([bundlePattern], cacheKey)
|
||||||
|
if (restoredEntry) {
|
||||||
|
core.info(`Restored ${bundle} with key ${cacheKey} to ${bundlePattern}`)
|
||||||
|
listener.markRestored(restoredEntry.key, restoredEntry.size)
|
||||||
} else {
|
} else {
|
||||||
this.debug(
|
core.info(`Did not restore ${bundle} with key ${cacheKey} to ${bundlePattern}`)
|
||||||
`No metafile found to restore ${bundle}: ${cacheMetaFile}`
|
tryDelete(bundleMetaFile)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCacheMetaFile(name: string): string {
|
private getBundleMetaFile(name: string): string {
|
||||||
return path.resolve(
|
return path.resolve(this.gradleUserHome, META_FILE_DIR, `${name}.cache`)
|
||||||
this.gradleUserHome,
|
}
|
||||||
'caches',
|
|
||||||
`.gradle-build-action.${name}.cache`
|
private async getBundleMetaFiles(): Promise<string[]> {
|
||||||
|
const metaFiles = path.resolve(this.gradleUserHome, META_FILE_DIR, '*.cache')
|
||||||
|
const globber = await glob.create(metaFiles)
|
||||||
|
const bundleFiles = await globber.glob()
|
||||||
|
return bundleFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
async beforeSave(listener: CacheListener): Promise<void> {
|
||||||
|
await this.reportGradleUserHomeSize('before saving common artifacts')
|
||||||
|
this.removeExcludedPaths()
|
||||||
|
await this.saveArtifactBundles(listener)
|
||||||
|
await this.reportGradleUserHomeSize(
|
||||||
|
"after saving common artifacts (only 'caches' and 'notifications' will be stored)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reportCacheEntrySize(label: string): Promise<void> {
|
private removeExcludedPaths(): void {
|
||||||
if (!this.cacheDebuggingEnabled) {
|
const rawPaths: string[] = core.getMultilineInput(EXCLUDE_PATHS_PARAMETER)
|
||||||
return
|
const resolvedPaths = rawPaths.map(x => path.resolve(this.gradleUserHome, x))
|
||||||
|
|
||||||
|
for (const p of resolvedPaths) {
|
||||||
|
this.debug(`Deleting excluded path: ${p}`)
|
||||||
|
tryDelete(p)
|
||||||
}
|
}
|
||||||
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 cache entry (directories >5M): ${label}`)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
result.stdout
|
|
||||||
.trimEnd()
|
|
||||||
.replace(/\t/g, ' ')
|
|
||||||
.split('\n')
|
|
||||||
.map(it => {
|
|
||||||
return ` ${it}`
|
|
||||||
})
|
|
||||||
.join('\n')
|
|
||||||
)
|
|
||||||
|
|
||||||
core.info('-----------------------')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeSave(): Promise<void> {
|
private async saveArtifactBundles(listener: CacheListener): Promise<void> {
|
||||||
await this.saveCommonArtifacts()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveCommonArtifacts(): Promise<void> {
|
|
||||||
const processes: Promise<void>[] = []
|
const processes: Promise<void>[] = []
|
||||||
for (const [bundle, pattern] of this.getCommonArtifactPaths()) {
|
for (const [bundle, pattern] of this.getArtifactBundles()) {
|
||||||
const p = this.saveCommonArtifactBundle(bundle, pattern)
|
const entryListener = listener.entry(bundle)
|
||||||
|
|
||||||
|
const p = this.saveArtifactBundle(bundle, pattern, entryListener)
|
||||||
// Run sequentially when debugging enabled
|
// Run sequentially when debugging enabled
|
||||||
if (this.cacheDebuggingEnabled) {
|
if (this.cacheDebuggingEnabled) {
|
||||||
await p
|
await p
|
||||||
@@ -128,56 +123,70 @@ export class GradleUserHomeCache extends AbstractCache {
|
|||||||
await Promise.all(processes)
|
await Promise.all(processes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async saveCommonArtifactBundle(
|
private async saveArtifactBundle(
|
||||||
bundle: string,
|
bundle: string,
|
||||||
artifactPath: string
|
artifactPath: string,
|
||||||
|
listener: CacheEntryListener
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const cacheMetaFile = this.getCacheMetaFile(bundle)
|
const bundleMetaFile = this.getBundleMetaFile(bundle)
|
||||||
|
|
||||||
const globber = await glob.create(artifactPath)
|
const globber = await glob.create(artifactPath, {
|
||||||
const commonArtifactFiles = await globber.glob()
|
implicitDescendants: false,
|
||||||
|
followSymbolicLinks: false
|
||||||
|
})
|
||||||
|
const bundleFiles = await globber.glob()
|
||||||
|
|
||||||
// Handle no matching files
|
// Handle no matching files
|
||||||
if (commonArtifactFiles.length === 0) {
|
if (bundleFiles.length === 0) {
|
||||||
this.debug(`No files found to cache for ${bundle}`)
|
this.debug(`No files found to cache for ${bundle}`)
|
||||||
if (fs.existsSync(cacheMetaFile)) {
|
if (fs.existsSync(bundleMetaFile)) {
|
||||||
fs.unlinkSync(cacheMetaFile)
|
tryDelete(bundleMetaFile)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const previouslyRestoredKey = fs.existsSync(cacheMetaFile)
|
const previouslyRestoredKey = fs.existsSync(bundleMetaFile)
|
||||||
? fs.readFileSync(cacheMetaFile, 'utf-8').trim()
|
? fs.readFileSync(bundleMetaFile, 'utf-8').trim()
|
||||||
: ''
|
: ''
|
||||||
const cacheKey = this.createCacheKey(bundle, commonArtifactFiles)
|
const cacheKey = this.createCacheKey(bundle, bundleFiles)
|
||||||
|
|
||||||
if (previouslyRestoredKey === cacheKey) {
|
if (previouslyRestoredKey === cacheKey) {
|
||||||
this.debug(
|
this.debug(`No change to previously restored ${bundle}. Not caching.`)
|
||||||
`No change to previously restored ${bundle}. Not caching.`
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
|
core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
|
||||||
await this.saveCache([artifactPath], cacheKey)
|
const savedEntry = await this.saveCache([artifactPath], cacheKey)
|
||||||
|
if (savedEntry !== undefined) {
|
||||||
this.debug(`Writing cache metafile: ${cacheMetaFile}`)
|
this.writeBundleMetaFile(bundleMetaFile, cacheKey)
|
||||||
fs.writeFileSync(cacheMetaFile, cacheKey)
|
listener.markSaved(savedEntry.key, savedEntry.size)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const file of commonArtifactFiles) {
|
for (const file of bundleFiles) {
|
||||||
fs.unlinkSync(file)
|
tryDelete(file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createCacheKey(bundle: string, files: string[]): string {
|
protected createCacheKey(bundle: string, files: string[]): string {
|
||||||
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
const relativeFiles = files.map(x =>
|
const relativeFiles = files.map(x => path.relative(this.gradleUserHome, x))
|
||||||
path.relative(this.gradleUserHome, x)
|
|
||||||
)
|
|
||||||
const key = hashFileNames(relativeFiles)
|
const key = hashFileNames(relativeFiles)
|
||||||
|
|
||||||
|
this.debug(`Generating cache key for ${bundle} from files: ${relativeFiles}`)
|
||||||
|
|
||||||
return `${cacheKeyPrefix}${bundle}-${key}`
|
return `${cacheKeyPrefix}${bundle}-${key}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private writeBundleMetaFile(metaFile: string, cacheKey: string): void {
|
||||||
|
this.debug(`Writing bundle metafile: ${metaFile}`)
|
||||||
|
|
||||||
|
const dirName = path.dirname(metaFile)
|
||||||
|
if (!fs.existsSync(dirName)) {
|
||||||
|
fs.mkdirSync(dirName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.writeFileSync(metaFile, cacheKey)
|
||||||
|
}
|
||||||
|
|
||||||
protected determineGradleUserHome(rootDir: string): string {
|
protected determineGradleUserHome(rootDir: string): string {
|
||||||
const customGradleUserHome = process.env['GRADLE_USER_HOME']
|
const customGradleUserHome = process.env['GRADLE_USER_HOME']
|
||||||
if (customGradleUserHome) {
|
if (customGradleUserHome) {
|
||||||
@@ -194,15 +203,54 @@ export class GradleUserHomeCache extends AbstractCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected getCachePath(): string[] {
|
protected getCachePath(): string[] {
|
||||||
return CACHE_PATH.map(x => path.resolve(this.gradleUserHome, x))
|
const rawPaths: string[] = core.getMultilineInput(INCLUDE_PATHS_PARAMETER)
|
||||||
|
rawPaths.push(META_FILE_DIR)
|
||||||
|
const resolvedPaths = rawPaths.map(x => this.resolveCachePath(x))
|
||||||
|
this.debug(`Using cache paths: ${resolvedPaths}`)
|
||||||
|
return resolvedPaths
|
||||||
}
|
}
|
||||||
|
|
||||||
private getCommonArtifactPaths(): Map<string, string> {
|
private resolveCachePath(rawPath: string): string {
|
||||||
return new Map(
|
if (rawPath.startsWith('!')) {
|
||||||
Array.from(COMMON_ARTIFACT_CACHES, ([key, value]) => [
|
const resolved = this.resolveCachePath(rawPath.substring(1))
|
||||||
key,
|
return `!${resolved}`
|
||||||
path.resolve(this.gradleUserHome, value)
|
}
|
||||||
])
|
return path.resolve(this.gradleUserHome, rawPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
private getArtifactBundles(): Map<string, string> {
|
||||||
|
const artifactBundleDefinition = core.getInput(ARTIFACT_BUNDLES_PARAMETER)
|
||||||
|
this.debug(`Using artifact bundle definition: ${artifactBundleDefinition}`)
|
||||||
|
const artifactBundles = JSON.parse(artifactBundleDefinition)
|
||||||
|
return new Map(Array.from(artifactBundles, ([key, value]) => [key, path.resolve(this.gradleUserHome, value)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
private async reportGradleUserHomeSize(label: string): Promise<void> {
|
||||||
|
if (!this.cacheDebuggingEnabled) {
|
||||||
|
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,6 +1,6 @@
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import {AbstractCache} from './cache-utils'
|
import {AbstractCache} from './cache-base'
|
||||||
|
|
||||||
// TODO: Maybe allow the user to override / tweak this set
|
// TODO: Maybe allow the user to override / tweak this set
|
||||||
const PATHS_TO_CACHE = [
|
const PATHS_TO_CACHE = [
|
||||||
@@ -10,7 +10,7 @@ const PATHS_TO_CACHE = [
|
|||||||
export class ProjectDotGradleCache extends AbstractCache {
|
export class ProjectDotGradleCache extends AbstractCache {
|
||||||
private rootDir: string
|
private rootDir: string
|
||||||
constructor(rootDir: string) {
|
constructor(rootDir: string) {
|
||||||
super('project', 'Project .gradle directory')
|
super('project', 'Project configuration cache')
|
||||||
this.rootDir = rootDir
|
this.rootDir = rootDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,49 +1,31 @@
|
|||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import * as cache from '@actions/cache'
|
import * as cache from '@actions/cache'
|
||||||
import * as github from '@actions/github'
|
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
const CACHE_PROTOCOL_VERSION = 'v4-'
|
||||||
|
|
||||||
|
const CACHE_DISABLED_PARAMETER = 'cache-disabled'
|
||||||
|
const CACHE_READONLY_PARAMETER = 'cache-read-only'
|
||||||
|
const CACHE_DEBUG_VAR = 'GRADLE_BUILD_ACTION_CACHE_DEBUG_ENABLED'
|
||||||
|
const CACHE_PREFIX_VAR = 'GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX'
|
||||||
|
|
||||||
export function isCacheDisabled(): boolean {
|
export function isCacheDisabled(): boolean {
|
||||||
return core.getBooleanInput('cache-disabled')
|
return core.getBooleanInput(CACHE_DISABLED_PARAMETER)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCacheReadOnly(): boolean {
|
export function isCacheReadOnly(): boolean {
|
||||||
return core.getBooleanInput('cache-read-only')
|
return core.getBooleanInput(CACHE_READONLY_PARAMETER)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCacheDebuggingEnabled(): boolean {
|
export function isCacheDebuggingEnabled(): boolean {
|
||||||
return process.env['CACHE_DEBUG_ENABLED'] ? true : false
|
return process.env[CACHE_DEBUG_VAR] ? true : false
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateCacheKey(cacheName: string): CacheKey {
|
export function getCacheKeyPrefix(): string {
|
||||||
// Prefix can be used to force change all cache keys
|
// Prefix can be used to force change all cache keys (defaults to cache protocol version)
|
||||||
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
return process.env[CACHE_PREFIX_VAR] || CACHE_PROTOCOL_VERSION
|
||||||
|
|
||||||
// At the most general level, share caches for all executions on the same OS
|
|
||||||
const runnerOs = process.env['RUNNER_OS'] || ''
|
|
||||||
const cacheKeyForOs = `${cacheKeyPrefix}${cacheName}|${runnerOs}`
|
|
||||||
|
|
||||||
// Prefer caches that run this job
|
|
||||||
const cacheKeyForJob = `${cacheKeyForOs}|${github.context.job}`
|
|
||||||
|
|
||||||
// Prefer (even more) jobs that run this job with the same context (matrix)
|
|
||||||
const cacheKeyForJobContext = `${cacheKeyForJob}[${determineJobContext()}]`
|
|
||||||
|
|
||||||
// Exact match on Git SHA
|
|
||||||
const cacheKey = `${cacheKeyForJobContext}-${github.context.sha}`
|
|
||||||
|
|
||||||
return new CacheKey(cacheKey, [
|
|
||||||
cacheKeyForJobContext,
|
|
||||||
cacheKeyForJob,
|
|
||||||
cacheKeyForOs
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
function determineJobContext(): string {
|
|
||||||
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
|
|
||||||
const workflowJobContext = core.getInput('workflow-job-context')
|
|
||||||
return hashStrings([workflowJobContext])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hashStrings(values: string[]): string {
|
export function hashStrings(values: string[]): string {
|
||||||
@@ -55,161 +37,51 @@ export function hashStrings(values: string[]): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hashFileNames(fileNames: string[]): string {
|
export function hashFileNames(fileNames: string[]): string {
|
||||||
return hashStrings(
|
return hashStrings(fileNames.map(x => x.replace(new RegExp(`\\${path.sep}`, 'g'), '/')))
|
||||||
fileNames.map(x => x.replace(new RegExp(`\\${path.sep}`, 'g'), '/'))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CacheKey {
|
export function handleCacheFailure(error: unknown, message: string): void {
|
||||||
key: string
|
if (error instanceof cache.ValidationError) {
|
||||||
restoreKeys: string[]
|
// Fail on cache validation errors
|
||||||
|
throw error
|
||||||
constructor(key: string, restoreKeys: string[]) {
|
|
||||||
this.key = key
|
|
||||||
this.restoreKeys = restoreKeys
|
|
||||||
}
|
}
|
||||||
}
|
if (error instanceof cache.ReserveCacheError) {
|
||||||
|
// Reserve cache errors are expected if the artifact has been previously cached
|
||||||
export abstract class AbstractCache {
|
if (isCacheDebuggingEnabled()) {
|
||||||
private cacheName: string
|
|
||||||
private cacheDescription: string
|
|
||||||
private cacheKeyStateKey: string
|
|
||||||
private cacheResultStateKey: string
|
|
||||||
|
|
||||||
protected readonly cacheDebuggingEnabled: boolean
|
|
||||||
|
|
||||||
constructor(cacheName: string, cacheDescription: string) {
|
|
||||||
this.cacheName = cacheName
|
|
||||||
this.cacheDescription = cacheDescription
|
|
||||||
this.cacheKeyStateKey = `CACHE_KEY_${cacheName}`
|
|
||||||
this.cacheResultStateKey = `CACHE_RESULT_${cacheName}`
|
|
||||||
this.cacheDebuggingEnabled = isCacheDebuggingEnabled()
|
|
||||||
}
|
|
||||||
|
|
||||||
async restore(): Promise<void> {
|
|
||||||
if (this.cacheOutputExists()) {
|
|
||||||
core.info(
|
|
||||||
`${this.cacheDescription} already exists. Not restoring from cache.`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheKey = generateCacheKey(this.cacheName)
|
|
||||||
|
|
||||||
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
|
||||||
|
|
||||||
const cacheResult = await this.restoreCache(
|
|
||||||
this.getCachePath(),
|
|
||||||
cacheKey.key,
|
|
||||||
cacheKey.restoreKeys
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!cacheResult) {
|
|
||||||
core.info(
|
|
||||||
`${this.cacheDescription} cache not found. Will start with empty.`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
core.saveState(this.cacheResultStateKey, cacheResult)
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
`Restored ${this.cacheDescription} from cache key: ${cacheResult}`
|
|
||||||
)
|
|
||||||
|
|
||||||
await this.afterRestore()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async restoreCache(
|
|
||||||
cachePath: string[],
|
|
||||||
cacheKey: string,
|
|
||||||
cacheRestoreKeys: string[] = []
|
|
||||||
): Promise<string | undefined> {
|
|
||||||
try {
|
|
||||||
return await cache.restoreCache(
|
|
||||||
cachePath,
|
|
||||||
cacheKey,
|
|
||||||
cacheRestoreKeys
|
|
||||||
)
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof cache.ValidationError) {
|
|
||||||
// Validation errors should fail the build action
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
// Warn about any other error and continue
|
|
||||||
core.warning(`Failed to restore ${cacheKey}: ${error}`)
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async afterRestore(): Promise<void> {}
|
|
||||||
|
|
||||||
async save(): Promise<void> {
|
|
||||||
if (!this.cacheOutputExists()) {
|
|
||||||
this.debug(`No ${this.cacheDescription} to cache.`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const cacheKey = core.getState(this.cacheKeyStateKey)
|
|
||||||
const cacheResult = core.getState(this.cacheResultStateKey)
|
|
||||||
|
|
||||||
if (!cacheKey) {
|
|
||||||
this.debug(
|
|
||||||
`${this.cacheDescription} existed prior to cache restore. Not saving.`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheResult && cacheKey === cacheResult) {
|
|
||||||
core.info(
|
|
||||||
`Cache hit occurred on the cache key ${cacheKey}, not saving cache.`
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.beforeSave()
|
|
||||||
|
|
||||||
core.info(
|
|
||||||
`Caching ${this.cacheDescription} with cache key: ${cacheKey}`
|
|
||||||
)
|
|
||||||
const cachePath = this.getCachePath()
|
|
||||||
await this.saveCache(cachePath, cacheKey)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async beforeSave(): Promise<void> {}
|
|
||||||
|
|
||||||
protected async saveCache(
|
|
||||||
cachePath: string[],
|
|
||||||
cacheKey: string
|
|
||||||
): Promise<void> {
|
|
||||||
try {
|
|
||||||
await cache.saveCache(cachePath, cacheKey)
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof cache.ValidationError) {
|
|
||||||
// Validation errors should fail the build action
|
|
||||||
throw error
|
|
||||||
} else if (error instanceof cache.ReserveCacheError) {
|
|
||||||
// Reserve cache errors are expected if the artifact has been previously cached
|
|
||||||
this.debug(error.message)
|
|
||||||
} else {
|
|
||||||
// Warn about any other error and continue
|
|
||||||
core.warning(String(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected debug(message: string): void {
|
|
||||||
if (this.cacheDebuggingEnabled) {
|
|
||||||
core.info(message)
|
core.info(message)
|
||||||
} else {
|
} else {
|
||||||
core.debug(message)
|
core.debug(message)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Warn on all other errors
|
||||||
|
core.warning(`${message}: ${error}`)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
protected abstract cacheOutputExists(): boolean
|
|
||||||
protected abstract getCachePath(): string[]
|
/**
|
||||||
|
* Attempt to delete a file or directory, waiting to allow locks to be released
|
||||||
|
*/
|
||||||
|
export async function tryDelete(file: string): Promise<void> {
|
||||||
|
const stat = fs.lstatSync(file)
|
||||||
|
for (let count = 0; count < 3; count++) {
|
||||||
|
try {
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
fs.rmdirSync(file, {recursive: true})
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(file)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} catch (error) {
|
||||||
|
if (count === 2) {
|
||||||
|
throw error
|
||||||
|
} else {
|
||||||
|
core.warning(String(error))
|
||||||
|
await delay(1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function delay(ms: number): Promise<void> {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, ms))
|
||||||
}
|
}
|
||||||
|
@@ -2,35 +2,93 @@ import {GradleUserHomeCache} from './cache-gradle-user-home'
|
|||||||
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
|
import {ProjectDotGradleCache} from './cache-project-dot-gradle'
|
||||||
import * as core from '@actions/core'
|
import * as core from '@actions/core'
|
||||||
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
||||||
|
import {CacheEntryListener, CacheListener} from './cache-base'
|
||||||
|
|
||||||
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
|
const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
|
||||||
|
const CACHE_LISTENER = 'CACHE_LISTENER'
|
||||||
|
|
||||||
export async function restore(buildRootDirectory: string): Promise<void> {
|
export async function restore(buildRootDirectory: string): Promise<void> {
|
||||||
if (isCacheDisabled()) {
|
if (isCacheDisabled()) {
|
||||||
core.debug('Cache read disabled')
|
core.info('Cache is disabled: will not restore state from previous builds.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await core.group('Restore Gradle state from cache', async () => {
|
await core.group('Restore Gradle state from cache', async () => {
|
||||||
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
core.saveState(BUILD_ROOT_DIR, buildRootDirectory)
|
||||||
return Promise.all([
|
|
||||||
new GradleUserHomeCache(buildRootDirectory).restore(),
|
const cacheListener = new CacheListener()
|
||||||
new ProjectDotGradleCache(buildRootDirectory).restore()
|
await new GradleUserHomeCache(buildRootDirectory).restore(cacheListener)
|
||||||
])
|
|
||||||
|
const projectDotGradleCache = new ProjectDotGradleCache(buildRootDirectory)
|
||||||
|
if (cacheListener.fullyRestored) {
|
||||||
|
// Only restore the configuration-cache if the Gradle Home is fully restored
|
||||||
|
await projectDotGradleCache.restore(cacheListener)
|
||||||
|
} else {
|
||||||
|
// Otherwise, prepare the cache key for later save()
|
||||||
|
core.info('Gradle Home cache not fully restored: not restoring configuration-cache state')
|
||||||
|
projectDotGradleCache.prepareCacheKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
core.saveState(CACHE_LISTENER, cacheListener.stringify())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(): Promise<void> {
|
export async function save(): Promise<void> {
|
||||||
|
const cacheListener: CacheListener = CacheListener.rehydrate(core.getState(CACHE_LISTENER))
|
||||||
|
|
||||||
if (isCacheReadOnly()) {
|
if (isCacheReadOnly()) {
|
||||||
core.debug('Cache is read-only: not saving cache entry')
|
core.info('Cache is read-only: will not save state for use in subsequent builds.')
|
||||||
|
logCachingReport(cacheListener)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await core.group('Caching Gradle state', async () => {
|
await core.group('Caching Gradle state', async () => {
|
||||||
const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
|
const buildRootDirectory = core.getState(BUILD_ROOT_DIR)
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
new GradleUserHomeCache(buildRootDirectory).save(),
|
new GradleUserHomeCache(buildRootDirectory).save(cacheListener),
|
||||||
new ProjectDotGradleCache(buildRootDirectory).save()
|
new ProjectDotGradleCache(buildRootDirectory).save(cacheListener)
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
logCachingReport(cacheListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
function logCachingReport(listener: CacheListener): void {
|
||||||
|
core.info(`---------- Caching Summary -------------
|
||||||
|
Restored Entries Count: ${getCount(listener.cacheEntries, e => e.restoredSize)}
|
||||||
|
Size: ${getSum(listener.cacheEntries, e => e.restoredSize)}
|
||||||
|
Saved Entries Count: ${getCount(listener.cacheEntries, e => e.savedSize)}
|
||||||
|
Size: ${getSum(listener.cacheEntries, e => e.savedSize)}`)
|
||||||
|
|
||||||
|
core.startGroup('Cache Entry details')
|
||||||
|
for (const entry of listener.cacheEntries) {
|
||||||
|
core.info(`Entry: ${entry.entryName}
|
||||||
|
Requested Key : ${entry.requestedKey ?? ''}
|
||||||
|
Restored Key : ${entry.restoredKey ?? ''}
|
||||||
|
Size: ${formatSize(entry.restoredSize)}
|
||||||
|
Saved Key : ${entry.savedKey ?? ''}
|
||||||
|
Size: ${formatSize(entry.savedSize)}`)
|
||||||
|
}
|
||||||
|
core.endGroup()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCount(
|
||||||
|
cacheEntries: CacheEntryListener[],
|
||||||
|
predicate: (value: CacheEntryListener) => number | undefined
|
||||||
|
): number {
|
||||||
|
return cacheEntries.filter(e => predicate(e) !== undefined).length
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSum(
|
||||||
|
cacheEntries: CacheEntryListener[],
|
||||||
|
predicate: (value: CacheEntryListener) => number | undefined
|
||||||
|
): string {
|
||||||
|
return formatSize(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)`
|
||||||
}
|
}
|
||||||
|
@@ -3,11 +3,7 @@ import fs from 'fs'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import {writeInitScript} from './build-scan-capture'
|
import {writeInitScript} from './build-scan-capture'
|
||||||
|
|
||||||
export async function execute(
|
export async function execute(executable: string, root: string, args: string[]): Promise<BuildResult> {
|
||||||
executable: string,
|
|
||||||
root: string,
|
|
||||||
args: string[]
|
|
||||||
): Promise<BuildResult> {
|
|
||||||
let buildScanUrl: string | undefined
|
let buildScanUrl: string | undefined
|
||||||
|
|
||||||
// TODO: instead of running with no-daemon, run `--stop` in post action.
|
// TODO: instead of running with no-daemon, run `--stop` in post action.
|
||||||
|
@@ -17,10 +17,7 @@ export function locateGradleWrapperScript(buildRootDirectory: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateGradleWrapper(buildRootDirectory: string): void {
|
function validateGradleWrapper(buildRootDirectory: string): void {
|
||||||
const wrapperProperties = path.resolve(
|
const wrapperProperties = path.resolve(buildRootDirectory, 'gradle/wrapper/gradle-wrapper.properties')
|
||||||
buildRootDirectory,
|
|
||||||
'gradle/wrapper/gradle-wrapper.properties'
|
|
||||||
)
|
|
||||||
if (!fs.existsSync(wrapperProperties)) {
|
if (!fs.existsSync(wrapperProperties)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Cannot locate a Gradle wrapper properties file at '${wrapperProperties}'. Specify 'gradle-version' or 'gradle-executable' for projects without Gradle wrapper configured.`
|
`Cannot locate a Gradle wrapper properties file at '${wrapperProperties}'. Specify 'gradle-version' or 'gradle-executable' for projects without Gradle wrapper configured.`
|
||||||
|
27
src/main.ts
27
src/main.ts
@@ -18,23 +18,25 @@ export async function run(): Promise<void> {
|
|||||||
const args: string[] = parseCommandLineArguments()
|
const args: string[] = parseCommandLineArguments()
|
||||||
|
|
||||||
const result = await execution.execute(
|
const result = await execution.execute(
|
||||||
await resolveGradleExecutable(
|
await resolveGradleExecutable(workspaceDirectory, buildRootDirectory),
|
||||||
workspaceDirectory,
|
|
||||||
buildRootDirectory
|
|
||||||
),
|
|
||||||
buildRootDirectory,
|
buildRootDirectory,
|
||||||
args
|
args
|
||||||
)
|
)
|
||||||
|
|
||||||
if (result.buildScanUrl) {
|
if (result.buildScanUrl) {
|
||||||
core.setOutput('build-scan-url', result.buildScanUrl)
|
core.setOutput('build-scan-url', result.buildScanUrl)
|
||||||
// TODO Include context about the invocation (eg step name) in this message
|
|
||||||
// Unfortunately it doesn't seem possible to access the current step name here
|
|
||||||
core.notice(`Gradle build scan: ${result.buildScanUrl}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.status !== 0) {
|
if (result.status !== 0) {
|
||||||
core.setFailed(`Gradle process exited with status ${result.status}`)
|
if (result.buildScanUrl) {
|
||||||
|
core.setFailed(`Gradle build failed: ${result.buildScanUrl}`)
|
||||||
|
} else {
|
||||||
|
core.setFailed(`Gradle build failed: process exited with status ${result.status}`)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (result.buildScanUrl) {
|
||||||
|
core.notice(`Gradle build succeeded: ${result.buildScanUrl}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(String(error))
|
core.setFailed(String(error))
|
||||||
@@ -46,10 +48,7 @@ export async function run(): Promise<void> {
|
|||||||
|
|
||||||
run()
|
run()
|
||||||
|
|
||||||
async function resolveGradleExecutable(
|
async function resolveGradleExecutable(workspaceDirectory: string, buildRootDirectory: string): Promise<string> {
|
||||||
workspaceDirectory: string,
|
|
||||||
buildRootDirectory: string
|
|
||||||
): Promise<string> {
|
|
||||||
const gradleVersion = core.getInput('gradle-version')
|
const gradleVersion = core.getInput('gradle-version')
|
||||||
if (gradleVersion !== '' && gradleVersion !== 'wrapper') {
|
if (gradleVersion !== '' && gradleVersion !== 'wrapper') {
|
||||||
return path.resolve(await provision.gradleVersion(gradleVersion))
|
return path.resolve(await provision.gradleVersion(gradleVersion))
|
||||||
@@ -66,9 +65,7 @@ async function resolveGradleExecutable(
|
|||||||
function resolveBuildRootDirectory(baseDirectory: string): string {
|
function resolveBuildRootDirectory(baseDirectory: string): string {
|
||||||
const buildRootDirectory = core.getInput('build-root-directory')
|
const buildRootDirectory = core.getInput('build-root-directory')
|
||||||
const resolvedBuildRootDirectory =
|
const resolvedBuildRootDirectory =
|
||||||
buildRootDirectory === ''
|
buildRootDirectory === '' ? path.resolve(baseDirectory) : path.resolve(baseDirectory, buildRootDirectory)
|
||||||
? path.resolve(baseDirectory)
|
|
||||||
: path.resolve(baseDirectory, buildRootDirectory)
|
|
||||||
return resolvedBuildRootDirectory
|
return resolvedBuildRootDirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,7 +7,7 @@ import * as cache from '@actions/cache'
|
|||||||
import * as toolCache from '@actions/tool-cache'
|
import * as toolCache from '@actions/tool-cache'
|
||||||
|
|
||||||
import * as gradlew from './gradlew'
|
import * as gradlew from './gradlew'
|
||||||
import {isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
import {handleCacheFailure, isCacheDisabled, isCacheReadOnly} from './cache-utils'
|
||||||
|
|
||||||
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
|
const gradleVersionsBaseUrl = 'https://services.gradle.org/versions'
|
||||||
|
|
||||||
@@ -19,9 +19,7 @@ export async function gradleVersion(version: string): Promise<string> {
|
|||||||
case 'current':
|
case 'current':
|
||||||
return gradleCurrent()
|
return gradleCurrent()
|
||||||
case 'rc':
|
case 'rc':
|
||||||
core.warning(
|
core.warning(`Specifying gradle-version 'rc' has been deprecated. Use 'release-candidate' instead.`)
|
||||||
`Specifying gradle-version 'rc' has been deprecated. Use 'release-candidate' instead.`
|
|
||||||
)
|
|
||||||
return gradleReleaseCandidate()
|
return gradleReleaseCandidate()
|
||||||
case 'release-candidate':
|
case 'release-candidate':
|
||||||
return gradleReleaseCandidate()
|
return gradleReleaseCandidate()
|
||||||
@@ -35,16 +33,12 @@ export async function gradleVersion(version: string): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function gradleCurrent(): Promise<string> {
|
async function gradleCurrent(): Promise<string> {
|
||||||
const versionInfo = await gradleVersionDeclaration(
|
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/current`)
|
||||||
`${gradleVersionsBaseUrl}/current`
|
|
||||||
)
|
|
||||||
return provisionGradle(versionInfo)
|
return provisionGradle(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gradleReleaseCandidate(): Promise<string> {
|
async function gradleReleaseCandidate(): Promise<string> {
|
||||||
const versionInfo = await gradleVersionDeclaration(
|
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-candidate`)
|
||||||
`${gradleVersionsBaseUrl}/release-candidate`
|
|
||||||
)
|
|
||||||
if (versionInfo && versionInfo.version && versionInfo.downloadUrl) {
|
if (versionInfo && versionInfo.version && versionInfo.downloadUrl) {
|
||||||
return provisionGradle(versionInfo)
|
return provisionGradle(versionInfo)
|
||||||
}
|
}
|
||||||
@@ -53,16 +47,12 @@ async function gradleReleaseCandidate(): Promise<string> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function gradleNightly(): Promise<string> {
|
async function gradleNightly(): Promise<string> {
|
||||||
const versionInfo = await gradleVersionDeclaration(
|
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/nightly`)
|
||||||
`${gradleVersionsBaseUrl}/nightly`
|
|
||||||
)
|
|
||||||
return provisionGradle(versionInfo)
|
return provisionGradle(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gradleReleaseNightly(): Promise<string> {
|
async function gradleReleaseNightly(): Promise<string> {
|
||||||
const versionInfo = await gradleVersionDeclaration(
|
const versionInfo = await gradleVersionDeclaration(`${gradleVersionsBaseUrl}/release-nightly`)
|
||||||
`${gradleVersionsBaseUrl}/release-nightly`
|
|
||||||
)
|
|
||||||
return provisionGradle(versionInfo)
|
return provisionGradle(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,34 +64,24 @@ async function gradle(version: string): Promise<string> {
|
|||||||
return provisionGradle(versionInfo)
|
return provisionGradle(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function gradleVersionDeclaration(
|
async function gradleVersionDeclaration(url: string): Promise<GradleVersionInfo> {
|
||||||
url: string
|
|
||||||
): Promise<GradleVersionInfo> {
|
|
||||||
return await httpGetGradleVersion(url)
|
return await httpGetGradleVersion(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function findGradleVersionDeclaration(
|
async function findGradleVersionDeclaration(version: string): Promise<GradleVersionInfo | undefined> {
|
||||||
version: string
|
const gradleVersions = await httpGetGradleVersions(`${gradleVersionsBaseUrl}/all`)
|
||||||
): Promise<GradleVersionInfo | undefined> {
|
|
||||||
const gradleVersions = await httpGetGradleVersions(
|
|
||||||
`${gradleVersionsBaseUrl}/all`
|
|
||||||
)
|
|
||||||
return gradleVersions.find((entry: GradleVersionInfo) => {
|
return gradleVersions.find((entry: GradleVersionInfo) => {
|
||||||
return entry.version === version
|
return entry.version === version
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function provisionGradle(
|
async function provisionGradle(versionInfo: GradleVersionInfo): Promise<string> {
|
||||||
versionInfo: GradleVersionInfo
|
|
||||||
): Promise<string> {
|
|
||||||
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
|
return core.group(`Provision Gradle ${versionInfo.version}`, async () => {
|
||||||
return locateGradleAndDownloadIfRequired(versionInfo)
|
return locateGradleAndDownloadIfRequired(versionInfo)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function locateGradleAndDownloadIfRequired(
|
async function locateGradleAndDownloadIfRequired(versionInfo: GradleVersionInfo): Promise<string> {
|
||||||
versionInfo: GradleVersionInfo
|
|
||||||
): Promise<string> {
|
|
||||||
const installsDir = path.join(os.homedir(), 'gradle-installations/installs')
|
const installsDir = path.join(os.homedir(), 'gradle-installations/installs')
|
||||||
const installDir = path.join(installsDir, `gradle-${versionInfo.version}`)
|
const installDir = path.join(installsDir, `gradle-${versionInfo.version}`)
|
||||||
if (fs.existsSync(installDir)) {
|
if (fs.existsSync(installDir)) {
|
||||||
@@ -120,13 +100,8 @@ async function locateGradleAndDownloadIfRequired(
|
|||||||
return executable
|
return executable
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadAndCacheGradleDistribution(
|
async function downloadAndCacheGradleDistribution(versionInfo: GradleVersionInfo): Promise<string> {
|
||||||
versionInfo: GradleVersionInfo
|
const downloadPath = path.join(os.homedir(), `gradle-installations/downloads/gradle-${versionInfo.version}-bin.zip`)
|
||||||
): Promise<string> {
|
|
||||||
const downloadPath = path.join(
|
|
||||||
os.homedir(),
|
|
||||||
`gradle-installations/downloads/gradle-${versionInfo.version}-bin.zip`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isCacheDisabled()) {
|
if (isCacheDisabled()) {
|
||||||
await downloadGradleDistribution(versionInfo, downloadPath)
|
await downloadGradleDistribution(versionInfo, downloadPath)
|
||||||
@@ -134,45 +109,32 @@ async function downloadAndCacheGradleDistribution(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = `gradle-${versionInfo.version}`
|
const cacheKey = `gradle-${versionInfo.version}`
|
||||||
const restoreKey = await cache.restoreCache([downloadPath], cacheKey)
|
try {
|
||||||
if (restoreKey) {
|
const restoreKey = await cache.restoreCache([downloadPath], cacheKey)
|
||||||
core.info(
|
if (restoreKey) {
|
||||||
`Restored Gradle distribution ${cacheKey} from cache to ${downloadPath}`
|
core.info(`Restored Gradle distribution ${cacheKey} from cache to ${downloadPath}`)
|
||||||
)
|
return downloadPath
|
||||||
return downloadPath
|
}
|
||||||
|
} catch (error) {
|
||||||
|
handleCacheFailure(error, `Restore Gradle distribution ${versionInfo.version} failed`)
|
||||||
}
|
}
|
||||||
core.info(
|
|
||||||
`Gradle distribution ${versionInfo.version} not found in cache. Will download.`
|
core.info(`Gradle distribution ${versionInfo.version} not found in cache. Will download.`)
|
||||||
)
|
|
||||||
await downloadGradleDistribution(versionInfo, downloadPath)
|
await downloadGradleDistribution(versionInfo, downloadPath)
|
||||||
|
|
||||||
if (!isCacheReadOnly()) {
|
if (!isCacheReadOnly()) {
|
||||||
try {
|
try {
|
||||||
await cache.saveCache([downloadPath], cacheKey)
|
await cache.saveCache([downloadPath], cacheKey)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Fail on validation errors or non-errors (the latter to keep Typescript happy)
|
handleCacheFailure(error, `Save Gradle distribution ${versionInfo.version} failed`)
|
||||||
if (
|
|
||||||
error instanceof cache.ValidationError ||
|
|
||||||
!(error instanceof Error)
|
|
||||||
) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
core.warning(error.message)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return downloadPath
|
return downloadPath
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadGradleDistribution(
|
async function downloadGradleDistribution(versionInfo: GradleVersionInfo, downloadPath: string): Promise<void> {
|
||||||
versionInfo: GradleVersionInfo,
|
|
||||||
downloadPath: string
|
|
||||||
): Promise<void> {
|
|
||||||
await toolCache.downloadTool(versionInfo.downloadUrl, downloadPath)
|
await toolCache.downloadTool(versionInfo.downloadUrl, downloadPath)
|
||||||
core.info(
|
core.info(`Downloaded ${versionInfo.downloadUrl} to ${downloadPath} (size ${fs.statSync(downloadPath).size})`)
|
||||||
`Downloaded ${versionInfo.downloadUrl} to ${downloadPath} (size ${
|
|
||||||
fs.statSync(downloadPath).size
|
|
||||||
})`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function executableFrom(installDir: string): string {
|
function executableFrom(installDir: string): string {
|
||||||
@@ -183,9 +145,7 @@ async function httpGetGradleVersion(url: string): Promise<GradleVersionInfo> {
|
|||||||
return JSON.parse(await httpGetString(url))
|
return JSON.parse(await httpGetString(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function httpGetGradleVersions(
|
async function httpGetGradleVersions(url: string): Promise<GradleVersionInfo[]> {
|
||||||
url: string
|
|
||||||
): Promise<GradleVersionInfo[]> {
|
|
||||||
return JSON.parse(await httpGetString(url))
|
return JSON.parse(await httpGetString(url))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user