GitHub Actions Matrix Preview
Paste a GitHub Actions workflow, a strategy: block or a matrix: block. The preview builds the cartesian product of the matrix variables, removes exclude partial matches, then applies include the way GitHub documents it: an entry extends every original combination it can join without overwriting an original value, and otherwise becomes a job of its own. It shows the job names, fail-fast and max-parallel, the count against the 256-job limit, and explains what each include and exclude entry did. Expressions such as fromJSON(...) are flagged, never guessed. Everything runs in your browser.
Matrix Preview
Client-Side Only
Expanded in this browser tab. The workflow is parsed and expanded by fixed rules taken from GitHub's matrix documentation. Nothing is uploaded, and expressions are reported rather than evaluated.
jobs.e2e
- Jobs
- 15 / 256
- Product
- 18
- Excluded
- 4
- Added by include
- 1
- fail-fast
- false
- max-parallel
- 6
Findings (4)
Warning jobs.e2e.strategy.matrix.include[2]
{os: windows-latest, browser: webkit} became a job of its own. The 2 combinations it would have joined were removed by exclude, which runs first. It has no shard, so matrix.shard is an empty string in this job.
Note jobs.e2e.strategy.matrix.include[0]
{os: ubuntu-latest, browser: chromium, coverage: true} is merged into 2 combinations, because it does not name shard. To change one job, give a value for every matrix variable.
Note jobs.e2e.strategy.matrix.include[1]
{os: ubuntu-latest, browser: chromium, node: 24} is merged into 2 combinations, because it does not name shard. To change one job, give a value for every matrix variable.
Note jobs.e2e.strategy.matrix.include[2]
This combination matches an exclude entry, but include is processed after exclude, so it runs anyway.
What exclude and include did, in order
- exclude[0]
{"os":"windows-latest","browser":"webkit"}removed 2 combinations - exclude[1]
{"os":"macos-latest","browser":"firefox"}removed 2 combinations - include[0]
{"os":"ubuntu-latest","browser":"chromium","coverage":true}merged into 2 jobs - include[1]
{"os":"ubuntu-latest","browser":"chromium","node":24}merged into 2 jobs - include[2]
{"os":"windows-latest","browser":"webkit"}added as a new job
Jobs in the order GitHub creates them
| # | Job name | os | browser | shard | coverage | node | From |
|---|---|---|---|---|---|---|---|
| 1 | e2e (ubuntu-latest, chromium, 1/2) | ubuntu-latest | chromium | 1/2 | true | 24 | product, extended by include[0], include[1] |
| 2 | e2e (ubuntu-latest, chromium, 2/2) | ubuntu-latest | chromium | 2/2 | true | 24 | product, extended by include[0], include[1] |
| 3 | e2e (ubuntu-latest, firefox, 1/2) | ubuntu-latest | firefox | 1/2 | - | - | product |
| 4 | e2e (ubuntu-latest, firefox, 2/2) | ubuntu-latest | firefox | 2/2 | - | - | product |
| 5 | e2e (ubuntu-latest, webkit, 1/2) | ubuntu-latest | webkit | 1/2 | - | - | product |
| 6 | e2e (ubuntu-latest, webkit, 2/2) | ubuntu-latest | webkit | 2/2 | - | - | product |
| 7 | e2e (windows-latest, chromium, 1/2) | windows-latest | chromium | 1/2 | - | - | product |
| 8 | e2e (windows-latest, chromium, 2/2) | windows-latest | chromium | 2/2 | - | - | product |
| 9 | e2e (windows-latest, firefox, 1/2) | windows-latest | firefox | 1/2 | - | - | product |
| 10 | e2e (windows-latest, firefox, 2/2) | windows-latest | firefox | 2/2 | - | - | product |
| 11 | e2e (macos-latest, chromium, 1/2) | macos-latest | chromium | 1/2 | - | - | product |
| 12 | e2e (macos-latest, chromium, 2/2) | macos-latest | chromium | 2/2 | - | - | product |
| 13 | e2e (macos-latest, webkit, 1/2) | macos-latest | webkit | 1/2 | - | - | product |
| 14 | e2e (macos-latest, webkit, 2/2) | macos-latest | webkit | 2/2 | - | - | product |
| 15 | e2e (windows-latest, webkit) | windows-latest | webkit | - | - | - | added by include[2] |
Frequently Asked Questions(click to expand)
Why did my include entry add a job instead of changing one?
An include entry joins every original combination it can join without changing one of that combination's matrix values. If it names a variable with a value no remaining combination has, such as experimental: true against experimental: [false], it becomes its own job. Combinations that exclude removed do not count, because exclude runs first.
Why did it change two jobs when I meant one?
An entry that leaves out a variable matches every value of that variable. To target one job, give the entry a value for every matrix variable.
Where do the job names come from?
GitHub does not document the default name. The preview follows the names GitHub gave jobs in public workflow runs: the job's name or ID, then the original matrix values in brackets, leaving out keys include added and empty strings. A name with a ${{ matrix.* }} expression is used as written.
Why does 3.10 show as 3.1?
Unquoted, 3.10 is a YAML number and 8.0 is the number 8. Quote version strings. The YAML Formatter shows how any value parses.
How many combinations do I actually need?
Usually fewer than the product. Choosing JUnit parameter rows without a matrix explosion covers picking boundary rows instead of crossing every dimension, and the guide to failing CI on flaky Playwright tests covers merging sharded reports before gating on them.