GitHub Actions Matrix Builds for Java: Complete Guide

Introduction

GitHub Actions matrix builds enable you to test your Java applications across multiple configurations simultaneously. This powerful feature allows you to run parallel jobs for different Java versions, operating systems, architectures, and testing scenarios, significantly speeding up your CI/CD pipeline.

Basic Matrix Configuration

1. Simple Java Version Matrix

# .github/workflows/java-matrix.yml name: Java Matrix Build on: push: branches: [ main, develop ] pull_request: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: java-version: [8, 11, 17, 21] architecture: [x64] name: Test Java ${{ matrix.java-version }} (${{ matrix.architecture }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} architecture: ${{ matrix.architecture }} distribution: 'temurin' cache: 'maven' - name: Validate dependencies run: mvn dependency:resolve - name: Run tests run: mvn verify env: CI: true

2. Multi-Dimensional Matrix

name: Multi-Platform Java Build on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: java-version: [11, 17, 21] os: [ubuntu-latest, windows-latest, macos-latest] architecture: [x64] include: # Include specific combinations - java-version: 8 os: ubuntu-latest architecture: x64 experimental: true name: Build Java ${{ matrix.java-version }} on ${{ matrix.os }} (${{ matrix.architecture }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} architecture: ${{ matrix.architecture }} distribution: 'temurin' - name: Build with Maven run: mvn -B package --file pom.xml env: CI: true - name: Upload artifacts if: matrix.experimental != true uses: actions/upload-artifact@v4 with: name: build-${{ matrix.java-version }}-${{ matrix.os }}-${{ matrix.architecture }} path: target/*.jar

Advanced Matrix Strategies

3. Conditional Matrix with Exclusions

name: Smart Java Matrix on: push: branches: [ main ] pull_request: branches: [ main ] jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: java-version: [8, 11, 17, 21] os: [ubuntu-latest, windows-latest, macos-latest] profile: [default, integration-tests] exclude: # Exclude Java 8 on Windows due to compatibility issues - java-version: 8 os: windows-latest # Exclude integration tests on macOS for performance - profile: integration-tests os: macos-latest include: # Add specific test configurations - java-version: 21 os: ubuntu-latest profile: performance-tests performance: true name: Test Java ${{ matrix.java-version }} on ${{ matrix.os }} - ${{ matrix.profile }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: 'maven' - name: Determine Maven goals id: goals run: | if [ "${{ matrix.profile }}" == "integration-tests" ]; then echo "goals=verify -Pintegration-tests" >> $GITHUB_OUTPUT elif [ "${{ matrix.profile }}" == "performance-tests" ]; then echo "goals=verify -Pperformance-tests" >> $GITHUB_OUTPUT else echo "goals=verify" >> $GITHUB_OUTPUT fi - name: Run tests run: mvn -B ${{ steps.goals.outputs.goals }} - name: Run performance benchmarks if: matrix.performance == true run: mvn exec:java -Dexec.mainClass="com.example.BenchmarkRunner"

4. Dynamic Matrix Generation

name: Dynamic Java Matrix on: push: branches: [ main ] workflow_dispatch: inputs: java-versions: description: 'Java versions to test' required: false default: '11,17,21' jobs: setup-matrix: runs-on: ubuntu-latest outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Set up matrix id: set-matrix run: | # Parse input or use default VERSIONS="${{ github.event.inputs.java-versions || '11,17,21' }}" # Convert to JSON array MATRIX_JSON=$(echo "$VERSIONS" | jq -R 'split(",") | map({java-version: .})') echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT echo "Generated matrix: $MATRIX_JSON" test: needs: setup-matrix runs-on: ubuntu-latest strategy: matrix: ${{ fromJSON(needs.setup-matrix.outputs.matrix) }} name: Test Java ${{ matrix.java-version }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: 'maven' - name: Run tests run: mvn -B verify

Java-Specific Matrix Examples

5. Build Tool Matrix (Maven vs Gradle)

