1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
{
  /**
    Path to Lix's source, normally the flake's "self" argument
  */
  self ? pkgs.lib.cleanSource ./.,
  /**
    Already instantiated Nixpkgs
  */
  pkgs,
  /**
    pre-commit-hooks source path, normally from the flake input
  */
  pre-commit-hooks,
}:
let
  inherit (pkgs) lib;
  # Import pre-commit bypassing the flake because flakes don't let
  # you have overlays. Also their implementation forces an
  # unnecessary reimport of nixpkgs for our use cases.
  tools = import (pre-commit-hooks + "/nix/call-tools.nix") pkgs;
  pre-commit-run = pkgs.callPackage (pre-commit-hooks + "/nix/run.nix") {
    inherit tools;
    isFlakes = true;
    # unused!
    gitignore-nix-src = builtins.throw "gitignore-nix-src is unused";
  };
in
pre-commit-run {
  src = self;
  hooks = {
    no-commit-to-branch = {
      enable = true;
      settings.branch = [ "main" ];
    };
    clang-format = {
      enable = true;
      package = pkgs.llvmPackages.libclang.python;
      entry = "${pkgs.llvmPackages.libclang.python}/bin/git-clang-format --binary ${pkgs.llvmPackages.clang-tools}/bin/clang-format";
      files = "^(lix/|tests/|contrib/plugins/)";
      types = [
        "c++"
        "file"
      ];
      excludes = [
        "^lix/pch/.*$"
        ''\.gen\.hh$''
      ];
      pass_filenames = false; # this will automatically format everything in the stage area of Git.
    };
    check-case-conflicts.enable = true;
    check-executables-have-shebangs = {
      enable = true;
      stages = [ "pre-commit" ];
    };
    check-shebang-scripts-are-executable = {
      enable = true;
      stages = [ "pre-commit" ];
    };
    check-symlinks = {
      enable = true;
      excludes = [ "^tests/functional/lang/symlink-resolution/broken$" ];
    };
    check-merge-conflicts.enable = true;
    end-of-file-fixer = {
      enable = true;
      excludes = [
        "\\.drv$"
        "^tests/functional/lang/"
        ''\.patch$''
        "^tests/functional2/.+\\.exp"
        "^tests/functional2/.+\\.nix"
      ];
    };
    mixed-line-endings = {
      enable = true;
      excludes = [
        "^tests/functional/lang/"
        "^tests/functional2/.+\\.exp"
        "^tests/functional2/.+\\.nix"
      ];
    };
    release-notes = {
      enable = true;
      package = pkgs.build-release-notes;
      files = ''^doc/manual/(change-authors\.yml|rl-next(-dev)?)'';
      pass_filenames = false;
      entry = ''
        ${lib.getExe pkgs.build-release-notes} --change-authors doc/manual/change-authors.yml doc/manual/rl-next
      '';
    };
    change-authors-sorted = {
      enable = true;
      package = pkgs.yq;
      files = ''^doc/manual/change-authors\.yml'';
      entry = "${pkgs.writeShellScript "change-authors-sorted" ''
        set -euo pipefail
        shopt -s inherit_errexit

        echo "changes necessary to sort $1:"
        diff -U3 <(${lib.getExe pkgs.yq} -y . "$1") <(${lib.getExe pkgs.yq} -Sy . "$1")
      ''}";
    };
    check-headers = {
      enable = true;
      package = pkgs.check-headers;
      files = "^(lix/|tests/)";
      types = [
        "c++"
        "file"
        "header"
      ];
      excludes = [
        "^lix/pch/.*$"
        # generated files; these will never actually be seen by this
        # check, and are left here as documentation
        ''(parser|lexer)-tab\.hh$''
        ''\.gen\.hh$''
      ];
      entry = lib.getExe pkgs.check-headers;
    };
    keep-sorted = {
      enable = true;
      package = pkgs.keep-sorted;
      entry = lib.getExe pkgs.keep-sorted;
    };
    # TODO: Once the test suite is nicer, clean up and start
    # enforcing trailing whitespace on tests that don't explicitly
    # check for it.
    trim-trailing-whitespace = {
      enable = true;
      stages = [ "pre-commit" ];
      excludes = [
        "^tests/functional/lang/"
        "^tests/functional2/.+\\.exp"
        "^tests/functional2/.+\\.nix"
        ''\.patch$''
      ];
    };
    treefmt = {
      enable = true;
      settings.formatters = [
        pkgs.nixfmt-rfc-style
        pkgs.ruff
      ];
    };
  };
}