Packages
Elixir code quality checker. Runs format, compile, Credo, Dialyzer, coverage, Sobelow and mix_audit in parallel from one mix task. Findings with file:line, a JSON report, scoped runs. Umbrella-aware.
Current section
Files
Jump to
Current section
Files
ex_quality
README.md
README.md
<p align="center">
<img src="assets/ex_quality.svg" alt="" width="128" height="128">
</p>
<p align="center">
<a href="https://hex.pm/packages/ex_quality"><img src="https://img.shields.io/hexpm/v/ex_quality.svg" alt="Hex version"></a>
<a href="https://hex.pm/packages/ex_quality"><img src="https://img.shields.io/hexpm/dt/ex_quality.svg" alt="Hex downloads"></a>
<a href="https://hexdocs.pm/ex_quality/"><img src="https://img.shields.io/badge/hex-docs-lightgreen.svg" alt="Hex docs"></a>
<a href="https://hex.pm/packages/ex_quality"><img src="https://img.shields.io/hexpm/l/ex_quality.svg" alt="License"></a>
</p>
# ExQuality
ExQuality is one command, `mix quality`, that runs an Elixir project's quality
tools in parallel and reports the whole gate in one shape. Each tool is one
stage with a status, a one-line summary, and findings that carry a
`file:line`, and the same results can be written as a JSON report for a script
to route on.
## Why: the output is the point
Every quality tool prints in its own format, at its own length, and says
nothing when it did not run, so reading a gate made of several tools means
reading several walls of text and inferring what is missing from them, and a
script that wants to act on a failure has to parse each tool its own way. With
ExQuality the gate is one run, one stream and one shape per stage: a passing
stage costs one line, a skipped stage says why it was skipped, and a failure
points at the `file:line` to fix.
## What a run looks like

Colour is a second channel over the `✓`, `○` and `✗`, never a replacement for
one. It is dropped when the output is not a terminal, so a CI log or a piped run
reads exactly the same minus the paint.
Three properties follow from that, and they are what the tool is for:
- **A passing stage costs one line.** Detail is printed for failures only. A
green run is one line per stage, not one report per tool.
- **Every stage the run considered is reported**, skipped ones included, with
the reason. Absence is never something a reader has to interpret, and a stage
that silently did not run cannot read as a stage that passed.
- **A failure is rendered as findings**: each one a `file:line`, a message and
the rule that produced it, grouped by file. Anything a parser could not
account for is printed verbatim rather than dropped.
Do not pipe a run through `head`, `tail` or `grep`. The output is already the
minimum needed to act, and truncating it removes findings, not noise. If you
want to route on a result rather than read it, ask for
[a JSON report](docs/reports.md).
## Installation
```elixir
def deps do
[{:ex_quality, "~> 0.16", only: :dev, runtime: false}]
end
```
Then set up the tools you want to run:
```bash
mix deps.get
mix quality.init # interactive; pre-selects credo, dialyzer, excoveralls
mix quality.init --skip-prompts
```
`mix quality.init` detects what is already installed, adds the rest to
`mix.exs`, runs `mix deps.get`, writes each tool's config, and creates a
`.quality.exs`. Nothing about it is required: ExQuality runs whatever the
project already depends on.
## Basic usage
```bash
mix quality --test-scope changed # between edits: only the tests covering changed code
mix quality --quick # while coding: drops Dialyzer and the coverage threshold
mix quality # before committing, and in CI: the full gate
mix quality --report .quality.json # the full gate, plus a JSON report to route on
```
A stage is enabled when the project depends on the tool behind it, so there is
nothing to switch on for the common tools. `--quick` narrows *which checks
run*; `--test-scope` narrows *how much code they run over*. Neither measures
coverage, so neither is the full gate: run a bare `mix quality` for that. The
flags, profiles and test scope are in [Configuration](docs/configuration.md).
## Working with a coding agent
ExQuality ships a [`usage-rules.md`](usage-rules.md) for AI coding assistants,
readable by [usage_rules](https://hex.pm/packages/usage_rules). It tells an
agent which command to run for which situation, how to read a failure, not to
truncate the output, and which fixes are never acceptable - lowering a coverage
threshold, adding a `.sobelow-conf` ignore - because a tool silencing its own
findings is a regression dressed as a pass.
Why one command with many speeds suits an agent loop, and how the full gate stays
distinguishable from a narrowed one, is in
[Why the gate is one command](docs/explanation/why-the-gate-is-one-command.md).
## Documentation
- Do
- [How to run the gate in CI and before each commit](docs/ci.md) - a pipeline step, attesting a full run, a warm Dialyzer PLT, a container image and a pre-commit hook
- [Routing on a report](docs/reports.md#routing-on-a-report) - reading which stages failed and their findings from a script, a section of the Reports reference until its guide page exists
- Look up
- [Configuration](docs/configuration.md) - the CLI flags, the `.quality.exs` keys, test scope, profiles, custom stages and precedence
- [Stages](docs/stages.md) - what each stage runs, when it is enabled, what it reports and where its threshold comes from
- [Reports](docs/reports.md) - the JSON report's fields: the top level, each stage and each finding
- [Umbrella projects](docs/umbrella.md) - how detection, findings, tests, coverage and Sobelow behave at an umbrella root
- [Usage rules](usage-rules.md) - the rules a coding agent reads: which command for which situation and which fixes are never acceptable
- [Changelog](CHANGELOG.md) - what changed in each version, and how to enable each new stage
- Understand
- [Why the gate is one command](docs/explanation/why-the-gate-is-one-command.md) - why the tools sit behind one command with one output shape, the alternatives, and what the choice costs
## Compatibility
- Elixir `~> 1.14`.
- One runtime dependency, `jason ~> 1.4`. ExQuality itself is a dev-only
dependency (`only: :dev, runtime: false`).
- The tools it runs are the project's own dependencies, at the versions the
project pins; a stage is reported as skipped when its tool is not installed.
## License
MIT
## Contributing
Issues and pull requests welcome at
[github.com/riddler/ex_quality](https://github.com/riddler/ex_quality).