This is a GitHub Action for caching Gradle caches. In other words, this is @actions/cache customized for Gradle.
Key improvements over @actions/cache and gradle-command-action are:
- π Gradle remote build cache backend (pulls only the needed entries from GitHub cache)
- π Support multiple remote caches via gradle-multi-cache (e.g. GitHub Actions + S3)
- π Simplified configuration (action name + gradle command is enough for most case)
- πΎ Less space usage (GitHub imposes overall 5GiB limit by default, so cache space matters)
- π Link to Build Scan in build results
- π‘ Gradle build failure markers added to the diff view (e.g.
compileJava
orcompileKotlin
markers right in the commit diff)
v1
uses node16
which has been deprecated, so consider upgrading to v2
.
The upgrade requires only changing the version, however the newer node20
might be missing
if you use an old runner.
Add the following code to your workflow file in the .github/workflows
directory.
Note: Like with gradle-command-action, you can
specify gradle-version: release
to test with the current release version of Gradle, gradle-version: nightly
for testing Gradle nightly builds,
an so on (see gradle-version
below).
Note: For the security reasons
you might want to use Git SHA rather than branch name or tag name.
In other words, to avoid accidental tag update, you might want to use
burrunan/gradle-cache-action@03c71a8ba93d670980695505f48f49daf43704a6
rather than burrunan/gradle-cache-action@v1
.
Please see releases page to find out the commit ids: https://github.com/burrunan/gradle-cache-action/releases
You might use the following references are:
v1
,v2
-- this is a moving qualifier. It points to the latest release amongv1.x
v1.0
,v1.1
, ... -- those are fixed versions. They won't change over time
For the best security you might want to use burrunan/gradle-cache-action@v1
(see the ids at https://github.com/burrunan/gradle-cache-action/releases)
- uses: burrunan/gradle-cache-action@v1
name: Build PROJECT_NAME
# Extra environment variables for Gradle execution (regular GitHub Actions feature)
# Note: env must be outside of "with"
env:
VARIABLE: VALUE
with:
# If you have multiple jobs, use distinct job-id in in case you want to split caches
# For instance, jobs with different JDK versions can't share caches
# RUNNER_OS is added to job-id automatically
job-id: jdk8
# Specifies arguments for Gradle execution
# If arguments is missing or empty, then Gradle is not executed
arguments: build
# arguments can be multi-line for better readability
# arguments: |
# --no-paralell
# build
# -x test
# Gradle version to use for execution:
# wrapper (default), current, rc, nightly, release-nightly, or
# versions like 6.6 (see https://services.gradle.org/versions/all)
gradle-version: wrapper
# Properties are passed as -Pname=value
properties: |
kotlin.js.compiler=ir
kotlin.parallel.tasks.in.project=true
By default, the action enables the local
build cache, and it adds a remote build cache
that stores the data in GitHub Actions cache.
However, you might want to enable the Gradle Build Cache
for your local builds to make them faster, or even add a remote cache instance, so your local
builds can reuse artifacts that are build on CI.
This is how you can enable local build cache (don't forget to add --build-cache
option or
org.gradle.caching=true
property):
// settings.gradle.kts
val isCiServer = System.getenv().containsKey("CI")
// Cache build artifacts, so expensive operations do not need to be re-computed
buildCache {
local {
isEnabled = !isCiServer
}
}
Here's how you can integrate build cache to existing projects:
- Apache Calcite: apache/calcite#2114
- Apache JMeter: apache/jmeter#611
- pgjdbc: pgjdbc/pgjdbc#1862
- junit-pioneer: junit-pioneer/junit-pioneer#325
- opentelemetry-java-instrumentation: open-telemetry/opentelemetry-java-instrumentation#1054
The default configuration should suit most of the cases, however, there are extra knobs:
- uses: burrunan/gradle-cache-action@v1
name: Cache .gradle
# Extra environment variables for Gradle execution (regular GitHub Actions feature)
env:
VARIABLE: VALUE
with:
# If you have multiple jobs, use distinct job-id in case you want to split caches
# For instance, jobs with different JDK versions can't share caches
# RUNNER_OS is added to job-id automatically
job-id: jdk8
# Overrides $HOME
# home-directory: /home/user
# Disable caching of $HOME/.gradle/caches/*.*/generated-gradle-jars
save-generated-gradle-jars: false
# Disable remote cache that proxies requests to GitHub Actions cache
remote-build-cache-proxy-enabled: false
# Set the cache key for Gradle version (e.g. in case multiple jobs use different versions)
# By default the value is `wrapper`, so the version is determined from the gradle-wrapper.properties
# Note: this argument specifies the version for Gradle execution (if `arguments` is present)
# Supported values:
# wrapper (default), current, rc, nightly, release-nightly, or
# versions like 6.6 (see https://services.gradle.org/versions/all)
gradle-version: 6.5.1-custom
# Makes all non-main branch builds to use read-only caching
read-only: ${{ github.ref != 'refs/heads/main' }}
# Arguments for Gradle execution
arguments: build jacocoReport
# Properties are passed as -Pname=value
properties: |
kotlin.js.compiler=ir
kotlin.parallel.tasks.in.project=true
# Relative path under $GITHUB_WORKSPACE where Git repository is placed
build-root-directory: sub/directory
# Activates only the caches that are relevant for executing gradle command.
# This is helpful when build job executes multiple gradle commands sequentially.
# Then the caching is implemented in the very first one, and the subsequent should be marked
# with execution-only-caches: true
execution-only-caches: true
# Disable caching of ~/.gradle/caches/build-cache-*
save-local-build-cache: false
# Disable caching of ~/.gradle/caches/modules-*
save-gradle-dependencies-cache: false
# Extra files to take into account for ~/.gradle/caches dependencies
gradle-dependencies-cache-key: |
gradle/dependencies.kt
buildSrc/**/Version.kt
# Disable caching of ~/.m2/repository/
save-maven-dependencies-cache: false
# Ignore some of the paths when caching Maven Local repository
maven-local-ignore-paths: |
org/example/
com/example/
# Enable concurrent cache save and restore
# Default is concurrent=false for better log readability
concurrent: true
# Disable publishing Gradle Build Scan URL to job report
gradle-build-scan-report: false
# Disable warning about missing distributionSha256Sum property in gradle-wrapper.properties
gradle-distribution-sha-256-sum-warning: false
The current GitHub Action's cache (both actions/cache action and @actions/cache npm package) is immutable.
The cache can't be updated, so it does not work very good for caches like "Gradle dependencies" or "Maven local repository".
gradle-cache-action
creates a layered cache, and it uses a small "index" cache to identify the required layers.
If only a small fraction of files changes, then the action reuses the existing caches, and it adds a layer on top of it.
That enables to save cache space (GitHub has a default limit of 5 GiB), and it reduces upload time as only
the cache receives only the updated files.
gradle-cache-action
launches a small proxy server that listens for Gradle requests and
then it redirects the requests to the @actions/cache
API.
That makes Gradle believe it is talking to a regular remote cache, and the cache receives only the relevant updates. The increased granularity enables GitHub to evict entries better (it removes unused entries automatically).
The action configures the URL to the cache proxy via the ~/.gradle/init.gradle
script, and
Gradle picks it up automatically
Note: Saving GitHub Actions caches might take noticeable time (e.g. 100 ms), so the cache uploads in the background. In other words, build scan would show virtually zero response times for cache save operations.
If your build already has a remote cache declared (e.g. you are using your own cache),
then gradle-cache-action
would configure both remote caches.
It would read from the GitHub cache first, and it would save data to both caches.
The multi-cache feature can be disabled via multi-cache-enabled: false
.
- Read and agree to the terms of service: https://gradle.com/terms-of-service
- Add
--scan
toarguments:
, and add the following tosettings.gradle.kts
plugins {
`gradle-enterprise`
}
val isCiServer = System.getenv().containsKey("CI")
if (isCiServer) {
gradleEnterprise {
buildScan {
termsOfServiceUrl = "https://gradle.com/terms-of-service"
termsOfServiceAgree = "yes"
tag("CI")
}
}
}
gradle-command-action
was started as a Kotlin/JS experiment for making a customized
@actions/cache that would make Gradle builds faster.
Then it turned out there's a proxy remote cache requests to the @actions/cache
API can be used when the caching
action executes Gradle, so the gradle-cache-action
got a Gradle execution feature.
Of course, the same could have been made in gradle-command-action, however:
-
The author was not familiar with TypeScript ecosystem (stdlib, typical libraries, testing libraries, etc.)
-
Caching logic is collections-heavy, and Kotlin stdlib shines here.
For instance, in Kotlin
list + list
adds lists, andarray.associateWith { valueFor(it) }
converts arrays to maps. This is easy to write without consulting StackOverflow, the code is readable, and it does not require you to fight with the compiler. -
A single language helps when building connected components.
gradle-cache-action
integrates with gradle-multi-cache and gradle-s3-build-cache, and they all are Kotlin-based.
Yes, you can. If you omit arguments:
, then the action runs in cache-only
mode.
It won't launch Gradle.
This might be complicated, see #15.
Currently, the workaround is to configure execution-only-caches: true
for all but one
gradle-cache-action
executions.
Then one of the actions would do the cache save and restore, and the rest would use their own
caches only.
Contributions are always welcome! If you'd like to contribute (and we hope you do) please open a pull request.
Apache 2.0
Vladimir Sitnikov sitnikov.vladimir@gmail.com