Contributing to ExRatatui

Copy Markdown View Source

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

  1. Clone the repo:
git clone https://github.com/mcass19/ex_ratatui.git
cd ex_ratatui
  1. Install dependencies:
  • Elixir 1.17+ and Erlang/OTP 26+
  • Rust toolchain via rustup
  1. 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 --cover locally 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 main for 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 @doc and a @spec. Prefer runnable ## Examples — doctests count toward the 100% coverage bar.
  • Every new public module should have a @moduledoc describing its purpose.
  • New widgets need three things:
    1. A @moduledoc with field descriptions and at least one usage example.
    2. An entry in the Building UIs guide.
    3. An entry in the widgets cheatsheet.
  • Any new feature or changed behaviour should have a CHANGELOG entry under [Unreleased], grouped under Added / Changed / Fixed / Removed per 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 docs locally and skim the output — broken links and missing extras entries 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 with ExRatatui.Runtime.inject_event/2 and 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 --cover before pushing.