git-hooks.nix

repository·master·Indexed 21 days ago

https://github.com/cachix/git-hooks.nix

A 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.

Tokens
2.8K
Snippets
6
Records
8
Agent score
24%

What's inside git-hooks.nix

  1. Define your own custom hooks

    master
    You can extend the library by defining custom git hooks. To do this, you should examine the existing hook implementations in 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.
  2. Integrate git-hooks.nix with standard Nix (non-Flake)

    master

    For projects using default.nix or shell.nix, you can integrate hooks directly into your environment.

    Using default.nix for CI

    Import 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.

    Using shell.nix for Development

    Import 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;
        };
      };
    }
  3. Guidelines for contributing new hooks

    master

    If you wish to contribute a new hook to the repository, ensure it meets the following criteria:

    1. Small Nix closure: The tool's Nix closure should be small, ideally under 50MB. Large closures (e.g., a Go installation resulting in 463MB) are considered problematic.
    2. Generality: The tool must be general-purpose. Language-specific tooling is acceptable, but tools specific to a single project are not.
    3. External Repository: The tool must reside in its own separate repository (even if it is just a simple bash script, unless it is a single-line command).
  4. Configure git-hooks.nix with devenv.sh

    master

    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;
    }
  5. Integrate git-hooks.nix with Nix Flakes

    master

    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.

    Usage Patterns:

    • Development: Run nix develop to enter a shell where hooks are automatically installed. To run hooks manually, use nix develop -c pre-commit run --all-files.
    • CI/Sandboxed Checks: Use 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.
    • Formatting: Define a 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;
            };
          });
        };
    }
  6. Configure clang-format language types

    master

    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++" ];
    };
  7. Define custom git hooks

    master

    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"];
    };
  8. Reference of built-in git hooks

    master

    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:

    • Languages: C/C++, Clojure, Crystal, Dart, Elixir, Golang, Haskell, Java, JavaScript/TypeScript, Lua, Nix, OCaml, PHP, Python, Rust, Shell, etc.
    • Data Formats: CUE, Dhall, HTML, JSON, LaTeX, Markdown, Nix, TOML, YAML.
    • Infrastructure/DevOps: Ansible, Dockerfile, Terraform, GitHub Actions (actionlint).
    • Utilities: Link checkers, Secret detection, Spell checkers, and Git-specific hooks.
    ### 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