name: Java Build Tool Matrix on: push: branches: [ main ] pull_request: branches: [ main ] jobs: build: runs-on: ubuntu-latest strategy: matrix: java-version: [11, 17] build-tool: [maven, gradle] name: Build with ${{ matrix.build-tool }} (Java ${{ matrix.java-version }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Setup Gradle if: matrix.build-tool == 'gradle' uses: gradle/actions/setup-gradle@v3 with: cache-write-only: false - name: Build with Maven if: matrix.build-tool == 'maven' run: | mvn -B clean compile test-compile mvn -B verify -DskipTests=false - name: Build with Gradle if: matrix.build-tool == 'gradle' run: | ./gradlew clean build ./gradlew test

6. Database Compatibility Matrix

name: Java Database Matrix on: push: branches: [ main ] jobs: database-test: runs-on: ubuntu-latest services: postgres: image: postgres:13 env: POSTGRES_PASSWORD: postgres POSTGRES_DB: testdb options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: testdb options: >- --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 3306:3306 strategy: matrix: java-version: [11, 17] database: [postgres, mysql, h2] name: DB Tests - Java ${{ matrix.java-version }} with ${{ matrix.database }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: 'maven' - name: Configure database properties run: | cat > src/test/resources/test-database.properties << EOF database.type=${{ matrix.database }} database.url=${{ matrix.database == 'postgres' && 'jdbc:postgresql://localhost:5432/testdb' || matrix.database == 'mysql' && 'jdbc:mysql://localhost:3306/testdb' || 'jdbc:h2:mem:testdb' }} database.username=${{ matrix.database == 'postgres' && 'postgres' || matrix.database == 'mysql' && 'root' || 'sa' }} database.password=${{ matrix.database == 'postgres' && 'postgres' || matrix.database == 'mysql' && 'root' || '' }} EOF - name: Run database tests run: mvn -B verify -Pdatabase-tests -Ddatabase.type=${{ matrix.database }}

Advanced Matrix Patterns

7. Fail-Fast and Continue-on-Error Strategies

name: Robust Java Matrix on: push: branches: [ main ] jobs: unit-tests: runs-on: ubuntu-latest strategy: matrix: java-version: [8, 11, 17, 21] os: [ubuntu-latest, windows-latest] fail-fast: false # Don't stop all jobs if one fails name: Unit Tests - Java ${{ matrix.java-version }} on ${{ matrix.os }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Run unit tests run: mvn -B test integration-tests: runs-on: ubuntu-latest strategy: matrix: java-version: [11, 17] test-profile: [api, database, security] continue-on-error: ${{ matrix.test-profile == 'security' }} # Allow security tests to fail name: Integration Tests - ${{ matrix.test-profile }} (Java ${{ matrix.java-version }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Run integration tests run: mvn -B verify -P${{ matrix.test-profile }}-tests

8. Matrix with Custom Parameters and Outputs

name: Custom Java Matrix on: push: branches: [ main ] jobs: analysis: runs-on: ubuntu-latest strategy: matrix: java-version: [11, 17, 21] analysis-type: [spotbugs, checkstyle, pmd, jacoco] include: - analysis-type: jacoco min-coverage: 0.8 - analysis-type: spotbugs fail-on-error: true name: ${{ matrix.analysis-type }} Analysis (Java ${{ matrix.java-version }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: 'maven' - name: Run code analysis id: analysis run: | case "${{ matrix.analysis-type }}" in "spotbugs") mvn -B spotbugs:check echo "result=success" >> $GITHUB_OUTPUT ;; "checkstyle") mvn -B checkstyle:check echo "result=success" >> $GITHUB_OUTPUT ;; "pmd") mvn -B pmd:check echo "result=success" >> $GITHUB_OUTPUT ;; "jacoco") mvn -B test jacoco:check echo "result=success" >> $GITHUB_OUTPUT ;; esac - name: Upload analysis results uses: actions/upload-artifact@v4 with: name: ${{ matrix.analysis-type }}-java-${{ matrix.java-version }} path: | target/site/* !target/site/jacoco/* if-no-files-found: ignore aggregate-results: runs-on: ubuntu-latest needs: analysis if: always() name: Aggregate Analysis Results steps: - name: Download all artifacts uses: actions/download-artifact@v4 with: path: artifacts - name: Generate summary run: | echo "# Code Analysis Summary" > summary.md echo "" >> summary.md echo "## Job Results" >> summary.md echo "" >> summary.md # Generate summary from all matrix jobs # This would typically parse results and create a report

Best Practices for Java Matrix Builds

9. Optimized Caching Strategy

name: Optimized Java Matrix on: push: branches: [ main ] jobs: test: runs-on: ubuntu-latest strategy: matrix: java-version: [11, 17, 21] profile: [unit, integration] name: Tests - Java ${{ matrix.java-version }} (${{ matrix.profile }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' cache: 'maven' - name: Cache Maven dependencies uses: actions/cache@v4 with: path: | ~/.m2/repository target/**/* key: maven-${{ hashFiles('**/pom.xml') }}-java-${{ matrix.java-version }} restore-keys: | maven-${{ hashFiles('**/pom.xml') }} maven- - name: Build and test run: | if [ "${{ matrix.profile }}" == "unit" ]; then mvn -B test -DskipITs=true else mvn -B verify -DskipTests=true -DskipUTs=true fi - name: Upload test results uses: actions/upload-artifact@v4 if: always() with: name: test-results-java-${{ matrix.java-version }}-${{ matrix.profile }} path: | target/surefire-reports/* target/failsafe-reports/* retention-days: 7

10. Environment-Specific Matrix

name: Environment-Specific Java Matrix on: workflow_dispatch: inputs: environment: type: choice description: 'Target environment' required: true options: - dev - staging - production jobs: deploy: runs-on: ubuntu-latest strategy: matrix: java-version: [11, 17] region: [us-east-1, eu-west-1, ap-southeast-1] include: - environment: dev java-version: 11 region: us-east-1 instance-type: t3.small - environment: staging java-version: 17 region: us-east-1 instance-type: t3.medium - environment: production java-version: 17 region: [us-east-1, eu-west-1, ap-southeast-1] instance-type: t3.large name: Deploy to ${{ inputs.environment }} - ${{ matrix.region }} (Java ${{ matrix.java-version }}) steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Java uses: actions/setup-java@v4 with: java-version: ${{ matrix.java-version }} distribution: 'temurin' - name: Build application run: mvn -B clean package -DskipTests - name: Deploy to environment run: | # Custom deployment script ./scripts/deploy.sh \ --environment ${{ inputs.environment }} \ --region ${{ matrix.region }} \ --instance-type ${{ matrix.instance-type }} env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Conclusion

GitHub Actions matrix builds provide a powerful way to test Java applications across multiple configurations. By leveraging matrices, you can:

  • Test compatibility across Java versions, operating systems, and architectures
  • Parallelize testing to reduce CI/CD pipeline duration
  • Manage complex configurations with includes, excludes, and conditional logic
  • Optimize resource usage with smart caching and fail-fast strategies
  • Generate comprehensive reports from multiple test runs

The examples provided demonstrate practical implementations for various Java testing scenarios, from simple version matrices to complex multi-dimensional configurations. Choose the patterns that best fit your project's needs and scale your testing strategy as your application grows.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper