Efficient CI/CD: Mastering GitHub Actions Matrix Builds for Java Projects

Table of Contents

Article

GitHub Actions Matrix builds are a powerful feature that allows you to run multiple jobs in parallel across different configurations. For Java teams, this means testing across multiple Java versions, operating systems, and dependency combinations simultaneously, significantly speeding up CI/CD pipelines while ensuring comprehensive compatibility.


What are Matrix Builds?

Matrix builds enable you to create multiple job configurations by specifying variables. GitHub Actions automatically generates and runs jobs for each combination, providing parallel execution and comprehensive testing coverage.

Key Benefits for Java Projects:

  • Multi-Version Testing: Test against multiple Java versions (8, 11, 17, 21)
  • Cross-Platform Validation: Ensure compatibility across Windows, Linux, macOS
  • Dependency Matrix Testing: Test with different dependency versions
  • Parallel Execution: Reduce overall build time
  • Comprehensive Coverage: Catch version-specific issues early

Basic Matrix Build 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:
name: Test on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn -B clean compile
- name: Run tests
run: mvn -B test

2. Multi-Dimensional Matrix

name: Multi-Dimensional Java Matrix
on: [push, pull_request]
jobs:
build:
name: Build - Java ${{ matrix.java }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
java: [11, 17, 21]
os: [ubuntu-latest, windows-latest, macos-latest]
# Exclude incompatible combinations
exclude:
- java: 21
os: windows-latest  # Temporary exclusion for Java 21 on Windows
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Validate Java version
run: java -version
- name: Build with Maven
run: mvn -B clean compile
- name: Run tests
run: mvn -B test

Advanced Matrix Strategies

1. Matrix with Different Build Tools

name: Multi-Build Tool Matrix
on: [push, pull_request]
jobs:
test:
name: Test - ${{ matrix.tool }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
tool: [maven, gradle]
include:
- tool: maven
command: mvn
cache-key: maven
- tool: gradle
command: gradle
cache-key: gradle
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: ${{ matrix.cache-key }}
- name: Build with ${{ matrix.tool }}
run: ${{ matrix.command }} -B build
- name: Test with ${{ matrix.tool }}
run: ${{ matrix.command }} -B test

2. Database Integration Testing Matrix

name: Database Integration Matrix
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
integration-test:
name: DB Tests - Java ${{ matrix.java }} with ${{ matrix.database }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
database: [postgresql-15, mysql-8, mariadb-10]
include:
- database: postgresql-15
db-image: postgres:15
db-port: 5432
db-name: testdb
db-user: testuser
db-password: testpass
- database: mysql-8
db-image: mysql:8.0
db-port: 3306
db-name: testdb
db-user: root
db-password: password
- database: mariadb-10
db-image: mariadb:10.11
db-port: 3306
db-name: testdb
db-user: root
db-password: password
services:
database:
image: ${{ matrix.db-image }}
ports:
- ${{ matrix.db-port }}:${{ matrix.db-port }}
env:
POSTGRES_DB: ${{ matrix.db-name }}
POSTGRES_USER: ${{ matrix.db-user }}
POSTGRES_PASSWORD: ${{ matrix.db-password }}
MYSQL_DATABASE: ${{ matrix.db-name }}
MYSQL_ROOT_PASSWORD: ${{ matrix.db-password }}
MARIADB_DATABASE: ${{ matrix.db-name }}
MARIADB_ROOT_PASSWORD: ${{ matrix.db-password }}
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Wait for database
run: |
echo "Waiting for database to be ready..."
sleep 30
- name: Run integration tests
run: |
mvn -B verify -Pintegration-test \
-Ddatabase.url=jdbc:postgresql://localhost:${{ matrix.db-port }}/${{ matrix.db-name }} \
-Ddatabase.username=${{ matrix.db-user }} \
-Ddatabase.password=${{ matrix.db-password }}
env:
DB_TYPE: ${{ matrix.database }}

Spring Boot Specific Matrix Configurations

1. Spring Profile Matrix Testing

name: Spring Profile Matrix
on: [push, pull_request]
jobs:
spring-test:
name: Spring Tests - ${{ matrix.profile }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
profile: [dev, test, prod]
include:
- profile: dev
active-profiles: dev,h2
- profile: test
active-profiles: test,postgresql
- profile: prod
active-profiles: prod,mysql,metrics
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Test with profile ${{ matrix.profile }}
run: |
mvn -B test \
-Dspring.profiles.active=${{ matrix.active-profiles }} \
-Dskip.integration.tests=true
- name: Integration tests
if: matrix.profile == 'test'
run: |
mvn -B verify -Pintegration-test \
-Dspring.profiles.active=${{ matrix.active-profiles }}

2. Spring Boot Version Compatibility Matrix

name: Spring Boot Version Matrix
on:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * 1'  # Weekly on Monday at 2 AM
jobs:
compatibility-test:
name: Spring Boot ${{ matrix.spring-boot }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
spring-boot: [3.1.0, 3.0.0, 2.7.0]
exclude:
- java: 21
spring-boot: 2.7.0  # Spring Boot 2.7 doesn't support Java 21
- java: 8
spring-boot: 3.1.0  # Spring Boot 3+ doesn't support Java 8
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Update Spring Boot version
run: |
mvn -B versions:set -DnewVersion=1.0.0-SNAPSHOT
mvn -B versions:update-parent -DparentVersion=${{ matrix.spring-boot }}
mvn -B versions:set-property \
-Dproperty=spring-boot.version \
-DnewVersion=${{ matrix.spring-boot }}
- name: Build and test
run: mvn -B clean verify

Gradle-Specific Matrix Configurations

1. Gradle Build Matrix

name: Gradle Matrix Build
on: [push, pull_request]
jobs:
gradle-test:
name: Gradle - Java ${{ matrix.java }} with ${{ matrix.gradle }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
java: [11, 17, 21]
gradle: [8.4, 8.3, 8.2]
os: [ubuntu-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'gradle'
- name: Setup Gradle ${{ matrix.gradle }}
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: ${{ matrix.gradle }}
- name: Make gradlew executable
if: matrix.os == 'ubuntu-latest'
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Run tests
run: ./gradlew test
- name: Run integration tests
run: ./gradlew integrationTest

2. Gradle Feature Variant Matrix

name: Gradle Feature Variant Matrix
on: [push, pull_request]
jobs:
variant-test:
name: Variant ${{ matrix.variant }} - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
variant: [free, pro, enterprise]
include:
- variant: free
build-args: -Pvariant=free
- variant: pro
build-args: -Pvariant=pro -PenablePremium=true
- variant: enterprise
build-args: -Pvariant=enterprise -PenablePremium=true -PenableEnterprise=true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'gradle'
- name: Build variant ${{ matrix.variant }}
run: ./gradlew build ${{ matrix.build-args }}
- name: Test variant ${{ matrix.variant }}
run: ./gradlew test ${{ matrix.build-args }}

Advanced Matrix Patterns

1. Dynamic Matrix Generation

name: Dynamic Matrix from JSON
on: [push, pull_request]
jobs:
generate-matrix:
name: Generate Build Matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate matrix configuration
id: set-matrix
run: |
MATRIX_JSON=$(cat <<EOF
{
"java": [11, 17, 21],
"os": ["ubuntu-latest", "windows-latest"],
"include": [
{
"java": 11,
"os": "ubuntu-latest",
"distribution": "temurin",
"experimental": false
},
{
"java": 17,
"os": "ubuntu-latest", 
"distribution": "temurin",
"experimental": false
},
{
"java": 21,
"os": "ubuntu-latest",
"distribution": "temurin", 
"experimental": true
}
]
}
EOF
)
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
build:
name: Build - Java ${{ matrix.java }} on ${{ matrix.os }}
needs: generate-matrix
runs-on: ${{ matrix.os }}
strategy:
matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: ${{ matrix.distribution || 'temurin' }}
cache: 'maven'
- name: Build
run: mvn -B clean compile
- name: Test
if: ${{ !matrix.experimental || matrix.experimental == false }}
run: mvn -B test

2. Conditional Matrix Execution

name: Conditional Matrix Builds
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
analysis:
name: Static Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: SpotBugs analysis
run: mvn -B spotbugs:check
- name: PMD analysis
run: mvn -B pmd:check
test:
name: Tests - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: analysis
if: ${{ always() && needs.analysis.result == 'success' }}
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Run unit tests
run: mvn -B test -Dskip.integration.tests=true
- name: Run integration tests
if: matrix.java == '17'  # Only run integration tests on Java 17
run: mvn -B verify -Pintegration-test
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: test
if: ${{ github.ref == 'refs/heads/main' && needs.test.result == 'success' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Deploy to production
run: mvn -B deploy -DskipTests
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Performance Optimization

1. Caching Strategies

name: Optimized Matrix with Caching
on: [push, pull_request]
jobs:
test:
name: Test - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 17, 21]
fail-fast: false  # Don't fail all if one fails
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: |
~/.m2/repository
**/target
key: maven-${{ hashFiles('**/pom.xml') }}-${{ matrix.java }}
restore-keys: |
maven-${{ hashFiles('**/pom.xml') }}
maven-
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Build and test
run: |
mvn -B clean test \
-Dmaven.test.failure.ignore=true \
-Dsurefire.rerunFailingTestsCount=2
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-java-${{ matrix.java }}
path: |
**/target/surefire-reports
**/target/failsafe-reports
retention-days: 7

2. Parallel Test Execution

name: Parallel Test Matrix
on: [push, pull_request]
jobs:
parallel-tests:
name: Parallel Tests - Shard ${{ matrix.shard }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [17]
shard: [1, 2, 3, 4]
total-shards: [4]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Run parallel tests
run: |
mvn -B test \
-DtestShard=${{ matrix.shard }}/${{ matrix.total-shards }} \
-Dparallel=classes \
-DthreadCount=4
- name: Merge test reports
if: matrix.shard == 1
run: mvn -B surefire-report:report-only

Security and Compliance Matrix

1. Security Scanning Matrix

name: Security Matrix Scan
on:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * *'  # Daily at 2 AM
jobs:
security-scan:
name: Security Scan - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: OWASP Dependency Check
run: |
mvn -B org.owasp:dependency-check-maven:check \
-Dformat=HTML \
-Dformat=JSON
- name: Upload security report
uses: actions/upload-artifact@v3
with:
name: security-report-java-${{ matrix.java }}
path: target/dependency-check-report.*
- name: SonarQube analysis
if: matrix.java == '17'
run: |
mvn -B sonar:sonar \
-Dsonar.projectKey=my-java-project \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }}

Complete Enterprise Example

name: Enterprise Java Matrix Pipeline
on:
push:
branches: [ main, develop, release/* ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * 1-5'  # Weekdays at 6 AM
env:
MAVEN_OPTS: -Djava.awt.headless=true -Dmaven.repo.local=/tmp/m2/repository
jobs:
quality-gates:
name: Quality Gates
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Check formatting
run: mvn -B spotless:check
- name: Check dependencies
run: mvn -B versions:display-dependency-updates
- name: License compliance
run: mvn -B license:check
compatibility-matrix:
name: Compatibility - ${{ matrix.java }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs: quality-gates
strategy:
matrix:
java: [11, 17, 21]
os: [ubuntu-latest, windows-latest, macos-latest]
exclude:
- java: 21
os: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Build
run: mvn -B clean compile
- name: Unit tests
run: mvn -B test -Dskip.integration.tests=true
- name: Integration tests
if: matrix.java == '17' && matrix.os == 'ubuntu-latest'
run: mvn -B verify -Pintegration-test
performance-matrix:
name: Performance - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: compatibility-matrix
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Performance tests
run: |
mvn -B verify -Pperformance-test \
-Dperf.test.duration=300s \
-Djava.version=${{ matrix.java }}
- name: Upload performance results
uses: actions/upload-artifact@v3
with:
name: perf-results-java-${{ matrix.java }}
path: target/performance-reports/
security-matrix:
name: Security - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: compatibility-matrix
strategy:
matrix:
java: [11, 17]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Security scan
run: |
mvn -B org.owasp:dependency-check-maven:check \
-DsuppressionFile=security-suppressions.xml
- name: Upload security report
uses: actions/upload-artifact@v3
with:
name: security-report-java-${{ matrix.java }}
path: target/dependency-check-report.*
deploy:
name: Deploy to Environment
runs-on: ubuntu-latest
needs: [compatibility-matrix, performance-matrix, security-matrix]
if: ${{ github.ref == 'refs/heads/main' && contains(needs.*.result, 'success') }}
environment: production
strategy:
matrix:
environment: [staging, production]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Deploy to ${{ matrix.environment }}
run: |
mvn -B deploy -DskipTests \
-Ddeploy.env=${{ matrix.environment }} \
-Ddeploy.token=${{ secrets.DEPLOY_TOKEN }}

Best Practices

1. Matrix Configuration Tips

# .github/workflows/best-practices.yml
name: Matrix Best Practices
on: [push, pull_request]
jobs:
optimized-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
# Use semantic version names
java-version: [11, 17, 21]
# Limit matrix size for PRs
include-pr:
- java-version: 17
full-test-suite: false
# Use fail-fast appropriately
fail-fast: ${{ github.event_name == 'pull_request' }}
# Set maximum parallel jobs
max-parallel: ${{ github.event_name == 'pull_request' && 2 || 10 }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: temurin
cache: maven

2. Resource Optimization

name: Resource Optimized Matrix
on: [push, pull_request]
jobs:
resource-aware:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
java: [11, 17, 21]
runner: [ubuntu-latest]
include:
- java: 21
runner: ubuntu-latest
resource-profile: high-memory
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java with optimized memory
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: temurin
cache: maven
java-package: jdk
check-latest: true

Conclusion

GitHub Actions Matrix builds provide Java teams with:

  • Comprehensive Testing: Validate across multiple Java versions, OS platforms, and configurations
  • Faster Feedback: Parallel execution reduces CI/CD pipeline duration
  • Early Issue Detection: Catch compatibility issues before production
  • Resource Efficiency: Optimize cloud resource usage
  • Scalable Pipelines: Handle complex testing scenarios efficiently

By implementing matrix builds, Java teams can ensure their applications work reliably across all supported environments while maintaining development velocity and code quality.


x

Article

GitHub Actions Matrix builds are a powerful feature that allows you to run multiple jobs in parallel across different configurations. For Java teams, this means testing across multiple Java versions, operating systems, and dependency combinations simultaneously, significantly speeding up CI/CD pipelines while ensuring comprehensive compatibility.


What are Matrix Builds?

Matrix builds enable you to create multiple job configurations by specifying variables. GitHub Actions automatically generates and runs jobs for each combination, providing parallel execution and comprehensive testing coverage.

Key Benefits for Java Projects:

  • Multi-Version Testing: Test against multiple Java versions (8, 11, 17, 21)
  • Cross-Platform Validation: Ensure compatibility across Windows, Linux, macOS
  • Dependency Matrix Testing: Test with different dependency versions
  • Parallel Execution: Reduce overall build time
  • Comprehensive Coverage: Catch version-specific issues early

Basic Matrix Build 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:
name: Test on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Build with Maven
run: mvn -B clean compile
- name: Run tests
run: mvn -B test

2. Multi-Dimensional Matrix

name: Multi-Dimensional Java Matrix
on: [push, pull_request]
jobs:
build:
name: Build - Java ${{ matrix.java }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
java: [11, 17, 21]
os: [ubuntu-latest, windows-latest, macos-latest]
# Exclude incompatible combinations
exclude:
- java: 21
os: windows-latest  # Temporary exclusion for Java 21 on Windows
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Validate Java version
run: java -version
- name: Build with Maven
run: mvn -B clean compile
- name: Run tests
run: mvn -B test

Advanced Matrix Strategies

1. Matrix with Different Build Tools

name: Multi-Build Tool Matrix
on: [push, pull_request]
jobs:
test:
name: Test - ${{ matrix.tool }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
tool: [maven, gradle]
include:
- tool: maven
command: mvn
cache-key: maven
- tool: gradle
command: gradle
cache-key: gradle
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: ${{ matrix.cache-key }}
- name: Build with ${{ matrix.tool }}
run: ${{ matrix.command }} -B build
- name: Test with ${{ matrix.tool }}
run: ${{ matrix.command }} -B test

2. Database Integration Testing Matrix

name: Database Integration Matrix
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
MAVEN_OPTS: -Dhttp.keepAlive=false -Dmaven.wagon.http.pool=false -Dmaven.wagon.httpconnectionManager.ttlSeconds=120
jobs:
integration-test:
name: DB Tests - Java ${{ matrix.java }} with ${{ matrix.database }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
database: [postgresql-15, mysql-8, mariadb-10]
include:
- database: postgresql-15
db-image: postgres:15
db-port: 5432
db-name: testdb
db-user: testuser
db-password: testpass
- database: mysql-8
db-image: mysql:8.0
db-port: 3306
db-name: testdb
db-user: root
db-password: password
- database: mariadb-10
db-image: mariadb:10.11
db-port: 3306
db-name: testdb
db-user: root
db-password: password
services:
database:
image: ${{ matrix.db-image }}
ports:
- ${{ matrix.db-port }}:${{ matrix.db-port }}
env:
POSTGRES_DB: ${{ matrix.db-name }}
POSTGRES_USER: ${{ matrix.db-user }}
POSTGRES_PASSWORD: ${{ matrix.db-password }}
MYSQL_DATABASE: ${{ matrix.db-name }}
MYSQL_ROOT_PASSWORD: ${{ matrix.db-password }}
MARIADB_DATABASE: ${{ matrix.db-name }}
MARIADB_ROOT_PASSWORD: ${{ matrix.db-password }}
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=3
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Wait for database
run: |
echo "Waiting for database to be ready..."
sleep 30
- name: Run integration tests
run: |
mvn -B verify -Pintegration-test \
-Ddatabase.url=jdbc:postgresql://localhost:${{ matrix.db-port }}/${{ matrix.db-name }} \
-Ddatabase.username=${{ matrix.db-user }} \
-Ddatabase.password=${{ matrix.db-password }}
env:
DB_TYPE: ${{ matrix.database }}

Spring Boot Specific Matrix Configurations

1. Spring Profile Matrix Testing

name: Spring Profile Matrix
on: [push, pull_request]
jobs:
spring-test:
name: Spring Tests - ${{ matrix.profile }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
profile: [dev, test, prod]
include:
- profile: dev
active-profiles: dev,h2
- profile: test
active-profiles: test,postgresql
- profile: prod
active-profiles: prod,mysql,metrics
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Test with profile ${{ matrix.profile }}
run: |
mvn -B test \
-Dspring.profiles.active=${{ matrix.active-profiles }} \
-Dskip.integration.tests=true
- name: Integration tests
if: matrix.profile == 'test'
run: |
mvn -B verify -Pintegration-test \
-Dspring.profiles.active=${{ matrix.active-profiles }}

2. Spring Boot Version Compatibility Matrix

name: Spring Boot Version Matrix
on:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * 1'  # Weekly on Monday at 2 AM
jobs:
compatibility-test:
name: Spring Boot ${{ matrix.spring-boot }} on Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
spring-boot: [3.1.0, 3.0.0, 2.7.0]
exclude:
- java: 21
spring-boot: 2.7.0  # Spring Boot 2.7 doesn't support Java 21
- java: 8
spring-boot: 3.1.0  # Spring Boot 3+ doesn't support Java 8
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Update Spring Boot version
run: |
mvn -B versions:set -DnewVersion=1.0.0-SNAPSHOT
mvn -B versions:update-parent -DparentVersion=${{ matrix.spring-boot }}
mvn -B versions:set-property \
-Dproperty=spring-boot.version \
-DnewVersion=${{ matrix.spring-boot }}
- name: Build and test
run: mvn -B clean verify

Gradle-Specific Matrix Configurations

1. Gradle Build Matrix

name: Gradle Matrix Build
on: [push, pull_request]
jobs:
gradle-test:
name: Gradle - Java ${{ matrix.java }} with ${{ matrix.gradle }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
java: [11, 17, 21]
gradle: [8.4, 8.3, 8.2]
os: [ubuntu-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'gradle'
- name: Setup Gradle ${{ matrix.gradle }}
uses: gradle/actions/setup-gradle@v3
with:
gradle-version: ${{ matrix.gradle }}
- name: Make gradlew executable
if: matrix.os == 'ubuntu-latest'
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Run tests
run: ./gradlew test
- name: Run integration tests
run: ./gradlew integrationTest

2. Gradle Feature Variant Matrix

name: Gradle Feature Variant Matrix
on: [push, pull_request]
jobs:
variant-test:
name: Variant ${{ matrix.variant }} - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17]
variant: [free, pro, enterprise]
include:
- variant: free
build-args: -Pvariant=free
- variant: pro
build-args: -Pvariant=pro -PenablePremium=true
- variant: enterprise
build-args: -Pvariant=enterprise -PenablePremium=true -PenableEnterprise=true
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'gradle'
- name: Build variant ${{ matrix.variant }}
run: ./gradlew build ${{ matrix.build-args }}
- name: Test variant ${{ matrix.variant }}
run: ./gradlew test ${{ matrix.build-args }}

Advanced Matrix Patterns

1. Dynamic Matrix Generation

name: Dynamic Matrix from JSON
on: [push, pull_request]
jobs:
generate-matrix:
name: Generate Build Matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Generate matrix configuration
id: set-matrix
run: |
MATRIX_JSON=$(cat <<EOF
{
"java": [11, 17, 21],
"os": ["ubuntu-latest", "windows-latest"],
"include": [
{
"java": 11,
"os": "ubuntu-latest",
"distribution": "temurin",
"experimental": false
},
{
"java": 17,
"os": "ubuntu-latest", 
"distribution": "temurin",
"experimental": false
},
{
"java": 21,
"os": "ubuntu-latest",
"distribution": "temurin", 
"experimental": true
}
]
}
EOF
)
echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
build:
name: Build - Java ${{ matrix.java }} on ${{ matrix.os }}
needs: generate-matrix
runs-on: ${{ matrix.os }}
strategy:
matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: ${{ matrix.distribution || 'temurin' }}
cache: 'maven'
- name: Build
run: mvn -B clean compile
- name: Test
if: ${{ !matrix.experimental || matrix.experimental == false }}
run: mvn -B test

2. Conditional Matrix Execution

name: Conditional Matrix Builds
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
analysis:
name: Static Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: SpotBugs analysis
run: mvn -B spotbugs:check
- name: PMD analysis
run: mvn -B pmd:check
test:
name: Tests - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: analysis
if: ${{ always() && needs.analysis.result == 'success' }}
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Run unit tests
run: mvn -B test -Dskip.integration.tests=true
- name: Run integration tests
if: matrix.java == '17'  # Only run integration tests on Java 17
run: mvn -B verify -Pintegration-test
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: test
if: ${{ github.ref == 'refs/heads/main' && needs.test.result == 'success' }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Deploy to production
run: mvn -B deploy -DskipTests
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Performance Optimization

1. Caching Strategies

name: Optimized Matrix with Caching
on: [push, pull_request]
jobs:
test:
name: Test - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [8, 11, 17, 21]
fail-fast: false  # Don't fail all if one fails
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Maven dependencies
uses: actions/cache@v3
with:
path: |
~/.m2/repository
**/target
key: maven-${{ hashFiles('**/pom.xml') }}-${{ matrix.java }}
restore-keys: |
maven-${{ hashFiles('**/pom.xml') }}
maven-
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Build and test
run: |
mvn -B clean test \
-Dmaven.test.failure.ignore=true \
-Dsurefire.rerunFailingTestsCount=2
- name: Upload test results
uses: actions/upload-artifact@v3
if: always()
with:
name: test-results-java-${{ matrix.java }}
path: |
**/target/surefire-reports
**/target/failsafe-reports
retention-days: 7

2. Parallel Test Execution

name: Parallel Test Matrix
on: [push, pull_request]
jobs:
parallel-tests:
name: Parallel Tests - Shard ${{ matrix.shard }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [17]
shard: [1, 2, 3, 4]
total-shards: [4]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Run parallel tests
run: |
mvn -B test \
-DtestShard=${{ matrix.shard }}/${{ matrix.total-shards }} \
-Dparallel=classes \
-DthreadCount=4
- name: Merge test reports
if: matrix.shard == 1
run: mvn -B surefire-report:report-only

Security and Compliance Matrix

1. Security Scanning Matrix

name: Security Matrix Scan
on:
push:
branches: [ main ]
schedule:
- cron: '0 2 * * *'  # Daily at 2 AM
jobs:
security-scan:
name: Security Scan - Java ${{ matrix.java }}
runs-on: ubuntu-latest
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: OWASP Dependency Check
run: |
mvn -B org.owasp:dependency-check-maven:check \
-Dformat=HTML \
-Dformat=JSON
- name: Upload security report
uses: actions/upload-artifact@v3
with:
name: security-report-java-${{ matrix.java }}
path: target/dependency-check-report.*
- name: SonarQube analysis
if: matrix.java == '17'
run: |
mvn -B sonar:sonar \
-Dsonar.projectKey=my-java-project \
-Dsonar.host.url=${{ secrets.SONAR_HOST_URL }} \
-Dsonar.login=${{ secrets.SONAR_TOKEN }}

Complete Enterprise Example

name: Enterprise Java Matrix Pipeline
on:
push:
branches: [ main, develop, release/* ]
pull_request:
branches: [ main ]
schedule:
- cron: '0 6 * * 1-5'  # Weekdays at 6 AM
env:
MAVEN_OPTS: -Djava.awt.headless=true -Dmaven.repo.local=/tmp/m2/repository
jobs:
quality-gates:
name: Quality Gates
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: 'maven'
- name: Check formatting
run: mvn -B spotless:check
- name: Check dependencies
run: mvn -B versions:display-dependency-updates
- name: License compliance
run: mvn -B license:check
compatibility-matrix:
name: Compatibility - ${{ matrix.java }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
needs: quality-gates
strategy:
matrix:
java: [11, 17, 21]
os: [ubuntu-latest, windows-latest, macos-latest]
exclude:
- java: 21
os: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Build
run: mvn -B clean compile
- name: Unit tests
run: mvn -B test -Dskip.integration.tests=true
- name: Integration tests
if: matrix.java == '17' && matrix.os == 'ubuntu-latest'
run: mvn -B verify -Pintegration-test
performance-matrix:
name: Performance - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: compatibility-matrix
strategy:
matrix:
java: [11, 17, 21]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
cache: 'maven'
- name: Performance tests
run: |
mvn -B verify -Pperformance-test \
-Dperf.test.duration=300s \
-Djava.version=${{ matrix.java }}
- name: Upload performance results
uses: actions/upload-artifact@v3
with:
name: perf-results-java-${{ matrix.java }}
path: target/performance-reports/
security-matrix:
name: Security - Java ${{ matrix.java }}
runs-on: ubuntu-latest
needs: compatibility-matrix
strategy:
matrix:
java: [11, 17]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- name: Security scan
run: |
mvn -B org.owasp:dependency-check-maven:check \
-DsuppressionFile=security-suppressions.xml
- name: Upload security report
uses: actions/upload-artifact@v3
with:
name: security-report-java-${{ matrix.java }}
path: target/dependency-check-report.*
deploy:
name: Deploy to Environment
runs-on: ubuntu-latest
needs: [compatibility-matrix, performance-matrix, security-matrix]
if: ${{ github.ref == 'refs/heads/main' && contains(needs.*.result, 'success') }}
environment: production
strategy:
matrix:
environment: [staging, production]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Java 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Deploy to ${{ matrix.environment }}
run: |
mvn -B deploy -DskipTests \
-Ddeploy.env=${{ matrix.environment }} \
-Ddeploy.token=${{ secrets.DEPLOY_TOKEN }}

Best Practices

1. Matrix Configuration Tips

# .github/workflows/best-practices.yml
name: Matrix Best Practices
on: [push, pull_request]
jobs:
optimized-matrix:
runs-on: ubuntu-latest
strategy:
matrix:
# Use semantic version names
java-version: [11, 17, 21]
# Limit matrix size for PRs
include-pr:
- java-version: 17
full-test-suite: false
# Use fail-fast appropriately
fail-fast: ${{ github.event_name == 'pull_request' }}
# Set maximum parallel jobs
max-parallel: ${{ github.event_name == 'pull_request' && 2 || 10 }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java-version }}
distribution: temurin
cache: maven

2. Resource Optimization

name: Resource Optimized Matrix
on: [push, pull_request]
jobs:
resource-aware:
runs-on: ${{ matrix.runner }}
strategy:
matrix:
java: [11, 17, 21]
runner: [ubuntu-latest]
include:
- java: 21
runner: ubuntu-latest
resource-profile: high-memory
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Java with optimized memory
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: temurin
cache: maven
java-package: jdk
check-latest: true

Conclusion

GitHub Actions Matrix builds provide Java teams with:

  • Comprehensive Testing: Validate across multiple Java versions, OS platforms, and configurations
  • Faster Feedback: Parallel execution reduces CI/CD pipeline duration
  • Early Issue Detection: Catch compatibility issues before production
  • Resource Efficiency: Optimize cloud resource usage
  • Scalable Pipelines: Handle complex testing scenarios efficiently

By implementing matrix builds, Java teams can ensure their applications work reliably across all supported environments while maintaining development velocity and code quality.

Leave a Reply

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


Macro Nepal Helper