Contributing¶
This page covers how the project is built and released, and its code style. How to report issues and what a good pull request looks like are in CONTRIBUTING.md; the environment, commands and tests are on the Development Setup page, and the architecture page explains the data pipeline.
Documentation lives in docs/ and is built with Zensical. Update it, the README and the ? help overlay in the same change as any user-visible behaviour, including new key bindings.
Project process¶
Continuous integration¶
GitHub Actions runs on every push to main and on pull requests:
| Job | What it does |
|---|---|
| Check & Test | cargo fmt --check, clippy with -D warnings, cargo test |
| End-to-end | Builds the binary and runs scripts/e2e.sh against the example stack |
| Build Binary | Release build; uploaded as an artifact, and attached to the release when one is published |
| Build & Push Docker Image | Multi-arch image (amd64, arm64) pushed to ghcr.io; runs for published releases only |
| Docs | Builds the site with --strict and deploys it to GitHub Pages when docs/, zensical.toml or requirements-docs.txt change on main |
| Draft release | On pushes to main, draftsman updates the draft release notes from the Conventional Commits since the last release |
Dependencies¶
Renovate opens pull requests for dependency, GitHub Action and Docker base image updates. Versions are pinned exactly (and Actions by commit SHA), updates wait three days after release, and vulnerability alerts are raised straight away. Minor and patch updates of actions are grouped; Rust crate and Docker updates are grouped and need a maintainer to merge. A lock-file refresh runs monthly.
Do not bump versions by hand unless a change needs it. If you add a dependency, pin it with = in Cargo.toml.
Releases¶
Releases follow semantic versioning.
- draftsman maintains a draft release. On every push to
mainit reads the Conventional Commits since the last release, groups them into sections by type (featis Features,fixis Bug Fixes,chore,buildandciare Maintenance,docsis Documentation, anything else is Other), and picks the next version itself: a breaking change (!or aBREAKING CHANGE:footer) is a major bump,feata minor one, anything else a patch. The sections are configured in.draftsman.yml. - A maintainer checks the draft and publishes it (from the GitHub release page, or with
draftsman publish), which creates thevX.Y.Ztag. - Publishing triggers CI to set the version in
Cargo.tomlfrom the tag, build the release binary and attach it to the release, and build and push the multi-arch Docker image toghcr.io/brpaz/tylomon.
The release notes are built from commit messages, not labels, so the squash-merge title of a pull request (which becomes the commit on main) must be a clear Conventional Commit. To keep a commit out of the release notes, add [skip changelog] anywhere in its message or a Skip-Changelog: true footer.
Code style¶
Formatting¶
- Standard
cargo fmt— no custom rustfmt configuration - Clippy with
-D warnings— zero warnings policy
Imports¶
Group imports in this order, separated by blank lines:
stdlibrary- External crates
- Crate-internal (
use crate::...)
use std::collections::HashMap;
use std::net::IpAddr;
use bollard::Docker;
use anyhow::Result;
use crate::capture::bridge::bridge_interface_name;
Error handling¶
- Use
anyhow::Resultfor fallible functions - Use
Optionfor parsing functions that may not find valid input - Use
anyhow!("message")for ad-hoc errors - Custom errors live in
src/error.rs - No
unwrap()in production code — use?,.ok(), orunwrap_or_else
Naming conventions¶
- Types:
PascalCase—FlowKey,HttpExchange - Functions:
snake_case—parse_packet,bridge_interface_name - Constants:
SCREAMING_SNAKE_CASE—PENDING_REQUEST_TIMEOUT - Enum variants:
PascalCase—Action::NewExchange
Struct style¶
- All fields
pubon data structs - Doc comments (
///) on public types and functions - Derive order:
Debug, Cloneminimum; addSerialize, Deserializefor data types;Hash, Eq, PartialEqfor key types
Testing¶
- Unit tests go inside the source file in a
#[cfg(test)] mod testsblock - Integration tests go in the
tests/directory. Pipeline behaviour is tested by generating a pcap and replaying it (seetests/replay_tests.rs) - UI logic is tested through
App(keys, clicks, drags) with layout areas set by hand - Use
.expect("description")instead of.unwrap()in tests
TUI guidelines¶
- Views are free functions:
pub fn render(frame, app, area, focused) - All application state lives in
App. Views read from it; the only things they write back are layout facts needed for mouse hit-testing (pane areas, scroll offsets) and the detail row cache - All keyboard input flows through
App::handle_key()and mouse input throughApp::update(), so behaviour is testable without a terminal - Colors and styles are defined in
Themeas static methods - Keep
Rows in the detail pane one screen line each (pre-wrapped), so mouse positions map straight onto text - Every key binding belongs in
views/help.rs,docs/reference/keybindings.mdand the README essentials table