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