Contributing¶
Thanks for helping. Bug reports, ideas and pull requests are welcome; please open an issue before a large change so we can agree on the approach.
Set up¶
You need Rust (stable), Node 24 (crates/vigily-ui/.nvmrc), uv and
protoc. Storage comes from kv0, a git dependency that cargo fetches
for you (see Storage dependency).
(cd crates/vigily-ui && npm ci && npm run build) # before cargo: build.rs embeds dist
cargo build
uv sync --no-install-project && uv run --no-sync maturin develop
uv run --no-sync python examples/demo.py && cargo run -p vigily-cli -- up
For UI work, npm run dev in crates/vigily-ui serves the app with hot reload on
http://127.0.0.1:5173 and proxies the API to a running vigily up.
Storage dependency¶
vigily depends on kv0 by git, on its main branch:
kv0 = { git = "https://github.com/sayef/kv0", branch = "main", version = "0.2", default-features = false }
Cargo builds from git; the version is what crates.io will use once kv0 is published, and it keeps
cargo deny from treating the dependency as a wildcard. deny.toml allows this one git source.
Cargo.lock pins the exact commit, so every build uses the same kv0 until someone runs
cargo update -p kv0.
To work on vigily and kv0 together, point cargo at a local checkout without editing Cargo.toml;
keep this in your own, uncommitted ~/.cargo/config.toml or pass it on the command line:
[patch."https://github.com/sayef/kv0"]
kv0 = { path = "../kv0/crates/kv0" }
vigily talks to kv0 through two thin adapters, crates/vigily-core/src/kv0_store.rs (runs and
metrics) and crates/vigily-core/src/records.rs (datasets, evaluations, schedules and alerts).
Moving to another kv0 branch is one change: the branch in Cargo.toml.
Checks¶
Run these before opening a pull request; CI runs the same:
cargo fmt --all && cargo clippy --workspace --all-targets -- -D warnings
cargo nextest run --workspace --exclude vigily-py # or cargo test
(cd crates/vigily-ui && npm run lint && npm run typecheck && npm run build)
uv run --no-sync ruff check && uv run --no-sync ruff format --check
uv run --no-sync maturin develop && uv run --no-sync pytest
cargo deny check --warn unmaintained # advisories, licences, sources
uvx --with-requirements docs/requirements.txt mkdocs build --strict
CI also runs these; each is a single binary, so uvx or cargo install is enough locally:
| Check | Command | Configuration |
|---|---|---|
| Spelling | typos |
_typos.toml |
| Workflow syntax | actionlint |
none |
| Workflow security | zizmor .github |
none; actions are pinned to commit hashes |
| Dockerfile | hadolint Dockerfile |
inline # hadolint ignore= comments with a reason |
| Unused dependencies | cargo machete |
[package.metadata.cargo-machete] in a crate's Cargo.toml |
| Minimum Rust version | cargo +1.94.1 check --workspace --all-targets |
rust-version in Cargo.toml |
When a check reports a false positive, add an ignore in its configuration with a comment that says why, rather than disabling the check.
Pre-commit hooks¶
pre-commit install (or prek install) runs formatting, clippy, ruff, eslint, typos, actionlint,
zizmor, hadolint and a secret scan on each commit; pre-commit run --all-files runs them on
everything. The eslint hook runs npm ci in crates/vigily-ui first if node_modules is missing,
so it works from a fresh clone.
UI changes¶
Read brand/DESIGN.md first: one accent, colours only as tokens, numbers in tabular
figures, every state designed. With vigily up running on the demo repository,
npm run qa in crates/vigily-ui takes screenshots of every screen in both themes at desktop and
phone widths, checks for console errors, overflow and small targets, and runs a keyboard-only pass.
Attach the relevant screenshots to the pull request.
scripts/screenshots.sh regenerates the screenshots in docs/assets/screenshots in one command:
it builds everything, logs the demo data into a fresh repository under /tmp/vigily, serves it,
runs npm run qa and keeps the screens the README and the docs use
(assets/src/screenshots.py). SKIP_BUILD=1 reuses what is already built.
Benchmarks¶
bench/scale.py logs synthetic runs and bench/scale.sh times the server's API on them (see
architecture). The Benchmarks workflow runs both on demand, or on a pull
request labelled bench, and writes the timings to the job summary; it never fails the build.
Releasing¶
Releases are automated and stay off until the RELEASE_ENABLED repository variable is true
(Settings, Secrets and variables, Actions, Variables). With it on:
- Every push to
mainupdates a release pull request (Release-plz workflow,release-plz.toml): the next version inCargo.tomland the notes under## [Unreleased]inCHANGELOG.mdmoved under that version. Edit the notes or the version in that pull request. - Merging it tags
vX.Y.Zand creates a draft GitHub release with those notes. - The Release workflow then builds the binaries (Linux on glibc 2.31, macOS for both
architectures) and the Python wheels, attaches them with their
.sha256files and publishes the release as a pre-release. It pushes the image toghcr.io/sayef/vigilyasvX.Y.ZandX.Y(amd64 and arm64, with provenance and an SBOM). - It installs the release with
scripts/install.shon Linux and macOS (Install check) and runs the Linux binaries on older distributions (Portability). - Only when all of that passed does it mark the release as the latest and tag the image
latest. A failed check leaves a pre-release thatlatestdownloads never see; fix it and release again, or run the Release workflow by hand for the tag.
A tag pushed by hand (git tag v0.2.0 && git push origin v0.2.0) goes through steps 3 to 5 as
well. Tags with a suffix, such as v0.2.0-rc.1, stay pre-releases and never become latest.
Nothing is published to crates.io or PyPI: kv0 is not on crates.io yet.
What the workflows need:
RELEASE_PLZ_TOKEN(secret, optional): a GitHub App token or fine-grained token with contents and pull-request write access, so the release pull request gets CI runs (one opened with the workflow's own token does not).- Settings, Actions, General: "Allow GitHub Actions to create and approve pull requests".
- The first image push creates the
vigilypackage on ghcr.io as private; make it public once in the package settings.
Commits¶
Small, focused commits with an imperative subject line ("Add a log scale to charts"). Use synthetic data in tests, fixtures and screenshots; never real names, emails or credentials.