Skip to content

Cut CI time in the plugin repositories #12

Closed

Description

@slachiewicz

The CI of the repositories that use apache/maven-gh-actions-shared/.github/workflows/maven-verify.yml costs more than it needs to, in two ways.

The matrix

Across the last 148 CI runs of these repositories (6,107 job-minutes), Windows took 42% of the job-minutes and macOS 25%. A run has up to 16 jobs: JDK 8, 21 and 25 with Maven 3, plus JDK 21 and 25 with Maven 4, on each OS, plus the fail-fast job. In bursts of Dependabot PRs, one job in ten waited 8 to 13 minutes for a runner, macOS the longest.

Each repository now excludes, through matrix-exclude:

  • on macOS, every JDK but the newest one in its matrix;
  • on Windows, every JDK but the oldest and the newest.

Linux keeps the full matrix. With the default JDK matrix that is 11 jobs instead of 16, 2 of them on macOS instead of 5, and about a third fewer job-minutes, going by the measured share of each OS and JDK. An OS-specific bug that shows up only on a middle JDK is the coverage given up.

The integration tests

Each IT is a Maven run of a few seconds, and mojo-parent sets no invoker.parallelThreads, so the ITs ran one at a time and were bound by JVM startup. Repositories with enough ITs to matter now set invoker.parallelThreads to 0.75C and start the IT JVMs with -XX:TieredStopAtLevel=1 -XX:+UseSerialGC through JAVA_TOOL_OPTIONS. These flags stay out of mojo-parent: every JVM that reads JAVA_TOOL_OPTIONS prints a Picked up line, which can break ITs that compare output, so each repository's own CI has to show it is safe.

The first three:

Repository Job-minutes per run Windows median job
mojohaus/versions#1411 — 28–30 → 13.7 min (Maven 4)
mojohaus/license-maven-plugin#753 150 → 77 12.8 → 6.1 min
mojohaus/flatten-maven-plugin#538 85 → 49 7.2 → 4.0 min

Two repositories forced one IT thread on Windows. The exec-maven-plugin profile cites MRESOLVER-372, fixed in Resolver 1.9.17 (Maven 3.9.5); CI runs Maven 3.10.0 and 4.0.0-rc-7.

Pull requests

The ITs, merged: mojohaus/versions#1411, mojohaus/license-maven-plugin#753, mojohaus/flatten-maven-plugin#538.

This sweep:

Not changed: templating-maven-plugin tests one JDK, rpm-maven-plugin runs on Linux only, and maven-native, buildplan-maven-plugin, native2ascii-maven-plugin, servicedocgen-maven-plugin and mojo-parent do not use the shared workflow.

Dependabot

Dependabot PRs were 124 of the 366 CI runs between 2026-09-10 and 2026-10-10. Each repository now checks monthly instead of daily; Maven minor and patch updates come as one PR, GitHub Actions updates as another, and Maven major updates one PR each. Security updates are not affected.

Activity

  1. 20 remaining items

  2. slachiewicz commented on Oct 11, 2026

    @slachiewicz
    MemberAuthor

    Results from mojohaus/versions, where the recipe started, and the lessons that carry over to the other plugin repositories.

    Stage (master run) Jobs Job-minutes Slowest job (Windows) Ubuntu macOS
    Baseline 17 226 28.6 min 15.0 11.0
    Parallel ITs, C1 + SerialGC (mojohaus/versions#1411) 17 131 14.0 8.7 9.7
    Fewer JDKs on macOS and Windows (mojohaus/versions#1412) 12 80 10.9 8.4 7.0
    After the test migrations 12 79 13.2 7.8 8.9

    One run per stage, so the exact percentages carry runner noise. Elsewhere: mojohaus/license-maven-plugin#753 went from 150 to 77 job-minutes per run, and mojohaus/flatten-maven-plugin#538 from 85 to 49.

    Lessons for the other repositories:

    • Measure before blaming flaky tests. The cancelled Windows jobs in versions had zero test failures across 16 logs; the ITs ran serially and hit the timeout.
    • Look for serial invoker settings. A Windows profile set invoker.parallelThreads=1, and mojo-parent sets no parallelism, so most invoker-heavy repositories run their ITs one at a time.
    • Start forked JVMs cheaply. C1 and SerialGC through JAVA_TOOL_OPTIONS cut local IT time by 32%. Where a test reads stderr (exec-maven-plugin), use <mavenOpts> instead, because every JVM prints "Picked up…". That is also why it is not in mojo-parent.
    • Trim the OS Ă— JDK matrix. Windows was 42% of estate job-minutes; every JDK on Linux and the edge JDKs on macOS and Windows keeps the useful coverage.
    • Check before adding caches. Caching the local repository and build output gave nothing measurable.
    • Move parameter-only ITs to unit tests for clarity, not speed. Moving 43 of 251 ITs in versions (Move the parameter-only ITs to unit tests and unify the test style versions#1414) made no measurable difference to CI time.
    • Write verify scripts as assertions. Three print-and-return-false scripts in versions checked nothing. Invoker fails a script whose last expression is anything other than true or null, so a trailing .each fails the IT.
    • Count tests per class when leaving JUnit 4. JUnit 3-style classes ran test* methods without @Test, and one dropped out silently during the migration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestgithub_actionsPull requests that update GitHub Actions code

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions