You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add extra cache key for multiple jobs on the same pom, but a different part #705
Description:
I want to optionally annotate the cache-key with an extra description, such that if we have multiple jobs, that all only have a partial execution of the pom, they can still get a full cached .m2 repo.
Something like:
- name: Set up JDKuses: actions/setup-java@v4with:
java-version: ${{ matrix.jdk }}cache: 'maven'cache-key-prefix: 'test'
Justification:
For example
runner 1 only runs mvn test (possibly in a matrix)
runner 2 triggers some lints with mvn check (runs in parallel to runner 1)
runner 3 runs the build & publish action after 1 and 2 are done
Right now, for a new pom the following happens:
runner 1 sees a new checksum, so it doesn't download an old cache, and then starts the regular.
at the end of runner 1, the current .m2 repo, that is only filled with the plugins to run mvn test gets submitted as the cache for this specific pom.xml
runner 2 actually races with this submission, with again a sub-optimal filled .m2 repo
runner 3 starts, downloads the cache of runner 1 or runner 2
the maven command that is run has to download quite some dependencies not needed for testing or checking.
as runner 3 stops, the java-action plugin does not upload this .m2 repo, as there was a cache hit when it started.
In situations like this, every run afterwards will have runner 3 re-download dependencies, and runner 1 or 2, depending on who lost the race. The current workaround is to add some spaces to the pom.xml just before the setup-java action is run.
Are you willing to submit a PR?
Yes, if I get an indication if such a PR has a chance of getting merged, I'm willing to allocate some time to add this.
Thanks for the request! The good news is you can achieve exactly this today, without any change to setup-java, by managing the cache directly with actions/cache.
How it works
When you omit the cache: input, setup-java does no caching at all — it neither restores nor saves. That leaves you free to drive actions/cache yourself with a fully custom key (including any prefix) plus restore-keys: fallbacks. The paths setup-java would normally cache are:
A "full build" job populates a shared cache; partial jobs use their own prefixed key and fall back to the shared one via restore-keys: — which is precisely the "get a full cached .m2" behavior you described.
jobs:
full-build:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4with:
distribution: temurinjava-version: '21'# NOTE: no `cache:` here — we manage caching ourselves
- uses: actions/cache@v4with:
path: | ~/.m2/repository ~/.m2/wrapper/distskey: full-build-maven-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pom.xml') }}
- run: mvn -B installunit-tests:
runs-on: ubuntu-lateststeps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4with:
distribution: temurinjava-version: '21'
- uses: actions/cache@v4with:
path: | ~/.m2/repository ~/.m2/wrapper/dists# Your own prefixed primary key...key: unit-tests-maven-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/pom.xml') }}# ...falling back to the shared full-build cache if it isn't present yetrestore-keys: | full-build-maven-${{ runner.os }}-${{ runner.arch }}-
- run: mvn -B test -pl :my-unit-module
Gradle/sbt work the same way — just swap the path: and hashFiles(...) globs for the lists above.
Why we lean toward this over a native cache-key-prefix
A prefix input alone wouldn't actually give you the behavior you want here: the crucial piece is restore-keys: (the fallback to a fuller cache), and setup-javaintentionally does not set restore-keys — see the note in src/cache.ts referencing #269 (we prefer a cold cache over a stale one after a dependency change). Managing the cache with actions/cache directly gives you full control over both the key prefix and the fallback chain, which is strictly more capable. This is also consistent with actions/setup-node and actions/setup-python, which don't offer a cache-key-prefix input either.
A couple of caveats:
Cache keys are already effectively namespaced per OS/arch; keep ${{ runner.os }}-${{ runner.arch }} in your key if you run a cross-platform matrix.
GitHub caches are immutable per key and scoped per branch (PRs can read base-branch caches but only write to their own scope) — this is true of the built-in cache too.
Given the above, I'm inclined to close this as already achievable with actions/cache. If you try the pattern above and hit a real gap, let me know and we can reconsider — happy to also add an "Advanced caching" example to the README.
hmm, yes, I made this feature request precisely because we didn't want to go manual actions/cache route. as the only thing I would want to change is the cache-key.
A prefix input alone wouldn't actually give you the behavior you want here: the crucial piece is restore-keys.
I disagree, what I want is job 1 in the same workflow to "lock-in" the cache for job 2 and 3. For example, job 1 only does a mvn compile while job 2 and 3 run mvn test and mvn deploy. The pom hasn't changed, so the cache for mvn compile will be restored, but the others will have to be redownloaded every time (as maven only downloads dependencies/plugins when they're needed). This gets compounded to all the following runners (on the main branch/PRs from this) that do not have changes in their pom.xml.
If we could change the cache keys for those 3 jobs with a separate prefix, all 3 jobs would fill their own cache, and then consequent runs would not need to download from the cache again.
The workaround we have right now is to append X amount of spaces to the pom.xml. This changes the hash in a reproducable/fixed way. But it's a bit ugly.
This is the multi-job race described here: parallel jobs each run a partial goal (mvn test, mvn check, build) sharing one pom-hash cache key, so whichever finishes first saves an incomplete ~/.m2, and on later runs a cache hit means the fuller job's extra downloads are never re-saved.
Rather than a cache-key-prefix input, the advanced-usage guide now documents a separate seed job that resolves project and plugin dependencies once, creating a comprehensive cache that all matrix legs reuse:
This removes the "add a space to pom.xml to bust the cache" workaround. Closing as addressed; if a first-class cache-key-prefix input is still wanted, let's track that as a separate enhancement.
Activity
v-gowridurgad commentedon Nov 12, 2024
Hi @DavyLandman,
Thank you for the feature request. We'll investigate it and get back to you as soon as we have updates.
brunoborges commentedon Jul 8, 2026
Thanks for the request! The good news is you can achieve exactly this today, without any change to
setup-java, by managing the cache directly withactions/cache.How it works
When you omit the
cache:input,setup-javadoes no caching at all — it neither restores nor saves. That leaves you free to driveactions/cacheyourself with a fully custom key (including any prefix) plusrestore-keys:fallbacks. The pathssetup-javawould normally cache are:~/.m2/repository,~/.m2/wrapper/dists~/.gradle/caches,~/.gradle/wrapper~/.ivy2/cache,~/.sbt,~/.cache/coursier(Linux) /~/Library/Caches/Coursier(macOS)Example: partial jobs sharing a fuller cache
A "full build" job populates a shared cache; partial jobs use their own prefixed key and fall back to the shared one via
restore-keys:— which is precisely the "get a full cached.m2" behavior you described.Gradle/sbt work the same way — just swap the
path:andhashFiles(...)globs for the lists above.Why we lean toward this over a native
cache-key-prefixA prefix input alone wouldn't actually give you the behavior you want here: the crucial piece is
restore-keys:(the fallback to a fuller cache), andsetup-javaintentionally does not setrestore-keys— see the note insrc/cache.tsreferencing #269 (we prefer a cold cache over a stale one after a dependency change). Managing the cache withactions/cachedirectly gives you full control over both the key prefix and the fallback chain, which is strictly more capable. This is also consistent withactions/setup-nodeandactions/setup-python, which don't offer acache-key-prefixinput either.A couple of caveats:
${{ runner.os }}-${{ runner.arch }}in your key if you run a cross-platform matrix.Given the above, I'm inclined to close this as already achievable with
actions/cache. If you try the pattern above and hit a real gap, let me know and we can reconsider — happy to also add an "Advanced caching" example to the README.DavyLandman commentedon Jul 8, 2026
hmm, yes, I made this feature request precisely because we didn't want to go manual
actions/cacheroute. as the only thing I would want to change is thecache-key.I disagree, what I want is job 1 in the same workflow to "lock-in" the cache for job 2 and 3. For example, job 1 only does a
mvn compilewhile job 2 and 3 runmvn testandmvn deploy. The pom hasn't changed, so the cache formvn compilewill be restored, but the others will have to be redownloaded every time (as maven only downloads dependencies/plugins when they're needed). This gets compounded to all the following runners (on the main branch/PRs from this) that do not have changes in theirpom.xml.If we could change the cache keys for those 3 jobs with a separate prefix, all 3 jobs would fill their own cache, and then consequent runs would not need to download from the cache again.
The workaround we have right now is to append X amount of spaces to the pom.xml. This changes the hash in a reproducable/fixed way. But it's a bit ugly.
brunoborges commentedon Jul 9, 2026
@DavyLandman have you tried having a job specific for
mvn dependency:go-offlinebefore all other jobs?docs: cover the parallel multi-job cache race in the seed-job example (…
docs: document seeding the Maven cache for plugin dependencies (#1094)
brunoborges commentedon Jul 9, 2026
Addressed by documentation in #1094.
This is the multi-job race described here: parallel jobs each run a partial goal (
mvn test,mvn check, build) sharing one pom-hash cache key, so whichever finishes first saves an incomplete~/.m2, and on later runs a cache hit means the fuller job's extra downloads are never re-saved.Rather than a
cache-key-prefixinput, the advanced-usage guide now documents a separate seed job that resolves project and plugin dependencies once, creating a comprehensive cache that all matrix legs reuse:This removes the "add a space to pom.xml to bust the cache" workaround. Closing as addressed; if a first-class
cache-key-prefixinput is still wanted, let's track that as a separate enhancement.DavyLandman commentedon Jul 10, 2026
ah, yes, that's a good idea 👍🏼
Improved caching behavior by a small pre-job that just seeds the cach…