mirror of
https://github.com/gradle/gradle-build-action.git
synced 2025-10-20 15:18:56 +08:00
Compare commits
61 Commits
v2.0-beta.
...
v2.0-rc.1
Author | SHA1 | Date | |
---|---|---|---|
|
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 | ||
|
21dee71590 | ||
|
83e2129213 | ||
|
b16787d530 | ||
|
204870af89 | ||
|
0918f5f2a4 | ||
|
1b1a3c48ad | ||
|
a7174b82a2 | ||
|
3de71f2c52 | ||
|
5576baa56b | ||
|
1026c62889 | ||
|
1fb8644035 | ||
|
5a5a5b4387 | ||
|
cc5cdb7fe0 | ||
|
e0d37eb073 | ||
|
bdd89aa34f | ||
|
18cdc8bf28 | ||
|
656ad4b5f2 |
3
.github/workflows/dev.yml
vendored
3
.github/workflows/dev.yml
vendored
@@ -1,5 +1,4 @@
|
|||||||
# make sure the build works and doesn't produce spurious changes
|
name: Verify generated outputs
|
||||||
name: dev
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
12
.github/workflows/failure-cases.yml
vendored
12
.github/workflows/failure-cases.yml
vendored
@@ -1,37 +1,33 @@
|
|||||||
# Run builds under certain failure conditions to allow the output to be manually inspected.
|
name: Execute failure cases
|
||||||
# These build invocations are informational only, and are expected to fail
|
|
||||||
name: failure-cases
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
|
||||||
push:
|
|
||||||
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:
|
||||||
|
|
||||||
wrapper-missing:
|
wrapper-missing:
|
||||||
continue-on-error: true
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Test wrapper missing
|
- name: Test wrapper missing
|
||||||
uses: ./
|
uses: ./
|
||||||
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
arguments: help
|
arguments: help
|
||||||
|
|
||||||
bad-configuration:
|
bad-configuration:
|
||||||
continue-on-error: true
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
- name: Test bad config value
|
- name: Test bad config value
|
||||||
uses: ./
|
uses: ./
|
||||||
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
arguments: help
|
arguments: help
|
||||||
|
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
|
71
.github/workflows/integTest-caching-config.yml
vendored
Normal file
71
.github/workflows/integTest-caching-config.yml
vendored
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
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"],
|
||||||
|
["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
|
||||||
|
["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"],
|
||||||
|
["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
|
||||||
|
["instrumented-jars", "caches/jars-*/*/"],
|
||||||
|
["kotlin-dsl", "caches/*/kotlin-dsl/*/*/"]
|
||||||
|
]
|
||||||
|
|
||||||
|
|
@@ -1,5 +1,4 @@
|
|||||||
# Verify the functionality works as expected
|
name: Test caching
|
||||||
name: integration-testing
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -7,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
|
||||||
@@ -15,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
|
||||||
@@ -23,56 +22,20 @@ 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
|
- name: Build with configuration-cache enabled
|
||||||
uses: ./
|
uses: ./
|
||||||
with:
|
with:
|
||||||
build-root-directory: __tests__/samples/basic
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
arguments: test --configuration-cache
|
arguments: test --configuration-cache
|
||||||
|
|
||||||
# Tests for executing with different Gradle versions.
|
|
||||||
# Each build verifies that it is executed with the expected Gradle version.
|
|
||||||
gradle-execution:
|
|
||||||
needs: seed-build
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
include:
|
|
||||||
- os: windows-latest
|
|
||||||
script-suffix: '.bat'
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout sources
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Test use defined Gradle version
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
gradle-version: 6.9
|
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
|
||||||
arguments: help -DgradleVersionCheck=6.9
|
|
||||||
cache-read-only: true
|
|
||||||
- name: Test use Gradle version alias
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
gradle-version: release-candidate
|
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
|
||||||
arguments: help -DgradleVersionCheck=7.2
|
|
||||||
cache-read-only: true
|
|
||||||
- name: Test use defined Gradle executable
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
gradle-executable: __tests__/samples/basic/gradlew${{ matrix.script-suffix }}
|
|
||||||
build-root-directory: __tests__/samples/no-wrapper
|
|
||||||
arguments: help -DgradleVersionCheck=7.1.1
|
|
||||||
cache-read-only: true
|
|
||||||
|
|
||||||
# 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
|
||||||
@@ -80,7 +43,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
|
||||||
|
|
||||||
@@ -89,7 +52,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
|
||||||
@@ -97,7 +60,7 @@ 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
|
||||||
|
|
||||||
@@ -106,7 +69,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
|
||||||
@@ -116,6 +79,25 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
VERIFY_CACHED_CONFIGURATION: true
|
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 --configuration-cache
|
||||||
cache-read-only: true
|
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
|
||||||
|
cache-read-only: true
|
||||||
|
gradle-home-cache-artifact-bundles: '[]'
|
||||||
|
|
107
.github/workflows/integTest-execution.yml
vendored
Normal file
107
.github/workflows/integTest-execution.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
name: Test Gradle execution
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Tests for executing with different Gradle versions.
|
||||||
|
# Each build verifies that it is executed with the expected Gradle version.
|
||||||
|
gradle-execution:
|
||||||
|
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 use defined Gradle version
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
gradle-version: 6.9
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
|
arguments: help -DgradleVersionCheck=6.9
|
||||||
|
- name: Test use Gradle version alias
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
gradle-version: release-candidate
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
|
arguments: help
|
||||||
|
- name: Test use defined Gradle executable
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
gradle-executable: __tests__/samples/groovy-dsl/gradlew${{ matrix.script-suffix }}
|
||||||
|
build-root-directory: __tests__/samples/no-wrapper
|
||||||
|
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')
|
||||||
|
|
61
.github/workflows/integTest-gradle-user-home.yml
vendored
Normal file
61
.github/workflows/integTest-gradle-user-home.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Test caching with a custom GRADLE_USER_HOME
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
env:
|
||||||
|
GRADLE_BUILD_ACTION_CACHE_KEY_PREFIX: ${{github.workflow}}#${{github.run_number}}-
|
||||||
|
GRADLE_USER_HOME: custom/gradle/home
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Test that the gradle-user-home cache will cache dependencies, by running build with --offline
|
||||||
|
dependencies-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 with --offline
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test --offline
|
||||||
|
cache-read-only: true
|
||||||
|
|
||||||
|
# Test that the gradle-user-home cache will cache and restore local build-cache
|
||||||
|
build-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 tasks from cache
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
build-root-directory: __tests__/samples/groovy-dsl
|
||||||
|
arguments: test -DverifyCachedBuild=true
|
||||||
|
cache-read-only: true
|
@@ -1,5 +1,4 @@
|
|||||||
# Make sure the action works on a clean machine without building
|
name: Test caching with Kotlin DSL
|
||||||
name: integration-testing-kotlin-dsl
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
@@ -7,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
|
||||||
@@ -26,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
|
170
README.md
170
README.md
@@ -2,9 +2,13 @@
|
|||||||
|
|
||||||
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` using the wrapper from the repository on ubuntu, macos and windows. The only prerequisite is to have Java installed: you define the version of Java you need to run the build using the `actions/setup-java` action.
|
The following workflow will run `./gradlew build` on ubuntu, macos and windows.
|
||||||
|
The only prerequisite is to have Java installed: you define the version of Java you need to run the build using the `actions/setup-java` action.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# .github/workflows/gradle-build-pr.yml
|
# .github/workflows/gradle-build-pr.yml
|
||||||
@@ -21,14 +25,29 @@ jobs:
|
|||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Gradle arguments
|
It is possible to configure multiple Gradle executions to run sequentially in the same job.
|
||||||
|
Each invocation will start its run with the filesystem state remaining from the previous execution.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
arguments: assemble
|
||||||
|
- uses: gradle/gradle-build-action@v2
|
||||||
|
with:
|
||||||
|
arguments: check
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gradle Execution
|
||||||
|
|
||||||
|
### 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
|
||||||
@@ -36,48 +55,56 @@ 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.
|
||||||
|
|
||||||
If you need to pass environment variables, simply use the GitHub Actions workflow syntax:
|
If you need to pass environment variables, use the GitHub Actions workflow syntax:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
|
with:
|
||||||
|
arguments: build
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run a build from a different directory
|
### Gradle build located in a subdirectory
|
||||||
|
|
||||||
|
By default, the action will execute Gradle in the root directory of your project.
|
||||||
|
Use the `build-root-directory` input to target a Gradle build in a subdirectory.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
build-root-directory: some/subdirectory
|
build-root-directory: some/subdirectory
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use a specific `gradle` executable
|
### Using a specific Gradle executable
|
||||||
|
|
||||||
|
The action will first look for a Gradle wrapper script in the root directory of your project.
|
||||||
|
If not found, `gradle` will be executed from the PATH.
|
||||||
|
Use the `gradle-executable` input to execute using a specific Gradle installation.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
gradle-executable: path/to/gradle
|
gradle-executable: /path/to/installed/gradle
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use a Gradle wrapper from a different directory
|
This mechanism can also be used to target a Gradle wrapper script that is located in a non-default location.
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: gradle/gradle-build-action@v1
|
|
||||||
with:
|
|
||||||
gradle-executable: path/to/gradlew
|
|
||||||
```
|
|
||||||
|
|
||||||
## Setup and use a declared Gradle version
|
### Download, install and use a specific Gradle version
|
||||||
|
|
||||||
|
The `gradle-build-action` is able to download and install a specific Gradle version to execute.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
gradle-version: 6.5
|
gradle-version: 6.5
|
||||||
```
|
```
|
||||||
@@ -94,7 +121,7 @@ Moreover, you can use the following aliases:
|
|||||||
| `nightly` | The latest [nightly](https://gradle.org/nightly/), fails if none. |
|
| `nightly` | The latest [nightly](https://gradle.org/nightly/), fails if none. |
|
||||||
| `release-nightly` | The latest [release nightly](https://gradle.org/release-nightly/), fails if none. |
|
| `release-nightly` | The latest [release nightly](https://gradle.org/release-nightly/), fails if none. |
|
||||||
|
|
||||||
This can be handy to, for example, automatically test your build with the next Gradle version once a release candidate is out:
|
This can be handy to automatically verify your build works with the latest release candidate of Gradle:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# .github/workflows/test-gradle-rc.yml
|
# .github/workflows/test-gradle-rc.yml
|
||||||
@@ -110,7 +137,7 @@ jobs:
|
|||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
gradle-version: release-candidate
|
gradle-version: release-candidate
|
||||||
arguments: build --dry-run # just test build configuration
|
arguments: build --dry-run # just test build configuration
|
||||||
@@ -118,28 +145,31 @@ jobs:
|
|||||||
|
|
||||||
## Caching
|
## Caching
|
||||||
|
|
||||||
This action provides 3 levels of caching to help speed up your GitHub Actions:
|
|
||||||
|
|
||||||
- `distributions` caches any distributions downloaded to satisfy a `gradle-version` parameter ;
|
|
||||||
- `gradle-user-home` caches downloaded dependencies, wrapper distributions, and other stuff from the Gradle User home directory ;
|
|
||||||
- `project-dot-gradle` caches stored [configuration-cache](https://docs.gradle.org/nightly/userguide/configuration_cache.html) state, saving time configuring the build.
|
|
||||||
|
|
||||||
Each of these are enabled by default. To save caching space, you can disable any of them as follows:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
distributions-cache-enabled: true
|
|
||||||
gradle-user-home-cache-enabled: true
|
|
||||||
project-dot-gradle-cache-enabled: true
|
|
||||||
```
|
|
||||||
|
|
||||||
The distributions cache uses a cache key that is unique to the downloaded distribution. This will not change over time.
|
|
||||||
|
|
||||||
The `gradle-user-home` and `project-dot-gradle` caches compute a cache key based on the current commit and the Gradle invocation.
|
|
||||||
As such, these are likely to change on each subsequent run of GitHub actions, allowing the most recent state to always be available in the GitHub actions cache.
|
|
||||||
|
|
||||||
By default, this action aims to cache any and all reusable state that may be speed up a subsequent build invocation.
|
By default, this action aims to cache any and all reusable state that may be speed up a subsequent build invocation.
|
||||||
|
|
||||||
At this time it is not possible to fine-tune this caching. If you have a legitimate use case for fine-grained caching or restricting which files are cached, please raise an issue.
|
The state that is cached includes:
|
||||||
|
- Any distributions downloaded to satisfy a `gradle-version` parameter ;
|
||||||
|
- A subset of the Gradle User Home directory, including downloaded dependencies, wrapper distributions, and the local build cache ;
|
||||||
|
- Any [configuration-cache](https://docs.gradle.org/nightly/userguide/configuration_cache.html) data stored in the project `.gradle` directory.
|
||||||
|
|
||||||
|
To reduce the space required for caching, this action makes a best effort to reduce duplication in cache entries.
|
||||||
|
|
||||||
|
Caching is enabled by default. You can disable caching for the action as follows:
|
||||||
|
```yaml
|
||||||
|
cache-disabled: true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cache keys
|
||||||
|
|
||||||
|
For cached distributions outside of Gradle User Home, the cache key is unique to the downloaded distribution. This 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.
|
||||||
|
As such, the cache key is likely to change on each subsequent run of GitHub actions.
|
||||||
|
This allows the most recent state to always be available in the GitHub actions cache.
|
||||||
|
|
||||||
|
To reduce duplication between cache entries, certain artifacts are cached independently based on their identity.
|
||||||
|
Artifacts that are cached independently include downloaded dependencies, downloaded wrapper distributions and generated Gradle API jars.
|
||||||
|
For example, this means that all jobs executing a particular version of the Gradle wrapper will share common entries for wrapper distributions and for generated Gradle API jars.
|
||||||
|
|
||||||
### Using the caches read-only
|
### Using the caches read-only
|
||||||
|
|
||||||
@@ -147,21 +177,50 @@ Cache storage space is limited for GitHub actions, and writing new cache 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 asfollows:
|
You can enable read-only caching for any of the caches as follows:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
distributions-cache-enabled: read-only
|
# Only write to the cache for builds on the 'main' branch.
|
||||||
gradle-user-home-cache-enabled: read-only
|
# Builds on other branches will only read existing entries from the cache.
|
||||||
project-dot-gradle-cache-enabled: read-only
|
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
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## Build scans
|
## Build scans
|
||||||
|
|
||||||
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will emit the link to the published build scan as an output named `build-scan-url`.
|
If your build publishes a [build scan](https://gradle.com/build-scans/) the `gradle-build-action` action will:
|
||||||
|
- Add a notice with the link to the GitHub Actions user interface
|
||||||
|
- Emit the link to the published build scan as an output named `build-scan-url`.
|
||||||
|
|
||||||
You can then use that link in subsequent actions of your workflow.
|
You can then use that link in subsequent actions of your workflow. For example:
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# .github/workflows/gradle-build-pr.yml
|
# .github/workflows/gradle-build-pr.yml
|
||||||
@@ -169,19 +228,16 @@ name: Run Gradle on PRs
|
|||||||
on: pull_request
|
on: pull_request
|
||||||
jobs:
|
jobs:
|
||||||
gradle:
|
gradle:
|
||||||
strategy:
|
runs-on: ubuntu-latest
|
||||||
matrix:
|
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-java@v1
|
- uses: actions/setup-java@v1
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11
|
||||||
- uses: gradle/gradle-build-action@v1
|
- uses: gradle/gradle-build-action@v2
|
||||||
|
id: gradle
|
||||||
with:
|
with:
|
||||||
arguments: build
|
arguments: build
|
||||||
id: gradle
|
|
||||||
- name: "Comment build scan url"
|
- name: "Comment build scan url"
|
||||||
uses: actions/github-script@v3
|
uses: actions/github-script@v3
|
||||||
if: github.event_name == 'pull_request' && failure()
|
if: github.event_name == 'pull_request' && failure()
|
||||||
|
@@ -11,5 +11,11 @@ describe('cacheUtils-utils', () => {
|
|||||||
const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
|
const hash = cacheUtils.hashStrings(['foo', 'bar', 'baz'])
|
||||||
expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
|
expect(hash).toBe('6df23dc03f9b54cc38a0fc1483df6e21')
|
||||||
})
|
})
|
||||||
|
it('normalized filenames', async () => {
|
||||||
|
const fileNames = ['/foo/bar/baz.zip', '../boo.html']
|
||||||
|
const posixHash = cacheUtils.hashFileNames(fileNames)
|
||||||
|
const windowsHash = cacheUtils.hashFileNames(fileNames)
|
||||||
|
expect(posixHash).toBe(windowsHash)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
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
|
|
@@ -1,5 +1,5 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id("com.gradle.enterprise") version("3.6.4")
|
id("com.gradle.enterprise") version("3.7")
|
||||||
}
|
}
|
||||||
|
|
||||||
gradleEnterprise {
|
gradleEnterprise {
|
||||||
|
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
|
|
@@ -1,3 +1,16 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.gradle.enterprise") version("3.7")
|
||||||
|
}
|
||||||
|
|
||||||
|
gradleEnterprise {
|
||||||
|
buildScan {
|
||||||
|
termsOfServiceUrl = "https://gradle.com/terms-of-service"
|
||||||
|
termsOfServiceAgree = "yes"
|
||||||
|
publishAlways()
|
||||||
|
uploadInBackground = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rootProject.name = 'no-wrapper'
|
rootProject.name = 'no-wrapper'
|
||||||
|
|
||||||
println "Using Gradle version: ${gradle.gradleVersion}"
|
println "Using Gradle version: ${gradle.gradleVersion}"
|
||||||
|
46
action.yml
46
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
|
||||||
|
default: |
|
||||||
|
[]
|
||||||
|
# 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"],
|
||||||
|
["dependency-jars", "caches/modules-*/files-*/**/*.jar"],
|
||||||
|
["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
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1642,9 +1642,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ansi-regex": {
|
"ansi-regex": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ansi-styles": {
|
"ansi-styles": {
|
||||||
@@ -6456,9 +6456,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"tmpl": {
|
"tmpl": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||||
"integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=",
|
"integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"to-fast-properties": {
|
"to-fast-properties": {
|
||||||
|
50
src/build-scan-capture.ts
Normal file
50
src/build-scan-capture.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
export function writeInitScript(): string {
|
||||||
|
const tmpDir = process.env['RUNNER_TEMP'] || ''
|
||||||
|
const initScript = path.resolve(tmpDir, 'build-scan-capture.init.gradle')
|
||||||
|
core.info(`Writing init script: ${initScript}`)
|
||||||
|
if (fs.existsSync(initScript)) {
|
||||||
|
return initScript
|
||||||
|
}
|
||||||
|
fs.writeFileSync(
|
||||||
|
initScript,
|
||||||
|
`
|
||||||
|
import org.gradle.util.GradleVersion
|
||||||
|
|
||||||
|
// Don't run against the included builds (if the main build has any).
|
||||||
|
def isTopLevelBuild = gradle.getParent() == null
|
||||||
|
if (isTopLevelBuild) {
|
||||||
|
def version = GradleVersion.current().baseVersion
|
||||||
|
def atLeastGradle4 = version >= GradleVersion.version("4.0")
|
||||||
|
def atLeastGradle6 = version >= GradleVersion.version("6.0")
|
||||||
|
|
||||||
|
if (atLeastGradle6) {
|
||||||
|
settingsEvaluated { settings ->
|
||||||
|
if (settings.pluginManager.hasPlugin("com.gradle.enterprise")) {
|
||||||
|
registerCallbacks(settings.extensions["gradleEnterprise"].buildScan, settings.rootProject.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (atLeastGradle4) {
|
||||||
|
projectsEvaluated { gradle ->
|
||||||
|
if (gradle.rootProject.pluginManager.hasPlugin("com.gradle.build-scan")) {
|
||||||
|
registerCallbacks(gradle.rootProject.extensions["buildScan"], gradle.rootProject.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def registerCallbacks(buildScanExtension, rootProjectName) {
|
||||||
|
buildScanExtension.with {
|
||||||
|
def scanFile = new File("gradle-build-scan.txt")
|
||||||
|
buildScanPublished { buildScan ->
|
||||||
|
scanFile.text = buildScan.buildScanUri
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
)
|
||||||
|
return initScript
|
||||||
|
}
|
@@ -5,35 +5,37 @@ 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, hashStrings} from './cache-utils'
|
import {
|
||||||
|
AbstractCache,
|
||||||
|
getCacheKeyPrefix,
|
||||||
|
hashFileNames,
|
||||||
|
tryDelete
|
||||||
|
} from './cache-utils'
|
||||||
|
|
||||||
// Which paths under Gradle User Home should be cached
|
const META_FILE_DIR = '.gradle-build-action'
|
||||||
// TODO: This should adapt for the `GRADLE_USER_HOME` environment variable
|
|
||||||
// TODO: Allow the user to override / tweak this set
|
|
||||||
const CACHE_PATH = ['~/.gradle/caches', '~/.gradle/notifications']
|
|
||||||
|
|
||||||
const COMMON_ARTIFACT_CACHES = new Map([
|
const INCLUDE_PATHS_PARAMETER = 'gradle-home-cache-includes'
|
||||||
['generated-gradle-jars', '~/.gradle/caches/*/generated-gradle-jars/*.jar'],
|
const EXCLUDE_PATHS_PARAMETER = 'gradle-home-cache-excludes'
|
||||||
['wrapper-zips', '~/.gradle/wrapper/dists/*/*/*.zip'],
|
const ARTIFACT_BUNDLES_PARAMETER = 'gradle-home-cache-artifact-bundles'
|
||||||
['dependency-jars', '~/.gradle/caches/modules-*/files-*/**/*.jar'],
|
|
||||||
['instrumented-jars', '~/.gradle/caches/jars-*/*/*.jar']
|
|
||||||
])
|
|
||||||
|
|
||||||
export class GradleUserHomeCache extends AbstractCache {
|
export class GradleUserHomeCache extends AbstractCache {
|
||||||
constructor() {
|
private gradleUserHome: string
|
||||||
|
|
||||||
|
constructor(rootDir: string) {
|
||||||
super('gradle', 'Gradle User Home')
|
super('gradle', 'Gradle User Home')
|
||||||
|
this.gradleUserHome = this.determineGradleUserHome(rootDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterRestore(): Promise<void> {
|
async afterRestore(): Promise<void> {
|
||||||
await this.reportCacheEntrySize('as restored from cache')
|
await this.reportGradleUserHomeSize('as restored from cache')
|
||||||
await this.restoreCommonArtifacts()
|
await this.restoreArtifactBundles()
|
||||||
await this.reportCacheEntrySize('after restoring common artifacts')
|
await this.reportGradleUserHomeSize('after restoring common artifacts')
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restoreCommonArtifacts(): Promise<void> {
|
private async restoreArtifactBundles(): Promise<void> {
|
||||||
const processes: Promise<void>[] = []
|
const processes: Promise<void>[] = []
|
||||||
for (const [bundle, pattern] of COMMON_ARTIFACT_CACHES) {
|
for (const [bundle, pattern] of this.getArtifactBundles()) {
|
||||||
const p = this.restoreCommonArtifactBundle(bundle, pattern)
|
const p = this.restoreArtifactBundle(bundle, pattern)
|
||||||
// Run sequentially when debugging enabled
|
// Run sequentially when debugging enabled
|
||||||
if (this.cacheDebuggingEnabled) {
|
if (this.cacheDebuggingEnabled) {
|
||||||
await p
|
await p
|
||||||
@@ -44,57 +46,204 @@ export class GradleUserHomeCache extends AbstractCache {
|
|||||||
await Promise.all(processes)
|
await Promise.all(processes)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async restoreCommonArtifactBundle(
|
private async restoreArtifactBundle(
|
||||||
bundle: string,
|
bundle: string,
|
||||||
pattern: string
|
artifactPath: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const cacheMetaFile = this.getCacheMetaFile(bundle)
|
const bundleMetaFile = this.getBundleMetaFile(bundle)
|
||||||
if (fs.existsSync(cacheMetaFile)) {
|
if (fs.existsSync(bundleMetaFile)) {
|
||||||
const cacheKey = fs.readFileSync(cacheMetaFile, 'utf-8').trim()
|
const cacheKey = fs.readFileSync(bundleMetaFile, 'utf-8').trim()
|
||||||
const restoreKey = await this.restoreCache([pattern], cacheKey)
|
const restoreKey = await this.restoreCache([artifactPath], cacheKey)
|
||||||
if (restoreKey) {
|
if (restoreKey) {
|
||||||
core.info(
|
core.info(
|
||||||
`Restored ${bundle} with key ${cacheKey} to ${pattern}`
|
`Restored ${bundle} with key ${cacheKey} to ${artifactPath}`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
this.debug(
|
this.debug(
|
||||||
`Failed to restore ${bundle} with key ${cacheKey} to ${pattern}`
|
`Did not restore ${bundle} with key ${cacheKey} to ${artifactPath}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.debug(
|
this.debug(
|
||||||
`No metafile found to restore ${bundle}: ${cacheMetaFile}`
|
`No metafile found to restore ${bundle}: ${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.getGradleUserHome(),
|
}
|
||||||
'caches',
|
|
||||||
`.gradle-build-action.${name}.cache`
|
async beforeSave(): Promise<void> {
|
||||||
|
await this.reportGradleUserHomeSize('before saving common artifacts')
|
||||||
|
this.removeExcludedPaths()
|
||||||
|
await this.saveArtifactBundles()
|
||||||
|
await this.reportGradleUserHomeSize(
|
||||||
|
"after saving common artifacts (only 'caches' and 'notifications' will be stored)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async reportCacheEntrySize(label: string): Promise<void> {
|
private removeExcludedPaths(): void {
|
||||||
|
const rawPaths: string[] = JSON.parse(
|
||||||
|
core.getInput(EXCLUDE_PATHS_PARAMETER)
|
||||||
|
)
|
||||||
|
const resolvedPaths = rawPaths.map(x =>
|
||||||
|
path.resolve(this.gradleUserHome, x)
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const p of resolvedPaths) {
|
||||||
|
this.debug(`Deleting excluded path: ${p}`)
|
||||||
|
tryDelete(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveArtifactBundles(): Promise<void> {
|
||||||
|
const processes: Promise<void>[] = []
|
||||||
|
for (const [bundle, pattern] of this.getArtifactBundles()) {
|
||||||
|
const p = this.saveArtifactBundle(bundle, pattern)
|
||||||
|
// Run sequentially when debugging enabled
|
||||||
|
if (this.cacheDebuggingEnabled) {
|
||||||
|
await p
|
||||||
|
}
|
||||||
|
processes.push(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(processes)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async saveArtifactBundle(
|
||||||
|
bundle: string,
|
||||||
|
artifactPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
const bundleMetaFile = this.getBundleMetaFile(bundle)
|
||||||
|
|
||||||
|
const globber = await glob.create(artifactPath, {
|
||||||
|
implicitDescendants: false,
|
||||||
|
followSymbolicLinks: false
|
||||||
|
})
|
||||||
|
const bundleFiles = await globber.glob()
|
||||||
|
|
||||||
|
// Handle no matching files
|
||||||
|
if (bundleFiles.length === 0) {
|
||||||
|
this.debug(`No files found to cache for ${bundle}`)
|
||||||
|
if (fs.existsSync(bundleMetaFile)) {
|
||||||
|
tryDelete(bundleMetaFile)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const previouslyRestoredKey = fs.existsSync(bundleMetaFile)
|
||||||
|
? fs.readFileSync(bundleMetaFile, 'utf-8').trim()
|
||||||
|
: ''
|
||||||
|
const cacheKey = this.createCacheKey(bundle, bundleFiles)
|
||||||
|
|
||||||
|
if (previouslyRestoredKey === cacheKey) {
|
||||||
|
this.debug(
|
||||||
|
`No change to previously restored ${bundle}. Not caching.`
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
|
||||||
|
await this.saveCache([artifactPath], cacheKey)
|
||||||
|
this.writeBundleMetaFile(bundleMetaFile, cacheKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const file of bundleFiles) {
|
||||||
|
tryDelete(file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createCacheKey(bundle: string, files: string[]): string {
|
||||||
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
|
const relativeFiles = files.map(x =>
|
||||||
|
path.relative(this.gradleUserHome, x)
|
||||||
|
)
|
||||||
|
const key = hashFileNames(relativeFiles)
|
||||||
|
|
||||||
|
this.debug(
|
||||||
|
`Generating cache key for ${bundle} from files: ${relativeFiles}`
|
||||||
|
)
|
||||||
|
|
||||||
|
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 {
|
||||||
|
const customGradleUserHome = process.env['GRADLE_USER_HOME']
|
||||||
|
if (customGradleUserHome) {
|
||||||
|
return path.resolve(rootDir, customGradleUserHome)
|
||||||
|
}
|
||||||
|
|
||||||
|
return path.resolve(os.homedir(), '.gradle')
|
||||||
|
}
|
||||||
|
|
||||||
|
protected cacheOutputExists(): boolean {
|
||||||
|
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
|
||||||
|
const dir = path.resolve(this.gradleUserHome, 'caches')
|
||||||
|
return fs.existsSync(dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCachePath(): string[] {
|
||||||
|
const rawPaths: string[] = JSON.parse(
|
||||||
|
core.getInput(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 resolveCachePath(rawPath: string): string {
|
||||||
|
if (rawPath.startsWith('!')) {
|
||||||
|
const resolved = this.resolveCachePath(rawPath.substring(1))
|
||||||
|
return `!${resolved}`
|
||||||
|
}
|
||||||
|
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) {
|
if (!this.cacheDebuggingEnabled) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const gradleUserHome = path.resolve(os.homedir(), '.gradle')
|
if (!fs.existsSync(this.gradleUserHome)) {
|
||||||
if (!fs.existsSync(gradleUserHome)) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const result = await exec.getExecOutput(
|
const result = await exec.getExecOutput(
|
||||||
'du',
|
'du',
|
||||||
['-h', '-c', '-t', '5M'],
|
['-h', '-c', '-t', '5M'],
|
||||||
{
|
{
|
||||||
cwd: gradleUserHome,
|
cwd: this.gradleUserHome,
|
||||||
silent: true,
|
silent: true,
|
||||||
ignoreReturnCode: true
|
ignoreReturnCode: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
core.info(`Gradle User Home cache entry (directories >5M): ${label}`)
|
core.info(`Gradle User Home (directories >5M): ${label}`)
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
result.stdout
|
result.stdout
|
||||||
@@ -109,84 +258,4 @@ export class GradleUserHomeCache extends AbstractCache {
|
|||||||
|
|
||||||
core.info('-----------------------')
|
core.info('-----------------------')
|
||||||
}
|
}
|
||||||
|
|
||||||
async beforeSave(): Promise<void> {
|
|
||||||
await this.saveCommonArtifacts()
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveCommonArtifacts(): Promise<void> {
|
|
||||||
const processes: Promise<void>[] = []
|
|
||||||
for (const [bundle, pattern] of COMMON_ARTIFACT_CACHES) {
|
|
||||||
const p = this.saveCommonArtifactBundle(bundle, pattern)
|
|
||||||
// Run sequentially when debugging enabled
|
|
||||||
if (this.cacheDebuggingEnabled) {
|
|
||||||
await p
|
|
||||||
}
|
|
||||||
processes.push(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
await Promise.all(processes)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async saveCommonArtifactBundle(
|
|
||||||
bundle: string,
|
|
||||||
pattern: string
|
|
||||||
): Promise<void> {
|
|
||||||
const cacheMetaFile = this.getCacheMetaFile(bundle)
|
|
||||||
|
|
||||||
const globber = await glob.create(pattern)
|
|
||||||
const commonArtifactFiles = await globber.glob()
|
|
||||||
|
|
||||||
// Handle no matching files
|
|
||||||
if (commonArtifactFiles.length === 0) {
|
|
||||||
this.debug(`No files found to cache for ${bundle}`)
|
|
||||||
if (fs.existsSync(cacheMetaFile)) {
|
|
||||||
fs.unlinkSync(cacheMetaFile)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const previouslyRestoredKey = fs.existsSync(cacheMetaFile)
|
|
||||||
? fs.readFileSync(cacheMetaFile, 'utf-8').trim()
|
|
||||||
: ''
|
|
||||||
const cacheKey = this.createCacheKey(
|
|
||||||
bundle,
|
|
||||||
hashStrings(commonArtifactFiles)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (previouslyRestoredKey === cacheKey) {
|
|
||||||
this.debug(
|
|
||||||
`No change to previously restored ${bundle}. Not caching.`
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
core.info(`Caching ${bundle} with cache key: ${cacheKey}`)
|
|
||||||
await this.saveCache([pattern], cacheKey)
|
|
||||||
|
|
||||||
this.debug(`Writing cache metafile: ${cacheMetaFile}`)
|
|
||||||
fs.writeFileSync(cacheMetaFile, cacheKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const file of commonArtifactFiles) {
|
|
||||||
fs.unlinkSync(file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected createCacheKey(bundle: string, key: string): string {
|
|
||||||
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
|
||||||
return `${cacheKeyPrefix}${bundle}-${key}`
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getGradleUserHome(): string {
|
|
||||||
return path.resolve(os.homedir(), '.gradle')
|
|
||||||
}
|
|
||||||
|
|
||||||
protected cacheOutputExists(): boolean {
|
|
||||||
// Need to check for 'caches' directory to avoid incorrect detection on MacOS agents
|
|
||||||
const dir = path.resolve(this.getGradleUserHome(), 'caches')
|
|
||||||
return fs.existsSync(dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getCachePath(): string[] {
|
|
||||||
return CACHE_PATH
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -2,22 +2,34 @@ 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 github from '@actions/github'
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
|
import * as path from 'path'
|
||||||
|
import * as fs from 'fs'
|
||||||
|
|
||||||
|
const CACHE_DISABLED_PARAMETER = 'cache-disabled'
|
||||||
|
const CACHE_READONLY_PARAMETER = 'cache-read-only'
|
||||||
|
const JOB_CONTEXT_PARAMETER = 'workflow-job-context'
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCacheKeyPrefix(): string {
|
||||||
|
// Prefix can be used to force change all cache keys (defaults to cache protocol version)
|
||||||
|
return process.env[CACHE_PREFIX_VAR] || 'v3-'
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateCacheKey(cacheName: string): CacheKey {
|
function generateCacheKey(cacheName: string): CacheKey {
|
||||||
// Prefix can be used to force change all cache keys
|
const cacheKeyPrefix = getCacheKeyPrefix()
|
||||||
const cacheKeyPrefix = process.env['CACHE_KEY_PREFIX'] || ''
|
|
||||||
|
|
||||||
// At the most general level, share caches for all executions on the same OS
|
// At the most general level, share caches for all executions on the same OS
|
||||||
const runnerOs = process.env['RUNNER_OS'] || ''
|
const runnerOs = process.env['RUNNER_OS'] || ''
|
||||||
@@ -41,7 +53,7 @@ function generateCacheKey(cacheName: string): CacheKey {
|
|||||||
|
|
||||||
function determineJobContext(): string {
|
function determineJobContext(): string {
|
||||||
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
|
// By default, we hash the full `matrix` data for the run, to uniquely identify this job invocation
|
||||||
const workflowJobContext = core.getInput('workflow-job-context')
|
const workflowJobContext = core.getInput(JOB_CONTEXT_PARAMETER)
|
||||||
return hashStrings([workflowJobContext])
|
return hashStrings([workflowJobContext])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,6 +65,40 @@ export function hashStrings(values: string[]): string {
|
|||||||
return hash.digest('hex')
|
return hash.digest('hex')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hashFileNames(fileNames: string[]): string {
|
||||||
|
return hashStrings(
|
||||||
|
fileNames.map(x => x.replace(new RegExp(`\\${path.sep}`, 'g'), '/'))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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))
|
||||||
|
}
|
||||||
|
|
||||||
class CacheKey {
|
class CacheKey {
|
||||||
key: string
|
key: string
|
||||||
restoreKeys: string[]
|
restoreKeys: string[]
|
||||||
@@ -91,6 +137,12 @@ export abstract class AbstractCache {
|
|||||||
|
|
||||||
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
core.saveState(this.cacheKeyStateKey, cacheKey.key)
|
||||||
|
|
||||||
|
this.debug(
|
||||||
|
`Requesting ${this.cacheDescription} with
|
||||||
|
key:${cacheKey.key}
|
||||||
|
restoreKeys:[${cacheKey.restoreKeys}]`
|
||||||
|
)
|
||||||
|
|
||||||
const cacheResult = await this.restoreCache(
|
const cacheResult = await this.restoreCache(
|
||||||
this.getCachePath(),
|
this.getCachePath(),
|
||||||
cacheKey.key,
|
cacheKey.key,
|
||||||
@@ -110,7 +162,13 @@ export abstract class AbstractCache {
|
|||||||
`Restored ${this.cacheDescription} from cache key: ${cacheResult}`
|
`Restored ${this.cacheDescription} from cache key: ${cacheResult}`
|
||||||
)
|
)
|
||||||
|
|
||||||
await this.afterRestore()
|
try {
|
||||||
|
await this.afterRestore()
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(
|
||||||
|
`Restore ${this.cacheDescription} failed in 'afterRestore': ${error}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -162,7 +220,14 @@ export abstract class AbstractCache {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.beforeSave()
|
try {
|
||||||
|
await this.beforeSave()
|
||||||
|
} catch (error) {
|
||||||
|
core.warning(
|
||||||
|
`Save ${this.cacheDescription} failed in 'beforeSave': ${error}`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
core.info(
|
core.info(
|
||||||
`Caching ${this.cacheDescription} with cache key: ${cacheKey}`
|
`Caching ${this.cacheDescription} with cache key: ${cacheKey}`
|
||||||
|
@@ -7,14 +7,16 @@ const BUILD_ROOT_DIR = 'BUILD_ROOT_DIR'
|
|||||||
|
|
||||||
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([
|
return Promise.all([
|
||||||
new GradleUserHomeCache().restore(),
|
new GradleUserHomeCache(buildRootDirectory).restore(),
|
||||||
new ProjectDotGradleCache(buildRootDirectory).restore()
|
new ProjectDotGradleCache(buildRootDirectory).restore()
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@@ -22,14 +24,16 @@ export async function restore(buildRootDirectory: string): Promise<void> {
|
|||||||
|
|
||||||
export async function save(): Promise<void> {
|
export async function save(): Promise<void> {
|
||||||
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.'
|
||||||
|
)
|
||||||
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().save(),
|
new GradleUserHomeCache(buildRootDirectory).save(),
|
||||||
new ProjectDotGradleCache(buildRootDirectory).save()
|
new ProjectDotGradleCache(buildRootDirectory).save()
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
@@ -1,29 +1,36 @@
|
|||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
import {writeInitScript} from './build-scan-capture'
|
||||||
|
|
||||||
export async function execute(
|
export async function execute(
|
||||||
executable: string,
|
executable: string,
|
||||||
root: string,
|
root: string,
|
||||||
argv: string[]
|
args: string[]
|
||||||
): Promise<BuildResult> {
|
): Promise<BuildResult> {
|
||||||
let publishing = false
|
|
||||||
let buildScanUrl: string | undefined
|
let buildScanUrl: string | undefined
|
||||||
|
|
||||||
const status: number = await exec.exec(executable, argv, {
|
// TODO: instead of running with no-daemon, run `--stop` in post action.
|
||||||
|
args.push('--no-daemon')
|
||||||
|
|
||||||
|
const initScript = writeInitScript()
|
||||||
|
args.push('--init-script')
|
||||||
|
args.push(initScript)
|
||||||
|
|
||||||
|
const buildScanFile = path.resolve(root, 'gradle-build-scan.txt')
|
||||||
|
if (fs.existsSync(buildScanFile)) {
|
||||||
|
fs.unlinkSync(buildScanFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
const status: number = await exec.exec(executable, args, {
|
||||||
cwd: root,
|
cwd: root,
|
||||||
ignoreReturnCode: true,
|
ignoreReturnCode: true
|
||||||
listeners: {
|
|
||||||
stdline: (line: string) => {
|
|
||||||
if (line.includes('Publishing build scan...')) {
|
|
||||||
publishing = true
|
|
||||||
}
|
|
||||||
if (publishing && line.startsWith('http')) {
|
|
||||||
buildScanUrl = line.trim()
|
|
||||||
publishing = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (fs.existsSync(buildScanFile)) {
|
||||||
|
buildScanUrl = fs.readFileSync(buildScanFile, 'utf-8')
|
||||||
|
}
|
||||||
|
|
||||||
return new BuildResultImpl(status, buildScanUrl)
|
return new BuildResultImpl(status, buildScanUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
17
src/main.ts
17
src/main.ts
@@ -16,8 +16,6 @@ export async function run(): Promise<void> {
|
|||||||
await caches.restore(buildRootDirectory)
|
await caches.restore(buildRootDirectory)
|
||||||
|
|
||||||
const args: string[] = parseCommandLineArguments()
|
const args: string[] = parseCommandLineArguments()
|
||||||
// TODO: instead of running with no-daemon, run `--stop` in post action.
|
|
||||||
args.push('--no-daemon')
|
|
||||||
|
|
||||||
const result = await execution.execute(
|
const result = await execution.execute(
|
||||||
await resolveGradleExecutable(
|
await resolveGradleExecutable(
|
||||||
@@ -30,13 +28,20 @@ export async function run(): Promise<void> {
|
|||||||
|
|
||||||
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))
|
||||||
|
Reference in New Issue
Block a user