Development¶
Prerequisites¶
- Python 3.13 or later
- uv for dependency management
Setup¶
Running¶
# Run the TUI
uv run pony tui
# Run any command
uv run pony doctor
uv run pony sync
uv run pony compose --to alice@example.com
Quality gates¶
All three checks must pass before merging:
# Lint and format
uv run ruff check src/ tests/ tests_calendar/
uv run ruff format --check src/ tests/ tests_calendar/
# Type checking (both checkers must pass)
uv run mypy
uv run basedpyright
# Tests — both suites, with the coverage gate
uv run python -m pytest
uv run mypy checks both packages (pony and chronos) from the
configuration in pyproject.toml; passing a path would check only part of
the project. pytest with no arguments runs tests/ and tests_calendar/
and enforces the coverage floor — never pass --no-cov.
Lint¶
Ruff handles both linting and formatting.
Selected rule sets: E, F, I, B, UP, N, ARG, SIM.
Type checking¶
Both mypy (strict mode) and
basedpyright (strict mode) are required.
The Textual framework's generic types produce some reportUnknownMemberType
warnings in basedpyright that are suppressed with inline comments.
Tests¶
Tests use Python's built-in unittest framework. There are two suites:
tests/ covers the mail client and everything that joins the two halves,
and tests_calendar/ is the calendar's own suite, which still exercises it
standalone. Both run together:
Async tests are plain async def functions. Do not use
unittest.IsolatedAsyncioTestCase: it closes the event loop on teardown,
and anything later in the session that reaches for the running loop then
fails depending on file order.
The test suite includes:
- Unit tests: MIME parsing, flag mapping, reply/forward quoting, attachment extraction, search query compilation, BBDB roundtrip
- Conformance tests: both Maildir and mbox backends run the same CRUD/flag test suite
- Sync tests: deterministic fixtures via
FakeImapSessioncover new message ingestion, flag reconciliation, conflict resolution, UIDVALIDITY reset - Index tests: SQLite schema, multi-field search, case sensitivity, checkpoints, pending operations, delete, batched transactions
- Contacts tests: upsert, search, harvest, delete, merge (two-way and three-way), BBDB import/export
- Mirror integrity tests: orphan file detection, stale index row detection
- Renderer tests: HTML stripping (style/script block removal), nested email rendering
- Calendar-in-the-app tests: switching between the two subsystems, the shared notification space, the line each half shows about the other, and the invitation flows in both directions
- Calendar tests (
tests_calendar/): CalDAV reconciliation againstFakeCalDAVSession, recurrence expansion, the occurrence and alarm caches, iTIP ingest, and the calendar's own screens
Building documentation¶
The documentation uses MkDocs with the Material theme.
# Install docs dependencies
uv sync --group docs
# Serve locally with live reload
uv run mkdocs serve
# Build static site
uv run mkdocs build
The built site goes to site/ (gitignored). GitHub Pages deployment is
automated via the .github/workflows/docs.yml workflow.
Regenerating screenshots¶
The screenshots in the README and docs are generated from synthetic demo data — no real account is involved. Two committed scripts drive this:
scripts/demo_seed.pybuilds a throwaway mail store (fake accounts, messages, contacts) from Pony's public repositories.scripts/capture_screenshots.pydrives the real Textual screens headlessly over that store, exports each as SVG, and rasterises to PNG indocs/assets/.
The SVG→PNG step requires Inkscape on PATH. The demo
clock is fixed, so re-runs are reproducible.
Building standalone executables¶
The primary distribution format is a platform-native binary built with PyInstaller. Each release produces two artifacts per platform: a platform installer and a portable archive suitable for package managers such as Homebrew (macOS) or Scoop (Windows).
| Platform | Installer | Portable archive |
|---|---|---|
| Windows | pony-windows-vX.Y.Z-setup.exe |
pony-windows-vX.Y.Z.zip |
| macOS | pony-macos-vX.Y.Z.dmg |
pony-macos-vX.Y.Z.tar.gz |
| Linux | pony-linux-vX.Y.Z.AppImage |
pony-linux-vX.Y.Z.tar.gz |
Prerequisites¶
This installs pyinstaller and mkdocs-material alongside the regular dev
dependencies.
Local build¶
# Full build: tests + docs + binary + portable archive
uv run python scripts/build.py
# Also produce the platform installer
uv run python scripts/build.py --installer
# Skip slow steps while iterating
uv run python scripts/build.py --skip-tests --skip-docs --installer
Artifacts land in artifacts/. An artifacts.json manifest lists every path.
What is bundled¶
pony.spec controls what goes into the binary. Two data trees are always
included:
site/— the pre-built HTML documentation (generated bymkdocs build)config-sample.toml— annotated configuration template
The pony docs command uses the bundled docs when running as a binary,
falling back to the online documentation
when running from source.
Platform installer notes¶
| Platform | Tool | Notes |
|---|---|---|
| Windows | Inno Setup 6+ | iscc must be on PATH. Script: installers/windows/pony.iss. Offers optional PATH registration. |
| macOS | hdiutil (built-in) |
Creates a drag-to-Applications DMG. |
| Linux | appimagetool |
Downloaded automatically on first run if absent. Produces a single portable AppImage. |
CI¶
The release.yml workflow is the single, dispatch-driven release pipeline.
Triggered manually (Actions → Create Release → Run workflow), it runs three
jobs in sequence: prepare reads the new version from the first CHANGELOG
heading, stamps pyproject.toml/version.py, and pushes the bump commit;
build runs tests, builds the docs, and compiles the binary plus installer
and portable archive for each platform (Linux, macOS, Windows); publish
tags the commit and creates the GitHub release with all binaries attached —
only after every build succeeds.
Project structure¶
pony/
src/pony/ # mail: the main package
src/chronos/ # calendar: CalDAV, recurrence, agenda screens
tests/ # mail test suite, plus the two halves together
tests_calendar/ # the calendar's own test suite
docs/ # documentation (MkDocs source)
.github/workflows/ # CI/CD workflows
mkdocs.yml # MkDocs configuration
pyproject.toml # project metadata and tool config
ai/ # agent-facing docs (architecture, conventions, status)
ai/calendar/ # the calendar's own specifications
scripts/ # build, demo seeding, documentation screenshots
config-sample.toml # annotated config template
CHANGELOG.md # release history
LICENSE # MIT license
GitHub Pages setup¶
To enable GitHub Pages for this repository:
- Go to Settings > Pages in the GitHub repository.
- Under Build and deployment, set Source to GitHub Actions.
- Push to the
mainbranch. Thedocs.ymlworkflow will build and deploy the documentation automatically. - The site will be available at
https://<username>.github.io/pony/.
No additional configuration is needed. The workflow uses the
actions/deploy-pages action with the github-pages environment.