repomatic.github.ci_status module¶
Which CI jobs are red, and which of those actually gate a merge.
repomatic names the jobs it generates, prefixing each matrix cell with a
glyph that records whether the cell is allowed to fail: ✅ for a required
one, ⁉️ for a probe running under continue-on-error. Reading that back
was left to whoever was watching CI, and it is easy to get wrong in three
specific ways this module exists to settle:
The glyph, not the position. Job names differ in shape across workflows:
tests.yamlemits✅ ubuntu-26.04 / py3.10while the release engine emitsworkflow / ✅ ubuntu-26.04, abc1234 build. Anything that splits on" / "and reads a fixed field strips the glyph off one of the two and files a required red as a probe, which reads as green.Jobs, not the run. A run’s own
conclusionissuccesswhile acontinue-on-errorprobe inside it crashed, and itsstatusstill readsqueuedwhile a dozen of its jobs have already finished. Neither answers “is anything broken”.A run that failed around its jobs. A
failureconclusion with no failed job is a workflow-level error: an invalidstrategy.matrixexpression, malformed YAML, a missing secret. There is no job log to read, and treating it as benign is how a persistently red workflow gets written off as a known artifact.
A job carrying no stability glyph is required. That covers every non-matrix job
(1️⃣ Run-once tests, 📦 Package install, 🛡️ Lint types), where the absence
of a marker means the job was never optional rather than that its status is
unknown. Only the two stability glyphs count: a job name may carry any other
emoji and still be required, which is why the test is for ⁉️ specifically
rather than for a decorated name.
- repomatic.github.ci_status.BATCH_RUN_LIMIT = 100¶
Runs fetched by the branch-wide listing
read_ci_status()starts with.Deep enough that every monitored workflow’s newest run on a freshly pushed branch sits inside it. A workflow whose newest run is older than the window is not misreported: it falls back to its own
latest_run()query.
- repomatic.github.ci_status.STABLE_GLYPH = '✅'¶
Marks a matrix cell that must pass. See
UNSTABLE_GLYPH.
- repomatic.github.ci_status.UNSTABLE_GLYPH = '⁉️'¶
Marks a matrix cell running under
continue-on-error.A red one never gates a merge, which is exactly why it has to be told apart from a required cell rather than counted with it. A release still fixes what it can: see
claude.mdon the genuinely-green goal.
- repomatic.github.ci_status.TERMINAL_STATUSES = frozenset({'completed'})¶
Job statuses meaning the job will not change again.
- repomatic.github.ci_status.CI_STATUS_HEADER_DEFS: tuple[tuple[str, str], ...] = (('Workflow', 'workflow'), ('Commit', 'commit'), ('Run status', 'run-status'), ('Verdict', 'verdict'))¶
Column definitions for the
ci-statustable.
- class repomatic.github.ci_status.JobStatus(name, status, conclusion)[source]¶
Bases:
objectOne job of one workflow run.
- property required: bool¶
Whether a failure here gates a merge.
Looks for the glyph anywhere in the raw name rather than at its start. The two shapes disagree on where it sits:
tests.yamlleads with it (⁉️ ubuntu-26.04 / py3.15-dev) while the release engine prefixes the workflow first (release / ⁉️ windows-11-arm, abc1234 build). A leading-position test passes the first and silently files the second as required; splitting on" / "gets it wrong the other way round. Containment is the one form both satisfy, and the templates emit exactly one glyph per name.
- class repomatic.github.ci_status.RunStatus(workflow, run_id, head_sha, status, conclusion, jobs=())[source]¶
Bases:
objectThe latest run of one workflow on one branch.
- class repomatic.github.ci_status.CIStatus(branch, runs=<factory>)[source]¶
Bases:
objectEvery monitored workflow’s latest run on a branch.
- repomatic.github.ci_status.monitored_workflows(workflow_dir)[source]¶
Every workflow a push to the default branch can start.
Derived from the tree rather than listed by hand, so a workflow added later is watched without anyone remembering to add it here. A reusable workflow is excluded: it has no runs of its own, only the ones its callers create.
- repomatic.github.ci_status.latest_run(workflow, branch)[source]¶
Read a workflow’s most recent run on branch, jobs included.
- Parameters:
- Return type:
- Returns:
The run, or
Nonewhen the workflow has none. An empty listing is not proof the workflow was filtered out: GitHub can sit on a push event for hours before materializing a run.
- repomatic.github.ci_status.read_ci_status(workflows, branch)[source]¶
Read the latest run of each workflow on branch.
Batched: one branch-wide run listing locates the newest run of every recently active workflow, then one
gh run viewper run reads its jobs, so a poll costs 2 + N calls where the per-workflow loop cost 2 per workflow. A workflow whose newest run is older than the listing window falls back to its ownlatest_run()query, so the batching never costs correctness.