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
- Delivers results faster by running tools in parallel and catching all issues in one go
- Checks the project consistently on every developer's local machine & on the CI
- Runs only the tools & tests that have failed in the last run
- Fixes issues automatically in the fix mode
Sports powerful features to enable ultimate flexibility.
- Add custom mix tasks, shell scripts and commands via configuration file
- Enhance you CI workflow to report status, retry random failures or autofix issues
- Empower umbrella projects with parallel recursion over child apps
- Design complex parallel workflows with cross-tool deps
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
- Retries ExUnit with the
--failed
flag
Read more in the introductory "One task to rule all Elixir analysis & testing tools" article.
Add ex_check
dependency in mix.exs
:
def deps do
[
{:ex_check, "~> 0.16.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.
If you want to take advantage of community curated tools, add following dependencies in mix.exs
:
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.
[
tools: [
{:dialyzer, false},
{:sobelow, false}
]
]
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:
[
fix: true
]
You may also enable the fix mode on the CI.
Learn more about the tools included in the check as well as its workflow, configuration and options on HexDocs 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" article.
With mix check
you can consistently run the same set of checks locally and on the CI. CI configuration also becomes trivial and comes out of the box with parallelism and error output from all checks at once regardless of which ones have failed.
Like on a local machine, all you have to do in order to use ex_check
on CI is run mix check
instead of mix test
. This repo features working CI configs for following providers:
- GitHub - .github/workflows/check.yml
Yes, ex_check
uses itself on the CI. Yay for recursion!
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:
mix check --fix && \
git diff-index --quiet HEAD -- && \
git config --global user.name 'Autofix' && \
git config --global user.email '[email protected]' && \
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.
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:
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.
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
:
def project do
[
# ...
preferred_cli_env: [
check: :test,
credo: :test,
dialyzer: :test,
doctor: :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, "~> 0.14.0", only: [:test], runtime: false},
{:ex_doc, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:sobelow, ">= 0.0.0", only: [:test], runtime: false},
{:mix_audit, ">= 0.0.0", only: [:test], runtime: false}
]
end
And the following in .check.exs
:
[
tools: [
{:compiler, env: %{"MIX_ENV" => "test"}},
{:formatter, env: %{"MIX_ENV" => "test"}},
{:ex_doc, env: %{"MIX_ENV" => "test"}}
]
]
Above setup will consistently check the project using just the test build, both locally and on the CI.
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.
See CHANGELOG.md.
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 file for more details.