Introduction: From Basic Pipelines to Industrial Solutions
A basic CI/CD pipeline for a Java project is good. But in real development, when a project grows to dozens of modules and the team consists of several developers, a simple `mvn clean test` no longer meets the requirements. Questions arise: how to speed up the build? How to test on different JDK versions? How to organize parallel test execution? How to automatically publish artifacts to Docker Hub?
In this article, we will cover advanced techniques for configuring GitHub Actions for Java projects. You will learn how to turn your pipeline from a linear sequence of steps into a powerful, parallel, and optimized continuous integration and delivery system.
1. Matrix Builds: Testing on Multiple JDKs and OS
One of the key tasks of CI is to ensure that your Java code works on all supported platforms. Manually running tests on Windows, macOS, and Linux with different JDK versions (11, 17, 21) is an unaffordable luxury. GitHub Actions offers an elegant solution: matrix builds.
How It Works
You define a matrix of parameters (JDK versions, operating systems), and GitHub automatically creates a separate job for each combination. All of them run in parallel.
Configuration Example
name: Java CI Matrixon: [push, pull_request]
jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macos-latest] java: [11, 17, 21] exclude: - os: windows-latest java: 21 # Exclude JDK 21 on Windows if not supported fail-fast: false # Do not stop other jobs if one fails
steps: - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java }} uses: actions/setup-java@v4 with: java-version: ${{ matrix.java }} distribution: 'temurin' - name: Build with Maven run: mvn -B clean verify --file pom.xmlKey Points:
strategy.matrix— defines parameter combinations.exclude— allows you to exclude knowingly non-working or unnecessary combinations.fail-fast: false— if one job fails, the others continue. This gives a complete picture of compatibility.
2. Dependency Caching: Speed Up Builds by 2-3 Times
Each pipeline run starts with a clean environment. Maven or Gradle downloads all dependencies from repositories anew. This can take 3-5 minutes. Caching solves this problem by saving the ~/.m2/repository folder (for Maven) or ~/.gradle/caches (for Gradle) between runs.
GitHub Actions provides a built-in action actions/cache. The cache key is formed based on the dependency file (pom.xml). If the file hasn't changed, the cache is restored instantly.
Example for Maven
steps: - uses: actions/checkout@v4
- name: Cache Maven dependencies uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven-
- name: Set up JDK 17 uses: actions/setup-java@v4 with: java-version: '17' distribution: 'temurin'
- name: Build with Maven run: mvn -B clean verify --file pom.xmlImportant: Caching should come before the build step. The key hashFiles('**/pom.xml') ensures the cache is updated only when dependencies change.
3. Parallel Test Execution and Build Artifacts
When your project contains hundreds or thousands of tests, and