A local Python CLI for developers and CI agents who need to explain whether a GitHub Actions workflow would run for synthetic push or pull_request events.
GitHub Actions trigger filters can be hard to reason about when branches, tags, paths, and *-ignore filters interact. github-actions-trigger-doctor loads one workflow file locally and returns a deterministic run/skip decision with separate event, ref, and path checks.
It does not call GitHub APIs, inspect the current repository, or require network access at runtime.
Use this when a workflow did not run and you want a small reproducible explanation before changing YAML. It is also useful in tests, pre-commit checks, generated repository validation, and AI coding-agent workflows where a machine-readable answer is better than manual inspection.
Developers, release engineers, CI maintainers, and automation agents that need local checks for the supported subset of GitHub Actions triggers:
pushandpull_requestevents- branch, tag, and path include filters
- branch, tag, and path ignore filters
.ymland.yamlworkflow files
- Deterministic CLI output with stable exit codes:
0runs,1skips,2invalid input. - JSON output for CI and automation.
- Python API exposing
load_workflow()andexplain_workflow(). - Preserves GitHub's literal
onkey when loading YAML. - Implements a deliberately small glob subset:
*,**, and?.
Create a workflow and evaluate a synthetic push:
cat > /tmp/ci.yml <<'YAML'
name: CI
on:
push:
branches: [main]
paths: ["src/**"]
YAML
actions-trigger-doctor explain \
--workflow /tmp/ci.yml \
--event push \
--ref refs/heads/main \
--changed-file src/app.pyExpected text output:
RUN: CI
event: matched
ref: matched
paths: matched
For JSON:
actions-trigger-doctor explain \
--workflow /tmp/ci.yml \
--event push \
--ref refs/heads/main \
--changed-file src/app.py \
--jsonExample JSON shape:
{"checks": {"event": {"matched": true, "reason": "workflow listens for push"}, "paths": {"matched": true, "reason": "src/app.py matched paths"}, "ref": {"matched": true, "reason": "branch main matched branches"}}, "event": "push", "path": "/tmp/ci.yml", "runs": true, "workflow": "CI"}From a checkout:
python -m pip install -e .For development tools:
python -m pip install -e .[dev]cat > /tmp/ci.yml <<'YAML'
name: CI
on:
push:
branches: [main]
paths: ["src/**"]
YAML
actions-trigger-doctor explain \
--workflow /tmp/ci.yml \
--event push \
--ref refs/heads/main \
--changed-file src/app.pyExpected output:
RUN: CI
event: matched
ref: matched
paths: matched
actions-trigger-doctor explain --workflow <path> --event <push|pull_request> [options]Options:
--workflow <path>: one.ymlor.yamlworkflow file.--event <push|pull_request>: synthetic event to evaluate.--ref <ref>: full Git ref forpush; defaults torefs/heads/main.--base-ref <branch>: target branch forpull_request; defaults tomain.--changed-file <path>: changed file relative to repo root; may be repeated.--json: print one JSON object instead of text.
Exit codes:
0: command completed and the workflow would run.1: command completed and the workflow would be skipped.2: invalid CLI usage, missing file, unsupported event, invalid YAML, or unsupported workflow trigger shape.
from actions_trigger_doctor import explain_workflow, load_workflow
workflow = load_workflow(".github/workflows/ci.yml")
result = explain_workflow(
workflow,
workflow_path=".github/workflows/ci.yml",
event="push",
ref="refs/heads/main",
changed_files=["src/app.py"],
)
print(result["runs"])
print(result["checks"]["paths"]["reason"])Invalid workflow shapes and unsupported events raise ValueError. Missing files raise FileNotFoundError.
There is no project configuration file. All inputs are passed through CLI flags or the Python API.
Supported workflow trigger shapes:
on: pushon: [push, pull_request]on: {push: null}on.push.branches,branches-ignore,tags,tags-ignore,paths,paths-ignoreon.pull_request.branches,branches-ignore,paths,paths-ignore
Unsupported trigger shapes fail with exit code 2. Leading ! patterns are rejected; use the explicit *-ignore keys.
The command is deterministic, local, and machine-readable:
actions-trigger-doctor explain \
--workflow .github/workflows/ci.yml \
--event push \
--ref refs/heads/main \
--changed-file src/app.py \
--jsonUse the exit code for gating and the JSON object for logs. Runtime execution does not need network access.
Install development dependencies:
python -m pip install -e .[dev]Run tests:
pytest -qBuild packages:
python -m buildThe GitHub Actions workflow installs the package with development extras and runs pytest -q on push and pull request.
It also runs python -m build so source and wheel packaging stay valid.
The test suite covers YAML loading, trigger normalization, branch and tag filtering, path filtering, CLI output contracts, error handling, package building, and console-script metadata.
If paths or paths-ignore exists and no --changed-file is provided, the path check is not matched.
If a workflow uses workflow_dispatch, schedule, pull_request_target, activity types, reusable workflow triggers, merge queue triggers, commit-message skip behavior, or full GitHub minimatch behavior, this first version reports an unsupported trigger shape instead of guessing.
Keep runtime behavior deterministic and local. Add tests for every supported trigger shape or exit-code change.
MIT. See LICENSE.