Trivial integration for Nix projects (wires up a few things behind the scenes)
Provide a low-overhead build of all the tooling available for the hooks to use (naive implementation of calling nix-shell does bring some latency when committing)
Common hooks for languages like Python, Haskell, Elm, etc. see all hook options
Run hooks as part of development and on during CI
https://devenv.sh/pre-commit-hooks/
Given the following flake.nix
example:
{
description = "An example project.";
inputs.pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
outputs = { self, nixpkgs, ... }@inputs:
let
supportedSystems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
in
{
checks = forAllSystems (system: {
pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run {
src = ./.;
hooks = {
nixpkgs-fmt.enable = true;
};
};
});
devShells = forAllSystems (system: {
default = nixpkgs.legacyPackages.${system}.mkShell {
inherit (self.checks.${system}.pre-commit-check) shellHook;
buildInputs = self.checks.${system}.pre-commit-check.enabledPackages;
};
});
};
}
Add /.pre-commit-config.yaml
to the .gitignore
.
To run the all the hooks on CI:
nix flake check
To install pre-commit hooks developers would run:
nix develop
Optionally use binary caches to avoid compilation:
nix-env -iA cachix -f https://cachix.org/api/v1/install
cachix use pre-commit-hooks
Integrate hooks to be built as part of default.nix
:
let
nix-pre-commit-hooks = import (builtins.fetchTarball "https://github.com/cachix/pre-commit-hooks.nix/tarball/master");
in {
# Configured with the module options defined in `modules/pre-commit.nix`:
pre-commit-check = nix-pre-commit-hooks.run {
src = ./.;
# If your hooks are intrusive, avoid running on each commit with a default_states like this:
# default_stages = ["manual" "push"];
hooks = {
elm-format.enable = true;
# override a package with a different version
ormolu.enable = true;
ormolu.package = pkgs.haskellPackages.ormolu;
ormolu.settings.defaultExtensions = [ "lhs" "hs" ];
# some hooks have more than one package, like clippy:
clippy.enable = true;
clippy.packageOverrides.cargo = pkgs.cargo;
clippy.packageOverrides.clippy = tools.clippy;
# some hooks provide settings
clippy.settings.allFeatures = true;
};
};
}
Run $ nix-build -A pre-commit-check
to perform the checks as a Nix derivation.
Integrate hooks to prepare environment as part of shell.nix
:
let
pre-commit = import ./default.nix;
in (import <nixpkgs> {}).mkShell {
shellHook = ''
${pre-commit.pre-commit-check.shellHook}
'';
buildInputs = pre-commit.pre-commit-check.enabledPackages;
}
Add /.pre-commit-config.yaml
to .gitignore
.
Run $ nix-shell
to execute shellHook
which will:
.pre-commit-config.yaml
config file symlink which
references the binaries, for speed and safe garbage collectionpre-commit
executable that git commit
will invoke.envrc
:
use nix
You may restrict which languages should be formatted by clang-format
using
clang-format.types_or
. For example to check only C and C++ files:
clang-format = {
enable = true;
types_or = lib.mkForce [ "c" "c++" ];
};
Otherwise, the default internal list is used which includes everything that clang-format supports.
go fmt
go test
dhall format
: built-in formatterSometimes it is useful to add a project specific command as an extra check that is not part of the pre-defined set of hooks provided by this project.
Example configuration:
let
nix-pre-commit-hooks = import (builtins.fetchTarball "https://github.com/cachix/pre-commit-hooks.nix/tarball/master");
in {
pre-commit-check = nix-pre-commit-hooks.run {
hooks = {
# ...
# Example custom hook for a C project using Make:
unit-tests = {
enable = true;
# The name of the hook (appears on the report table):
name = "Unit tests";
# The command to execute (mandatory):
entry = "make check";
# The pattern of files to run on (default: "" (all))
# see also https://pre-commit.com/#hooks-files
files = "\\.(c|h)$";
# List of file types to run on (default: [ "file" ] (all files))
# see also https://pre-commit.com/#filtering-files-with-types
# You probably only need to specify one of `files` or `types`:
types = [ "text" "c" ];
# Exclude files that were matched by these patterns (default: [ ] (none)):
excludes = [ "irrelevant\\.c" ];
# The language of the hook - tells pre-commit
# how to install the hook (default: "system")
# see also https://pre-commit.com/#supported-languages
language = "system";
# Set this to false to not pass the changed files
# to the command (default: true):
pass_filenames = false;
# Which git hooks the command should run for (default: [ "pre-commit" ]):
stages = ["pre-push"];
};
};
};
}
Custom hooks are defined with the same schema as pre-defined hooks.
Everyone is encouraged to add new hooks.
Have a look at the existing hooks and the options.
There's no guarantee the hook will be accepted, but the general guidelines are:
< 50MB
. A problematic example: $ du -sh $(nix-build -A go)
463M /nix/store/v4ys4lrjngf62lvvrdbs7r9kbxh9nqaa-go-1.18.6