repomatic.matrix.model module¶
GitHub Actions job-matrix model.
Variations, includes, excludes, and their expansion into the JSON payload
workflow strategy.matrix keys consume.
- repomatic.matrix.model.GITHUB_MATRIX_JOB_LIMIT = 256¶
Most jobs GitHub expands one
strategy.matrixinto.GitHub refuses a matrix past this size, so
Matrix.solve()reports one before a workflow meets the refusal.
- repomatic.matrix.model.JOB_STATE_KEY = 'state'¶
Job key carrying whether a cell runs as
stableorunstable.Matrix.pivot_counts()tallies it in each cell, absent a caller’s choice.
- repomatic.matrix.model.OS_AXIS = 'os'¶
Job key naming the runner image: the default grid column.
- repomatic.matrix.model.PYTHON_VERSION_AXIS = 'python-version'¶
Job key naming the interpreter version: the default grid row.
- repomatic.matrix.model.RESERVED_MATRIX_KEYWORDS = ('include', 'exclude')¶
Keys GitHub reserves inside a
strategy.matrixblock.Neither can name a variation axis, since both already mean something to the matrix expander.
Matrix._check_idsrejects them.
- repomatic.matrix.model.stale_axis_values(entry, axes)[source]¶
Return the
entrykey/value pairs absent from the matrixaxes.A non-empty result means an
excludedirective can never match a combination: one of its keys is not a live axis, or its value is absent from that axis.Matrix.prune()drops such a directive silently, since GitHub rejects a matrix whose excludes name unknown keys; this is the predicate behind that decision, exposed so callers can also report the drift instead of only absorbing it (seeMetadata.stale_test_matrix_excludes).
- class repomatic.matrix.model.Matrix[source]¶
Bases:
objectA matrix as defined by GitHub’s actions workflows.
See GitHub official documentation on how-to implement variations of jobs in a workflow.
Note
Why matrices are pre-computed in the
metadatajobGitHub Actions matrix outputs are not cumulative — the last job in a matrix wins (community discussion). This makes a matrix-based job terminal in a dependency graph: no downstream job can depend on its aggregated outputs.
The workaround is a single preliminary
metadatajob that computes all matrices upfront. Downstream jobs depend on that job and consume the pre-built matrices, rather than computing them themselves.A matrix starts empty and is populated through its own methods, never through the constructor:
matrix()renders the result as a newdictfor serialization, and__getitem__reads a single axis, but the object itself is not a mapping: it holds axes, includes and excludes as separate state.The implementation respects the order in which items were inserted. This provides a natural and visual sorting that should ease the inspection and debugging of large matrix.
- matrix()[source]¶
Returns a copy of the matrix, with its
includeandexcludedirectives.A directive list that holds nothing is left out, as GitHub expects. The copy is detached from the model: changing it changes no axis here.
- add_variation(variation_id, values)[source]¶
Extend the axis variation_id with values, dropping duplicates.
- Raises:
ValueError – When values holds nothing, or holds something other than a string.
- Return type:
- replace_variation_value(variation_id, old, new)[source]¶
Replace a single value within a variation axis.
The new value takes the position of the old value. If the new value already exists elsewhere in the axis, the later duplicate is removed.
Silently skips if the axis does not exist or does not contain the old value, making the operation idempotent.
- Return type:
- remove_variation_value(variation_id, value)[source]¶
Remove a single value from a variation axis.
If the axis becomes empty after removal, it is deleted entirely.
Silently skips if the axis does not exist or does not contain the value, making the operation idempotent.
- Return type:
- add_includes(*new_includes)[source]¶
Add one or more
includespecial directives to the matrix.- Return type:
- add_excludes(*new_excludes)[source]¶
Add one or more
excludespecial directives to the matrix.- Return type:
- prune()[source]¶
Remove no-op exclude directives and log about them.
An exclude is a no-op when it references a key that is not a variation axis at all, or when the key exists but the value is not present in that axis. Either way the exclude can never match any combination produced by
product(), and GitHub Actions rejects excludes that reference non-existent matrix keys.- Return type:
- product()[source]¶
Yield every combination of the declared axes, before any directive.
Respects the order of the axes and of their values.
- solve()[source]¶
Expand the matrix to explicit jobs, applying
excludetheninclude.Reproduces GitHub’s documented matrix algorithm:
Build the cross-product of the base variations.
Drop every combination matching an
excludedirective. A directive matches when all of its keys equal the combination’s, so a partial directive removes a whole slice.Process
includedirectives in order. Each is merged into every product combination it does not conflict with (it conflicts when it would overwrite an original axis value). A directive merging into no combination is appended as a new standalone job.
Note
includedirectives augment combinations from the base cross-product only, never jobs created by an earlierinclude. An excluded combination is resurrected solely when anincludefully re-specifies it, so it merges into nothing and is appended: a partialincludethat augments surviving jobs does not bring excluded slices back. GitHub remains the authoritative expander, but this follows its documented rules so downstreamfull-includejob lists (whichmatrix()serializes verbatim) match what GitHub would run.A matrix expanding past
GITHUB_MATRIX_JOB_LIMITjobs is logged as critical, since GitHub refuses to run it.
- job_keys()[source]¶
Every key a job of the solved matrix carries, sorted.
Read off the solved jobs rather than off the declared axes, so a key only an
includedirective sets is listed, and a key whose directive anexcludecancelled is not. That makes it the set of keyspivot_counts()can lay a grid out on.
- pivot_counts(row_axis='python-version', col_axis='os', cell_key='state')[source]¶
Tally, per grid intersection, the jobs carrying each
cell_keyvalue.A grid has two axes, so a matrix varying on a third lands several jobs on one intersection. The counts keep them apart: a cell standing for five jobs of one state and a cell standing for one job are told apart by their count.
- Parameters:
- Return type:
- Returns:
A
(row_value, col_value)toCountermapping, in first-seen job order, each counter keyed in first-seen order too. An intersection no job occupies is absent from the mapping rather than present with an empty counter.