Skip to main content

CONTRIBUTING.md

# Contributing to rujira_ex

Thanks for working on the Rujira domain library. This document covers setup, conventions, verification, and the release flow.

## Repository

- Source: <https://github.com/RujiraNetwork/rujira_ex>
- Hex package: `rujira_ex`
- License: MIT

## Setup

```sh
mix deps.get
mix compile
```

Requires Elixir `~> 1.14` and Erlang/OTP 26+.

## Conventions

Two documents are required reading before sending a PR:

- [`guides/conventions.md`](guides/conventions.md) — naming, return values, error atoms, numeric parsing, struct defaults
- [`guides/architecture.md`](guides/architecture.md) — protocol module shape, event pipeline, adding a new protocol

## Branches and commits

Commit messages follow [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/):

- Format: `type(scope): description` on a single line. Use `type(scope)!: …` for breaking changes.
- Allowed `type`s: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`.
- `scope` is **required** in this project (the spec makes it optional) and names the area being changed: `core`, `deps`, `fin`, `thorchain`, `events`, etc.

Branch names mirror the commit shape: `type/scope-short-desc` (e.g. `feat/fin-range-pricing`, `fix/contracts-spec`).

## Verification — all five must pass

Run before opening or updating a PR:

```sh
mix format --check-formatted
mix compile --warnings-as-errors
mix test
mix credo --strict
mix dialyzer
```

CI runs the same five. A failure on any of them blocks merge.

## Pull requests

- One PR per concern. Splitting is cheap; bundled refactors are expensive to review.
- Title follows the commit format (`type(scope): description`).
- Body explains the **why**. The diff already shows the what.
- Tests cover both the happy path and the error tuple.
- Update `guides/` if you change a documented convention.

## Versioning and releases

The package follows **Semantic Versioning** (<https://semver.org>):

- `MAJOR` — breaking public-API change
- `MINOR` — backwards-compatible feature
- `PATCH` — backwards-compatible fix

The library is in `0.x.y` while the public surface stabilizes. In that range, treat `MINOR` bumps as potentially breaking and pin to `~> 0.x` accordingly.

### Cutting a release

1. Land all PRs targeting the release on `main`.
2. Bump `@version` in `mix.exs`.
3. Update `README.md` and any docs that reference the version.
4. Verify locally:
   ```sh
   mix format --check-formatted
   mix compile --warnings-as-errors
   mix test
   mix credo --strict
   mix dialyzer
   mix hex.build       # confirms the package builds cleanly
   ```
5. Commit: `chore(release): vX.Y.Z`.
6. Tag the commit: `git tag vX.Y.Z && git push origin vX.Y.Z`. The tag must match `@version` in `mix.exs`.
7. Publish to Hex: `mix hex.publish` (requires an authenticated maintainer).
8. Cut a GitHub release from the tag with a short changelog.

Only tagged commits get published. Do not publish from a dirty working tree.

## Adding a dependency

- Justify it in the PR description. "Easier than writing it" is not enough — the runtime cost of every dep is real.
- Prefer single-purpose libraries over kitchen-sink frameworks.
- Pin with `~> MAJOR.MINOR` for stable libs and `~> 0.MINOR` for pre-1.0.
- Dev-only tools (`credo`, `dialyxir`, `ex_doc`) must have `only: [:dev, :test], runtime: false`.

## Reporting bugs

Open a GitHub issue with:

- rujira_ex version
- Elixir / OTP version
- Minimal reproduction (a failing test is ideal)
- Expected vs. actual behavior

## Migration status — consumers

`Rujira.Contracts.get/1` currently supports two protocol-module shapes:

- New: `module.new/1` taking a single `map()` with `"address"` merged in
- Legacy: `module.from_config(address, config)` — fallback for incremental migrators

The legacy path is **temporary** and will be removed once the in-house consumers finish migrating. New code MUST implement `new/1`. The fallback is marked with a `TODO` in `lib/rujira/contracts.ex`.