mix quality (ExQuality v0.13.0)
View SourceRuns code quality checks in parallel with actionable feedback.
Automatically fixes formatting issues, then runs all analysis stages in parallel with streaming output.
Execution Phases
- Auto-fix - Runs
mix formatto fix formatting - Compile - Compiles dev + test environments in parallel
- Analysis - Runs enabled checks in parallel (credo, dialyzer, doctor, tests)
--until-first-failure replaces all three with one sequential pass, cheapest
stage first, stopping at the first failure. See "An inner loop" below.
Usage
mix qualityOptions
--quick- Quick mode for development: skips dialyzer and coverage enforcement--skip-dialyzer- Skip Dialyzer type checking--skip-credo- Skip Credo static analysis--skip-doctor- Skip Doctor documentation checks--skip-gettext- Skip Gettext translation checks--skip-sobelow- Skip Sobelow security analysis--skip-dependencies- Skip dependency checks (unused deps and security audit)--skip KEY- Skip any stage by key, built-in or custom; repeatable--profile NAME- Run a named bundle of options from.quality.exs--test-scope all|changed|GLOB- How much of the suite to run--until-first-failure- Stop at the first failing stage, cheapest first--verbose- Show full output even on success--format json- Write a JSON report to stdout, human output to stderr--report PATH- Write a JSON report to PATH, human output to stdout (--report -writes it to stdout, as--format jsondoes)
Passing Test Options
You can pass extra arguments to mix test or mix coveralls using --:
mix quality -- --only integration
mix quality --quick -- --include slow --seed 0Arguments after -- are passed directly to the test command.
Alternatively, configure test args in .quality.exs:
test: [
args: ["--only", "integration"]
]CLI args (after --) override config file args (no merge).
Auto-Detection
Stages are automatically enabled based on installed dependencies:
:credo→ enables Credo stage:dialyxir→ enables Dialyzer stage:doctor→ enables Doctor stage:gettext→ enables Gettext translation checks:sobelow→ enables Sobelow security analysis:mix_audit→ enables security audit in Dependencies stage:excoveralls→ usesmix coverallsinstead ofmix test
Quick Mode
Use --quick during active development when you haven't finished all
implementation tasks (like writing tests). Quick mode:
- Skips Dialyzer (slow)
- Runs
mix testinstead ofmix coveralls(tests must pass, but coverage threshold is not enforced)
This lets you iterate quickly while still catching obvious issues.
An inner loop, as opposed to a gate
A full run is a good thing to require before a push and a bad thing to run
between edits, and every switch above narrows which checks run when the
expensive question is how much code they run over. On a large suite the tests
are most of the wall clock, and --quick does not touch them: it removes
dialyzer and the coverage threshold and still runs every test.
mix quality --test-scope changed # only the tests covering your edits
mix quality --profile loop # a named bundle, see below
mix quality --until-first-failure # stop at the first thing to fix--test-scope changed maps the files you have changed, committed or not, to
the test files covering them. A scope that resolves to no test files runs the
whole suite instead, because a green run of nothing is the one result that
would be worse than a slow one. Coverage is reported as skipped on a scoped
run, never as a number over a subset. See ExQuality.Scope.
A profile gives that fast path a name, so a project's docs and its agent instructions can point at one word instead of a switch list:
profiles: [
loop: [stages: [:format, :compile, :credo], test: [scope: :changed]],
gate: []
]A run with no --profile behaves exactly as it did before profiles existed.
See ExQuality.Config for how a profile merges, and what an unknown name does.
Configuration
Create .quality.exs in your project root to customize behavior
or override auto-detection. See Config for options.
Custom Stages
A project's own check - a house rule, a schema linter, a shell script gate -
runs as a stage of the run rather than beside it, declared under custom: in
.quality.exs. It gets the same parallelism, timing, printer and JSON report
as a built-in stage, which is what lets a caller route its findings. See
ExQuality.Custom.
Example Output
Running quality checks...
✓ Format: No changes needed (0.1s)
✓ Compile: dev + test compiled (warnings as errors) (1.8s)
Running analysis stages in parallel...
○ Doctor: skipped (:doctor not installed)
✓ Credo: No issues (1.2s)
✓ Tests: 248 passed, 0 failed, 87.3% coverage (5.2s)
✓ Dialyzer: No warnings (32.1s)
✓ All quality checks passed!Skipped Stages
A stage that is disabled, or whose tool is not installed, prints a line saying so with the reason. A run never leaves out a stage silently: a missing stage would otherwise read as a stage that passed.
Machine-Readable Output
The exit code says the run failed, not what failed. A caller that wants to route on the result asks for a report instead of scraping the console:
mix quality --format json # report on stdout, human on stderr
mix quality --report .quality.json # human on stdout, report to a file
mix quality --report - # the same as --format json--report PATH is usually the most useful, because it leaves the human
stream intact, and it can be given alongside --format json.
The root of the report carries profile, scope and base_ref, because
status alone does not say what a run is evidence for: a green run over three
test files and a green full run are different claims. See ExQuality.Report
for the shape.
Dialyzer PLT
A run that has to build the Dialyzer PLT says so while it happens
(⋯ Dialyzer: building PLT (this is a one-time cost)) and reports it in the
stage summary, because a multi-minute wait behind a single line of output
otherwise reads as a hang. mix quality.plt builds it outside a run, which
is what a container image or a CI job should cache.