Contributing

Dev setup, the checks CI runs, commit conventions and how releases are cut.

Contributing

The repo is at github.com/bgalmes/rubit-mcp-mail.

Development

./.venv/bin/python -m pytest -q

The full set of checks CI runs:

ruff check .
ruff format --check .
pyright
pytest -q

CI runs the suite on Python 3.11, 3.12 and 3.13 on Ubuntu, plus a Windows-only pytest job.

Building the installers

The published installers are built by .github/workflows/build-installers.yml, on Ubuntu and Windows runners. It builds twice with PyInstaller: first the console binary (the MCP server), then a windowed binary that embeds the first one via --add-binary. That second file is the installer you download.

Both builds are smoke-tested before upload — --help, a real MCP stdio initialize handshake, and on Linux an Xvfb window check.

You need the build extra: pip install -e ".[dev,build]".

Commit messages

Commits on main drive automatic versioning via python-semantic-release, so they must follow Conventional Commits:

PrefixEffect on version
fix:patch
feat:minor
BREAKING CHANGE: in the footer, or ! after the typemajor

Other types (docs:, refactor:, test:, chore:, ci:) don't trigger a release.

PRs are squash-merged, so the squash commit message is what the release tooling parses — not the individual commits on the branch.Website changes must not use feat: or fix:. The parser keys on the type, not the scope, so feat(site): ... would bump the Python package and cut a new installer release. Use docs(site):, chore(site): or ci(site):.

How a release happens

Four steps, one of them deliberately manual:

Every merge to main prepares a release

auto-release.yml runs python-semantic-release, force-pushes a release/next branch, and opens a PR carrying only the pyproject.toml + CHANGELOG.md diff.

A maintainer merges that PR

By hand. This is load-bearing: GitHub raises no events for actions taken by GITHUB_TOKEN, so a bot-merged release PR would fire no push event and nothing downstream would run. A human merge is a real event.

The release is published

publish-release.yml tags the merge commit, extracts the notes from CHANGELOG.md, and creates the GitHub release — a prerelease for a -rc.N version, a final release otherwise. It then calls the installer build directly, for the same GITHUB_TOKEN reason.

Promoting to stable

promote-release.yml is dispatched manually. It drops the -rc.N suffix via a release/promote branch and the same publish path.

The release/next and release/promote branches belong to the release bot — never branch off them or commit to them by hand.