Photo credit: Unsplash: glenncarstenspeters
In the new project I mentioned in the previous post, I’m focusing on practicing good habits, which my CI setup is a large part of. The other big part is keeping things clean and well written before they are even committed into the repository.
To accomplish this task, I set up pre-commit. Pre-commit is a really easy way to set up some useful git pre-commit hooks, as well as any custom command you want to run.
Install
Pre-commit can be installed into your python setup, or on OSX, use homebrew and run brew install pre-commit
You’ll create a .pre-commit-config.yaml
in the root of the git working directory that describes the hooks you want to fire on pre-commit.
Then you can setup your actual hook to fire everything described in .pre-commit-config.yaml
by running pre-commit install
. You can then modify the .pre-commit-config.yaml
file and adjust your hooks without another install. If you ever need to skip this stuff for a commit, just add --no-verify
to the commit command (but don’t! fix the problem instead!)
Basics
I’ll start with the common hooks, defined in .pre-commit-config.yaml
:
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v1.4.0
hooks:
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-yaml
- id: end-of-file-fixer
- id: no-commit-to-branch
args: [-b, master, -b, production, -b, staging]
Here I’m grabbing the hooks that are public and part of the pre-commit core. I’m using a couple specific ones for things I care about:
- trailing-whitespace: Making sure that I don’t leave any extra spaces at the end of lines
- check-merge-conflict: Making sure we never commit any unresolved merge conflicts
- check-yaml: Verify yaml syntax and make sure all files are valid before committing them
- end-of-file-fixer: Make sure my files end in a newline, and only a newline
- no-commit-to-brach: Force myself to follow good flow practices and develop on a branch, and merge in GitLab
So those are the basics, now onto my Elixir specific fun.
Elixir Hooks
Starting a new section in .pre-commit-config.yaml
, we’ll define an array of custom hooks:
- repo: local
hooks:
Tests
First, we’ll make sure the tests run, if any source or test file changes:
- id: mix-test
name: 'elixir: mix test'
entry: mix test
language: system
pass_filenames: false
files: \.exs*$
The files
pattern will be what changes this would trigger on, and we set pass_filenames
as false, so that we run the full suite.
Format
Then, we’ll make sure all changed source and elixir script files are formatted correctly. Leaving the pass_filenames
at its default of true, will just run the formatter on changed files.
- id: mix-format
name: 'elixir: mix format'
entry: mix format --check-formatted
language: system
files: \.exs*$
Compile
Then, we’ll make sure everything compiles without warnings.
- id: mix-compile
name: 'elixir: mix compile'
entry: mix compile --force --warnings-as-errors
language: system
pass_filenames: false
files: \.ex$
Convention
Finally, we’ll run credo on the entire project if any elixir or elixir script file changes.
- id: mix-credo
name: 'elixir: mix credo'
entry: mix credo
language: system
pass_filenames: false
files: \.exs*$
Putting it all together
Here is the final file. It may get to be too slow as the project gets larger, and since CI does a lot of these same checks, I’ll likely shorten it up, or move a few things to taking passed files again, rather than running on the full project even if only one piece changes.
.pre-commit-config.yaml
- repo: local
hooks:
- id: mix-test
name: 'elixir: mix test'
entry: mix test
language: system
pass_filenames: false
files: \.exs*$
- id: mix-format
name: 'elixir: mix format'
entry: mix format --check-formatted
language: system
files: \.exs*$
- id: mix-compile
name: 'elixir: mix compile'
entry: mix compile --force --warnings-as-errors
language: system
pass_filenames: false
files: \.ex$
- id: mix-credo
name: 'elixir: mix credo'
entry: mix credo
language: system
pass_filenames: false
files: \.exs*$
- repo: git://github.com/pre-commit/pre-commit-hooks
sha: v1.4.0
hooks:
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-yaml
- id: end-of-file-fixer
- id: no-commit-to-branch
args: [-b, master, -b, production, -b, staging]
I hope this helps you keep your Elixir projects nice!