Packages

One task to efficiently run all code analysis & testing tools in an Elixir project. Community-maintained fork of ex_check.

Current section

Files

Jump to
ex_check_ng README.md
Raw

README.md

# ![ex_check](./assets/logo-with-name.svg)
[![Hex version](<https://img.shields.io/hexpm/v/ex_check_ng.svg?color=hsl(265,40%,60%)>)](https://hex.pm/packages/ex_check_ng)
[![Hex docs](<https://img.shields.io/badge/hex-docs-lightgreen.svg?color=hsl(265,40%,60%)>)](https://hexdocs.pm/ex_check_ng/)
[![Build status](https://img.shields.io/github/actions/workflow/status/fschoenfeldt/ex_check/check.yml?branch=master)](https://github.com/fschoenfeldt/ex_check/actions)
[![Downloads](https://img.shields.io/hexpm/dt/ex_check_ng.svg)](https://hex.pm/packages/ex_check_ng)
[![License](https://img.shields.io/github/license/fschoenfeldt/ex_check.svg)](https://github.com/fschoenfeldt/ex_check/blob/master/LICENSE.md)
[![Last updated](https://img.shields.io/github/last-commit/fschoenfeldt/ex_check.svg)](https://github.com/fschoenfeldt/ex_check/commits/master)
> **`ex_check_ng`** is a community-maintained fork of
> [`ex_check`](https://github.com/karolsluszniak/ex_check) (dormant since 2024). Module namespace
> (`ExCheck`) and the `mix check` task are unchanged — drop-in replacement. Install as
> `{:ex_check_ng, "~> 1.0", only: [:dev], runtime: false}`.
![Demo](./assets/demo-67x16.svg)
**Run all code checking tools with a single convenient `mix check` command.**
---
Takes seconds to setup, saves hours in the long term.
- Comes out of the box with a [predefined set of curated tools](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-tools)
- Delivers results faster by [running tools in parallel and catching all issues in one go](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-workflow)
- Checks the project consistently on every developer's local machine & [on the CI](#continuous-integration)
- Runs only the tools & tests that have [failed in the last run](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-retrying-failed-tools)
- Fixes issues automatically in [the fix mode](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-fix-mode)
Sports powerful features to enable ultimate flexibility.
- Add custom mix tasks, shell scripts and commands via [configuration file](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-configuration-file)
- Enhance you CI workflow to [report status](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-manifest-file), [retry random failures](#random-failures) or [autofix issues](#autofixing)
- Empower umbrella projects with [parallel recursion over child apps](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-umbrella-projects)
- Design complex parallel workflows with [cross-tool deps](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-cross-tool-dependencies)
Takes care of the little details, so you don't have to.
- Compiles the project and collects compilation warnings in one go
- Ensures that output from tools is [ANSI formatted & colorized](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html#module-tool-processes-and-ansi-formatting)
- Retries ExUnit with the `--failed` flag
Read more in the introductory ["One task to rule all Elixir analysis & testing tools"](https://cloudless.studio/one-task-to-rule-all-elixir-analysis-testing-tools) article.
## Getting started
Add `ex_check_ng` dependency in `mix.exs`:
```elixir
def deps do
[
{:ex_check_ng, "~> 1.0", only: [:dev], runtime: false}
]
end
```
Fetch the dependency:
```
mix deps.get
```
Run the check:
```
mix check
```
That's it - `mix check` will detect and run all the available tools.
### Community tools
If you want to take advantage of community curated tools, add following dependencies in `mix.exs`:
```elixir
def deps do
[
{:credo, ">= 0.0.0", only: [:dev], runtime: false},
{:dialyxir, ">= 0.0.0", only: [:dev], runtime: false},
{:doctor, ">= 0.0.0", only: [:dev], runtime: false},
{:ex_doc, ">= 0.0.0", only: [:dev], runtime: false},
{:gettext, ">= 0.0.0", only: [:dev], runtime: false},
{:sobelow, ">= 0.0.0", only: [:dev], runtime: false},
{:mix_audit, ">= 0.0.0", only: [:dev], runtime: false}
]
end
```
You may also generate `.check.exs` to adjust the check:
```
mix check.gen.config
```
Among others, this allows to permanently disable specific tools and avoid the skipped notices:
```elixir
[
tools: [
{:dialyzer, false},
{:sobelow, false}
]
]
```
### Usage rules for coding agents
`ex_check_ng` ships [usage rules](https://hexdocs.pm/usage_rules) — concise, authoritative
guidance written for LLM coding agents (Claude Code, Cursor, ...) on how to drive `mix check`,
including using `mix check --format agent` for machine-readable output.
It is recommended to sync these into your project's agent rules file so agents automatically pick
the right flags. Add [`usage_rules`](https://hex.pm/packages/usage_rules):
```elixir
def deps do
[
{:usage_rules, "~> 1.2", only: [:dev], runtime: false}
]
end
```
Configure the sync in `mix.exs` (the config is the source of truth):
```elixir
def project do
[
# ...
usage_rules: [
file: "AGENTS.md",
usage_rules: [:ex_check_ng]
]
]
end
```
Then run:
```
mix deps.get
mix usage_rules.sync
```
This keeps an `ex_check_ng` section in your `AGENTS.md` in sync with the rules shipped by the
package, so coding agents run `mix check --format agent` instead of parsing human-oriented output.
### Output formats
`mix check` renders its results through pluggable reporters selected with `--format` (default
`pretty`). Batched formats (`agent`, `json`, `junit`) may be written to a file with `--output PATH`
instead of stdout.
| Format | Use case |
| -------- | ---------------------------------------------------------------------------------- |
| `pretty` | Live colored terminal output for humans (default). |
| `agent` | JSON status header + raw failure blocks, optimized for LLM/agent consumption. |
| `json` | Single machine-readable JSON object describing the whole run. |
| `github` | GitHub Actions log groups + a Markdown summary appended to `$GITHUB_STEP_SUMMARY`. |
| `junit` | JUnit XML report for CI systems like GitLab CI and Jenkins. |
```bash
mix check --format agent # LLM-friendly
mix check --format json --output check.json # machine-readable file
mix check --format github # inside a GitHub Actions job
mix check --format junit --output report.xml # JUnit report for CI
```
`github` and `pretty` stream to the terminal and reject `--output`. See
[Continuous Integration](#continuous-integration) for wiring the CI formats into a pipeline.
### Local-only fix mode
You should keep local and CI configuration as consistent as possible by putting together the project-specific `.check.exs`. Still, you may introduce local-only config by creating the `~/.check.exs` file. This may be useful to enforce global flags on all local runs. For example, the following config will enable the fix mode in local (writable) environment:
```elixir
[
fix: true
]
```
> You may also [enable the fix mode on the CI](#autofixing).
## Documentation
Learn more about the tools included in the check as well as its workflow, configuration and options [on HexDocs](https://hexdocs.pm/ex_check_ng/Mix.Tasks.Check.html) or by running `mix help check`.
Want to write your own code check? Get yourself started by reading the ["Writing your first Elixir code check"](https://cloudless.studio/writing-your-first-elixir-code-check) article.
## Continuous Integration
On GitHub Actions, `--format github` adds collapsible
per-tool log groups and a run summary on the job page:
```yaml
# .github/workflows/check.yml
name: check
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: "1.20"
otp-version: "28"
- run: mix deps.get
- run: mix check --format github
```
For CI systems with a JUnit test report (GitLab CI, Jenkins, ...), use `mix check --format junit
--output report.xml`. See [Output formats](#output-formats) for the full list.
`ex_check` uses itself on the CI — see [our own workflow](https://github.com/fschoenfeldt/ex_check/blob/master/.github/workflows/check.yml).
### Autofixing
You may automatically fix and commit back trivial issues by triggering the fix mode on the CI as well. In order to do so, you'll need a CI script or workflow similar to the example below:
```bash
mix check --fix && \
git diff-index --quiet HEAD -- && \
git config --global user.name 'Autofix' && \
git config --global user.email 'autofix@example.com' && \
git add --all && \
git commit --message "Autofix" && \
git push
```
First, we perform the check in the fix mode. Then, if no unfixable issues have occurred and if fixes were actually made, we proceed to commit and push these fixes.
Of course your CI will need to have write permissions to the source repository.
### Random failures
You may take advantage of the automatic retry feature to efficiently re-run failed tools & tests multiple times. For instance, following shell command runs check up to three times: `mix check || mix check || mix check`. And here goes an alternative without the logical operators:
```bash
mix check
mix check --retry
mix check --retry
```
This will work as expected because the `--retry` flag will ensure that only failed tools are executed, resulting in no-op if previous run has succeeded.
## Troubleshooting
### Duplicate builds
If, as suggested above, you've added `ex_check` and curated tools to `only: [:dev]`, you're keeping the test environment reserved for `ex_unit`. While a clean setup, it comes at the expense of Mix having to compile your app twice - in order to prepare `:test` build just for `ex_unit` and `:dev` build for other tools. This costs precious time both on local machine and on the CI. It may also cause issues if you set `MIX_ENV=test`, which is a common practice on the CI.
You may avoid this issue by running `mix check` and all the tools it depends on in the test environment. In such case you may want to have the following config in `mix.exs`:
```elixir
def cli do
[
preferred_envs: [
check: :test,
credo: :test,
dialyzer: :test,
doctor: :test,
docs: :test,
format: :test,
sobelow: :test,
"deps.audit": :test
]
]
end
def deps do
[
{:credo, ">= 0.0.0", only: [:test], runtime: false},
{:dialyxir, ">= 0.0.0", only: [:test], runtime: false},
{:doctor, ">= 0.0.0", only: [:test], runtime: false},
{:ex_check_ng, "~> 1.0", only: [:test], runtime: false},
{:ex_doc, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:mix_audit, ">= 0.0.0", only: [:test], runtime: false}
{:sobelow, ">= 0.0.0", only: [:test], runtime: false},
]
end
```
And the following in `.check.exs`:
```elixir
[
tools: [
{:compiler, env: %{"MIX_ENV" => "test"}},
{:ex_doc, env: %{"MIX_ENV" => "test"}}
{:formatter, env: %{"MIX_ENV" => "test"}},
]
]
```
Above setup will consistently check the project using just the test build, both locally and on the CI.
### `unused_deps` false negatives
You may encounter an issue with the `unused_deps` check failing on the CI while passing locally, caused by fetching only dependencies for specific env. If that happens, remove the `--only test` (or similar) from your `mix deps.get` invocation on the CI to fix the issue.
## Changelog
See [CHANGELOG.md](./CHANGELOG.md).
## Copyright and License
Copyright (c) 2019 Karol Słuszniak
This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the [LICENSE.md](./LICENSE.md) file for more details.