- 
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/git-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 checkTo 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/git-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-checkto 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.yamlto.gitignore.Run $ nix-shellto executeshellHookwhich will:- build the tools and .pre-commit-config.yamlconfig file symlink which references the binaries, for speed and safe garbage collection
- provide the pre-commitexecutable thatgit commitwill invoke
 
- build the tools and 
.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.
- autoflake
- black
- check-builtin-literals
- check-docstring-first
- check-python
- fix-encoding-pragma
- flake8
- isort
- mypy
- name-tests-test
- pylint
- pyright
- python-debug-statements
- poetry
- pyupgrade
- ruff
- ruff-format
- sort-requirements-txt
- gofmt: Runs go fmt
- golangci-lint
- gotest: Runs go test
- govet
- revive
- staticcheck
- terraform-format: built-in formatter (using OpenTofu's- fmt)
- terraform-validate: built-in validator (using OpenTofu's- validate)
- tflint
- actionlint
- check-added-large-files
- check-case-conflicts
- check-executables-have-shebangs
- checkmake
- check-shebang-scripts-are-executable
- check-symlinks
- check-vcs-permalinks
- check-xml
- cmake-format
- crystal
- detect-aws-credentials
- detect-private-keys
- dhall format: built-in formatter
- editorconfig-checker
- end-of-file-fixer
- fix-byte-order-marker
- hadolint
- headache
- mixed-line-endings
- mkdocs-linkcheck
- prettier
- sort-file-contents
- tagref
- topiary
- treefmt
- trim-trailing-whitespace
Sometimes 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/git-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:
- Nix closure of the tool should be small e.g. < 50MB. A problematic example:
  $ du -sh $(nix-build -A go)
  463M  /nix/store/v4ys4lrjngf62lvvrdbs7r9kbxh9nqaa-go-1.18.6- The tool must not be very specific (e.g. language tooling is OK, but project specific tooling is not)
- The tool needs to live in a separate repository (even if a simple bash script, unless it's a oneliner)
