matrix-optimizer
Matrix Optimizer
Configure and optimize GitHub Actions matrix strategies for efficient multi-version and multi-platform testing.
Quick Start
Basic matrix for testing multiple Node.js versions:
strategy:
matrix:
node-version: [16, 18, 20]
Instructions
Step 1: Identify Matrix Dimensions
Common matrix dimensions:
- Language versions: Node.js, Python, Ruby, Go versions
- Operating systems: ubuntu, macos, windows
- Architectures: x64, arm64
- Dependency versions: Database versions, framework versions
- Feature flags: Different configuration options
Example dimensions:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [16, 18, 20]
# This creates 9 jobs (3 OS × 3 versions)
Step 2: Configure Matrix Strategy
Basic matrix:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
node-version: [18, 20]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm test
Matrix with include:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
node-version: [18, 20]
include:
# Add specific combination
- os: windows-latest
node-version: 20
# Add extra variables for specific combination
- os: ubuntu-latest
node-version: 20
experimental: true
Matrix with exclude:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [16, 18, 20]
exclude:
# Skip Node 16 on Windows
- os: windows-latest
node-version: 16
# Skip Node 16 on macOS
- os: macos-latest
node-version: 16
Step 3: Optimize for Cost and Speed
Fail-fast strategy:
strategy:
fail-fast: false # Continue all jobs even if one fails
matrix:
node-version: [16, 18, 20]
Max parallel jobs:
strategy:
max-parallel: 2 # Limit concurrent jobs
matrix:
node-version: [16, 18, 20]
Conditional matrix:
strategy:
matrix:
os: [ubuntu-latest]
# Add more OS only on main branch
${{ github.ref == 'refs/heads/main' && fromJSON('["macos-latest", "windows-latest"]') || fromJSON('[]') }}
Step 4: Use Matrix Variables
In job steps:
steps:
- name: Display matrix values
run: |
echo "OS: ${{ matrix.os }}"
echo "Version: ${{ matrix.node-version }}"
echo "Experimental: ${{ matrix.experimental }}"
In job configuration:
jobs:
test:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental == true }}
strategy:
matrix:
os: [ubuntu-latest]
node-version: [18, 20]
include:
- node-version: 21
experimental: true
Step 5: Name Jobs Clearly
jobs:
test:
name: Test on ${{ matrix.os }} with Node ${{ matrix.node-version }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
node-version: [18, 20]
Common Patterns
Language Version Matrix
Node.js:
strategy:
matrix:
node-version: [16, 18, 20]
steps:
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
Python:
strategy:
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
Go:
strategy:
matrix:
go-version: ['1.20', '1.21', '1.22']
steps:
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
Cross-Platform Matrix
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
include:
# Platform-specific configurations
- os: ubuntu-latest
install-cmd: sudo apt-get install
- os: macos-latest
install-cmd: brew install
- os: windows-latest
install-cmd: choco install
steps:
- name: Install dependencies
run: ${{ matrix.install-cmd }} package-name
Database Version Matrix
strategy:
matrix:
postgres-version: [12, 13, 14, 15]
services:
postgres:
image: postgres:${{ matrix.postgres-version }}
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
Feature Flag Matrix
strategy:
matrix:
feature:
- name: baseline
flags: ''
- name: new-parser
flags: '--enable-new-parser'
- name: experimental
flags: '--enable-experimental'
steps:
- name: Run tests
run: npm test ${{ matrix.feature.flags }}
Optimization Strategies
Reduce Matrix Size
Before (12 jobs):
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [16, 18, 20, 21]
After (7 jobs):
matrix:
# Test all versions on Linux only
os: [ubuntu-latest]
node-version: [16, 18, 20, 21]
include:
# Test latest version on other platforms
- os: macos-latest
node-version: 21
- os: windows-latest
node-version: 21
Conditional Matrix Expansion
strategy:
matrix:
# Always test on Linux
os: [ubuntu-latest]
node-version: [18, 20]
include:
# Full matrix only on main branch or release tags
- ${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}:
os: [macos-latest, windows-latest]
Parallel vs Sequential
High parallelism (faster, more expensive):
strategy:
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- run: npm test -- --shard=${{ matrix.shard }}/8
Limited parallelism (slower, cheaper):
strategy:
max-parallel: 2
matrix:
shard: [1, 2, 3, 4, 5, 6, 7, 8]
Caching Across Matrix
steps:
- uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-${{ matrix.node-version }}-
${{ runner.os }}-node-
Advanced Patterns
Dynamic Matrix from JSON
jobs:
setup:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- id: set-matrix
run: |
# Generate matrix dynamically
MATRIX='{"include":[{"os":"ubuntu-latest","version":"18"},{"os":"macos-latest","version":"20"}]}'
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
test:
needs: setup
strategy:
matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
runs-on: ${{ matrix.os }}
steps:
- run: echo "Testing on ${{ matrix.os }} with version ${{ matrix.version }}"
Matrix with Outputs
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
outputs:
result-${{ matrix.os }}: ${{ steps.test.outputs.result }}
steps:
- id: test
run: echo "result=passed" >> $GITHUB_OUTPUT
Reusable Matrix Workflow
# .github/workflows/reusable-matrix.yml
on:
workflow_call:
inputs:
versions:
required: true
type: string
jobs:
test:
strategy:
matrix:
version: ${{ fromJSON(inputs.versions) }}
runs-on: ubuntu-latest
steps:
- run: echo "Testing version ${{ matrix.version }}"
# Caller workflow
jobs:
test:
uses: ./.github/workflows/reusable-matrix.yml
with:
versions: '["18", "20", "21"]'
Troubleshooting
Too many jobs:
- Use
excludeto remove unnecessary combinations - Test all versions on one OS, latest version on others
- Use conditional matrix expansion for PRs vs main
Jobs failing inconsistently:
- Set
fail-fast: falseto see all failures - Check for race conditions or timing issues
- Verify platform-specific dependencies
Slow matrix execution:
- Increase
max-parallelif budget allows - Optimize caching strategy
- Consider test sharding within jobs
Matrix not expanding:
- Verify JSON syntax in
fromJSON() - Check that matrix variables are properly referenced
- Ensure
includeandexcludesyntax is correct
Best Practices
- Start small: Begin with minimal matrix, expand as needed
- Test locally first: Verify one configuration works before expanding
- Use fail-fast: false: See all failures, not just first
- Name jobs clearly: Include matrix values in job names
- Cache effectively: Use matrix values in cache keys
- Optimize for PRs: Smaller matrix for PRs, full matrix for main
- Document matrix: Explain why each dimension is needed
- Monitor costs: Track runner minutes usage
More from armanzeroeight/fastagent-plugins
gcp-cost-optimizer
Analyzes GCP costs and provides optimization recommendations including committed use discounts, rightsizing, and unused resources. Use when optimizing GCP spending or analyzing GCP costs.
15kubernetes-best-practices
Provides production-ready Kubernetes manifest guidance including resource management, security, high availability, and configuration best practices. This skill should be used when working with Kubernetes YAML files, deployments, pods, services, or when users mention k8s, container orchestration, or cloud-native applications.
11schema-designer
Design database schemas with proper normalization, relationships, constraints, and indexes. Use when creating database tables, modeling data relationships, or designing database structure.
11api-documentation-generator
Generate OpenAPI/Swagger specifications and API documentation from code or design. Use when creating API docs, generating OpenAPI specs, or documenting REST APIs.
9goroutine-patterns
Implement Go concurrency patterns using goroutines, channels, and synchronization primitives. Use when building concurrent systems, implementing parallelism, or managing goroutine lifecycles. Trigger words include "goroutine", "channel", "concurrent", "parallel", "sync", "context".
9inventory-manager
Organizes Ansible inventory files, manages host groups, and configures dynamic inventory. Use when organizing Ansible inventory, managing host groups, or setting up dynamic inventory sources.
9