CI/CD Guide¶
Everything in .github/workflows/ explained: what runs, when, why, and what to do when something goes red.
Workflows¶
| File | Triggers | Purpose |
|---|---|---|
ci.yml |
push to main, PR to main |
The correctness gate: pytest + coverage across a {ubuntu, windows, macos} x {3.12, 3.13} matrix (plus non-blocking 3.10/3.11 signal), then builds the wheel/sdist, twine checks them, and installs from wheel/sdist/editable/GitHub in fresh environments with a real smoke test. |
quality.yml |
push to main, PR to main |
The cleanliness gate: ruff check + ruff format --check, mypy --strict, documentation validation (every relative Markdown link, every fenced python snippet in the root and package READMEs), and notebook execution. |
docs.yml |
push/PR to main touching docs/**, mkdocs.yml, or a README.md |
Builds the mkdocs site and uploads it as an artifact. Not a deploy step (no gh-pages push yet - see Future Work). |
benchmark.yml |
push to main, PR to main |
A performance smoke test (scripts/quality/perf_smoke.py) - generous wall-clock ceilings on a handful of representative operations, meant to catch a catastrophic regression (an accidental O(n²), a broken cache), not to track performance over time. |
dependency-review.yml |
PR to main |
GitHub's dependency-review-action: flags newly-introduced dependencies with a moderate-or-worse known vulnerability or an unusual license, as a PR comment. |
codeql.yml |
push/PR to main, weekly (Monday 06:00 UTC) |
GitHub CodeQL static analysis (security-and-quality query suite) for Python. |
security.yml |
push/PR to main touching pyproject.toml, weekly (Wednesday 06:00 UTC) |
pip-audit against every installed dependency (mineproductivity[dev]) - see Known Exceptions below. |
release.yml |
push of a v*.*.* tag |
Builds and twine checks the wheel/sdist, verifies the tag matches mineproductivity.__version__, and creates a GitHub Release with both artifacts attached. Does not publish to PyPI (see Release Flow). |
All workflows use actions/setup-python's built-in cache: pip (keyed on pyproject.toml) - no separate actions/cache step is needed for the dependency install itself.
Branch Strategy¶
Trunk-based: a single long-lived branch, main. Contributors work on short-lived feat/<description>, fix/<description>, etc. branches (per CONTRIBUTING.md) and open a PR back into main. There is no develop branch and no long-lived release branch - every release is cut from a specific commit on main via a tag.
Release Flow¶
- Land every change intended for the release on
mainthrough the normal PR process (ci.yml+quality.ymlgreen). - Bump
src/mineproductivity/__init__.py's__version__, updateCHANGELOG.md(see the existing entries for the expected format), update any version references inREADME.md, and update the hardcoded version assertion intests/unit/core/test_public_api.py::TestPackageVersion. - Merge that version-bump PR to
main. - Tag the resulting commit:
git tag v<version>(matching__version__exactly -release.ymlverifies this and fails the release if they disagree) andgit push origin v<version>. release.ymlbuilds the wheel and sdist, runstwine check --strict, and creates a GitHub Release for the tag with both artifacts attached and auto-generated release notes (pointing readers atCHANGELOG.mdfor the curated version).- PyPI publishing is intentionally not wired up.
release.ymlcontains a fully commented-outpublish-pypijob using PyPI Trusted Publishing (OIDC - no API token to manage or leak). To enable it: register this repository/workflow as a Trusted Publisher on PyPI, then uncomment the job.
Nothing in this guide changes how versions are decided - see ROADMAP.md for the phasing and CHANGELOG.md's own header note on software-version-vs-architecture-version independence.
Developer Workflow¶
pip install -e ".[dev]"(see the root README's Getting Started section for the full extras table).- Make your change. Run locally before opening a PR:
pytest,ruff check .,ruff format .,mypy.pre-commit installwires the first two into a pre-commit hook automatically. - Touching a package README's code snippets, any relative Markdown link, or a notebook? Run
python scripts/quality/check_docs.pylocally - it is exactly whatquality.yml'sdocs-validationjob runs. - Open a PR against
main.ci.yml,quality.yml,docs.yml(if docs changed),benchmark.yml,dependency-review.yml, andcodeql.ymlall run automatically. - All required checks green + review approval → merge.
Failure Handling¶
ci.yml/testmatrix fails on one OS only: almost always a real platform difference (path separators,tzdataavailability - Windows needs thetzdataPyPI package since it has no OS-native IANA database, already handled viasys_platform == 'win32'markers inpyproject.toml). Reproduce locally with the same OS if possible; do not skip the platform.ci.yml/testmatrix fails on Python 3.10 or 3.11 only: expected and non-blocking (continue-on-error: true) -pyproject.tomldeclaresrequires-python = ">=3.12", so these two cells are informational only, not a merge gate.ci.yml/ install-validation fails: the package installs but something in the smoke test (scripts/quality/smoke_test.py) broke - usually a genuinely missing runtime dependency that onlypip install -e .(editable, pulling the dev environment's already-resolved packages) was hiding.quality.yml/docs-validationfails on a snippet: re-runpython scripts/quality/check_docs.pylocally. If the failing block is a deliberately illustrative fragment (references a caller's own variable, e.g.bench.id), it belongs in that script'sKNOWN_ILLUSTRATIVE_FRAGMENTSset, not a real bug - but confirm that by hand before adding it there.quality.yml/notebooksfails: run the same command locally:jupyter nbconvert --to notebook --execute --inplace notebooks/beginner/*.ipynb(afterpython -m ipykernel install --user --name ci-kerneland passing--ExecutePreprocessor.kernel_name=ci-kernel).nbconvertfails fast on the first cell that raises.benchmark.ymlfails: a wall-clock ceiling inscripts/quality/perf_smoke.pywas exceeded by a wide margin (the budgets are deliberately generous). Treat this as a real regression to investigate, not noise - but also account for CI runner variance before assuming the worst; re-run once before deep-diving.security.ymlfails: a genuinely new vulnerability was found. Check whether a newer version of the flagged package satisfies the existingpyproject.tomlconstraint (often just needspip install --upgrade+ a lockstep CI cache bust); if the fix requires crossing a major version, treat it as a dependency-upgrade PR of its own, tested like any other change.release.ymlfails on the version-match check: the pushed tag doesn't matchmineproductivity.__version__. Delete the tag (git tag -d vX.Y.Z && git push origin :refs/tags/vX.Y.Z), fix whichever side is wrong, and re-tag.
Known Exceptions¶
security.ymlignoresPYSEC-2026-113(a real Apache Arrow C++ Use-After-Free, fixed inpyarrow>=23.0.1) becausepyproject.toml'seventsextra currently pinspyarrow>=14,<19. Bumping across that many majorpyarrowreleases is a dependency-compatibility change requiringevents'ArrowEventCodec/ParquetEventCodecto be re-validated against the new API - out of scope for CI/CD tooling work. Tracked here rather than silently ignored; remove the--ignore-vulnflag insecurity.ymlonce thepyarrowconstraint is actually bumped and re-tested.docs.ymldoes not usemkdocs build --strict.docs/architecture/*.md(the locked design specifications) legitimately cross-link to the rootREADME.mdand to package READMEs undersrc/mineproductivity/, both outside mkdocs' owndocs_dir. That's correct for GitHub's file browser and unresolvable within a single mkdocs site build;--strictwould fail on those links permanently, for reasons unrelated to whether the site itself is sound.ci.yml's Python matrix does not fail on 3.10/3.11. See Failure Handling above.
Future Work¶
- Deploy
docs.yml's built site to GitHub Pages (actions/deploy-pages) once the maintainer decides this repository should have a public docs site. - Enable the commented-out
publish-pypijob inrelease.ymlonce a PyPI Trusted Publisher is registered. - Revisit the
pyarrowversion constraint (see Known Exceptions) as its own dependency-upgrade change. - Coverage-percentage badge once a coverage-reporting service (Codecov, Coveralls) is connected - deliberately not added yet to avoid a broken badge pointing at an unconfigured service.
References¶
.github/workflows/- the workflow files themselves.CONTRIBUTING.md- the PR workflow this guide's Developer Workflow section summarizes.docs/governance/RELEASE_CHECKLIST.md- the per-package Definition of Done this guide's automation enforces mechanically where possible.CHANGELOG.md,ROADMAP.md- release history and phasing.