presubmit

CI Hex.pm Docs

A linter for git commits. It checks the commit, the staged index, or the working tree against rules that understand Elixir, such as "an added controller is routed", "an added migration sorts last", and "a move contains nothing but the move".

The idea and the name come from Chromium's PRESUBMIT.py.

Setup

# mix.exs
{:presubmit, "~> 0.1.0", only: [:dev, :test], runtime: false}
mix presubmit.install # commit-msg hook: checks each commit (staged changes + message) before it exists
mix presubmit # by hand: the working tree if dirty, else HEAD
mix presubmit --list # what is configured

Add import_deps: [:presubmit] to .formatter.exs. In CI, check every commit (HEAD on a pull request is a synthetic merge, so use the range):

- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- run: mix presubmit --range ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}

mix presubmit --help lists the sources (--head, --rev, --staged, --worktree, --range) and options (--format json, --warnings-as-errors).

Configuration

Without .presubmit.exs the defaults apply: every rule that does not fail an ordinary commit, plus the Phoenix and Ecto sets when those libraries are present, with the opinionated rules as warnings. The output says what was enabled and why. To choose, the file is a list of rule sets:

[
Presubmit.Rules.Elixir,
{Presubmit.Rules.Ecto, except: [:migrations_reversible], since: "origin/main"},
{Presubmit.Rules.ExUnit, only: [:behaviour_changes_tested], warn: [:behaviour_changes_tested]},
{Presubmit.Rules.Message, subject: ~r/^\[[a-z_-]+\] /},
{Presubmit.Rules.Shape, in: ~r{^apps/core/}},
MyApp.CommitRules
]

Each entry takes only:/except: to select rules, warn: to report without failing, in: to restrict the set to changes under a path, and the set's own options. A commit can exempt itself with a Presubmit-Skip: rule, rule or No-Presubmit: true trailer, which is announced on every run.

Rules

Each set documents its rules and options:

Your own rules are a module with use Presubmit.RuleSet and rule declarations over the verbs in Presubmit.Assertions and the queries in Presubmit.Query; see Presubmit.RuleSet. Rules see source shapes, not macro output: what a macro generates is invisible to them (see each adapter under Presubmit.Adapters).

License

MIT