|
15 | 15 | - [Tencent Kona](#Tencent-Kona) |
16 | 16 | - [Installing custom Java package type](#Installing-custom-Java-package-type) |
17 | 17 | - [JavaFX Maven project](#JavaFX-Maven-project) |
| 18 | +- [Ensuring the Maven cache is complete (plugin dependencies)](#ensuring-the-maven-cache-is-complete-plugin-dependencies) |
18 | 19 | - [Installing custom Java architecture](#Installing-custom-Java-architecture) |
19 | 20 | - [Installing JDK without setting as default](#Installing-JDK-without-setting-as-default) |
20 | 21 | - [Installing custom Java distribution from local file](#Installing-Java-from-local-file) |
@@ -285,6 +286,117 @@ To run the JavaFX application in CI: |
285 | 286 | run: mvn --no-transfer-progress javafx:run |
286 | 287 | ``` |
287 | 288 |
|
| 289 | +## Ensuring the Maven cache is complete (plugin dependencies) |
| 290 | + |
| 291 | +When you enable `cache: maven`, the action caches your local Maven repository |
| 292 | +(`~/.m2/repository`) keyed on a hash of your `pom.xml` files, and — at the end of |
| 293 | +the job — saves whatever was downloaded during that run. It does **not** re-save |
| 294 | +the cache when the key already matches (a cache *hit*). |
| 295 | + |
| 296 | +Maven resolves **plugin** dependencies lazily: it only downloads the plugins and |
| 297 | +plugin dependencies required by the goals that actually execute. As a result, the |
| 298 | +run that first creates the cache determines what is stored. If that run executed a |
| 299 | +"thin" goal such as `mvn compile`, plugins bound to later phases are never |
| 300 | +resolved. For example, `maven-shade-plugin` (bound to `package`) pulls in |
| 301 | +`plexus-archiver`, `commons-compress`, `io.airlift:aircompressor` and |
| 302 | +`org.tukaani:xz` — none of which a `compile` run downloads. Those artifacts are |
| 303 | +therefore absent from the cache, and because the action does not re-save on a |
| 304 | +hit, every later `test`/`verify`/`package` job re-downloads them on every run. |
| 305 | + |
| 306 | +### Seed the cache with a resolution step |
| 307 | + |
| 308 | +To populate `~/.m2` as comprehensively as possible on the run that creates the |
| 309 | +cache, run a dependency-resolution "seed" command before your build. Choose a |
| 310 | +command based on how thorough you need it to be: |
| 311 | + |
| 312 | +| Seed command | Resolves plugin dependencies? | Notes | |
| 313 | +|--------------|:-----------------------------:|-------| |
| 314 | +| `mvn dependency:resolve` | No | Resolves project dependencies only — misses plugin dependencies (e.g. `aircompressor`). | |
| 315 | +| `mvn dependency:resolve-plugins` | Yes | Resolves plugins **and their dependencies**. | |
| 316 | +| `mvn dependency:go-offline` | Yes | Resolves project and plugin dependencies (a superset). | |
| 317 | +| `mvn dependency:go-offline dependency:resolve-plugins` | Yes (most thorough) | Recommended default. Use `dependency:resolve dependency:resolve-plugins` if `go-offline` is flaky or insufficient for your project. | |
| 318 | + |
| 319 | +Single job — seed, then build (the cache saved at the end of this run contains |
| 320 | +the full set): |
| 321 | + |
| 322 | +```yaml |
| 323 | +steps: |
| 324 | +- uses: actions/checkout@v6 |
| 325 | +- uses: actions/setup-java@v5 |
| 326 | + with: |
| 327 | + distribution: 'temurin' |
| 328 | + java-version: '25' |
| 329 | + cache: 'maven' |
| 330 | +- name: Seed the Maven cache |
| 331 | + run: mvn -B dependency:go-offline dependency:resolve-plugins |
| 332 | +- name: Build with Maven |
| 333 | + run: mvn -B verify --file pom.xml |
| 334 | +``` |
| 335 | + |
| 336 | +Separate seed job — useful for a matrix where different legs run different goals |
| 337 | +(`test`, `check`, `verify`, `-Pprofile1`, ...) but all share the same `~/.m2` |
| 338 | +cache. Without a seed, whichever job finishes first creates the cache from its |
| 339 | +own partial `.m2`, and parallel jobs race to save an equally partial cache; the |
| 340 | +seed job instead creates one comprehensive cache that every other job reuses: |
| 341 | + |
| 342 | +```yaml |
| 343 | +jobs: |
| 344 | + seed-cache: |
| 345 | + runs-on: ubuntu-latest |
| 346 | + steps: |
| 347 | + - uses: actions/checkout@v6 |
| 348 | + - uses: actions/setup-java@v5 |
| 349 | + with: |
| 350 | + distribution: 'temurin' |
| 351 | + java-version: '25' |
| 352 | + cache: 'maven' |
| 353 | + - name: Seed the Maven cache |
| 354 | + run: mvn -B dependency:go-offline dependency:resolve-plugins |
| 355 | +
|
| 356 | + build: |
| 357 | + needs: seed-cache |
| 358 | + runs-on: ubuntu-latest |
| 359 | + strategy: |
| 360 | + matrix: |
| 361 | + goal: ['test', 'verify', 'test -Pprofile1'] |
| 362 | + steps: |
| 363 | + - uses: actions/checkout@v6 |
| 364 | + - uses: actions/setup-java@v5 |
| 365 | + with: |
| 366 | + distribution: 'temurin' |
| 367 | + java-version: '25' |
| 368 | + cache: 'maven' |
| 369 | + - name: Build |
| 370 | + run: mvn -B ${{ matrix.goal }} --file pom.xml |
| 371 | +``` |
| 372 | + |
| 373 | +### Caveats |
| 374 | + |
| 375 | +- **The seed only helps on the run that creates the cache.** Once a cache exists |
| 376 | + for the current `pom.xml` hash, later runs get a hit and any additional |
| 377 | + downloads are not saved. On an existing repository whose cache is already |
| 378 | + incomplete, invalidate it once (for example by changing `cache-dependency-path` |
| 379 | + or deleting the repository's caches) so a complete cache is created from the |
| 380 | + seed. |
| 381 | +- **Static resolution is not exhaustive.** `go-offline`/`resolve-plugins` resolve |
| 382 | + the statically declared plugin set for the *active* profiles and modules. |
| 383 | + Profile-gated plugins, conditionally-active modules, and artifacts a plugin |
| 384 | + fetches at execution time may still be missed. For the most complete cache, |
| 385 | + seed with the fullest goal set your CI actually uses (for example |
| 386 | + `mvn -B verify` with every profile enabled). |
| 387 | +- **Multi-module projects:** run the seed at the reactor root so every module's |
| 388 | + plugins are resolved. |
| 389 | + |
| 390 | +> [!NOTE] |
| 391 | +> The same "the cache stores only what the creating run downloaded, and is not |
| 392 | +> re-saved on a hit" behavior applies to `cache: gradle`, since Gradle also |
| 393 | +> resolves dependencies and plugin/buildscript classpaths lazily. Gradle has no |
| 394 | +> direct equivalent of `dependency:go-offline`, so for complete and fine-grained |
| 395 | +> dependency caching on Gradle projects we recommend |
| 396 | +> [`gradle/actions/setup-gradle`](https://github.com/gradle/actions/tree/main/setup-gradle), |
| 397 | +> which provides purpose-built caching (see the |
| 398 | +> [setup-gradle documentation](https://github.com/gradle/actions/blob/main/docs/setup-gradle.md)). |
| 399 | + |
288 | 400 | ## Installing custom Java architecture |
289 | 401 |
|
290 | 402 | ```yaml |
|
0 commit comments