Define your own custom hooks
mastermodules/hooks.nix and the available configuration options in modules/pre-commit.nix to ensure your custom hook follows the established patterns and integrates correctly with the Nix-based pre-commit workflow.repository·master·Indexed 21 days ago
https://github.com/cachix/git-hooks.nixA Nix-based tool for integrating git hooks via pre-commit into Nix projects. It provides low-overhead tooling for development and CI, supporting integration with devenv.sh, Nix Flakes, and standard Nix (default.nix/shell.nix). The library includes a wide array of built-in hooks for various languages (Nix, Python, Rust, Shell, etc.), data formats, and DevOps tools, while allowing users to define their own custom hooks.
modules/hooks.nix and the available configuration options in modules/pre-commit.nix to ensure your custom hook follows the established patterns and integrates correctly with the Nix-based pre-commit workflow.For projects using default.nix or shell.nix, you can integrate hooks directly into your environment.
default.nix for CIImport the hooks via fetchTarball and use the run function to define a pre-commit-check derivation. This allows you to run checks using nix-build -A pre-commit-check.
shell.nix for DevelopmentImport the hooks into your shell.nix and include the shellHook and enabledPackages in your mkShell definition. This will automatically build the necessary tools and symlink the .pre-commit-config.yaml file when you run nix-shell.
# Example for default.nix integration
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 {
src = ./.;
hooks = {
elm-format.enable = true;
ormolu.enable = true;
ormolu.package = pkgs.haskellPackages.ormolu;
};
};
}If you wish to contribute a new hook to the repository, ensure it meets the following criteria:
If you use devenv.sh, you can enable various language-specific hooks by setting their .enable option to true within the git-hooks.hooks attribute set. This provides a low-overhead way to integrate tools like nixfmt, black, and shellcheck into your development workflow.
Commonly used hooks include:
nixfmt.enable (Nix formatting)black.enable (Python formatting)shellcheck.enable (Shell linting)mdsh.enable (Markdown shell examples)You can also override specific packages (e.g., ormolu.package) or provide settings for complex hooks like clippy.
{ inputs, ... }:
{
git-hooks.hooks = {
nixfmt.enable = true;
black.enable = true;
shellcheck.enable = true;
mdsh.enable = true;
# Override a package with a different version
ormolu.enable = true;
ormolu.package = pkgs.haskellPackages.ormolu;
# Complex hook with overrides and settings
clippy.enable = true;
clippy.packageOverrides.cargo = pkgs.cargo;
clippy.packageOverrides.clippy = pkgs.clippy;
clippy.settings.allFeatures = true;
# Custom hook
my-custom-hook = {
enable = true;
entry = "./on-pre-commit.sh";
};
};
# Use alternative pre-commit implementations
git-hooks.package = pkgs.prek;
}To use git-hooks.nix in a Flake-based project, you can define checks for CI, devShells for local development, and a formatter for easy formatting.
Important: Add /.pre-commit-config.yaml to your .gitignore as it is auto-generated from your Nix configuration.
nix develop to enter a shell where hooks are automatically installed. To run hooks manually, use nix develop -c pre-commit run --all-files.nix flake check to run hooks in a sandbox. Note that sandboxed hooks cannot access the internet or modify files, making them unsuitable for formatting hooks that auto-fix files.formatter in your flake to use nix fmt for a seamless experience.{
description = "An example project";
inputs = {
systems.url = "github:nix-systems/default";
git-hooks.url = "github:cachix/git-hooks.nix";
};
outputs = { self, systems, nixpkgs }: @inputs:
let
forEachSystem = nixpkgs.lib.genAttrs (import systems);
in {
# Run the hooks with `nix fmt`.
formatter = forEachSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
config = self.checks.${system}.pre-commit-check.config;
inherit (config) package configFile;
script = "${pkgs.lib.getExe package} run --all-files --config ${configFile}";
in pkgs.writeShellScriptBin "pre-commit-run" script
);
# Run the hooks in a sandbox with `nix flake check`.
checks = forEachSystem (system: {
pre-commit-check = inputs.git-hooks.lib.${system}.run {
src = ./.;
hooks = { nixfmt.enable = true; };
};
});
# Enter a development shell with `nix develop`.
devShells = forEachSystem (system: {
default = let
pkgs = nixpkgs.legacyPackages.${system};
inherit (self.checks.${system}.pre-commit-check) shellHook enabledPackages;
in pkgs.mkShell {
inherit shellHook;
buildInputs = enabledPackages;
};
});
};
}The clang-format hook allows you to restrict formatting to specific languages using the types_or option. If this option is not provided, the hook uses its default internal list of supported languages.
clang-format = {
enable = true;
types_or = lib.mkForce [ "c" "c++" ];
};You can define project-specific commands as custom hooks by providing a configuration object to the hooks attribute. Custom hooks follow the same schema as pre-defined hooks.
Key Configuration Options:
enable: Set to true to activate the hook.name: The display name in the report table.entry: The mandatory command to execute.files: A regex pattern of files to run on (e.g., "\.(c|h)$").types: A list of file types to filter by (e.g., ["text", "c"]).excludes: A regex pattern to exclude files.language: Use "unsupported" (or "system" in older pre-commit versions) when using externally managed tools like Nix.pass_filenames: If false, the command will not receive the list of changed files as arguments.stages: Which git stages to run on (e.g., ["pre-push"]).# Example custom hook for a C project using Make
unit-tests = {
enable = true;
name = "Unit tests";
entry = "make check";
files = "\.(c|h)$";
types = [ "text" "c" ];
excludes = [ "irrelevant\.c" ];
language = "unsupported";
pass_filenames = false;
stages = ["pre-push"];
};The project provides a wide array of built-in git hooks for various programming languages, configuration formats, and utility tasks. Each hook is integrated with Nix to ensure reproducible execution.
Common categories include:
### Built-in hooks
#### Ansible
- ansible-lint
#### C/C++/C#
- clang-format (supports `types_or` configuration)
- clang-tidy
- cmake-format
#### JavaScript/TypeScript
- biome
- denofmt
- denolint
- eslint
- oxfmt
- oxlint
- rome (alias to biome)
#### Nix
- alejandra
- deadnix
- flake-checker
- nil
- nixf-diagnose
- nixfmt (supports >=v1.0)
- nixfmt-classic (v0.6.0)
- nixfmt-rfc-style
- nixpkgs-fmt
- statix
#### Python
- black
- flake8
- isort
- mypy
- ruff
- ruff-format
- uv
- (and many others...)
#### Rust
- cargo-check
- cargo-sort
- clippy
- rustfmt