This repository demonstrates how to work around GitHub Actions' 256 matrix job limit using fromJSON and batching strategies, with practical examples for iOS monorepo testing scenarios.
π New to this pattern? Check out the Quick Reference Guide for a condensed overview with code examples.
ποΈ Want to understand the architecture? See the Architecture Documentation for detailed diagrams and data flow.
GitHub Actions limits matrix jobs to a maximum of 256 combinations per workflow. For large monorepos with hundreds of components, this becomes a significant constraint when you need to run tests across all components.
This repository demonstrates three complementary strategies to exceed the 256 job limit:
Use fromJSON() to dynamically generate matrix values from job outputs, enabling runtime-based matrix configuration.
Split large test suites into multiple batches (each β€256 jobs) that run in parallel as separate matrix jobs.
Combine reusable workflows with dynamic matrices to create maintainable, scalable test infrastructure.
matrix-256.yml- A workflow with exactly 256 matrix combinations (16Γ16)matrix-260.yml- A workflow exceeding the limit (13Γ20 = 260) - will fail
A reusable workflow template for running component tests. This demonstrates:
- How to create a reusable workflow that can be called with different inputs
- Proper input validation and parameter passing
- Standardized test execution pattern
Usage:
uses: ./.github/workflows/reusable-test.yml
with:
component: 'component-1'
batch: '1'Demonstrates the batching strategy using fromJSON with inline test execution.
Features:
- Dynamic matrix generation based on test scope (single, all, custom)
- Automatic splitting into batches of 256 jobs each
- Support for 300+ components (demonstrated with 3 batches = 768 potential jobs)
- Conditional batch execution (batches only run if needed)
- Aggregated summary reporting
How to run:
# Test a single component
gh workflow run dynamic-matrix-batched.yml -f test_scope=single
# Test all components (300+, exceeds 256 limit)
gh workflow run dynamic-matrix-batched.yml -f test_scope=all
# Test custom component list
gh workflow run dynamic-matrix-batched.yml -f test_scope=custom -f component_filter="component-1,component-5,component-10"Combines reusable workflows with dynamic matrices and batching.
Features:
- Calls the reusable workflow with matrix strategy
- Configurable component count (default: 300)
- Automatic batch calculation and distribution
- Clean separation of concerns (generation vs. execution)
How to run:
# Test 300 components (2 batches)
gh workflow run dynamic-matrix-reusable.yml -f num_components=300
# Test 100 components (1 batch)
gh workflow run dynamic-matrix-reusable.yml -f num_components=100
# Test 600 components (3 batches)
gh workflow run dynamic-matrix-reusable.yml -f num_components=600A realistic iOS monorepo testing scenario demonstrating the complete solution.
Features:
- Smart test scope determination based on which component was modified
- Core component detection (modifying core components triggers all tests)
- Automatic batching for 300 components
- Simulated iOS test execution (XCTest pattern)
- Comprehensive test reporting
Business logic:
- Single component modified: Run tests only for that component
- Core component modified: Run tests for ALL 300 components
- Manual trigger: Choose to test all or specific components
How to run:
# Test only the modified component
gh workflow run ios-monorepo-example.yml \
-f trigger_type=modified_single_component \
-f modified_component=component-42
# Simulate core component change (tests all 300)
gh workflow run ios-monorepo-example.yml \
-f trigger_type=modified_core_component
# Manually test all components
gh workflow run ios-monorepo-example.yml \
-f trigger_type=manual_all_componentsAn advanced example showing git-based change detection and file-based component mapping.
Features:
- Git diff analysis to determine changed files (simulated)
- File-to-component mapping logic
- Support for up to 1024 components across 4 batches (256 each)
- Three different strategies:
changed_files: Test only components affected by changesall_components: Test all 400 components (requires 2 batches)specific_batch: Test a specific batch by number
- Demonstrates real-world monorepo change detection patterns
How to run:
# Test only changed components (based on git diff)
gh workflow run advanced-file-based-matrix.yml \
-f strategy=changed_files
# Test all 400 components
gh workflow run advanced-file-based-matrix.yml \
-f strategy=all_components
# Test specific batch (e.g., batch 2 = components 257-512)
gh workflow run advanced-file-based-matrix.yml \
-f strategy=specific_batch \
-f batch_number=2jobs:
generate-matrix:
outputs:
matrix: ${{ steps.create.outputs.components }}
steps:
- id: create
run: |
# Generate JSON array dynamically
components='["component-1","component-2","component-3"]'
echo "components=$components" >> $GITHUB_OUTPUT
test:
needs: generate-matrix
strategy:
matrix:
component: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
steps:
- run: echo "Testing ${{ matrix.component }}"jobs:
# Generate multiple batch outputs
generate:
outputs:
batch1: ${{ steps.create.outputs.batch1 }} # Components 1-256
batch2: ${{ steps.create.outputs.batch2 }} # Components 257-512
has_batch2: ${{ steps.create.outputs.has_batch2 }}
# Each batch runs separately
test-batch-1:
strategy:
matrix:
component: ${{ fromJSON(needs.generate.outputs.batch1) }}
# ... max 256 jobs
test-batch-2:
if: needs.generate.outputs.has_batch2 == 'true'
strategy:
matrix:
component: ${{ fromJSON(needs.generate.outputs.batch2) }}
# ... another max 256 jobsjobs:
test-batch-2:
needs: generate-matrix
# Only run if this batch has components
if: needs.generate-matrix.outputs.has_batch2 == 'true'
strategy:
matrix:
component: ${{ fromJSON(needs.generate-matrix.outputs.batch2) }}jobs:
call-reusable:
strategy:
matrix:
component: ${{ fromJSON(needs.generate.outputs.batch1) }}
uses: ./.github/workflows/reusable-test.yml
with:
component: ${{ matrix.component }}- Scalability: Can handle unlimited components by adding more batches
- Flexibility: Dynamic matrix generation based on runtime conditions
- Efficiency: Only run tests for affected components when possible
- Maintainability: Reusable workflows reduce duplication
- Visibility: Clear batch organization and comprehensive reporting
- Parallel Execution: All batches run concurrently for maximum speed
- Runner Availability: Ensure sufficient GitHub-hosted or self-hosted runners
- Rate Limits: Be aware of API rate limits with large job counts
- Execution Time: More jobs = longer overall workflow time (mitigated by parallelization)
- Cost: GitHub-hosted runners incur costs based on minutes used
- Max Parallel: Use
max-parallelto limit concurrent jobs and avoid overwhelming infrastructure
- Use
fail-fast: falsein matrix strategy to see all failures - Implement
max-parallelto control concurrency - Add comprehensive summary jobs to aggregate results
- Use conditional execution to skip empty batches
- Cache dependencies to speed up individual jobs
- Monitor costs when running hundreds of jobs
- Extract common logic: In production, consider extracting batch generation logic into a shared script or action to reduce duplication
Note: The workflows in this repository intentionally duplicate batch generation logic across files for demonstration and clarity. In a production environment, you should extract common patterns into reusable scripts or composite actions.
For the iOS monorepo scenario:
- Detect which component(s) were modified
- Determine if modified component is "core" (affects everything)
- Generate appropriate test matrix:
- Single component: 1 job
- Core component: 300 jobs (split into 2 batches)
- Execute tests in parallel across batches
- Aggregate and report results
This approach provides the flexibility to run targeted tests for efficiency while maintaining the ability to run comprehensive test suites when needed.
- ποΈ Architecture Documentation - Detailed diagrams and data flow
- π Quick Reference Guide - Condensed patterns and code examples
- π GitHub Actions: fromJSON
- π GitHub Actions: Matrix Strategy
- π GitHub Actions: Reusable Workflows