Thanks for your interest in contributing!
ExRatatui is a Rust NIF project, so contributions touch both Elixir and Rust code.
This guide will help you get set up.
Setup
- Clone the repo:
git clone https://github.com/mcass19/ex_ratatui.git
cd ex_ratatui
- Install dependencies:
- Elixir 1.17+ and Erlang/OTP 26+
- Rust toolchain via rustup
- Fetch deps and compile from source:
mix deps.get
export EX_RATATUI_BUILD=true
mix compile
The EX_RATATUI_BUILD=true env var tells the library to compile the Rust NIF from source instead of downloading a precompiled binary.
Running Tests
# Elixir tests (includes doctests)
mix test
# Elixir tests with coverage report
mix test --cover
# Rust
mix rust.check
Note: CI enforces 100% test coverage on the Elixir side (NIF modules are excluded). If you add new public functions or branches, make sure to add corresponding tests. Run
mix test --coverlocally to check before pushing.
Distribution tests
Full cross-node integration tests for the Erlang distribution transport are tagged :distributed and excluded by default (they require the test node to be distributed). To run them:
# Run only distributed tests
elixir --sname test -S mix test --only distributed
# Run all tests (unit + distributed)
elixir --sname test -S mix test --include distributed
Regression tests
The :slow tag is reserved for heavyweight regression tests such as isolated parallel cold-compile checks that would otherwise dominate local test time.
# Run only slow tests
mix test --only slow
# Include slow tests alongside the default suite
mix test --include slow
Branching and Commits
- Branch from
mainfor all work. - Keep commits focused and atomic — one logical change per commit.
- Prefix commit subjects with a type:
feat:,fix:,docs:,test:,refactor:,chore:.
Pull Requests
Before submitting a PR, make sure the following pass:
mix format --check-formatted
mix compile --warnings-as-errors
mix credo --strict
mix dialyzer
mix test --cover
mix rust.check
mix hex.audit
(cd native/ex_ratatui && cargo audit)
cargo audit comes from cargo install cargo-audit --locked.
Argus
CI also runs Argus, an OTP and concurrency analysis, on the Elixir 1.20 leg, and fails on any error or warning. It needs Soufflé (on Ubuntu, the release .deb) and is installed as an escript with mix escript.install hex argus_beam. It reads the compiled beams, so run it after mix compile:
argus --project beams --ebin _build/dev/lib/ex_ratatui/ebin \
--dep-ebin _build/dev/lib/rustler_precompiled/ebin --dep-ebin _build/dev/lib/telemetry/ebin
This prints the findings and exits 0; CI fails when any of them is an error or a warning. Findings we accept go in argus.config, each with a comment saying why.
PR Guidelines
- Each PR should stay focused on a single feature or fix.
- Follow the existing code style and patterns in the codebase.
- Make sure CI passes before requesting review.
Documentation Expectations
- Every new public function should include both a
@docand a@spec. Prefer runnable## Examples— doctests count toward the 100% coverage bar. - Every new public module should have a
@moduledocdescribing its purpose. - New widgets need three things:
- A
@moduledocwith field descriptions and at least one usage example. - An entry in the Building UIs guide.
- An entry in the widgets cheatsheet.
- A
- Any new feature or changed behaviour should have a CHANGELOG entry under
[Unreleased], grouped underAdded/Changed/Fixed/Removedper Keep a Changelog. - Breaking changes should include a "Migration" note in the CHANGELOG entry that explains what callers need to change.
- If a feature introduces a guide-worthy topic, add or update a guide in
guides/and update the README accordingly. - Run
mix docslocally and skim the output — broken links and missingextrasentries are easy to miss.
Testing Expectations
- Widgets — add a widget-level test using the headless backend (
ExRatatui.init_test_terminal/2). See Testing. - Runtime / app behaviour — add an app-level test under
test_mode, driving the runtime withExRatatui.Runtime.inject_event/2and asserting on snapshots or emitted messages. - Transports — if your change touches SSH or Erlang distribution, extend the tagged suites (
:distributed,:slow). - Coverage is enforced at 100% on the Elixir side (NIFs excluded). Run
mix test --coverbefore pushing.