Thank you for your interest in contributing. This document is the onboarding entry point for development setup, testing, and pull requests.
Before you start, install the runtime tools listed in the README:
| Tool | Notes |
|---|---|
| Python 3.10+ | CLI runtime |
| Docker | Container execution (Linux containers) |
| act | Run GitHub Actions locally |
| mikefarah/yq v4+ | Required — see below |
Local CI requires mikefarah/yq v4+. Disambiguation, per-OS install commands, and verification: README — yq (mikefarah/yq v4 or newer).
All Python work happens under cli/:
cd cli
pip install -e ".[dev]"The [dev] extra installs pytest, ruff, mypy, pre-commit, and type stubs (cli/pyproject.toml).
Optional — install git hooks so lint and typecheck run before each commit:
pre-commit installHooks are defined in .pre-commit-config.yaml and mirror the CI lint and typecheck jobs.
From cli/:
pytestThis runs unit tests under tests/ and excludes tests/integration/ by default (norecursedirs in pyproject.toml). CI runs the same default set with coverage:
pytest --cov=localci --cov-report=term-missing -m "not integration"Integration tests exercise real act and Docker subprocesses. They require both tools installed and a running Docker daemon. When unavailable, the suite skips automatically.
To run them locally:
pytest tests/integration -m integration -vCI runs integration tests in a separate job after lint, typecheck, and unit tests pass (see .github/workflows/ci.yml).
From cli/:
ruff check localci/ tests/
ruff format localci/ tests/Ruff configuration lives in cli/pyproject.toml ([tool.ruff]).
From cli/:
mypy localci/Mypy runs in strict mode. Legacy CLI modules are temporarily suppressed via per-module overrides; localci.core.* and localci.utils.* must pass strict typing. Do not add new modules to the ignore_errors list.
Run all hooks against the full tree (useful before pushing):
pre-commit run -a- Branch from
develop. - Keep changes focused; link related issues in the PR description.
- Ensure CI is green: lint, typecheck, unit tests, and integration tests must pass.
- Request review from at least one maintainer. PRs need at least one approving review before merge.
- Update
CHANGELOG.mdfor user-visible changes under## [Unreleased].
developis the default branch where day-to-day work lands and releases are cut from.- To cut release
X.Y.Zwhencli/pyproject.tomlalready declares that version:- Move the
## [Unreleased]entries in CHANGELOG.md under a new## [X.Y.Z] - YYYY-MM-DDheading, leave## [Unreleased]empty, and update the link references at the bottom. - Create
RELEASE_NOTES_vX.Y.Z.mdfrom the new## [X.Y.Z]section (see RELEASE_NOTES_v0.1.0.md for the format). - Merge the changelog and release-notes update to
developvia PR; wait for CI to be green. - Create an annotated tag
vX.Y.Zon the mergeddevelopcommit and push it (git tag -a vX.Y.Zthengit push origin vX.Y.Z). IfvX.Y.Zalready exists, stop and verifygit rev-parse vX.Y.Z^{commit}matches the merged release commit (git rev-parse origin/develop); do not publish from a tag that points elsewhere. - Validate the tag: confirm
git show vX.Y.Z:CHANGELOG.mdhas## [X.Y.Z] - YYYY-MM-DD(notTBD) and CI is green on the tagged commit. - Publish the GitHub Release
(
gh release create vX.Y.Z --notes-file RELEASE_NOTES_vX.Y.Z.md) only after steps 4–5 confirm the tag points to the merged release commit. - Confirm the
[Unreleased]and[X.Y.Z]compare links at the bottom of CHANGELOG.md resolve after the new tag is created.
- Move the
Current maintainer: @bradjin8 (see .github/CODEOWNERS).
For usage questions and configuration, see the User Guide.