diff --git a/modules/hooks.nix b/modules/hooks.nix index b0c44203..9f07fd36 100644 --- a/modules/hooks.nix +++ b/modules/hooks.nix @@ -1,27 +1,22 @@ { config, lib, pkgs, hookModule, ... }: let - inherit (config) hooks tools settings; cfg = config; - inherit (lib) flatten mapAttrs mapAttrsToList mkDefault mkOption mkRemovedOptionModule mkRenamedOptionModule types; - - cargoManifestPathArg = - lib.optionalString - (settings.rust.cargoManifestPath != null) - "--manifest-path ${lib.escapeShellArg settings.rust.cargoManifestPath}"; - - mkCmdArgs = predActionList: - lib.concatStringsSep - " " - (builtins.foldl' - (acc: entry: - acc ++ lib.optional (builtins.elemAt entry 0) (builtins.elemAt entry 1)) - [ ] - predActionList); - - migrateBinPathToPackage = hook: binPath: - if hook.settings.binPath == null - then "${hook.package}${binPath}" - else hook.settings.binPath; + inherit (lib) flatten mapAttrsToList mkOption mkRemovedOptionModule mkRenamedOptionModule types; + + # Helper function to create hook options with unified descriptions + # Takes an attribute set: { name, description, modules, [specialArgs], [visible], ... } + mkHook = module: description: + mkOption { + inherit description; + type = types.submoduleWith ({ + modules = [ hookModule ] ++ [ + module + # Set name and description + ({ name, ... }: { config = { inherit name description; }; }) + ]; + }); + default = { }; + }; in { imports = @@ -61,7 +56,24 @@ in config.hookModule = { imports = [ ./hook.nix ]; - config._module.args.default_stages = cfg.default_stages; + config._module.args = { + inherit pkgs; + inherit (cfg) default_stages settings tools; + mkCmdArgs = predActionList: + lib.concatStringsSep + " " + (builtins.foldl' + (acc: entry: + acc ++ lib.optional (builtins.elemAt entry 0) (builtins.elemAt entry 1)) + [ ] + predActionList); + + migrateBinPathToPackage = hook: binPath: + if hook.settings.binPath == null + then "${hook.package}${binPath}" + else hook.settings.binPath; + + }; }; config._module.args.hookModule = config.hookModule; @@ -85,1943 +97,190 @@ in # PLEASE keep this sorted alphabetically. options.hooks = { - alejandra = mkOption { - description = "alejandra hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - check = - mkOption { - type = types.bool; - description = "Check if the input is already formatted and disable writing in-place the modified content"; - default = false; - example = true; - }; - exclude = - mkOption { - type = types.listOf types.str; - description = "Files or directories to exclude from formatting."; - default = [ ]; - example = [ "flake.nix" "./templates" ]; - }; - threads = - mkOption { - type = types.nullOr types.int; - description = "Number of formatting threads to spawn."; - default = null; - example = 8; - }; - verbosity = - mkOption { - type = types.enum [ "normal" "quiet" "silent" ]; - description = "Whether informational messages or all messages should be hidden or not."; - default = "normal"; - example = "quiet"; - }; - }; - }; - }; - ansible-lint = mkOption { - description = "ansible-lint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = mkOption { - type = types.str; - description = "Path to the YAML configuration file."; - # an empty string translates to use default configuration of the - # underlying ansible-lint binary - default = ""; - }; - subdir = mkOption { - type = types.str; - description = "Path to the Ansible subdirectory."; - default = ""; - }; - }; - }; - }; - autoflake = mkOption { - description = "autoflake hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "Path to autoflake binary."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.autoflake}/bin/autoflake" - ''; - }; - - flags = - mkOption { - type = types.str; - description = "Flags passed to autoflake."; - default = "--in-place --expand-star-imports --remove-duplicate-keys --remove-unused-variables"; - }; - }; - }; - }; - biome = mkOption { - description = "biome hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr (types.oneOf [ types.str types.path ]); - description = '' - `biome` binary path. - For example, if you want to use the `biome` binary from `node_modules`, use `"./node_modules/.bin/biome"`. - Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. - ''; - default = null; - defaultText = lib.literalExpression '' - "''${tools.biome}/bin/biome" - ''; - example = lib.literalExpression '' - "./node_modules/.bin/biome" - ''; - }; - - write = - mkOption { - type = types.bool; - description = "Whether to edit files inplace."; - default = true; - }; - - configPath = mkOption { - type = types.str; - description = "Path to the configuration JSON file"; - # an empty string translates to use default configuration of the - # underlying biome binary (i.e biome.json if exists) - default = ""; - }; - }; - }; - }; - black = mkOption { - description = "black hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - flags = mkOption { - type = types.str; - description = "Flags passed to black. See all available [here](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#command-line-options)."; - default = ""; - example = "--skip-magic-trailing-comma"; - }; - }; - }; - }; - cabal2nix = mkOption { - description = "cabal2nix hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - outputFilename = - mkOption { - type = types.str; - description = "The name of the output file generated after running `cabal2nix`."; - default = "default.nix"; - }; - }; - }; - }; - clippy = mkOption { - description = "clippy hook"; - type = types.submodule - ({ config, ... }: { - imports = [ hookModule ]; - options.packageOverrides = { - cargo = mkOption { - type = types.package; - description = "The cargo package to use"; - }; - clippy = mkOption { - type = types.package; - description = "The clippy package to use"; - }; - }; - options.settings = { - denyWarnings = mkOption { - type = types.bool; - description = "Fail when warnings are present"; - default = false; - }; - offline = mkOption { - type = types.bool; - description = "Run clippy offline"; - default = true; - }; - allFeatures = mkOption { - type = types.bool; - description = "Run clippy with --all-features"; - default = false; - }; - extraArgs = mkOption { - type = types.str; - description = "Additional arguments to pass to clippy"; - default = ""; - }; - }; - - config.extraPackages = [ - config.packageOverrides.cargo - config.packageOverrides.clippy - ]; - }); - }; - cmake-format = mkOption { - description = "cmake-format hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = mkOption { - type = types.str; - description = "Path to the configuration file (.json,.python,.yaml)"; - default = ""; - example = ".cmake-format.json"; - }; - }; - }; - }; - credo = mkOption { - description = "credo hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - strict = - mkOption { - type = types.bool; - description = "Whether to auto-promote the changes."; - default = true; - }; - }; - }; - }; - deadnix = mkOption { - description = "deadnix hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - edit = - mkOption { - type = types.bool; - description = "Remove unused code and write to source file."; - default = false; - }; - - exclude = - mkOption { - type = types.listOf types.str; - description = "Files to exclude from analysis."; - default = [ ]; - }; - - hidden = - mkOption { - type = types.bool; - description = "Recurse into hidden subdirectories and process hidden .*.nix files."; - default = false; - }; - - noLambdaArg = - mkOption { - type = types.bool; - description = "Don't check lambda parameter arguments."; - default = false; - }; - - noLambdaPatternNames = - mkOption { - type = types.bool; - description = "Don't check lambda pattern names (don't break nixpkgs `callPackage`)."; - default = false; - }; - - noUnderscore = - mkOption { - type = types.bool; - description = "Don't check any bindings that start with a `_`."; - default = false; - }; - - quiet = - mkOption { - type = types.bool; - description = "Don't print a dead code report."; - default = false; - }; - }; - }; - }; - denofmt = mkOption { - description = "denofmt hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - write = - mkOption { - type = types.bool; - description = "Whether to edit files inplace."; - default = true; - }; - configPath = - mkOption { - type = types.str; - description = "Path to the configuration JSON file"; - # an empty string translates to use default configuration of the - # underlying deno binary (i.e deno.json or deno.jsonc) - default = ""; - }; - }; - }; - }; - denolint = mkOption { - description = "denolint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - format = - mkOption { - type = types.enum [ "default" "compact" "json" ]; - description = "Output format."; - default = "default"; - }; - - configPath = - mkOption { - type = types.str; - description = "Path to the configuration JSON file"; - # an empty string translates to use default configuration of the - # underlying deno binary (i.e deno.json or deno.jsonc) - default = ""; - }; - }; - }; - }; - dune-fmt = mkOption { - description = "dune-fmt hook"; - type = types.submodule - ({ config, ... }: { - imports = [ hookModule ]; - options.settings = { - auto-promote = - mkOption { - type = types.bool; - description = "Whether to auto-promote the changes."; - default = true; - }; - - extraRuntimeInputs = - mkOption { - type = types.listOf types.package; - description = "Extra runtimeInputs to add to the environment, eg. `ocamlformat`."; - default = [ ]; - }; - }; - - config.extraPackages = config.settings.extraRuntimeInputs; - }); - }; - eclint = mkOption { - description = "eclint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - fix = - mkOption { - type = types.bool; - description = "Modify files in place rather than showing the errors."; - default = false; - }; - summary = - mkOption { - type = types.bool; - description = "Only show number of errors per file."; - default = false; - }; - color = - mkOption { - type = types.enum [ "auto" "always" "never" ]; - description = "When to generate colored output."; - default = "auto"; - }; - exclude = - mkOption { - type = types.listOf types.str; - description = "Filter to exclude files."; - default = [ ]; - }; - verbosity = - mkOption { - type = types.enum [ 0 1 2 3 4 ]; - description = "Log level verbosity"; - default = 0; - }; - }; - }; - }; - eslint = mkOption { - description = "eslint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr (types.oneOf [ types.str types.path ]); - description = '' - `eslint` binary path. - For example, if you want to use the `eslint` binary from `node_modules`, use `"./node_modules/.bin/eslint"`. - Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. - ''; - default = null; - defaultText = lib.literalExpression '' - "''${tools.eslint}/bin/eslint" - ''; - example = lib.literalExpression '' - "./node_modules/.bin/eslint" - ''; - }; - - extensions = - mkOption { - type = types.str; - description = - "The pattern of files to run on, see [https://pre-commit.com/#hooks-files](https://pre-commit.com/#hooks-files)."; - default = "\\.js$"; - }; - }; - }; - }; - flake8 = mkOption { - description = "flake8 hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "flake8 binary path. Should be used to specify flake8 binary from your Python environment."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.flake8}/bin/flake8" - ''; - }; - extendIgnore = - mkOption { - type = types.listOf types.str; - description = "List of additional ignore codes"; - default = [ ]; - example = [ "E501" ]; - }; - format = - mkOption { - type = types.str; - description = "Output format."; - default = "default"; - }; - }; - }; - }; - flynt = mkOption { - description = "flynt hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - aggressive = - mkOption { - type = types.bool; - description = "Include conversions with potentially changed behavior."; - default = false; - }; - binPath = - mkOption { - type = types.nullOr types.str; - description = "flynt binary path. Can be used to specify the flynt binary from an existing Python environment."; - default = null; - }; - dry-run = - mkOption { - type = types.bool; - description = "Do not change files in-place and print diff instead."; - default = false; - }; - exclude = - mkOption { - type = types.listOf types.str; - description = "Ignore files with given strings in their absolute path."; - default = [ ]; - }; - fail-on-change = - mkOption { - type = types.bool; - description = "Fail when diff is not empty (for linting purposes)."; - default = true; - }; - line-length = - mkOption { - type = types.nullOr types.int; - description = "Convert expressions spanning multiple lines, only if the resulting single line will fit into this line length limit."; - default = null; - }; - no-multiline = - mkOption { - type = types.bool; - description = "Convert only single line expressions."; - default = false; - }; - quiet = - mkOption { - type = types.bool; - description = "Run without output."; - default = false; - }; - string = - mkOption { - type = types.bool; - description = "Interpret the input as a Python code snippet and print the converted version."; - default = false; - }; - transform-concats = - mkOption { - type = types.bool; - description = "Replace string concatenations with f-strings."; - default = false; - }; - verbose = - mkOption { - type = types.bool; - description = "Run with verbose output."; - default = false; - }; - }; - }; - }; - fourmolu = mkOption { - description = "fourmolu hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings.defaultExtensions = mkOption { - type = types.listOf types.str; - description = "Haskell language extensions to enable."; - default = [ ]; - }; - }; - }; - golines = mkOption { - description = "golines hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - flags = mkOption { - type = types.str; - description = "Flags passed to golines. See all available [here](https://github.com/segmentio/golines?tab=readme-ov-file#options)"; - default = ""; - example = "-m 120"; - }; - }; - }; - }; - headache = mkOption { - description = "headache hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - header-file = mkOption { - type = types.str; - description = "Path to the header file."; - default = ".header"; - }; - }; - }; - }; - hlint = mkOption { - description = "hlint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - hintFile = - mkOption { - type = types.nullOr (types.oneOf [ types.str types.path ]); - description = "Path to hlint.yaml. By default, hlint searches for .hlint.yaml in the project root."; - default = null; - }; - }; - }; - }; - hpack = mkOption { - description = "hpack hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - silent = - mkOption { - type = types.bool; - description = "Whether generation should be silent."; - default = false; - }; - }; - }; - }; - isort = mkOption { - description = "isort hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - profile = - mkOption { - type = types.enum [ "" "black" "django" "pycharm" "google" "open_stack" "plone" "attrs" "hug" "wemake" "appnexus" ]; - description = "Built-in profiles to allow easy interoperability with common projects and code styles."; - default = ""; - }; - flags = - mkOption { - type = types.str; - description = "Flags passed to isort. See all available [here](https://pycqa.github.io/isort/docs/configuration/options.html)."; - default = ""; - }; - }; - }; - }; - latexindent = mkOption { - description = "latexindent hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - flags = - mkOption { - type = types.str; - description = "Flags passed to latexindent. See available flags [here](https://latexindentpl.readthedocs.io/en/latest/sec-how-to-use.html#from-the-command-line)"; - default = "--local --silent --overwriteIfDifferent"; - }; - }; - }; - }; - lacheck = mkOption { - description = "lacheck hook"; - type = types.submodule { - imports = [ hookModule ]; - }; - }; - lua-ls = mkOption { - description = "lua-ls hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - checklevel = mkOption { - type = types.enum [ "Error" "Warning" "Information" "Hint" ]; - description = - "The diagnostic check level"; - default = "Warning"; - }; - configuration = mkOption { - type = types.attrs; - description = - "See https://github.com/LuaLS/lua-language-server/wiki/Configuration-File#luarcjson"; - default = { }; - }; - }; - }; - }; - lychee = mkOption { - description = "lychee hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = - mkOption { - type = types.str; - description = "Path to the config file."; - default = ""; - }; - flags = - mkOption { - type = types.str; - description = "Flags passed to lychee. See all available [here](https://lychee.cli.rs/#/usage/cli)."; - default = ""; - }; - }; - }; - }; - markdownlint = mkOption { - description = "markdownlint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configuration = - mkOption { - type = types.attrs; - description = - "See https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.jsonc"; - default = { }; - }; - }; - }; - }; - mdl = mkOption { - description = "mdl hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = - mkOption { - type = types.str; - description = "The configuration file to use."; - default = ""; - }; - git-recurse = - mkOption { - type = types.bool; - description = "Only process files known to git when given a directory."; - default = false; - }; - ignore-front-matter = - mkOption { - type = types.bool; - description = "Ignore YAML front matter."; - default = false; - }; - json = - mkOption { - type = types.bool; - description = "Format output as JSON."; - default = false; - }; - rules = - mkOption { - type = types.listOf types.str; - description = "Markdown rules to use for linting. Per default all rules are processed."; - default = [ ]; - }; - rulesets = - mkOption { - type = types.listOf types.str; - description = "Specify additional ruleset files to load."; - default = [ ]; - }; - show-aliases = - mkOption { - type = types.bool; - description = "Show rule alias instead of rule ID when viewing rules."; - default = false; - }; - warnings = - mkOption { - type = types.bool; - description = "Show Kramdown warnings."; - default = false; - }; - skip-default-ruleset = - mkOption { - type = types.bool; - description = "Do not load the default markdownlint ruleset. Use this option if you only want to load custom rulesets."; - default = false; - }; - style = - mkOption { - type = types.str; - description = "Select which style mdl uses."; - default = "default"; - }; - tags = - mkOption { - type = types.listOf types.str; - description = "Markdown rules to use for linting containing the given tags. Per default all rules are processed."; - default = [ ]; - }; - verbose = - mkOption { - type = types.bool; - description = "Increase verbosity."; - default = false; - }; - }; - }; - }; - mkdocs-linkcheck = mkOption { - description = "mkdocs-linkcheck hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr (types.oneOf [ types.str types.path ]); - description = "mkdocs-linkcheck binary path. Should be used to specify the mkdocs-linkcheck binary from your Python environment."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.mkdocs-linkcheck}/bin/mkdocs-linkcheck" - ''; - }; - - path = - mkOption { - type = types.str; - description = "Path to check"; - default = ""; - }; - - local-only = - mkOption { - type = types.bool; - description = "Whether to only check local links."; - default = false; - }; - - recurse = - mkOption { - type = types.bool; - description = "Whether to recurse directories under path."; - default = false; - }; - - extension = - mkOption { - type = types.str; - description = "File extension to scan for."; - default = ""; - }; - - method = - mkOption { - type = types.enum [ "get" "head" ]; - description = "HTTP method to use when checking external links."; - default = "get"; - }; - }; - }; - }; - mypy = mkOption { - description = "mypy hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "Mypy binary path. Should be used to specify the mypy executable in an environment containing your typing stubs."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.mypy}/bin/mypy" - ''; - }; - }; - }; - }; - nixfmt = mkOption { - description = "Deprecated nixfmt hook. Use nixfmt-classic or nixfmt-rfc-style instead."; - visible = false; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - width = - mkOption { - type = types.nullOr types.int; - description = "Line width."; - default = null; - }; - }; - }; - }; - nixfmt-classic = mkOption { - description = "nixfmt (classic) hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - width = - mkOption { - type = types.nullOr types.int; - description = "Line width."; - default = null; - }; - }; - }; - }; - nixfmt-rfc-style = mkOption { - description = "nixfmt (RFC 166 style) hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - width = - mkOption { - type = types.nullOr types.int; - description = "Line width."; - default = null; - }; - }; - }; - }; - no-commit-to-branch = mkOption { - description = "no-commit-to-branch-hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - branch = - mkOption { - description = "Branches to disallow commits to."; - type = types.listOf types.str; - default = [ "main" ]; - example = [ "main" "master" ]; - }; - pattern = - mkOption { - description = "RegEx patterns for branch names to disallow commits to."; - type = types.listOf types.str; - default = [ ]; - example = [ "ma.*" ]; - }; - }; - }; - }; - ormolu = mkOption { - description = "ormolu hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - defaultExtensions = - mkOption { - type = types.listOf types.str; - description = "Haskell language extensions to enable."; - default = [ ]; - }; - cabalDefaultExtensions = - mkOption { - type = types.bool; - description = "Use `default-extensions` from `.cabal` files."; - default = false; - }; - }; - }; - }; - php-cs-fixer = mkOption { - description = "php-cs-fixer hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "PHP-CS-Fixer binary path."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.php-cs-fixer}/bin/php-cs-fixer" - ''; - }; - }; - }; - }; - phpcbf = mkOption { - description = "phpcbf hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "PHP_CodeSniffer binary path."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.phpcbf}/bin/phpcbf" - ''; - }; - }; - }; - }; - phpcs = mkOption { - description = "phpcs hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "PHP_CodeSniffer binary path."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.phpcs}/bin/phpcs" - ''; - }; - }; - }; - }; - phpstan = mkOption { - description = "phpstan hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "PHPStan binary path."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.phpstan}/bin/phpstan" - ''; - }; - }; - }; - }; - # See all CLI flags for prettier [here](https://prettier.io/docs/en/cli.html). - # See all options for prettier [here](https://prettier.io/docs/en/options.html). - prettier = mkOption { - description = "prettier hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - description = '' - `prettier` binary path. - For example, if you want to use the `prettier` binary from `node_modules`, use `"./node_modules/.bin/prettier"`. - Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. - ''; - type = types.nullOr (types.oneOf [ types.str types.path ]); - default = null; - defaultText = lib.literalExpression '' - "''${tools.prettier}/bin/prettier" - ''; - example = lib.literalExpression '' - "./node_modules/.bin/prettier" - ''; - }; - allow-parens = - mkOption { - description = "Include parentheses around a sole arrow function parameter."; - default = "always"; - type = types.enum [ "always" "avoid" ]; - }; - bracket-same-line = - mkOption { - description = "Put > of opening tags on the last line instead of on a new line."; - type = types.bool; - default = false; - }; - cache = - mkOption { - description = "Only format changed files."; - type = types.bool; - default = false; - }; - cache-location = - mkOption { - description = "Path to the cache file location used by `--cache` flag."; - type = types.str; - default = "./node_modules/.cache/prettier/.prettier-cache"; - }; - cache-strategy = - mkOption { - description = "Strategy for the cache to use for detecting changed files."; - type = types.nullOr (types.enum [ "metadata" "content" ]); - default = null; - }; - check = - mkOption { - description = "Output a human-friendly message and a list of unformatted files, if any."; - type = types.bool; - default = false; - }; - list-different = - mkOption { - description = "Print the filenames of files that are different from Prettier formatting."; - type = types.bool; - default = true; - }; - color = - mkOption { - description = "Colorize error messages."; - type = types.bool; - default = true; - }; - configPath = - mkOption { - description = "Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js)."; - type = types.str; - default = ""; - }; - config-precedence = - mkOption { - description = "Defines how config file should be evaluated in combination of CLI options."; - type = types.enum [ "cli-override" "file-override" "prefer-file" ]; - default = "cli-override"; - }; - embedded-language-formatting = - mkOption { - description = "Control how Prettier formats quoted code embedded in the file."; - type = types.enum [ "auto" "off" ]; - default = "auto"; - }; - end-of-line = - mkOption { - description = "Which end of line characters to apply."; - type = types.enum [ "lf" "crlf" "cr" "auto" ]; - default = "lf"; - }; - html-whitespace-sensitivity = - mkOption { - description = "How to handle whitespaces in HTML."; - type = types.enum [ "css" "strict" "ignore" ]; - default = "css"; - }; - ignore-path = - mkOption { - description = "Path to a file containing patterns that describe files to ignore. - By default, prettier looks for `./.gitignore` and `./.prettierignore`. - Multiple values are accepted."; - type = types.listOf (types.oneOf [ types.str types.path ]); - default = [ ]; - }; - ignore-unknown = - mkOption { - description = "Ignore unknown files."; - type = types.bool; - default = true; - }; - insert-pragma = - mkOption { - description = "Insert @format pragma into file's first docblock comment."; - type = types.bool; - default = false; - }; - jsx-single-quote = - mkOption { - description = "Use single quotes in JSX."; - type = types.bool; - default = false; - }; - log-level = - mkOption { - description = "What level of logs to report."; - type = types.enum [ "silent" "error" "warn" "log" "debug" ]; - default = "log"; - example = "debug"; - }; - no-bracket-spacing = - mkOption { - description = "Do not print spaces between brackets."; - type = types.bool; - default = false; - }; - no-config = - mkOption { - description = "Do not look for a configuration file."; - type = types.bool; - default = false; - }; - no-editorconfig = - mkOption { - description = "Don't take .editorconfig into account when parsing configuration."; - type = types.bool; - default = false; - }; - no-error-on-unmatched-pattern = - mkOption { - description = "Prevent errors when pattern is unmatched."; - type = types.bool; - default = false; - }; - no-semi = - mkOption { - description = "Do not print semicolons, except at the beginning of lines which may need them."; - type = types.bool; - default = false; - }; - parser = - mkOption { - description = "Which parser to use."; - type = types.enum [ "" "flow" "babel" "babel-flow" "babel-ts" "typescript" "acorn" "espree" "meriyah" "css" "less" "scss" "json" "json5" "json-stringify" "graphql" "markdown" "mdx" "vue" "yaml" "glimmer" "html" "angular" "lwc" ]; - default = ""; - }; - print-width = - mkOption { - type = types.int; - description = "Line length that the printer will wrap on."; - default = 80; - }; - prose-wrap = - mkOption { - description = "When to or if at all hard wrap prose to print width."; - type = types.enum [ "always" "never" "preserve" ]; - default = "preserve"; - }; - plugins = - mkOption { - description = "Add plugins from paths."; - type = types.listOf types.str; - default = [ ]; - }; - quote-props = - mkOption { - description = "Change when properties in objects are quoted."; - type = types.enum [ "as-needed" "consistent" "preserve" ]; - default = "as-needed"; - }; - require-pragma = - mkOption { - description = "Require either '@prettier' or '@format' to be present in the file's first docblock comment."; - type = types.bool; - default = false; - }; - single-attribute-per-line = - mkOption { - description = "Enforce single attribute per line in HTML, Vue andJSX."; - type = types.bool; - default = false; - }; - single-quote = - mkOption { - description = "Number of spaces per indentation-level."; - type = types.bool; - default = false; - }; - tab-width = - mkOption { - description = "Line length that the printer will wrap on."; - type = types.int; - default = 2; - }; - trailing-comma = - mkOption { - description = "Print trailing commas wherever possible in multi-line comma-separated syntactic structures."; - type = types.enum [ "all" "es5" "none" ]; - default = "all"; - }; - use-tabs = - mkOption { - type = types.bool; - description = "Indent with tabs instead of spaces."; - default = false; - }; - vue-indent-script-and-style = - mkOption { - description = "Indent script and style tags in Vue files."; - type = types.bool; - default = false; - }; - with-node-modules = - mkOption { - type = types.bool; - description = "Process files inside 'node_modules' directory."; - default = false; - }; - write = - mkOption { - description = "Edit files in-place."; - type = types.bool; - default = true; - }; - }; - }; - }; - pretty-format-json = mkOption - { - description = "pretty-format-json hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - autofix = - mkOption { - type = types.bool; - description = "Automatically format JSON files."; - default = false; - }; - indent = - mkOption { - type = types.nullOr (types.oneOf [ types.int types.str ]); - description = "Control the indentation (either a number for a number of spaces or a string of whitespace). Defaults to 2 spaces."; - default = null; - }; - no-ensure-ascii = - mkOption { - type = types.bool; - description = "Preserve unicode characters instead of converting to escape sequences."; - default = false; - }; - no-sort-keys = - mkOption { - type = types.bool; - description = "When autofixing, retain the original key ordering (instead of sorting the keys)."; - default = false; - }; - top-keys = - mkOption { - type = types.listOf types.str; - description = "Keys to keep at the top of mappings."; - default = [ ]; - }; - }; - }; - }; - proselint = mkOption { - description = "proselint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - config = - mkOption { - type = types.str; - description = "Multiline-string configuration passed as config file."; - default = ""; - example = '' - { - "checks": { - "typography.diacritical_marks": false - } - } - ''; - }; - configPath = - mkOption { - type = types.str; - description = "Path to the config file."; - default = ""; - }; - flags = - mkOption { - type = types.str; - description = "Flags passed to proselint."; - default = ""; - }; - }; - }; - }; - psalm = mkOption { - description = "psalm hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "Psalm binary path."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.psalm}/bin/psalm" - ''; - }; - }; - }; - }; - pylint = mkOption { - description = "pylint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "Pylint binary path. Should be used to specify Pylint binary from your Python environment."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.pylint}/bin/pylint" - ''; - }; - reports = - mkOption { - type = types.bool; - description = "Whether to display a full report."; - default = false; - }; - score = - mkOption { - type = types.bool; - description = "Whether to activate the evaluation score."; - default = true; - }; - }; - }; - }; - pyright = mkOption { - description = "pyright hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "Pyright binary path. Should be used to specify the pyright executable in an environment containing your typing stubs."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.pyright}/bin/pyright" - ''; - }; - }; - }; - }; - pyupgrade = mkOption { - description = "pyupgrade hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr types.str; - description = "pyupgrade binary path. Should be used to specify the pyupgrade binary from your Python environment."; - default = null; - defaultText = lib.literalExpression '' - "''${tools.pyupgrade}/bin/pyupgrade" - ''; - }; - }; - }; - }; - reuse = mkOption { - description = "reuse hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - flags = mkOption { - type = types.str; - description = "Flags passed to reuse. For available options run 'reuse lint --help'"; - default = ""; - example = "--json"; - }; - }; - }; - }; - revive = mkOption { - description = "revive hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = - mkOption { - type = types.str; - description = "Path to the configuration TOML file."; - # an empty string translates to use default configuration of the - # underlying revive binary - default = ""; - }; - }; - }; - }; - ripsecrets = mkOption { - description = "ripsecrets hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - additionalPatterns = - mkOption { - type = types.listOf types.str; - description = "Additional regex patterns used to find secrets. If there is a matching group in the regex the matched group will be tested for randomness before being reported as a secret."; - default = [ ]; - }; - }; - }; - }; - rome = mkOption { - description = "Deprecated rome hook. Use biome instead."; - visible = false; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binPath = - mkOption { - type = types.nullOr (types.oneOf [ types.str types.path ]); - description = '' - `rome` binary path. - For example, if you want to use the `rome` binary from `node_modules`, use `"./node_modules/.bin/rome"`. - Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. - ''; - default = null; - defaultText = lib.literalExpression '' - "''${tools.rome}/bin/rome - ''; - example = lib.literalExpression '' - "./node_modules/.bin/rome" - ''; - }; - - write = - mkOption { - type = types.bool; - description = "Whether to edit files inplace."; - default = true; - }; - - configPath = mkOption { - type = types.str; - description = "Path to the configuration JSON file"; - # an empty string translates to use default configuration of the - # underlying biome binary (i.e biome.json if exists) - default = ""; - }; - }; - }; - }; - rustfmt = mkOption { - description = '' - Additional rustfmt settings - - Override the `rustfmt` and `cargo` packages by setting `hooks.rustfmt.packageOverrides`. - - ``` - hooks.rustfmt.packageOverrides.cargo = pkgs.cargo; - hooks.rustfmt.packageOverrides.rustfmt = pkgs.rustfmt; - ``` - ''; - type = types.submodule ({ config, ... }: { - imports = [ hookModule ]; - options = { - packageOverrides = { - cargo = mkOption { - type = types.package; - description = "The cargo package to use."; - }; - rustfmt = mkOption { - type = types.package; - description = "The rustfmt package to use."; - }; - }; - settings = - let - nameType = types.strMatching "[][*?!0-9A-Za-z_-]+"; - in - { - all = mkOption { - type = types.bool; - description = "Format all packages, and also their local path-based dependencies"; - default = true; - }; - check = mkOption { - type = types.bool; - description = "Run rustfmt in check mode"; - default = false; - }; - color = mkOption { - type = types.enum [ "auto" "always" "never" ]; - description = "Coloring the output"; - default = "always"; - }; - config = mkOption { - type = types.attrs; - description = "Override configuration values"; - default = { }; - apply = config: - let - config' = lib.mapAttrsToList - (key: value: "${key}=${toString value}") - config; - in - if config != { } - then - (builtins.concatStringsSep "," config') - else - null; - }; - config-path = mkOption { - type = types.nullOr types.str; - description = "Path to rustfmt.toml config file"; - default = null; - }; - emit = mkOption { - type = types.nullOr (types.enum [ "files" "stdout" ]); - description = "What data to emit and how"; - default = null; - }; - files-with-diff = mkOption { - type = types.bool; - description = ""; - default = hooks.rustfmt.settings.message-format == "short"; - }; - manifest-path = mkOption { - type = types.nullOr types.str; - description = "Path to Cargo.toml"; - default = settings.rust.cargoManifestPath; - }; - message-format = mkOption { - type = types.nullOr (types.enum [ "human" "short" ]); - description = "The output format of diagnostic messages"; - default = null; - }; - package = mkOption { - type = types.listOf nameType; - description = "Package(s) to check"; - default = [ ]; - }; - verbose = mkOption { - type = types.bool; - description = "Use verbose output"; - default = false; - }; - }; - }; - config.extraPackages = [ - config.packageOverrides.cargo - config.packageOverrides.rustfmt - ]; - }); - }; - shfmt = mkOption { - description = "shfmt hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - simplify = mkOption { - type = types.bool; - description = "Simplify the code."; - default = true; - }; - }; - }; - }; - statix = mkOption { - description = "statix hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - config = - mkOption { - type = types.nullOr types.str; - description = "Path to statix.toml or its parent directory."; - default = null; - }; - - format = - mkOption { - type = types.enum [ "stderr" "errfmt" "json" ]; - description = "Error Output format."; - default = "errfmt"; - }; - - ignore = - mkOption { - type = types.listOf types.str; - description = "Globs of file patterns to skip."; - default = [ ]; - example = [ "flake.nix" "_*" ]; - }; - - unrestricted = - mkOption { - type = types.bool; - description = "Don't respect .gitignore files."; - default = false; - example = true; - }; - }; - }; - }; - sort-file-contents = mkOption { - description = "sort-file-contents-hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - ignore-case = - mkOption { - type = types.bool; - description = "Fold lower case to upper case characters."; - default = false; - }; - unique = - mkOption { - type = types.bool; - description = "Ensure each line is unique."; - default = false; - }; - }; - }; - }; - treefmt = mkOption { - description = '' - Treefmt hook. - - Include any additional formatters configured by treefmt as `hooks.treefmt.settings.formatters`. - - ``` - hooks.treefmt.settings.formatters = [ - pkgs.nixpkgs-fmt - pkgs.black - ]; - ``` - - Override `treefmt` itself by setting `hooks.treefmt.packageOverrides.treefmt`. - - ``` - hooks.treefmt.packageOverrides.treefmt = pkgs.treefmt; - ``` - ''; - type = types.submodule - ({ config, ... }: - { - imports = [ hookModule ]; - options.packageOverrides = { - treefmt = mkOption { - type = types.package; - description = "The treefmt package to use"; - }; - }; - options.settings = { - fail-on-change = - mkOption { - type = types.bool; - description = "Fail if some files require re-formatting."; - default = true; - }; - no-cache = - mkOption { - type = types.bool; - description = "Ignore the evaluation cache entirely."; - default = true; - }; - formatters = mkOption { - type = types.listOf types.package; - description = "The formatter packages configured by treefmt"; - default = [ ]; - }; - }; - - config.extraPackages = config.settings.formatters; - }); - }; - typos = mkOption { - description = "typos hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - binary = - mkOption { - type = types.bool; - description = "Whether to search binary files."; - default = false; - }; - color = - mkOption { - type = types.enum [ "auto" "always" "never" ]; - description = "When to use generate output."; - default = "auto"; - }; - configuration = - mkOption { - type = types.str; - description = "Multiline-string configuration passed as config file. If set, config set in `typos.settings.configPath` gets ignored."; - default = ""; - example = '' - [files] - ignore-dot = true - - [default] - binary = false - - [type.py] - extend-glob = [] - ''; - }; - - configPath = - mkOption { - type = types.str; - description = "Path to a custom config file."; - default = ""; - example = ".typos.toml"; - }; - - diff = - mkOption { - type = types.bool; - description = "Print a diff of what would change."; - default = false; - }; - - exclude = - mkOption { - type = types.str; - description = "Ignore files and directories matching the glob."; - default = ""; - example = "*.nix"; - }; - - format = - mkOption { - type = types.enum [ "silent" "brief" "long" "json" ]; - description = "Output format to use."; - default = "long"; - }; - - hidden = - mkOption { - type = types.bool; - description = "Search hidden files and directories."; - default = false; - }; - - ignored-words = - mkOption { - type = types.listOf types.str; - description = "Spellings and words to ignore."; - default = [ ]; - example = [ - "MQTT" - "mosquitto" - ]; - }; - - locale = - mkOption { - type = types.enum [ "en" "en-us" "en-gb" "en-ca" "en-au" ]; - description = "Which language to use for spell checking."; - default = "en"; - }; - - no-check-filenames = - mkOption { - type = types.bool; - description = "Skip verifying spelling in file names."; - default = false; - }; - - no-check-files = - mkOption { - type = types.bool; - description = "Skip verifying spelling in files."; - default = false; - }; - - no-unicode = - mkOption { - type = types.bool; - description = "Only allow ASCII characters in identifiers."; - default = false; - }; - - quiet = - mkOption { - type = types.bool; - description = "Less output per occurrence."; - default = false; - }; - - verbose = - mkOption { - type = types.bool; - description = "More output per occurrence."; - default = false; - }; - - write = - mkOption { - type = types.bool; - description = "Fix spelling in files by writing them. Cannot be used with `typos.settings.diff`."; - default = false; - }; - }; - }; - }; - vale = mkOption { - description = "vale hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configuration = - mkOption { - type = types.str; - description = "Multiline-string configuration passed as config file."; - default = ""; - example = '' - MinAlertLevel = suggestion - [*] - BasedOnStyles = Vale - ''; - }; - configPath = - mkOption { - type = types.str; - description = "Path to the config file."; - default = ""; - }; - flags = - mkOption { - type = types.str; - description = "Flags passed to vale."; - default = ""; - }; - }; - }; - }; - yamlfmt = mkOption { - description = "yamlfmt hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - configPath = - mkOption { - type = types.str; - description = "Path to a custom configuration file."; - # An empty string translates to yamlfmt looking for a configuration file in the - # following locations (by order of preference): - # a file named .yamlfmt, yamlfmt.yml, yamlfmt.yaml, .yamlfmt.yaml or .yamlfmt.yml in the current working directory - # See details [here](https://github.com/google/yamlfmt/blob/main/docs/config-file.md#config-file-discovery) - default = ""; - example = ".yamlfmt"; - }; - lint-only = - mkOption { - type = types.bool; - description = "Only lint the files, do not format them in place."; - default = true; - }; - }; - }; - }; - yamllint = mkOption { - description = "yamllint hook"; - type = types.submodule { - imports = [ hookModule ]; - options.settings = { - # `list-files` is not useful for a pre-commit hook as it always exits with exit code 0 - # `no-warnings` is not useful for a pre-commit hook as it exits with exit code 2 and the hook - # therefore fails when warnings level problems are detected but there is no output - configuration = mkOption { - type = types.str; - description = "Multiline-string configuration passed as config file. If set, configuration file set in `yamllint.settings.configPath` gets ignored."; - default = ""; - example = '' - --- - - extends: relaxed - - rules: - indentation: enable - ''; - }; - configData = mkOption { - type = types.str; - description = "Serialized YAML object describing the configuration."; - default = ""; - example = "{extends: relaxed, rules: {line-length: {max: 120}}}"; - }; - configPath = mkOption { - type = types.str; - description = "Path to a custom configuration file."; - # An empty string translates to yamllint looking for a configuration file in the - # following locations (by order of preference): - # a file named .yamllint, .yamllint.yaml or .yamllint.yml in the current working directory - # a filename referenced by $YAMLLINT_CONFIG_FILE, if set - # a file named $XDG_CONFIG_HOME/yamllint/config or ~/.config/yamllint/config, if present - default = ""; - }; - format = mkOption { - type = types.enum [ "parsable" "standard" "colored" "github" "auto" ]; - description = "Format for parsing output."; - default = "auto"; - }; - preset = mkOption { - type = types.enum [ "default" "relaxed" ]; - description = "The configuration preset to use."; - default = "default"; - }; - strict = mkOption { - type = types.bool; - description = "Return non-zero exit code on warnings as well as errors."; - default = true; - }; - }; - }; - }; + actionlint = mkHook ./hooks/actionlint.nix "Static checker for GitHub Actions workflow files"; + alejandra = mkHook ./hooks/alejandra.nix "The Uncompromising Nix Code Formatter"; + annex = mkHook ./hooks/annex.nix "Runs the git-annex hook for large file support"; + ansible-lint = mkHook ./hooks/ansible-lint.nix "Ansible linter"; + autoflake = mkHook ./hooks/autoflake.nix "Remove unused imports and variables from Python code"; + bats = mkHook ./hooks/bats.nix "Run bash unit tests"; + beautysh = mkHook ./hooks/beautysh.nix "Format shell files"; + biome = mkHook ./hooks/biome.nix "A toolchain for web projects, aimed to provide functionalities to maintain them"; + black = mkHook ./hooks/black.nix "The uncompromising Python code formatter"; + cabal-fmt = mkHook ./hooks/cabal-fmt.nix "Format Cabal files"; + cabal-gild = mkHook ./hooks/cabal-gild.nix "Format Cabal files"; + cabal2nix = mkHook ./hooks/cabal2nix.nix "Run `cabal2nix` on all `*.cabal` files to generate corresponding `.nix` files"; + cargo-check = mkHook ./hooks/cargo-check.nix "Check the cargo package for errors"; + check-added-large-files = mkHook ./hooks/check-added-large-files.nix "Prevent very large files to be committed (e.g. binaries)."; + check-builtin-literals = mkHook ./hooks/check-builtin-literals.nix "Require literal syntax when initializing empty or zero builtin types in Python."; + check-case-conflicts = mkHook ./hooks/check-case-conflicts.nix "Check for files that would conflict in case-insensitive filesystems."; + check-docstring-first = mkHook ./hooks/check-docstring-first.nix "Check that all docstrings appear above the code."; + check-executables-have-shebangs = mkHook ./hooks/check-executables-have-shebangs.nix "Ensure that all non-binary executables have shebangs."; + check-json = mkHook ./hooks/check-json.nix "Check syntax of JSON files."; + check-merge-conflicts = mkHook ./hooks/check-merge-conflicts.nix "Check for files that contain merge conflict strings."; + check-python = mkHook ./hooks/check-python.nix "Check syntax of Python file by parsing Python abstract syntax tree."; + check-shebang-scripts-are-executable = mkHook ./hooks/check-shebang-scripts-are-executable.nix "Ensure that all (non-binary) files with a shebang are executable."; + check-symlinks = mkHook ./hooks/check-symlinks.nix "Find broken symlinks."; + check-toml = mkHook ./hooks/check-toml.nix "Check syntax of TOML files."; + check-vcs-permalinks = mkHook ./hooks/check-vcs-permalinks.nix "Ensure that links to VCS websites are permalinks."; + check-xml = mkHook ./hooks/check-xml.nix "Check syntax of XML files."; + check-yaml = mkHook ./hooks/check-yaml.nix "Check syntax of YAML files."; + checkmake = mkHook ./hooks/checkmake.nix "Experimental linter/analyzer for Makefiles"; + chktex = mkHook ./hooks/chktex.nix "LaTeX semantic checker"; + circleci = mkHook ./hooks/circleci.nix "Validate CircleCI config files."; + clang-format = mkHook ./hooks/clang-format.nix "Format your code using `clang-format`."; + clang-tidy = mkHook ./hooks/clang-tidy.nix "Static analyzer for C++ code."; + clippy = mkHook ./hooks/clippy.nix "Lint Rust code."; + cljfmt = mkHook ./hooks/cljfmt.nix "A tool for formatting Clojure code."; + cmake-format = mkHook ./hooks/cmake-format.nix "A tool for formatting CMake-files."; + commitizen = mkHook ./hooks/commitizen.nix "Check whether the current commit message follows committing rules."; + conform = mkHook ./hooks/conform.nix "Policy enforcement for commits."; + convco = mkHook ./hooks/convco.nix "A tool for checking and creating conventional commits"; + credo = mkHook ./hooks/credo.nix "Runs a static code analysis using Credo"; + crystal = mkHook ./hooks/crystal.nix "A tool that automatically formats Crystal source code"; + cspell = mkHook ./hooks/cspell.nix "A Spell Checker for Code"; + dart-analyze = mkHook ./hooks/dart-analyze.nix "Dart analyzer"; + dart-format = mkHook ./hooks/dart-format.nix "Dart formatter"; + deadnix = mkHook ./hooks/deadnix.nix "Scan Nix files for dead code (unused variable bindings)."; + denofmt = mkHook ./hooks/denofmt.nix "Auto-format JavaScript, TypeScript, Markdown, and JSON files."; + denolint = mkHook ./hooks/denolint.nix "Lint JavaScript/TypeScript source code."; + detect-aws-credentials = mkHook ./hooks/detect-aws-credentials.nix "Detect AWS credentials from the AWS cli credentials file."; + detect-private-keys = mkHook ./hooks/detect-private-keys.nix "Detect the presence of private keys."; + dhall-format = mkHook ./hooks/dhall-format.nix "Dhall code formatter."; + dialyzer = mkHook ./hooks/dialyzer.nix "Runs a static code analysis using Dialyzer"; + dune-fmt = mkHook ./hooks/dune-fmt.nix "Runs dune-build-opam-files to ensure OCaml and Dune are in sync"; + dune-opam-sync = mkHook ./hooks/dune-opam-sync.nix "Check that Dune-generated OPAM files are in sync."; + eclint = mkHook ./hooks/eclint.nix "EditorConfig linter written in Go."; + editorconfig-checker = mkHook ./hooks/editorconfig-checker.nix "Verify that the files are in harmony with the `.editorconfig`."; + elm-format = mkHook ./hooks/elm-format.nix "Format Elm files."; + elm-review = mkHook ./hooks/elm-review.nix "Analyzes Elm projects, to help find mistakes before your users find them."; + elm-test = mkHook ./hooks/elm-test.nix "Run unit tests and fuzz tests for Elm code."; + end-of-file-fixer = mkHook ./hooks/end-of-file-fixer.nix "Ensures that a file is either empty, or ends with a single newline."; + eslint = mkHook ./hooks/eslint.nix "Find and fix problems in your JavaScript code."; + fix-byte-order-marker = mkHook ./hooks/fix-byte-order-marker.nix "Remove UTF-8 byte order marker."; + fix-encoding-pragma = mkHook ./hooks/fix-encoding-pragma.nix "Adds # -*- coding: utf-8 -*- to the top of Python files."; + flake8 = mkHook ./hooks/flake8.nix "Check the style and quality of Python files."; + flake-checker = mkHook ./hooks/flake-checker.nix "Run health checks on your flake-powered Nix projects."; + flynt = mkHook ./hooks/flynt.nix "CLI tool to convert a python project's %-formatted strings to f-strings."; + forbid-new-submodules = mkHook ./hooks/forbid-new-submodules.nix "Prevent addition of new Git submodules."; + fourmolu = mkHook ./hooks/fourmolu.nix "Haskell code prettifier."; + fprettify = mkHook ./hooks/fprettify.nix "Auto-formatter for modern Fortran code."; + gitlint = mkHook ./hooks/gitlint.nix "Linting for your git commit messages"; + gofmt = mkHook ./hooks/gofmt.nix "A tool that automatically formats Go source code"; + golangci-lint = mkHook ./hooks/golangci-lint.nix "Fast linters runner for Go."; + golines = mkHook ./hooks/golines.nix "A golang formatter that fixes long lines"; + gotest = mkHook ./hooks/gotest.nix "Run go tests"; + govet = mkHook ./hooks/govet.nix "Checks correctness of Go programs."; + gptcommit = mkHook ./hooks/gptcommit.nix "Generate a commit message using GPT3."; + hadolint = mkHook ./hooks/hadolint.nix "Dockerfile linter, validate inline bash."; + headache = mkHook ./hooks/headache.nix "Lightweight tool for managing headers in source code files."; + hlint = mkHook ./hooks/hlint.nix "Haskell linter"; + hpack = mkHook ./hooks/hpack.nix "A modern format for Haskell packages"; + html-tidy = mkHook ./hooks/html-tidy.nix "HTML linter."; + hunspell = mkHook ./hooks/hunspell.nix "Spell checker and morphological analyzer."; + isort = mkHook ./hooks/isort.nix "A Python utility/library to sort imports."; + juliaformatter = mkHook ./hooks/juliaformatter.nix "Run JuliaFormatter.jl against Julia source files"; + lacheck = mkHook ./hooks/lacheck.nix "LaTeX checker"; + latexindent = mkHook ./hooks/latexindent.nix "Perl script to add indentation to LaTeX files"; + lua-ls = mkHook ./hooks/lua-ls.nix "Lua language server"; + luacheck = mkHook ./hooks/luacheck.nix "A tool for linting and static analysis of Lua code."; + lychee = mkHook ./hooks/lychee.nix "A fast, async, stream-based link checker that finds broken hyperlinks and mail addresses inside Markdown, HTML, reStructuredText, or any other text file or website."; + markdownlint = mkHook ./hooks/markdownlint.nix "Style checker and linter for markdown files."; + mdformat = mkHook ./hooks/mdformat.nix "CommonMark compliant Markdown formatter"; + mdl = mkHook ./hooks/mdl.nix "A tool to check markdown files and flag style issues."; + mdsh = mkHook ./hooks/mdsh.nix "Markdown shell pre-processor."; + mix-format = mkHook ./hooks/mix-format.nix "Runs the built-in Elixir syntax formatter"; + mix-test = mkHook ./hooks/mix-test.nix "Runs the built-in Elixir test framework"; + mixed-line-endings = mkHook ./hooks/mixed-line-endings.nix "Resolve mixed line endings."; + mkdocs-linkcheck = mkHook ./hooks/mkdocs-linkcheck.nix "Validate links associated with markdown-based, statically generated websites"; + mypy = mkHook ./hooks/mypy.nix "Static type checker for Python"; + name-tests-test = mkHook ./hooks/name-tests-test.nix "Verify that Python test files are named correctly."; + nil = mkHook ./hooks/nil.nix "Incremental analysis assistant for writing in Nix."; + nixfmt = (mkHook ./hooks/nixfmt.nix "Deprecated Nix code prettifier. Use nixfmt-classic or nixfmt-rfc-style instead.") // { visible = false; }; + nixfmt-classic = mkHook ./hooks/nixfmt-classic.nix "Nix code prettifier (classic)"; + nixfmt-rfc-style = mkHook ./hooks/nixfmt-rfc-style.nix "Nix code prettifier (RFC 166 style)."; + nixpkgs-fmt = mkHook ./hooks/nixpkgs-fmt.nix "Nix code prettifier."; + no-commit-to-branch = mkHook ./hooks/no-commit-to-branch.nix "Disallow committing to certain branch/branches."; + ocp-indent = mkHook ./hooks/ocp-indent.nix "A tool to indent OCaml code."; + opam-lint = mkHook ./hooks/opam-lint.nix "OCaml package manager configuration checker"; + openapi-spec-validator = mkHook ./hooks/openapi-spec-validator.nix "Validate OpenAPI specifications."; + ormolu = mkHook ./hooks/ormolu.nix "Haskell source code formatter"; + php-cs-fixer = mkHook ./hooks/php-cs-fixer.nix "Lint PHP files."; + phpcbf = mkHook ./hooks/phpcbf.nix "Lint PHP files."; + phpcs = mkHook ./hooks/phpcs.nix "Lint PHP files."; + phpstan = mkHook ./hooks/phpstan.nix "Static analysis of PHP files."; + poetry-check = mkHook ./hooks/poetry-check.nix "Check the validity of the pyproject.toml file."; + poetry-lock = mkHook ./hooks/poetry-lock.nix "Update the poetry.lock file."; + pre-commit-hook-ensure-sops = mkHook ./hooks/pre-commit-hook-ensure-sops.nix "Ensure that sops files are encrypted."; + prettier = mkHook ./hooks/prettier.nix "Opinionated multi-language code formatter."; + pretty-format-json = mkHook ./hooks/pretty-format-json.nix "Pretty format JSON"; + proselint = mkHook ./hooks/proselint.nix "A linter for prose"; + psalm = mkHook ./hooks/psalm.nix "PHP static analysis tool"; + purs-tidy = mkHook ./hooks/purs-tidy.nix "Format purescript files."; + purty = mkHook ./hooks/purty.nix "Format purescript files"; + pylint = mkHook ./hooks/pylint.nix "Lint Python files."; + pyright = mkHook ./hooks/pyright.nix "Static type checker for Python"; + python-debug-statements = mkHook ./hooks/python-debug-statements.nix "Check for debugger imports and py37+ `breakpoint()` calls in python source."; + pyupgrade = mkHook ./hooks/pyupgrade.nix "Upgrade syntax for newer versions of Python."; + reuse = mkHook ./hooks/reuse.nix "reuse is a tool for compliance with the REUSE recommendations."; + revive = mkHook ./hooks/revive.nix "A linter for Go source code."; + ripsecrets = mkHook ./hooks/ripsecrets.nix "Prevent committing secret keys into your source code"; + rome = (mkHook ./hooks/rome.nix "Deprecated rome hook. Use biome instead.") // { visible = false; }; + ruff = mkHook ./hooks/ruff.nix "An extremely fast Python linter, written in Rust."; + ruff-format = mkHook ./hooks/ruff-format.nix "An extremely fast Python code formatter, written in Rust."; + rustfmt = mkHook ./hooks/rustfmt.nix '' + Rust code formatter + + Override the `rustfmt` and `cargo` packages by setting `hooks.rustfmt.packageOverrides`. + + ``` + hooks.rustfmt.packageOverrides.cargo = pkgs.cargo; + hooks.rustfmt.packageOverrides.rustfmt = pkgs.rustfmt; + ``` + ''; + selene = mkHook ./hooks/selene.nix "A blazing-fast modern Lua linter written in Rust."; + shellcheck = mkHook ./hooks/shellcheck.nix "Format shell files."; + shfmt = mkHook ./hooks/shfmt.nix "Format shell files."; + single-quoted-strings = mkHook ./hooks/single-quoted-strings.nix "Replace double quoted strings with single quoted strings."; + sort-file-contents = mkHook ./hooks/sort-file-contents.nix "Sort the lines in specified files (defaults to alphabetical)."; + sort-requirements-txt = mkHook ./hooks/sort-requirements-txt.nix "Sort requirements in requirements.txt and constraints.txt files."; + sort-simple-yaml = mkHook ./hooks/sort-simple-yaml.nix "Sort simple YAML files which consist only of top-level keys, preserving comments and blocks."; + staticcheck = mkHook ./hooks/staticcheck.nix "State of the art linter for the Go programming language."; + statix = mkHook ./hooks/statix.nix "Lints and suggestions for the Nix programming language"; + stylish-haskell = mkHook ./hooks/stylish-haskell.nix "A simple Haskell code prettifier."; + stylua = mkHook ./hooks/stylua.nix "An opinionated code formatter for Lua."; + tagref = mkHook ./hooks/tagref.nix "Have tagref check all references and tags."; + taplo = mkHook ./hooks/taplo.nix "Format TOML files with taplo fmt"; + terraform-format = mkHook ./hooks/terraform-format.nix "Format Terraform (`.tf`) files."; + terraform-validate = mkHook ./hooks/terraform-validate.nix "Validates terraform configuration files (`.tf`)."; + tflint = mkHook ./hooks/tflint.nix "A pluggable Terraform linter."; + topiary = mkHook ./hooks/topiary.nix "A universal formatter engine within the Tree-sitter ecosystem, with support for many languages."; + treefmt = mkHook ./hooks/treefmt.nix '' + One CLI to format the code tree + + Include any additional formatters configured by treefmt as `hooks.treefmt.settings.formatters`. + + ``` + hooks.treefmt.settings.formatters = [ + pkgs.nixpkgs-fmt + pkgs.black + ]; + ``` + + Override `treefmt` itself by setting `hooks.treefmt.packageOverrides.treefmt`. + + ``` + hooks.treefmt.packageOverrides.treefmt = pkgs.treefmt; + ``` + ''; + trim-trailing-whitespace = mkHook ./hooks/trim-trailing-whitespace.nix "Trim trailing whitespace."; + trufflehog = mkHook ./hooks/trufflehog.nix "Secrets scanner."; + typos = mkHook ./hooks/typos.nix "Source code spell checker"; + typstfmt = mkHook ./hooks/typstfmt.nix "Format Typst files."; + typstyle = mkHook ./hooks/typstyle.nix "Beautiful and reliable typst code formatter."; + vale = mkHook ./hooks/vale.nix "A markup-aware linter for prose built with speed and extensibility in mind."; + yamlfmt = mkHook ./hooks/yamlfmt.nix "Formatter for YAML files."; + yamllint = mkHook ./hooks/yamllint.nix "Linter for YAML files."; + zprint = mkHook ./hooks/zprint.nix "Beautifully format Clojure and Clojurescript source code and s-expressions."; }; config.warnings = @@ -2033,2075 +292,4 @@ in The new RFC 166-style nixfmt is available as `hooks.nixfmt-rfc-style`. ''; - - # PLEASE keep this sorted alphabetically. - config.hooks = mapAttrs (_: mapAttrs (_: mkDefault)) - rec { - actionlint = - { - name = "actionlint"; - description = "Static checker for GitHub Actions workflow files"; - files = "^.github/workflows/"; - types = [ "yaml" ]; - package = tools.actionlint; - entry = "${hooks.actionlint.package}/bin/actionlint"; - }; - alejandra = - { - name = "alejandra"; - description = "The Uncompromising Nix Code Formatter"; - package = tools.alejandra; - entry = - let - cmdArgs = - mkCmdArgs (with hooks.alejandra.settings; [ - [ check "--check" ] - [ (exclude != [ ]) "--exclude ${lib.strings.concatStringsSep " --exclude " (map lib.escapeShellArg (lib.unique exclude))}" ] - [ (verbosity == "quiet") "-q" ] - [ (verbosity == "silent") "-qq" ] - [ (threads != null) "--threads ${toString threads}" ] - ]); - in - "${hooks.alejandra.package}/bin/alejandra ${cmdArgs}"; - files = "\\.nix$"; - }; - annex = - { - name = "annex"; - description = "Runs the git-annex hook for large file support"; - package = tools.git-annex; - entry = "${hooks.annex.package}/bin/git-annex pre-commit"; - }; - ansible-lint = - { - name = "ansible-lint"; - description = "Ansible linter"; - package = tools.ansible-lint; - entry = - let - cmdArgs = - mkCmdArgs [ - [ (hooks.ansible-lint.settings.configPath != "") "-c ${hooks.ansible-lint.settings.configPath}" ] - ]; - in - "${hooks.ansible-lint.package}/bin/ansible-lint ${cmdArgs}"; - files = if hooks.ansible-lint.settings.subdir != "" then "${hooks.ansible-lint.settings.subdir}/" else ""; - }; - autoflake = - { - name = "autoflake"; - description = "Remove unused imports and variables from Python code"; - - package = tools.autoflake; - entry = - let - binPath = migrateBinPathToPackage hooks.autoflake "/bin/autoflake"; - in - "${binPath} ${hooks.autoflake.settings.flags}"; - types = [ "python" ]; - }; - biome = - { - name = "biome"; - description = "A toolchain for web projects, aimed to provide functionalities to maintain them"; - types_or = [ "javascript" "jsx" "ts" "tsx" "json" ]; - - package = tools.biome; - entry = - let - binPath = migrateBinPathToPackage hooks.biome "/bin/biome"; - cmdArgs = - mkCmdArgs [ - [ (hooks.biome.settings.write) "--write" ] - [ (hooks.biome.settings.configPath != "") "--config-path ${hooks.biome.settings.configPath}" ] - ]; - in - "${binPath} check ${cmdArgs}"; - }; - bats = - { - name = "bats"; - description = "Run bash unit tests"; - types = [ "shell" ]; - types_or = [ "bats" "bash" ]; - package = tools.bats; - entry = "${hooks.bats.package}/bin/bats -p"; - }; - beautysh = - { - name = "beautysh"; - description = "Format shell files"; - types = [ "shell" ]; - package = tools.beautysh; - entry = "${hooks.beautysh.package}/bin/beautysh"; - }; - black = - { - name = "black"; - description = "The uncompromising Python code formatter"; - package = tools.black; - entry = "${hooks.black.package}/bin/black ${hooks.black.settings.flags}"; - types = [ "file" "python" ]; - }; - cabal-fmt = - { - name = "cabal-fmt"; - description = "Format Cabal files"; - package = tools.cabal-fmt; - entry = "${hooks.cabal-fmt.package}/bin/cabal-fmt --inplace"; - files = "\\.cabal$"; - }; - cabal-gild = - { - name = "cabal-gild"; - description = "Format Cabal files"; - package = tools.cabal-gild; - entry = - let - script = pkgs.writeShellScript "precommit-cabal-gild" '' - for file in "$@"; do - ${hooks.cabal-gild.package}/bin/cabal-gild --io="$file" - done - ''; - in - builtins.toString script; - files = "\\.cabal$"; - }; - cabal2nix = - { - name = "cabal2nix"; - description = "Run `cabal2nix` on all `*.cabal` files to generate corresponding `.nix` files"; - package = tools.cabal2nix-dir; - entry = "${hooks.cabal2nix.package}/bin/cabal2nix-dir --outputFileName=${hooks.cabal2nix.settings.outputFilename}"; - files = "\\.cabal$"; - after = [ "hpack" ]; - }; - cargo-check = - { - name = "cargo-check"; - description = "Check the cargo package for errors"; - package = tools.cargo; - entry = "${hooks.cargo-check.package}/bin/cargo check ${cargoManifestPathArg}"; - files = "\\.rs$"; - pass_filenames = false; - }; - checkmake = { - name = "checkmake"; - description = "Experimental linter/analyzer for Makefiles"; - types = [ "makefile" ]; - package = tools.checkmake; - entry = - ## NOTE: `checkmake` 0.2.2 landed in nixpkgs on 12 April 2023. Once - ## this gets into a NixOS release, the following code will be useless. - lib.throwIf - (hooks.checkmake.package == null) - "The version of nixpkgs used by git-hooks.nix must have `checkmake` in version at least 0.2.2 for it to work on non-Linux systems." - "${hooks.checkmake.package}/bin/checkmake"; - }; - check-added-large-files = - { - name = "check-added-large-files"; - description = "Prevent very large files to be committed (e.g. binaries)."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-added-large-files.package}/bin/check-added-large-files"; - stages = [ "pre-commit" "pre-push" "manual" ]; - }; - check-builtin-literals = - { - name = "check-builtin-literals"; - description = "Require literal syntax when initializing empty or zero builtin types in Python."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-builtin-literals.package}/bin/check-builtin-literals"; - types = [ "python" ]; - }; - check-case-conflicts = - { - name = "check-case-conflicts"; - description = "Check for files that would conflict in case-insensitive filesystems."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-case-conflicts.package}/bin/check-case-conflict"; - types = [ "file" ]; - }; - check-docstring-first = - { - name = "check-docstring-above"; - description = "Check that all docstrings appear above the code."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-docstring-first.package}/bin/check-docstring-first"; - types = [ "python" ]; - }; - check-executables-have-shebangs = - { - name = "check-executables-have-shebangs"; - description = "Ensure that all non-binary executables have shebangs."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-executables-have-shebangs.package}/bin/check-executables-have-shebangs"; - types = [ "text" "executable" ]; - stages = [ "pre-commit" "pre-push" "manual" ]; - }; - check-json = - { - name = "check-json"; - description = "Check syntax of JSON files."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-json.package}/bin/check-json"; - types = [ "json" ]; - }; - check-merge-conflicts = - { - name = "check-merge-conflicts"; - description = "Check for files that contain merge conflict strings."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-merge-conflicts.package}/bin/check-merge-conflict"; - types = [ "text" ]; - }; - check-python = - { - name = "check-python"; - description = "Check syntax of Python file by parsing Python abstract syntax tree."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-python.package}/bin/check-ast"; - types = [ "python" ]; - }; - check-shebang-scripts-are-executable = - { - name = "check-shebang-scripts-are-executable"; - description = "Ensure that all (non-binary) files with a shebang are executable."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-shebang-scripts-are-executable.package}/bin/check-shebang-scripts-are-executable"; - types = [ "text" ]; - stages = [ "pre-commit" "pre-push" "manual" ]; - }; - check-symlinks = - { - name = "check-symlinks"; - description = "Find broken symlinks."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-symlinks.package}/bin/check-symlinks"; - types = [ "symlink" ]; - }; - check-toml = - { - name = "check-toml"; - description = "Check syntax of TOML files."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-toml.package}/bin/check-toml"; - types = [ "toml" ]; - }; - check-vcs-permalinks = - { - name = "check-vcs-permalinks"; - description = "Ensure that links to VCS websites are permalinks."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-vcs-permalinks.package}/bin/check-vcs-permalinks"; - types = [ "text" ]; - }; - check-xml = - { - name = "check-xml"; - description = "Check syntax of XML files."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-xml.package}/bin/check-xml"; - types = [ "xml" ]; - }; - check-yaml = - { - name = "check-yaml"; - description = "Check syntax of YAML files."; - package = tools.pre-commit-hooks; - entry = "${hooks.check-yaml.package}/bin/check-yaml --multi"; - types = [ "yaml" ]; - }; - chktex = - { - name = "chktex"; - description = "LaTeX semantic checker"; - types = [ "file" "tex" ]; - package = tools.chktex; - entry = "${hooks.chktex.package}/bin/chktex"; - }; - circleci = - { - name = "circleci"; - description = "Validate CircleCI config files."; - package = tools.circleci-cli; - entry = builtins.toString (pkgs.writeShellScript "precommit-circleci" '' - set -e - failed=false - for file in "$@"; do - if ! ${hooks.circleci.package}/bin/circleci config validate "$file" 2>&1 - then - echo "Config file at $file is invalid, check the errors above." - failed=true - fi - done - if [[ $failed == "true" ]]; then - exit 1 - fi - ''); - files = "^.circleci/"; - types = [ "yaml" ]; - }; - clang-format = - { - name = "clang-format"; - description = "Format your code using `clang-format`."; - package = tools.clang-tools; - entry = "${hooks.clang-format.package}/bin/clang-format -style=file -i"; - # Source: - # https://github.com/pre-commit/mirrors-clang-format/blob/46516e8f532c8f2d55e801c34a740ebb8036365c/.pre-commit-hooks.yaml - types_or = [ - "c" - "c++" - "c#" - "cuda" - "java" - "javascript" - "json" - "objective-c" - "proto" - ]; - }; - clang-tidy = { - name = "clang-tidy"; - description = "Static analyzer for C++ code."; - package = tools.clang-tools; - entry = "${hooks.clang-tidy.package}/bin/clang-tidy --fix"; - types_or = [ "c" "c++" "c#" "objective-c" ]; - }; - clippy = - let - inherit (hooks.clippy) packageOverrides; - wrapper = pkgs.symlinkJoin { - name = "clippy-wrapped"; - paths = [ packageOverrides.clippy ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/cargo-clippy \ - --prefix PATH : ${lib.makeBinPath [ packageOverrides.cargo ]} - ''; - }; - in - { - name = "clippy"; - description = "Lint Rust code."; - package = wrapper; - packageOverrides = { cargo = tools.cargo; clippy = tools.clippy; }; - entry = "${hooks.clippy.package}/bin/cargo-clippy clippy ${cargoManifestPathArg} ${lib.optionalString hooks.clippy.settings.offline "--offline"} ${lib.optionalString hooks.clippy.settings.allFeatures "--all-features"} ${hooks.clippy.settings.extraArgs} -- ${lib.optionalString hooks.clippy.settings.denyWarnings "-D warnings"}"; - files = "\\.rs$"; - pass_filenames = false; - }; - cljfmt = - { - name = "cljfmt"; - description = "A tool for formatting Clojure code."; - package = tools.cljfmt; - entry = "${hooks.cljfmt.package}/bin/cljfmt fix"; - types_or = [ "clojure" "clojurescript" "edn" ]; - }; - cmake-format = - { - name = "cmake-format"; - description = "A tool for formatting CMake-files."; - package = tools.cmake-format; - entry = - let - maybeConfigPath = - if hooks.cmake-format.settings.configPath == "" - # Searches automatically for the config path. - then "" - else "-C ${hooks.cmake-format.settings.configPath}"; - in - "${hooks.cmake-format.package}/bin/cmake-format --check ${maybeConfigPath}"; - files = "\\.cmake$|CMakeLists.txt"; - }; - commitizen = - { - name = "commitizen check"; - description = '' - Check whether the current commit message follows committing rules. - ''; - package = tools.commitizen; - entry = "${hooks.commitizen.package}/bin/cz check --allow-abort --commit-msg-file"; - stages = [ "commit-msg" ]; - }; - conform = { - name = "conform enforce"; - description = "Policy enforcement for commits."; - package = tools.conform; - entry = "${hooks.conform.package}/bin/conform enforce --commit-msg-file"; - stages = [ "commit-msg" ]; - }; - convco = { - name = "convco"; - package = tools.convco; - entry = - let - convco = hooks.convco.package; - script = pkgs.writeShellScript "precommit-convco" '' - cat $1 | ${convco}/bin/convco check --from-stdin - ''; - # need version >= 0.4.0 for the --from-stdin flag - toolVersionCheck = lib.versionAtLeast convco.version "0.4.0"; - in - lib.throwIf (convco == null || !toolVersionCheck) "The version of Nixpkgs used by git-hooks.nix does not have the `convco` package (>=0.4.0). Please use a more recent version of Nixpkgs." - builtins.toString - script; - stages = [ "commit-msg" ]; - }; - credo = { - name = "credo"; - description = "Runs a static code analysis using Credo"; - package = tools.elixir; - entry = - let strict = if hooks.credo.settings.strict then "--strict" else ""; - in "${hooks.credo.package}/bin/mix credo ${strict}"; - files = "\\.exs?$"; - }; - crystal = { - name = "crystal"; - description = "A tool that automatically formats Crystal source code"; - package = tools.crystal; - entry = "${hooks.crystal.package}/bin/crystal tool format"; - files = "\\.cr$"; - }; - cspell = - { - name = "cspell"; - description = "A Spell Checker for Code"; - package = tools.cspell; - entry = "${hooks.cspell.package}/bin/cspell"; - }; - dart-analyze = { - name = "dart analyze"; - description = "Dart analyzer"; - package = tools.dart; - entry = "${hooks.dart-analyze.package}/bin/dart analyze"; - types = [ "dart" ]; - }; - dart-format = { - name = "dart format"; - description = "Dart formatter"; - package = tools.dart; - entry = "${hooks.dart-format.package}/bin/dart format"; - types = [ "dart" ]; - }; - deadnix = - { - name = "deadnix"; - description = "Scan Nix files for dead code (unused variable bindings)."; - package = tools.deadnix; - entry = - let - cmdArgs = - mkCmdArgs (with hooks.deadnix.settings; [ - [ noLambdaArg "--no-lambda-arg" ] - [ noLambdaPatternNames "--no-lambda-pattern-names" ] - [ noUnderscore "--no-underscore" ] - [ quiet "--quiet" ] - [ hidden "--hidden" ] - [ edit "--edit" ] - [ (exclude != [ ]) "--exclude ${lib.escapeShellArgs exclude}" ] - ]); - in - "${hooks.deadnix.package}/bin/deadnix ${cmdArgs} --fail"; - files = "\\.nix$"; - }; - denofmt = - { - name = "denofmt"; - description = "Auto-format JavaScript, TypeScript, Markdown, and JSON files."; - types_or = [ "javascript" "jsx" "ts" "tsx" "markdown" "json" ]; - package = tools.deno; - entry = - let - cmdArgs = - mkCmdArgs [ - [ (!hooks.denofmt.settings.write) "--check" ] - [ (hooks.denofmt.settings.configPath != "") "-c ${hooks.denofmt.settings.configPath}" ] - ]; - in - "${hooks.denofmt.package}/bin/deno fmt ${cmdArgs}"; - }; - denolint = - { - name = "denolint"; - description = "Lint JavaScript/TypeScript source code."; - types_or = [ "javascript" "jsx" "ts" "tsx" ]; - package = tools.deno; - entry = - let - cmdArgs = - mkCmdArgs [ - [ (hooks.denolint.settings.format == "compact") "--compact" ] - [ (hooks.denolint.settings.format == "json") "--json" ] - [ (hooks.denolint.settings.configPath != "") "-c ${hooks.denolint.settings.configPath}" ] - ]; - in - "${hooks.denolint.package}/bin/deno lint ${cmdArgs}"; - }; - detect-aws-credentials = - { - name = "detect-aws-credentials"; - description = "Detect AWS credentials from the AWS cli credentials file."; - package = tools.pre-commit-hooks; - entry = "${hooks.detect-aws-credentials.package}/bin/detect-aws-credentials --allow-missing-credentials"; - types = [ "text" ]; - }; - detect-private-keys = - { - name = "detect-private-keys"; - description = "Detect the presence of private keys."; - package = tools.pre-commit-hooks; - entry = "${hooks.detect-private-keys.package}/bin/detect-private-key"; - types = [ "text" ]; - }; - dhall-format = { - name = "dhall-format"; - description = "Dhall code formatter."; - package = tools.dhall; - entry = "${hooks.dhall-format.package}/bin/dhall format"; - files = "\\.dhall$"; - }; - dialyzer = { - name = "dialyzer"; - description = "Runs a static code analysis using Dialyzer"; - package = tools.elixir; - entry = "${hooks.dialyzer.package}/bin/mix dialyzer"; - files = "\\.exs?$"; - }; - dune-fmt = { - name = "dune-fmt"; - description = "Runs Dune's formatters on the code tree."; - package = tools.dune-fmt; - entry = - let - auto-promote = if hooks.dune-fmt.settings.auto-promote then "--auto-promote" else ""; - run-dune-fmt = pkgs.writeShellApplication { - name = "run-dune-fmt"; - runtimeInputs = hooks.dune-fmt.settings.extraRuntimeInputs; - text = "${hooks.dune-fmt.package}/bin/dune-fmt ${auto-promote}"; - }; - in - "${run-dune-fmt}/bin/run-dune-fmt"; - pass_filenames = false; - }; - dune-opam-sync = { - name = "dune/opam sync"; - description = "Check that Dune-generated OPAM files are in sync."; - package = tools.dune-build-opam-files; - entry = "${hooks.dune-opam-sync.package}/bin/dune-build-opam-files"; - files = "(\\.opam$)|(\\.opam.template$)|((^|/)dune-project$)"; - ## We don't pass filenames because they can only be misleading. Indeed, - ## we need to re-run `dune build` for every `*.opam` file, but also when - ## the `dune-project` file has changed. - pass_filenames = false; - }; - eclint = - { - name = "eclint"; - description = "EditorConfig linter written in Go."; - types = [ "file" ]; - package = tools.eclint; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.eclint.settings; [ - [ fix "-fix" ] - [ summary "-summary" ] - [ (color != "auto") "-color ${color}" ] - [ (exclude != [ ]) "-exclude ${lib.escapeShellArgs exclude}" ] - [ (verbosity != 0) "-verbosity ${toString verbosity}" ] - ]); - in - "${hooks.eclint.package}/bin/eclint ${cmdArgs}"; - }; - editorconfig-checker = - { - name = "editorconfig-checker"; - description = "Verify that the files are in harmony with the `.editorconfig`."; - package = tools.editorconfig-checker; - entry = "${hooks.editorconfig-checker.package}/bin/editorconfig-checker"; - types = [ "file" ]; - }; - end-of-file-fixer = - { - name = "end-of-file-fixer"; - description = "Ensures that a file is either empty, or ends with a single newline."; - package = tools.pre-commit-hooks; - entry = "${hooks.end-of-file-fixer.package}/bin/end-of-file-fixer"; - types = [ "text" ]; - }; - elm-format = - { - name = "elm-format"; - description = "Format Elm files."; - package = tools.elm-format; - entry = "${hooks.elm-format.package}/bin/elm-format --yes --elm-version=0.19"; - files = "\\.elm$"; - }; - elm-review = - { - name = "elm-review"; - description = "Analyzes Elm projects, to help find mistakes before your users find them."; - package = tools.elm-review; - entry = "${hooks.elm-review.package}/bin/elm-review"; - files = "\\.elm$"; - pass_filenames = false; - }; - elm-test = - { - name = "elm-test"; - description = "Run unit tests and fuzz tests for Elm code."; - package = tools.elm-test; - entry = "${hooks.elm-test.package}/bin/elm-test"; - files = "\\.elm$"; - pass_filenames = false; - }; - eslint = - { - name = "eslint"; - description = "Find and fix problems in your JavaScript code."; - - package = tools.eslint; - entry = - let - binPath = migrateBinPathToPackage hooks.eslint "/bin/eslint"; - in - "${binPath} --fix"; - files = "${hooks.eslint.settings.extensions}"; - }; - fix-byte-order-marker = - { - name = "fix-byte-order-marker"; - description = "Remove UTF-8 byte order marker."; - package = tools.pre-commit-hooks; - entry = "${hooks.fix-byte-order-marker.package}/bin/fix-byte-order-marker"; - types = [ "text" ]; - }; - fix-encoding-pragma = - { - name = "fix-encoding-pragma"; - description = "Adds \# -*- coding: utf-8 -*- to the top of Python files.'"; - package = tools.pre-commit-hooks; - entry = "${hooks.fix-encoding-pragma.package}/bin/fix-encoding-pragma"; - types = [ "python" ]; - }; - flake8 = - let - extendIgnoreStr = - if lib.lists.length hooks.flake8.settings.extendIgnore > 0 - then "--extend-ignore " + builtins.concatStringsSep "," hooks.flake8.settings.extendIgnore - else ""; - in - { - name = "flake8"; - description = "Check the style and quality of Python files."; - - package = tools.flake8; - entry = - let - binPath = migrateBinPathToPackage hooks.flake8 "/bin/flake8"; - in - "${binPath} --format ${hooks.flake8.settings.format} ${extendIgnoreStr}"; - types = [ "python" ]; - }; - flake-checker = { - name = "flake-checker"; - description = "Run health checks on your flake-powered Nix projects."; - package = tools.flake-checker; - entry = "${hooks.flake-checker.package}/bin/flake-checker -f"; - files = "(^flake\\.nix$|^flake\\.lock$)"; - pass_filenames = false; - }; - flynt = - { - name = "flynt"; - description = "CLI tool to convert a python project's %-formatted strings to f-strings."; - package = tools.flynt; - entry = - let - binPath = migrateBinPathToPackage hooks.flynt "/bin/flynt"; - cmdArgs = - mkCmdArgs (with hooks.flynt.settings; [ - [ aggressive "--aggressive" ] - [ dry-run "--dry-run" ] - [ (exclude != [ ]) "--exclude ${lib.escapeShellArgs exclude}" ] - [ fail-on-change "--fail-on-change" ] - [ (line-length != null) "--line-length ${toString line-length}" ] - [ no-multiline "--no-multiline" ] - [ quiet "--quiet" ] - [ string "--string" ] - [ transform-concats "--transform-concats" ] - [ verbose "--verbose" ] - ]); - in - "${binPath} ${cmdArgs}"; - types = [ "python" ]; - }; - forbid-new-submodules = - { - name = "forbid-new-submodules"; - description = "Prevent addition of new Git submodules."; - package = tools.pre-commit-hooks; - entry = "${hooks.forbid-new-submodules.package}/bin/forbid-new-submodules"; - types = [ "directory" ]; - }; - fourmolu = - { - name = "fourmolu"; - description = "Haskell code prettifier."; - package = tools.fourmolu; - entry = - "${hooks.fourmolu.package}/bin/fourmolu --mode inplace ${ -lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.fourmolu.settings.defaultExtensions) -}"; - files = "\\.l?hs(-boot)?$"; - }; - fprettify = { - name = "fprettify"; - description = "Auto-formatter for modern Fortran code."; - types = [ "fortran " ]; - package = tools.fprettify; - entry = "${hooks.fprettify.package}/bin/fprettify"; - }; - gitlint = { - name = "gitlint"; - description = "Linting for your git commit messages"; - package = tools.gitlint; - entry = "${hooks.gitlint.package}/bin/gitlint --staged --msg-filename"; - stages = [ "commit-msg" ]; - }; - gofmt = - { - name = "gofmt"; - description = "A tool that automatically formats Go source code"; - package = tools.go; - entry = - let - script = pkgs.writeShellScript "precommit-gofmt" '' - set -e - failed=false - for file in "$@"; do - # redirect stderr so that violations and summaries are properly interleaved. - if ! ${hooks.gofmt.package}/bin/gofmt -l -w "$file" 2>&1 - then - failed=true - fi - done - if [[ $failed == "true" ]]; then - exit 1 - fi - ''; - in - builtins.toString script; - files = "\\.go$"; - }; - golangci-lint = { - name = "golangci-lint"; - description = "Fast linters runner for Go."; - package = tools.golangci-lint; - entry = - let - script = pkgs.writeShellScript "precommit-golangci-lint" '' - set -e - for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do - ${hooks.golangci-lint.package}/bin/golangci-lint run ./"$dir" - done - ''; - in - builtins.toString script; - files = "\\.go$"; - # to avoid multiple invocations of the same directory input, provide - # all file names in a single run. - require_serial = true; - }; - golines = - { - name = "golines"; - description = "A golang formatter that fixes long lines"; - package = tools.golines; - entry = - let - script = pkgs.writeShellScript "precommit-golines" '' - set -e - failed=false - for file in "$@"; do - # redirect stderr so that violations and summaries are properly interleaved. - if ! ${hooks.golines.package}/bin/golines ${hooks.golines.settings.flags} -w "$file" 2>&1 - then - failed=true - fi - done - if [[ $failed == "true" ]]; then - exit 1 - fi - ''; - in - builtins.toString script; - files = "\\.go$"; - }; - gotest = { - name = "gotest"; - description = "Run go tests"; - package = tools.go; - entry = - let - script = pkgs.writeShellScript "precommit-gotest" '' - set -e - # find all directories that contain tests - dirs=() - for file in "$@"; do - # either the file is a test - if [[ "$file" = *_test.go ]]; then - dirs+=("$(dirname "$file")") - continue - fi - - # or the file has an associated test - filename="''${file%.go}" - test_file="''${filename}_test.go" - if [[ -f "$test_file" ]]; then - dirs+=("$(dirname "$test_file")") - continue - fi - done - - # ensure we are not duplicating dir entries - IFS=$'\n' sorted_dirs=($(sort -u <<<"''${dirs[*]}")); unset IFS - - # test each directory one by one - for dir in "''${sorted_dirs[@]}"; do - ${hooks.gotest.package}/bin/go test "./$dir" - done - ''; - in - builtins.toString script; - files = "\\.go$"; - # to avoid multiple invocations of the same directory input, provide - # all file names in a single run. - require_serial = true; - }; - govet = - { - name = "govet"; - description = "Checks correctness of Go programs."; - package = tools.go; - entry = - let - # go vet requires package (directory) names as inputs. - script = pkgs.writeShellScript "precommit-govet" '' - set -e - for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do - ${hooks.govet.package}/bin/go vet -C ./"$dir" - done - ''; - in - builtins.toString script; - # to avoid multiple invocations of the same directory input, provide - # all file names in a single run. - require_serial = true; - files = "\\.go$"; - }; - gptcommit = { - name = "gptcommit"; - description = "Generate a commit message using GPT3."; - package = tools.gptcommit; - entry = - let - script = pkgs.writeShellScript "precommit-gptcomit" '' - ${hooks.gptcommit.package}/bin/gptcommit prepare-commit-msg --commit-source \ - "$PRE_COMMIT_COMMIT_MSG_SOURCE" --commit-msg-file "$1" - ''; - in - lib.throwIf (hooks.gptcommit.package == null) "The version of Nixpkgs used by git-hooks.nix does not have the `gptcommit` package. Please use a more recent version of Nixpkgs." - toString - script; - stages = [ "prepare-commit-msg" ]; - }; - hadolint = - { - name = "hadolint"; - description = "Dockerfile linter, validate inline bash."; - package = tools.hadolint; - entry = "${hooks.hadolint.package}/bin/hadolint"; - files = "Dockerfile$"; - }; - headache = - { - name = "headache"; - description = "Lightweight tool for managing headers in source code files."; - ## NOTE: Supported `files` are taken from - ## https://github.com/Frama-C/headache/blob/master/config_builtin.txt - files = "(\\.ml[ily]?$)|(\\.fmli?$)|(\\.[chy]$)|(\\.tex$)|(Makefile)|(README)|(LICENSE)"; - package = tools.headache; - entry = - ## NOTE: `headache` made into in nixpkgs on 12 April 2023. At the - ## next NixOS release, the following code will become irrelevant. - lib.throwIf - (hooks.headache.package == null) - "The version of nixpkgs used by git-hooks.nix does not have `ocamlPackages.headache`. Please use a more recent version of nixpkgs." - "${hooks.headache.package}/bin/headache -h ${hooks.headache.settings.header-file}"; - }; - hindent = - { - name = "hindent"; - description = "Haskell code prettifier."; - package = tools.hindent; - entry = "${hooks.hindent.package}/bin/hindent"; - files = "\\.l?hs(-boot)?$"; - }; - hlint = - { - name = "hlint"; - description = "HLint gives suggestions on how to improve your source code."; - package = tools.hlint; - entry = "${hooks.hlint.package}/bin/hlint${if hooks.hlint.settings.hintFile == null then "" else " --hint=${hooks.hlint.settings.hintFile}"}"; - files = "\\.l?hs(-boot)?$"; - }; - hpack = - { - name = "hpack"; - description = "`hpack` converts package definitions in the hpack format (`package.yaml`) to Cabal files."; - package = tools.hpack-dir; - entry = "${hooks.hpack.package}/bin/hpack-dir --${if hooks.hpack.settings.silent then "silent" else "verbose"}"; - files = "(\\.l?hs(-boot)?$)|(\\.cabal$)|((^|/)package\\.yaml$)"; - # We don't pass filenames because they can only be misleading. - # Indeed, we need to rerun `hpack` in every directory: - # 1. In which there is a *.cabal file, or - # 2. Below which there are haskell files, or - # 3. In which there is a package.yaml that references haskell files - # that have been changed at arbitrary locations specified in that - # file. - # In other words: We have no choice but to always run `hpack` on every `package.yaml` directory. - pass_filenames = false; - }; - html-tidy = - { - name = "html-tidy"; - description = "HTML linter."; - package = tools.html-tidy; - entry = "${hooks.html-tidy.package}/bin/tidy -quiet -errors"; - files = "\\.html$"; - }; - hunspell = - { - name = "hunspell"; - description = "Spell checker and morphological analyzer."; - package = tools.hunspell; - entry = "${hooks.hunspell.package}/bin/hunspell -l"; - files = "\\.((txt)|(html)|(xml)|(md)|(org)|(rst)|(tex)|(odf)|\\d)$"; - }; - isort = - { - name = "isort"; - description = "A Python utility / library to sort imports."; - types = [ "file" "python" ]; - package = tools.isort; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.isort.settings; [ - [ (profile != "") " --profile ${profile}" ] - ]); - in - "${hooks.isort.package}/bin/isort${cmdArgs} ${hooks.isort.settings.flags}"; - }; - juliaformatter = - { - description = "Run JuliaFormatter.jl against Julia source files"; - files = "\\.jl$"; - package = tools.julia-bin; - entry = '' - ${hooks.juliaformatter.package}/bin/julia -e ' - using Pkg - Pkg.activate(".") - using JuliaFormatter - format(ARGS) - out = Cmd(`git diff --name-only`) |> read |> String - if out == "" - exit(0) - else - @error "Some files have been formatted !!!" - write(stdout, out) - exit(1) - end' - ''; - }; - latexindent = - { - name = "latexindent"; - description = "Perl script to add indentation to LaTeX files."; - types = [ "file" "tex" ]; - package = tools.latexindent; - entry = "${hooks.latexindent.package}/bin/latexindent ${hooks.latexindent.settings.flags}"; - }; - lacheck = - let - script = pkgs.writeShellScript "precommit-mdsh" '' - for file in $(echo "$@"); do - "${hooks.lacheck.package}/bin/lacheck" "$file" - done - ''; - in - { - name = "lacheck"; - description = "A consistency checker for LaTeX documents."; - types = [ "file" "tex" ]; - package = tools.lacheck; - entry = "${script}"; - }; - lua-ls = - let - # .luarc.json has to be in a directory, - # or lua-language-server will hang forever. - luarc = pkgs.writeText ".luarc.json" (builtins.toJSON hooks.lua-ls.settings.configuration); - luarc-dir = pkgs.stdenv.mkDerivation { - name = "luarc"; - unpackPhase = "true"; - installPhase = '' - mkdir $out - cp ${luarc} $out/.luarc.json - ''; - }; - script = pkgs.writeShellApplication { - name = "lua-ls-lint"; - runtimeInputs = [ hooks.lua-ls.package pkgs.jq ]; - checkPhase = ""; # The default checkPhase depends on GHC - text = '' - set -e - export logpath="$(mktemp -d)" - lua-language-server --check $(realpath .) \ - --checklevel="${hooks.lua-ls.settings.checklevel}" \ - --configpath="${luarc-dir}/.luarc.json" \ - --logpath="$logpath" - if [[ -f $logpath/check.json ]]; then - echo "+++++++++++++++ lua-language-server diagnostics +++++++++++++++" - cat $logpath/check.json - diagnostic_count=$(jq 'length' $logpath/check.json) - if [ "$diagnostic_count" -gt 0 ]; then - exit 1 - fi - fi - ''; - }; - in - { - name = "lua-ls"; - description = "Uses the lua-language-server CLI to statically type-check and lint Lua code."; - package = tools.lua-language-server; - entry = "${script}/bin/lua-ls-lint"; - files = "\\.lua$"; - pass_filenames = false; - }; - luacheck = - { - name = "luacheck"; - description = "A tool for linting and static analysis of Lua code."; - types = [ "file" "lua" ]; - package = tools.luacheck; - entry = "${hooks.luacheck.package}/bin/luacheck"; - }; - lychee = { - name = "lychee"; - description = "A fast, async, stream-based link checker that finds broken hyperlinks and mail addresses inside Markdown, HTML, reStructuredText, or any other text file or website."; - package = tools.lychee; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.lychee.settings; [ - [ (configPath != "") " --config ${configPath}" ] - ]); - in - "${hooks.lychee.package}/bin/lychee${cmdArgs} ${hooks.lychee.settings.flags}"; - types = [ "text" ]; - }; - markdownlint = - { - name = "markdownlint"; - description = "Style checker and linter for markdown files."; - package = tools.markdownlint-cli; - entry = "${hooks.markdownlint.package}/bin/markdownlint -c ${pkgs.writeText "markdownlint.json" (builtins.toJSON hooks.markdownlint.settings.configuration)}"; - files = "\\.md$"; - }; - mdformat = { - name = "mdformat"; - description = "CommonMark compliant Markdown formatter"; - package = tools.mdformat; - entry = "${hooks.mdformat.package}/bin/mdformat"; - types = [ "markdown" ]; - }; - mdl = - { - name = "mdl"; - description = "A tool to check markdown files and flag style issues."; - package = tools.mdl; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.mdl.settings; [ - [ (configPath != "") "--config ${configPath}" ] - [ git-recurse "--git-recurse" ] - [ ignore-front-matter "--ignore-front-matter" ] - [ json "--json" ] - [ (rules != [ ]) "--rules ${lib.strings.concatStringsSep "," rules}" ] - [ (rulesets != [ ]) "--rulesets ${lib.strings.concatStringsSep "," rulesets}" ] - [ show-aliases "--show-aliases" ] - [ warnings "--warnings" ] - [ skip-default-ruleset "--skip-default-ruleset" ] - [ (style != "") "--style ${style}" ] - [ (tags != [ ]) "--tags ${lib.strings.concatStringsSep "," tags}" ] - [ verbose "--verbose" ] - ]); - in - "${hooks.mdl.package}/bin/mdl ${cmdArgs}"; - files = "\\.md$"; - }; - mdsh = - let - script = pkgs.writeShellScript "precommit-mdsh" '' - for file in $(echo "$@"); do - ${hooks.mdsh.package}/bin/mdsh -i "$file" - done - ''; - in - { - name = "mdsh"; - description = "Markdown shell pre-processor."; - package = tools.mdsh; - entry = toString script; - files = "\\.md$"; - }; - mixed-line-endings = { - name = "mixed-line-endings"; - description = "Resolve mixed line endings."; - package = tools.pre-commit-hooks; - entry = "${hooks.mixed-line-endings.package}/bin/mixed-line-ending"; - types = [ "text" ]; - }; - mix-format = { - name = "mix-format"; - description = "Runs the built-in Elixir syntax formatter"; - package = tools.elixir; - entry = "${hooks.mix-format.package}/bin/mix format"; - files = "\\.exs?$"; - }; - mix-test = { - name = "mix-test"; - description = "Runs the built-in Elixir test framework"; - package = tools.elixir; - entry = "${hooks.mix-test.package}/bin/mix test"; - files = "\\.exs?$"; - }; - mkdocs-linkcheck = { - name = "mkdocs-linkcheck"; - description = "Validate links associated with markdown-based, statically generated websites."; - package = tools.mkdocs-linkcheck; - entry = - let - binPath = migrateBinPathToPackage hooks.mkdocs-linkcheck "/bin/mkdocs-linkcheck"; - cmdArgs = - mkCmdArgs - (with hooks.mkdocs-linkcheck.settings; [ - [ local-only " --local" ] - [ recurse " --recurse" ] - [ (extension != "") " --ext ${extension}" ] - [ (method != "") " --method ${method}" ] - [ (path != "") " ${path}" ] - ]); - in - "${binPath}${cmdArgs}"; - types = [ "text" "markdown" ]; - }; - mypy = - { - name = "mypy"; - description = "Static type checker for Python"; - - package = tools.mypy; - entry = migrateBinPathToPackage hooks.mypy "/bin/mypy"; - files = "\\.py$"; - }; - name-tests-test = - { - name = "mypy"; - description = "Verify that Python test files are named correctly."; - package = tools.pre-commit-hooks; - entry = "${hooks.name-tests-test.package}/bin/tests_should_end_in_test.py"; - files = "(^|/)tests/\.+\\.py$"; - }; - nil = - { - name = "nil"; - description = "Incremental analysis assistant for writing in Nix."; - package = tools.nil; - entry = - let - script = pkgs.writeShellScript "precommit-nil" '' - errors=false - echo Checking: $@ - for file in $(echo "$@"); do - ${hooks.nil.package}/bin/nil diagnostics "$file" - exit_code=$? - - if [[ $exit_code -ne 0 ]]; then - echo \"$file\" failed with exit code: $exit_code - errors=true - fi - done - if [[ $errors == true ]]; then - exit 1 - fi - ''; - in - builtins.toString script; - files = "\\.nix$"; - }; - nixfmt = - { - name = "nixfmt-deprecated"; - description = "Deprecated Nix code prettifier. Use nixfmt-classic."; - package = tools.nixfmt; - entry = "${hooks.nixfmt.package}/bin/nixfmt ${lib.optionalString (hooks.nixfmt.settings.width != null) "--width=${toString hooks.nixfmt.settings.width}"}"; - files = "\\.nix$"; - }; - nixfmt-classic = - { - name = "nixfmt-classic"; - description = "Nix code prettifier (classic)."; - package = tools.nixfmt-classic; - entry = "${hooks.nixfmt-classic.package}/bin/nixfmt ${lib.optionalString (hooks.nixfmt-classic.settings.width != null) "--width=${toString hooks.nixfmt-classic.settings.width}"}"; - files = "\\.nix$"; - }; - nixfmt-rfc-style = - { - name = "nixfmt-rfc-style"; - description = "Nix code prettifier (RFC 166 style)."; - package = tools.nixfmt-rfc-style; - entry = "${hooks.nixfmt-rfc-style.package}/bin/nixfmt ${lib.optionalString (hooks.nixfmt-rfc-style.settings.width != null) "--width=${toString hooks.nixfmt-rfc-style.settings.width}"}"; - files = "\\.nix$"; - }; - nixpkgs-fmt = - { - name = "nixpkgs-fmt"; - description = "Nix code prettifier."; - package = tools.nixpkgs-fmt; - entry = "${hooks.nixpkgs-fmt.package}/bin/nixpkgs-fmt"; - files = "\\.nix$"; - }; - no-commit-to-branch = - { - name = "no-commit-to-branch"; - description = "Disallow committing to certain branch/branches."; - pass_filenames = false; - always_run = true; - package = tools.pre-commit-hooks; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.no-commit-to-branch.settings; [ - [ (branch != [ ]) "--branch ${lib.strings.concatStringsSep " --branch " branch}" ] - [ (pattern != [ ]) "--pattern ${lib.strings.concatStringsSep " --pattern " pattern}" ] - ]); - in - "${hooks.no-commit-to-branch.package}/bin/no-commit-to-branch ${cmdArgs}"; - }; - ocp-indent = - { - name = "ocp-indent"; - description = "A tool to indent OCaml code."; - package = tools.ocp-indent; - entry = "${hooks.ocp-indent.package}/bin/ocp-indent --inplace"; - files = "\\.mli?$"; - }; - opam-lint = - { - name = "opam lint"; - description = "OCaml package manager configuration checker."; - package = tools.opam; - entry = "${hooks.opam-lint.package}/bin/opam lint"; - files = "\\.opam$"; - }; - openapi-spec-validator = - { - name = "openapi spec validator"; - description = "A tool to validate OpenAPI spec files"; - package = tools.openapi-spec-validator; - entry = "${hooks.openapi-spec-validator.package}/bin/openapi-spec-validator"; - files = ".*openapi.*\\.(json|yaml|yml)$"; - }; - ormolu = - { - name = "ormolu"; - description = "Haskell code prettifier."; - package = tools.ormolu; - entry = - let - extensions = - lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) hooks.ormolu.settings.defaultExtensions); - cabalExtensions = - if hooks.ormolu.settings.cabalDefaultExtensions then "--cabal-default-extensions" else ""; - in - "${hooks.ormolu.package}/bin/ormolu --mode inplace ${extensions} ${cabalExtensions}"; - files = "\\.l?hs(-boot)?$"; - }; - php-cs-fixer = - { - name = "php-cs-fixer"; - description = "Lint PHP files."; - - package = tools.php-cs-fixer; - entry = - let - binPath = migrateBinPathToPackage hooks.php-cs-fixer "/bin/php-cs-fixer"; - in - "${binPath} fix"; - types = [ "php" ]; - }; - phpcbf = - { - name = "phpcbf"; - description = "Lint PHP files."; - - package = tools.phpcbf; - entry = migrateBinPathToPackage hooks.phpcbf "/bin/phpcbf"; - types = [ "php" ]; - }; - phpcs = - { - name = "phpcs"; - description = "Lint PHP files."; - - package = tools.phpcs; - entry = migrateBinPathToPackage hooks.phpcs "/bin/phpcs"; - types = [ "php" ]; - }; - phpstan = - { - name = "phpstan"; - description = "Static Analysis of PHP files."; - - package = tools.phpstan; - entry = - let - binPath = migrateBinPathToPackage hooks.phpstan "/bin/phpstan"; - in - "${binPath} analyse"; - types = [ "php" ]; - }; - poetry-check = { - name = "poetry check"; - description = "Check the Poetry config for errors"; - package = tools.poetry; - entry = "${hooks.poetry-check.package}/bin/poetry check"; - files = "^(poetry\\.lock$|pyproject\\.toml)$"; - pass_filenames = false; - }; - poetry-lock = { - name = "poetry lock"; - description = "Update the Poetry lock file"; - package = tools.poetry; - entry = "${hooks.poetry-lock.package}/bin/poetry lock"; - files = "^(poetry\\.lock$|pyproject\\.toml)$"; - pass_filenames = false; - }; - pre-commit-hook-ensure-sops = { - name = "pre-commit-hook-ensure-sops"; - package = tools.pre-commit-hook-ensure-sops; - entry = - ## NOTE: pre-commit-hook-ensure-sops landed in nixpkgs on 8 July 2022. Once it reaches a - ## release of NixOS, the `throwIf` piece of code below will become - ## useless. - lib.throwIf - (hooks.pre-commit-hook-ensure-sops.package == null) - "The version of nixpkgs used by git-hooks.nix does not have the `pre-commit-hook-ensure-sops` package. Please use a more recent version of nixpkgs." - '' - ${hooks.pre-commit-hook-ensure-sops.package}/bin/pre-commit-hook-ensure-sops - ''; - files = "^secrets"; - }; - # See all CLI flags for prettier [here](https://prettier.io/docs/en/cli.html). - # See all options for prettier [here](https://prettier.io/docs/en/options.html). - prettier = - { - name = "prettier"; - description = "Opinionated multi-language code formatter."; - types = [ "text" ]; - - package = tools.prettier; - entry = - let - binPath = migrateBinPathToPackage hooks.prettier "/bin/prettier"; - cmdArgs = - mkCmdArgs - (with hooks.prettier.settings; [ - [ (allow-parens != "always") "--allow-parens ${allow-parens}" ] - [ bracket-same-line "--bracket-same-line" ] - [ cache "--cache" ] - [ (cache-location != "./node_modules/.cache/prettier/.prettier-cache") "--cache-location ${cache-location}" ] - [ (cache-strategy != null) "--cache-strategy ${cache-strategy}" ] - [ check "--check" ] - [ (!color) "--no-color" ] - [ (configPath != "") "--config ${configPath}" ] - [ (config-precedence != "cli-override") "--config-precedence ${config-precedence}" ] - [ (embedded-language-formatting != "auto") "--embedded-language-formatting ${embedded-language-formatting}" ] - [ (end-of-line != "lf") "--end-of-line ${end-of-line}" ] - [ (html-whitespace-sensitivity != "css") "--html-whitespace-sensitivity ${html-whitespace-sensitivity}" ] - [ (ignore-path != [ ]) "--ignore-path ${lib.escapeShellArgs ignore-path}" ] - [ ignore-unknown "--ignore-unknown" ] - [ insert-pragma "--insert-pragma" ] - [ jsx-single-quote "--jsx-single-quote" ] - [ list-different "--list-different" ] - [ (log-level != "log") "--log-level ${log-level}" ] - [ no-bracket-spacing "--no-bracket-spacing" ] - [ no-config "--no-config" ] - [ no-editorconfig "--no-editorconfig" ] - [ no-error-on-unmatched-pattern "--no-error-on-unmatched-pattern" ] - [ no-semi "--no-semi" ] - [ (parser != "") "--parser ${parser}" ] - [ (print-width != 80) "--print-width ${toString print-width}" ] - [ (prose-wrap != "preserve") "--prose-wrap ${prose-wrap}" ] - [ (plugins != [ ]) "--plugin ${lib.strings.concatStringsSep " --plugin " plugins}" ] - [ (quote-props != "as-needed") "--quote-props ${quote-props}" ] - [ require-pragma "--require-pragma" ] - [ single-attribute-per-line "--single-attribute-per-line" ] - [ single-quote "--single-quote" ] - [ (tab-width != 2) "--tab-width ${toString tab-width}" ] - [ (trailing-comma != "all") "--trailing-comma ${trailing-comma}" ] - [ use-tabs "--use-tabs" ] - [ vue-indent-script-and-style "--vue-indent-script-and-style" ] - [ with-node-modules "--with-node-modules" ] - [ write "--write" ] - ]); - in - "${binPath} ${cmdArgs}"; - }; - pretty-format-json = - { - name = "pretty-format-json"; - description = "Formats JSON files."; - package = tools.pre-commit-hooks; - entry = - let - binPath = "${hooks.pretty-format-json.package}/bin/pretty-format-json"; - cmdArgs = mkCmdArgs (with hooks.pretty-format-json.settings; [ - [ autofix "--autofix" ] - [ (indent != null) "--indent ${toString indent}" ] - [ no-ensure-ascii "--no-ensure-ascii" ] - [ no-sort-keys "--no-sort-keys" ] - [ (top-keys != [ ]) "--top-keys ${lib.strings.concatStringsSep "," top-keys}" ] - ]); - in - "${binPath} ${cmdArgs}"; - types = [ "json" ]; - }; - proselint = - { - name = "proselint"; - description = "A linter for prose."; - types = [ "text" ]; - package = tools.proselint; - entry = - let - configFile = builtins.toFile "proselint-config.json" "${hooks.proselint.settings.config}"; - cmdArgs = - mkCmdArgs - (with hooks.proselint.settings; [ - [ (configPath != "") " --config ${configPath}" ] - [ (config != "" && configPath == "") " --config ${configFile}" ] - ]); - in - "${hooks.proselint.package}/bin/proselint${cmdArgs} ${hooks.proselint.settings.flags}"; - }; - psalm = - { - name = "psalm"; - description = "Static Analysis of PHP files."; - - package = tools.psalm; - entry = migrateBinPathToPackage hooks.psalm "/bin/psalm"; - types = [ "php" ]; - }; - purs-tidy = - { - name = "purs-tidy"; - description = "Format purescript files."; - package = tools.purs-tidy; - entry = "${hooks.purs-tidy.package}/bin/purs-tidy format-in-place"; - files = "\\.purs$"; - }; - purty = - { - name = "purty"; - description = "Format purescript files."; - package = tools.purty; - entry = "${hooks.purty.package}/bin/purty"; - files = "\\.purs$"; - }; - pylint = - { - name = "pylint"; - description = "Lint Python files."; - - package = tools.pylint; - entry = - let - binPath = migrateBinPathToPackage hooks.pylint "/bin/pylint"; - cmdArgs = - mkCmdArgs - (with hooks.pylint.settings; [ - [ reports "-ry" ] - [ (! score) "-sn" ] - ]); - in - "${binPath} ${cmdArgs}"; - types = [ "python" ]; - }; - pyright = - { - name = "pyright"; - description = "Static type checker for Python"; - - package = tools.pyright; - entry = migrateBinPathToPackage hooks.pyright "/bin/pyright"; - files = "\\.py$"; - }; - python-debug-statements = - { - name = "python-debug-statements"; - description = "Check for debugger imports and py37+ `breakpoint()` calls in python source."; - package = tools.pre-commit-hooks; - entry = "${hooks.python-debug-statements.package}/bin/debug-statement-hook"; - types = [ "python" ]; - }; - pyupgrade = - { - name = "pyupgrade"; - description = "Automatically upgrade syntax for newer versions."; - - package = tools.pyupgrade; - entry = migrateBinPathToPackage hooks.pyupgrade "/bin/pyupgrade"; - types = [ "python" ]; - }; - reuse = - { - name = "reuse"; - description = "reuse is a tool for compliance with the REUSE recommendations."; - package = tools.reuse; - entry = "${hooks.reuse.package}/bin/reuse lint ${hooks.reuse.settings.flags}"; - types = [ "file" ]; - pass_filenames = false; - }; - revive = - { - name = "revive"; - description = "A linter for Go source code."; - package = tools.revive; - entry = - let - cmdArgs = - mkCmdArgs [ - [ true "-set_exit_status" ] - [ (hooks.revive.settings.configPath != "") "-config ${hooks.revive.settings.configPath}" ] - ]; - # revive works with both files and directories; however some lints - # may fail (e.g. package-comment) if they run on an individual file - # rather than a package/directory scope; given this let's get the - # directories from each individual file. - script = pkgs.writeShellScript "precommit-revive" '' - set -e - for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do - ${hooks.revive.package}/bin/revive ${cmdArgs} ./"$dir" - done - ''; - in - builtins.toString script; - files = "\\.go$"; - # to avoid multiple invocations of the same directory input, provide - # all file names in a single run. - require_serial = true; - }; - ripsecrets = - { - name = "ripsecrets"; - description = "Prevent committing secret keys into your source code"; - package = tools.ripsecrets; - entry = - let - cmdArgs = mkCmdArgs ( - with hooks.ripsecrets.settings; [ - [ true "--strict-ignore" ] - [ - (additionalPatterns != [ ]) - "--additional-pattern ${lib.strings.concatStringsSep " --additional-pattern " additionalPatterns}" - ] - ] - ); - in - "${hooks.ripsecrets.package}/bin/ripsecrets ${cmdArgs}"; - types = [ "text" ]; - }; - rome = - { - name = "rome-deprecated"; - description = ""; - types_or = [ "javascript" "jsx" "ts" "tsx" "json" ]; - package = tools.biome; - entry = - let - binPath = migrateBinPathToPackage hooks.rome "/bin/biome"; - cmdArgs = - mkCmdArgs [ - [ (hooks.rome.settings.write) "--apply" ] - [ (hooks.rome.settings.configPath != "") "--config-path ${hooks.rome.settings.configPath}" ] - ]; - in - "${binPath} check ${cmdArgs}"; - }; - ruff = - { - name = "ruff"; - description = "An extremely fast Python linter, written in Rust."; - package = tools.ruff; - entry = "${hooks.ruff.package}/bin/ruff check --fix"; - types = [ "python" ]; - }; - ruff-format = - { - name = "ruff-format"; - description = "An extremely fast Python code formatter, written in Rust."; - package = tools.ruff; - entry = "${hooks.ruff.package}/bin/ruff format"; - types = [ "python" ]; - }; - rustfmt = - let - mkAdditionalArgs = args: lib.optionalString (args != "") " -- ${args}"; - - inherit (hooks.rustfmt) packageOverrides; - wrapper = pkgs.symlinkJoin { - name = "rustfmt-wrapped"; - paths = [ packageOverrides.rustfmt ]; - nativeBuildInputs = [ pkgs.makeWrapper ]; - postBuild = '' - wrapProgram $out/bin/cargo-fmt \ - --prefix PATH : ${lib.makeBinPath (builtins.attrValues packageOverrides)} - ''; - }; - in - { - name = "rustfmt"; - description = "Format Rust code."; - package = wrapper; - packageOverrides = { inherit (tools) cargo rustfmt; }; - entry = - let - inherit (hooks) rustfmt; - inherit (rustfmt) settings; - cargoArgs = lib.cli.toGNUCommandLineShell { } { - inherit (settings) all package verbose manifest-path; - }; - rustfmtArgs = lib.cli.toGNUCommandLineShell { } { - inherit (settings) check emit config-path color files-with-diff config verbose; - }; - in - "${rustfmt.package}/bin/cargo-fmt fmt ${cargoArgs}${mkAdditionalArgs rustfmtArgs}"; - files = "\\.rs$"; - pass_filenames = false; - }; - selene = { - name = "selene"; - description = "A blazing-fast modern Lua linter written in Rust."; - types = [ "lua" ]; - package = tools.selene; - entry = "${hooks.selene.package}/bin/selene"; - }; - shellcheck = - { - name = "shellcheck"; - description = "Format shell files."; - types = [ "shell" ]; - package = tools.shellcheck; - entry = "${hooks.shellcheck.package}/bin/shellcheck"; - }; - shfmt = - { - name = "shfmt"; - description = "Format shell files."; - types = [ "shell" ]; - package = tools.shfmt; - entry = - let - simplify = if hooks.shfmt.settings.simplify then "-s" else ""; - in - "${hooks.shfmt.package}/bin/shfmt -w -l ${simplify}"; - }; - single-quoted-strings = - { - name = "single-quoted-strings"; - description = "Replace double quoted strings with single quoted strings."; - package = tools.pre-commit-hooks; - entry = "${hooks.single-quoted-strings.package}/bin/double-quote-string-fixer"; - types = [ "python" ]; - }; - sort-file-contents = - { - name = "sort-file-contents"; - description = "Sort the lines in specified files (defaults to alphabetical)."; - types = [ "text" ]; - package = tools.pre-commit-hooks; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.sort-file-contents.settings; - [ - [ ignore-case "--ignore-case" ] - [ unique "--unique" ] - ]); - in - "${hooks.sort-file-contents.package}/bin/file-contents-sorter ${cmdArgs}"; - }; - sort-requirements-txt = - { - name = "sort-requirements.txt"; - description = "Sort requirements in requirements.txt and constraints.txt files."; - package = tools.pre-commit-hooks; - entry = "${hooks.sort-requirements-txt.package}/bin/requirements-txt-fixer"; - files = "\\.*(requirements|constraints)\\.*\\.txt$"; - }; - sort-simple-yaml = - { - name = "sort-simple-yaml"; - description = "Sort simple YAML files which consist only of top-level keys, preserving comments and blocks."; - package = tools.pre-commit-hooks; - entry = "${hooks.sort-simple-yaml.package}/bin/sort-simple-yaml"; - files = "(\\.yaml$)|(\\.yml$)"; - }; - staticcheck = - { - name = "staticcheck"; - description = "State of the art linter for the Go programming language"; - package = tools.go-tools; - # staticheck works with directories. - entry = - let - script = pkgs.writeShellScript "precommit-staticcheck" '' - err=0 - for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do - ${hooks.staticcheck.package}/bin/staticcheck ./"$dir" - code="$?" - if [[ "$err" -eq 0 ]]; then - err="$code" - fi - done - exit $err - ''; - in - builtins.toString script; - files = "\\.go$"; - # to avoid multiple invocations of the same directory input, provide - # all file names in a single run. - require_serial = true; - }; - statix = - { - name = "statix"; - description = "Lints and suggestions for the Nix programming language."; - package = tools.statix; - entry = - let - inherit (hooks.statix) package settings; - mkOptionName = k: - if builtins.stringLength k == 1 - then "-${k}" - else "--${k}"; - options = lib.cli.toGNUCommandLineShell - { - # instead of repeating the option name for each element, - # create a single option with a space-separated list of unique values. - mkList = k: v: if v == [ ] then [ ] else [ (mkOptionName k) ] ++ lib.unique v; - } - settings; - in - "${package}/bin/statix check ${options}"; - files = "\\.nix$"; - pass_filenames = false; - }; - stylish-haskell = - { - name = "stylish-haskell"; - description = "A simple Haskell code prettifier"; - package = tools.stylish-haskell; - entry = "${hooks.stylish-haskell.package}/bin/stylish-haskell --inplace"; - files = "\\.l?hs(-boot)?$"; - }; - stylua = - { - name = "stylua"; - description = "An Opinionated Lua Code Formatter."; - types = [ "file" "lua" ]; - package = tools.stylua; - entry = "${hooks.stylua.package}/bin/stylua --respect-ignores"; - }; - tagref = - { - name = "tagref"; - description = '' - Have tagref check all references and tags. - ''; - package = tools.tagref; - entry = "${hooks.tagref.package}/bin/tagref"; - types = [ "text" ]; - pass_filenames = false; - }; - taplo = - { - name = "taplo"; - description = "Format TOML files with taplo fmt"; - package = tools.taplo; - entry = "${hooks.taplo.package}/bin/taplo fmt"; - types = [ "toml" ]; - }; - terraform-format = - { - name = "terraform-format"; - description = "Format Terraform (`.tf`) files."; - package = tools.opentofu; - entry = "${lib.getExe hooks.terraform-format.package} fmt -check -diff"; - files = "\\.tf$"; - }; - terraform-validate = - { - name = "terraform-validate"; - description = "Validates terraform configuration files (`.tf`)."; - package = tools.terraform-validate; - entry = "${hooks.terraform-validate.package}/bin/terraform-validate"; - files = "\\.(tf(vars)?|terraform\\.lock\\.hcl)$"; - excludes = [ "\\.terraform/.*$" ]; - require_serial = true; - }; - tflint = - { - name = "tflint"; - description = "A Pluggable Terraform Linter."; - package = tools.tflint; - entry = "${hooks.tflint.package}/bin/tflint"; - files = "\\.tf$"; - }; - topiary = - { - name = "topiary"; - description = "A universal formatter engine within the Tree-sitter ecosystem, with support for many languages."; - package = tools.topiary; - entry = - ## NOTE: Topiary landed in nixpkgs on 2 Dec 2022. Once it reaches a - ## release of NixOS, the `throwIf` piece of code below will become - ## useless. - lib.throwIf - (hooks.topiary.package == null) - "The version of nixpkgs used by git-hooks.nix does not have the `topiary` package. Please use a more recent version of nixpkgs." - ( - let - topiary-inplace = pkgs.writeShellApplication { - name = "topiary-inplace"; - text = '' - for file; do - ${hooks.topiary.package}/bin/topiary --in-place --input-file "$file" - done - ''; - }; - in - "${topiary-inplace}/bin/topiary-inplace" - ); - files = "(\\.json$)|(\\.toml$)|(\\.mli?$)"; - }; - treefmt = - let - inherit (hooks.treefmt) packageOverrides settings; - wrapper = - pkgs.writeShellApplication { - name = "treefmt"; - runtimeInputs = [ - packageOverrides.treefmt - ] ++ settings.formatters; - - text = - '' - exec treefmt "$@" - ''; - }; - in - { - name = "treefmt"; - description = "One CLI to format the code tree."; - types = [ "file" ]; - pass_filenames = true; - package = wrapper; - packageOverrides = { treefmt = tools.treefmt; }; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.treefmt.settings; [ - [ fail-on-change "--fail-on-change" ] - [ no-cache "--no-cache" ] - ]); - in - "${hooks.treefmt.package}/bin/treefmt ${cmdArgs}"; - }; - trim-trailing-whitespace = - { - name = "trim-trailing-whitespace"; - description = "Trim trailing whitespace."; - types = [ "text" ]; - stages = [ "pre-commit" "pre-push" "manual" ]; - package = tools.pre-commit-hooks; - entry = "${hooks.trim-trailing-whitespace.package}/bin/trailing-whitespace-fixer"; - }; - trufflehog = - { - name = "trufflehog"; - description = "Secrets scanner"; - entry = - let - script = pkgs.writeShellScript "precommit-trufflehog" '' - set -e - ${hooks.trufflehog.package}/bin/trufflehog --no-update git "file://$(git rev-parse --show-toplevel)" --since-commit HEAD --only-verified --fail - ''; - in - builtins.toString script; - package = tools.trufflehog; - - # trufflehog expects to run across the whole repo, not particular files - pass_filenames = false; - }; - typos = - { - name = "typos"; - description = "Source code spell checker"; - package = tools.typos; - entry = - let - # Concatenate config in config file with section for ignoring words generated from list of words to ignore - configuration = "${hooks.typos.settings.configuration}" + lib.strings.optionalString (hooks.typos.settings.ignored-words != [ ]) "\n\[default.extend-words\]" + lib.strings.concatMapStrings (x: "\n${x} = \"${x}\"") hooks.typos.settings.ignored-words; - configFile = builtins.toFile "typos-config.toml" configuration; - cmdArgs = - mkCmdArgs - (with hooks.typos.settings; [ - [ binary "--binary" ] - [ (color != "auto") "--color ${color}" ] - [ (configuration != "") "--config ${configFile}" ] - [ (configPath != "" && configuration == "") "--config ${configPath}" ] - [ diff "--diff" ] - [ (exclude != "") "--exclude ${exclude} --force-exclude" ] - [ (format != "long") "--format ${format}" ] - [ hidden "--hidden" ] - [ (locale != "en") "--locale ${locale}" ] - [ no-check-filenames "--no-check-filenames" ] - [ no-check-files "--no-check-files" ] - [ no-unicode "--no-unicode" ] - [ quiet "--quiet" ] - [ verbose "--verbose" ] - [ (write && !diff) "--write-changes" ] - ]); - in - "${hooks.typos.package}/bin/typos ${cmdArgs}"; - types = [ "text" ]; - }; - typstfmt = { - name = "typstfmt"; - description = "format typst"; - package = tools.typstfmt; - entry = "${hooks.typstfmt.package}/bin/typstfmt"; - files = "\\.typ$"; - }; - typstyle = { - name = "typstyle"; - description = "Beautiful and reliable typst code formatter"; - package = tools.typstyle; - entry = - lib.throwIf - (hooks.typstyle.package == null) - "The version of nixpkgs used by git-hooks.nix must contain typstyle" - "${hooks.typstyle.package}/bin/typstyle -i"; - files = "\\.typ$"; - }; - vale = { - name = "vale"; - description = "A markup-aware linter for prose built with speed and extensibility in mind."; - package = tools.vale; - entry = - let - # TODO: was .vale.ini, threw error in Nix - configFile = builtins.toFile "vale.ini" "${hooks.vale.settings.configuration}"; - cmdArgs = - mkCmdArgs - (with hooks.vale.settings; [ - [ (configPath != "") " --config ${configPath}" ] - [ (configuration != "" && configPath == "") " --config ${configFile}" ] - ]); - in - "${hooks.vale.package}/bin/vale${cmdArgs} ${hooks.vale.settings.flags}"; - types = [ "text" ]; - }; - yamlfmt = - { - name = "yamlfmt"; - description = "Formatter for YAML files."; - types = [ "file" "yaml" ]; - package = tools.yamlfmt; - entry = - let - cmdArgs = - mkCmdArgs - (with hooks.yamlfmt.settings; [ - # Exit with non-zero status if the file is not formatted - [ lint-only "-lint" ] - # Do not print the diff - [ lint-only "-quiet" ] - # See https://github.com/google/yamlfmt/blob/main/docs/config-file.md#config-file-discovery - [ (configPath != "") "-conf ${configPath}" ] - ]); - in - "${hooks.yamlfmt.package}/bin/yamlfmt ${cmdArgs}"; - }; - yamllint = - { - name = "yamllint"; - description = "Linter for YAML files."; - types = [ "file" "yaml" ]; - package = tools.yamllint; - entry = - let - configFile = builtins.toFile "yamllint.yaml" "${hooks.yamllint.settings.configuration}"; - cmdArgs = - mkCmdArgs - (with hooks.yamllint.settings; [ - # Priorize multiline configuration over serialized configuration and configuration file - [ (configuration != "") "--config-file ${configFile}" ] - [ (configData != "" && configuration == "") "--config-data \"${configData}\"" ] - [ (configPath != "" && configData == "" && configuration == "" && preset == "default") "--config-file ${configPath}" ] - [ (format != "auto") "--format ${format}" ] - [ (preset != "default" && configuration == "") "--config-data ${preset}" ] - [ strict "--strict" ] - ]); - in - "${hooks.yamllint.package}/bin/yamllint ${cmdArgs}"; - }; - zprint = - { - name = "zprint"; - description = "Beautifully format Clojure and Clojurescript source code and s-expressions."; - package = tools.zprint; - entry = "${hooks.zprint.package}/bin/zprint '{:search-config? true}' -w"; - types_or = [ "clojure" "clojurescript" "edn" ]; - }; - - }; } diff --git a/modules/hooks/actionlint.nix b/modules/hooks/actionlint.nix new file mode 100644 index 00000000..d9f63003 --- /dev/null +++ b/modules/hooks/actionlint.nix @@ -0,0 +1,9 @@ +{ config, tools, lib, ... }: +{ + config = { + files = "^.github/workflows/"; + types = [ "yaml" ]; + package = tools.actionlint; + entry = "${config.package}/bin/actionlint"; + }; +} diff --git a/modules/hooks/alejandra.nix b/modules/hooks/alejandra.nix new file mode 100644 index 00000000..393f4738 --- /dev/null +++ b/modules/hooks/alejandra.nix @@ -0,0 +1,53 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + check = + mkOption { + type = types.bool; + description = "Check if the input is already formatted and disable writing in-place the modified content"; + default = false; + example = true; + }; + exclude = + mkOption { + type = types.listOf types.str; + description = "Files or directories to exclude from formatting."; + default = [ ]; + example = [ "flake.nix" "./templates" ]; + }; + threads = + mkOption { + type = types.nullOr types.int; + description = "Number of formatting threads to spawn."; + default = null; + example = 8; + }; + verbosity = + mkOption { + type = types.enum [ "normal" "quiet" "silent" ]; + description = "Whether informational messages or all messages should be hidden or not."; + default = "normal"; + example = "quiet"; + }; + }; + + config = { + package = tools.alejandra; + entry = + let + cmdArgs = + mkCmdArgs (with config.settings; [ + [ check "--check" ] + [ (exclude != [ ]) "--exclude ${lib.strings.concatStringsSep " --exclude " (map lib.escapeShellArg (lib.unique exclude))}" ] + [ (verbosity == "quiet") "-q" ] + [ (verbosity == "silent") "-qq" ] + [ (threads != null) "--threads ${toString threads}" ] + ]); + in + "${config.package}/bin/alejandra ${cmdArgs}"; + files = "\\.nix$"; + }; +} diff --git a/modules/hooks/annex.nix b/modules/hooks/annex.nix new file mode 100644 index 00000000..ade32055 --- /dev/null +++ b/modules/hooks/annex.nix @@ -0,0 +1,7 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.git-annex; + entry = "${config.package}/bin/git-annex pre-commit"; + }; +} diff --git a/modules/hooks/ansible-lint.nix b/modules/hooks/ansible-lint.nix new file mode 100644 index 00000000..d2d8dab4 --- /dev/null +++ b/modules/hooks/ansible-lint.nix @@ -0,0 +1,33 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = mkOption { + type = types.str; + description = "Path to the YAML configuration file."; + # an empty string translates to use default configuration of the + # underlying ansible-lint binary + default = ""; + }; + subdir = mkOption { + type = types.str; + description = "Path to the Ansible subdirectory."; + default = ""; + }; + }; + + config = { + package = tools.ansible-lint; + entry = + let + cmdArgs = + mkCmdArgs [ + [ (config.settings.configPath != "") "-c ${config.settings.configPath}" ] + ]; + in + "${config.package}/bin/ansible-lint ${cmdArgs}"; + files = if config.settings.subdir != "" then "${config.settings.subdir}/" else ""; + }; +} diff --git a/modules/hooks/autoflake.nix b/modules/hooks/autoflake.nix new file mode 100644 index 00000000..92f1b7e6 --- /dev/null +++ b/modules/hooks/autoflake.nix @@ -0,0 +1,34 @@ +{ tools, config, lib, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "Path to autoflake binary."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/autoflake" + ''; + }; + + flags = + mkOption { + type = types.str; + description = "Flags passed to autoflake."; + default = "--in-place --expand-star-imports --remove-duplicate-keys --remove-unused-variables"; + }; + }; + + config = { + package = tools.autoflake; + entry = + let + binPath = migrateBinPathToPackage config "/bin/autoflake"; + in + "${binPath} ${config.settings.flags}"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/bats.nix b/modules/hooks/bats.nix new file mode 100644 index 00000000..b54e584f --- /dev/null +++ b/modules/hooks/bats.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + types = [ "shell" ]; + types_or = [ "bats" "bash" ]; + package = tools.bats; + entry = "${config.package}/bin/bats -p"; + }; +} diff --git a/modules/hooks/beautysh.nix b/modules/hooks/beautysh.nix new file mode 100644 index 00000000..f9a80507 --- /dev/null +++ b/modules/hooks/beautysh.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + types = [ "shell" ]; + package = tools.beautysh; + entry = "${config.package}/bin/beautysh"; + }; +} diff --git a/modules/hooks/biome.nix b/modules/hooks/biome.nix new file mode 100644 index 00000000..71dc3244 --- /dev/null +++ b/modules/hooks/biome.nix @@ -0,0 +1,54 @@ +{ tools, config, lib, mkCmdArgs, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr (types.oneOf [ types.str types.path ]); + description = '' + `biome` binary path. + For example, if you want to use the `biome` binary from `node_modules`, use `"./node_modules/.bin/biome"`. + Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. + ''; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/biome" + ''; + example = lib.literalExpression '' + "./node_modules/.bin/biome" + ''; + }; + + write = + mkOption { + type = types.bool; + description = "Whether to edit files inplace."; + default = true; + }; + + configPath = mkOption { + type = types.str; + description = "Path to the configuration JSON file"; + # an empty string translates to use default configuration of the + # underlying biome binary (i.e biome.json if exists) + default = ""; + }; + }; + + config = { + types_or = [ "javascript" "jsx" "ts" "tsx" "json" ]; + package = tools.biome; + entry = + let + binPath = migrateBinPathToPackage config "/bin/biome"; + cmdArgs = + mkCmdArgs [ + [ (config.settings.write) "--write" ] + [ (config.settings.configPath != "") "--config-path ${config.settings.configPath}" ] + ]; + in + "${binPath} check ${cmdArgs}"; + }; +} diff --git a/modules/hooks/black.nix b/modules/hooks/black.nix new file mode 100644 index 00000000..8080e4df --- /dev/null +++ b/modules/hooks/black.nix @@ -0,0 +1,20 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + flags = mkOption { + type = types.str; + description = "Flags passed to black. See all available [here](https://black.readthedocs.io/en/stable/usage_and_configuration/the_basics.html#command-line-options)."; + default = ""; + example = "--skip-magic-trailing-comma"; + }; + }; + + config = { + package = tools.black; + entry = "${config.package}/bin/black ${config.settings.flags}"; + types = [ "file" "python" ]; + }; +} diff --git a/modules/hooks/cabal-fmt.nix b/modules/hooks/cabal-fmt.nix new file mode 100644 index 00000000..0f4d6e8f --- /dev/null +++ b/modules/hooks/cabal-fmt.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.cabal-fmt; + entry = "${config.package}/bin/cabal-fmt --inplace"; + files = "\\.cabal$"; + }; +} diff --git a/modules/hooks/cabal-gild.nix b/modules/hooks/cabal-gild.nix new file mode 100644 index 00000000..65ca0f7d --- /dev/null +++ b/modules/hooks/cabal-gild.nix @@ -0,0 +1,16 @@ +{ tools, config, lib, pkgs, ... }: +{ + config = { + package = tools.cabal-gild; + entry = + let + script = pkgs.writeShellScript "precommit-cabal-gild" '' + for file in "$@"; do + ${config.package}/bin/cabal-gild --io="$file" + done + ''; + in + builtins.toString script; + files = "\\.cabal$"; + }; +} diff --git a/modules/hooks/cabal2nix.nix b/modules/hooks/cabal2nix.nix new file mode 100644 index 00000000..e71ebc59 --- /dev/null +++ b/modules/hooks/cabal2nix.nix @@ -0,0 +1,21 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + outputFilename = + mkOption { + type = types.str; + description = "The name of the output file generated after running `cabal2nix`."; + default = "default.nix"; + }; + }; + + config = { + package = tools.cabal2nix-dir; + entry = "${config.package}/bin/cabal2nix-dir --outputFileName=${config.settings.outputFilename}"; + files = "\\.cabal$"; + after = [ "hpack" ]; + }; +} diff --git a/modules/hooks/cargo-check.nix b/modules/hooks/cargo-check.nix new file mode 100644 index 00000000..81958f47 --- /dev/null +++ b/modules/hooks/cargo-check.nix @@ -0,0 +1,16 @@ +{ tools, lib, settings, config, ... }: +let + inherit (settings.rust) cargoManifestPath; + cargoManifestPathArg = + lib.optionalString + (cargoManifestPath != null) + "--manifest-path ${lib.escapeShellArg cargoManifestPath}"; +in +{ + config = { + package = tools.cargo; + entry = "${config.package}/bin/cargo check ${cargoManifestPathArg}"; + files = "\\.rs$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/check-added-large-files.nix b/modules/hooks/check-added-large-files.nix new file mode 100644 index 00000000..996e11ef --- /dev/null +++ b/modules/hooks/check-added-large-files.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-added-large-files"; + stages = [ "pre-commit" "pre-push" "manual" ]; + }; +} diff --git a/modules/hooks/check-builtin-literals.nix b/modules/hooks/check-builtin-literals.nix new file mode 100644 index 00000000..eb3290e7 --- /dev/null +++ b/modules/hooks/check-builtin-literals.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-builtin-literals"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/check-case-conflicts.nix b/modules/hooks/check-case-conflicts.nix new file mode 100644 index 00000000..0e471c3b --- /dev/null +++ b/modules/hooks/check-case-conflicts.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-case-conflict"; + types = [ "file" ]; + }; +} diff --git a/modules/hooks/check-docstring-first.nix b/modules/hooks/check-docstring-first.nix new file mode 100644 index 00000000..be4db2b0 --- /dev/null +++ b/modules/hooks/check-docstring-first.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-docstring-first"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/check-executables-have-shebangs.nix b/modules/hooks/check-executables-have-shebangs.nix new file mode 100644 index 00000000..e97efea5 --- /dev/null +++ b/modules/hooks/check-executables-have-shebangs.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-executables-have-shebangs"; + types = [ "text" "executable" ]; + stages = [ "pre-commit" "pre-push" "manual" ]; + }; +} diff --git a/modules/hooks/check-json.nix b/modules/hooks/check-json.nix new file mode 100644 index 00000000..af56868c --- /dev/null +++ b/modules/hooks/check-json.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-json"; + types = [ "json" ]; + }; +} diff --git a/modules/hooks/check-merge-conflicts.nix b/modules/hooks/check-merge-conflicts.nix new file mode 100644 index 00000000..e36e31b7 --- /dev/null +++ b/modules/hooks/check-merge-conflicts.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-merge-conflict"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/check-python.nix b/modules/hooks/check-python.nix new file mode 100644 index 00000000..28f901a9 --- /dev/null +++ b/modules/hooks/check-python.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-ast"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/check-shebang-scripts-are-executable.nix b/modules/hooks/check-shebang-scripts-are-executable.nix new file mode 100644 index 00000000..5ffef257 --- /dev/null +++ b/modules/hooks/check-shebang-scripts-are-executable.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-shebang-scripts-are-executable"; + types = [ "text" ]; + stages = [ "pre-commit" "pre-push" "manual" ]; + }; +} diff --git a/modules/hooks/check-symlinks.nix b/modules/hooks/check-symlinks.nix new file mode 100644 index 00000000..810c3e54 --- /dev/null +++ b/modules/hooks/check-symlinks.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-symlinks"; + types = [ "symlink" ]; + }; +} diff --git a/modules/hooks/check-toml.nix b/modules/hooks/check-toml.nix new file mode 100644 index 00000000..920db480 --- /dev/null +++ b/modules/hooks/check-toml.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-toml"; + types = [ "toml" ]; + }; +} diff --git a/modules/hooks/check-vcs-permalinks.nix b/modules/hooks/check-vcs-permalinks.nix new file mode 100644 index 00000000..26052114 --- /dev/null +++ b/modules/hooks/check-vcs-permalinks.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-vcs-permalinks"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/check-xml.nix b/modules/hooks/check-xml.nix new file mode 100644 index 00000000..a54d9080 --- /dev/null +++ b/modules/hooks/check-xml.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-xml"; + types = [ "xml" ]; + }; +} diff --git a/modules/hooks/check-yaml.nix b/modules/hooks/check-yaml.nix new file mode 100644 index 00000000..4972bc16 --- /dev/null +++ b/modules/hooks/check-yaml.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/check-yaml --multi"; + types = [ "yaml" ]; + }; +} diff --git a/modules/hooks/checkmake.nix b/modules/hooks/checkmake.nix new file mode 100644 index 00000000..9aa2f45d --- /dev/null +++ b/modules/hooks/checkmake.nix @@ -0,0 +1,14 @@ +{ tools, config, lib, ... }: +{ + config = { + types = [ "makefile" ]; + package = tools.checkmake; + entry = + ## NOTE: `checkmake` 0.2.2 landed in nixpkgs on 12 April 2023. Once + ## this gets into a NixOS release, the following code will be useless. + lib.throwIf + (config.package == null) + "The version of nixpkgs used by git-hooks.nix must have `checkmake` in version at least 0.2.2 for it to work on non-Linux systems." + "${config.package}/bin/checkmake"; + }; +} diff --git a/modules/hooks/chktex.nix b/modules/hooks/chktex.nix new file mode 100644 index 00000000..d17f4716 --- /dev/null +++ b/modules/hooks/chktex.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + types = [ "file" "tex" ]; + package = tools.chktex; + entry = "${config.package}/bin/chktex"; + }; +} diff --git a/modules/hooks/circleci.nix b/modules/hooks/circleci.nix new file mode 100644 index 00000000..50c0c6c4 --- /dev/null +++ b/modules/hooks/circleci.nix @@ -0,0 +1,22 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.circleci-cli; + entry = builtins.toString (pkgs.writeShellScript "precommit-circleci" '' + set -e + failed=false + for file in "$@"; do + if ! ${config.package}/bin/circleci config validate "$file" 2>&1 + then + echo "Config file at $file is invalid, check the errors above." + failed=true + fi + done + if [[ $failed == "true" ]]; then + exit 1 + fi + ''); + files = "^.circleci/"; + types = [ "yaml" ]; + }; +} diff --git a/modules/hooks/clang-format.nix b/modules/hooks/clang-format.nix new file mode 100644 index 00000000..50caf492 --- /dev/null +++ b/modules/hooks/clang-format.nix @@ -0,0 +1,20 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.clang-tools; + entry = "${config.package}/bin/clang-format -style=file -i"; + # Source: + # https://github.com/pre-commit/mirrors-clang-format/blob/46516e8f532c8f2d55e801c34a740ebb8036365c/.pre-commit-hooks.yaml + types_or = [ + "c" + "c++" + "c#" + "cuda" + "java" + "javascript" + "json" + "objective-c" + "proto" + ]; + }; +} diff --git a/modules/hooks/clang-tidy.nix b/modules/hooks/clang-tidy.nix new file mode 100644 index 00000000..ac856878 --- /dev/null +++ b/modules/hooks/clang-tidy.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.clang-tools; + entry = "${config.package}/bin/clang-tidy --fix"; + types_or = [ "c" "c++" "c#" "objective-c" ]; + }; +} diff --git a/modules/hooks/clippy.nix b/modules/hooks/clippy.nix new file mode 100644 index 00000000..1955600b --- /dev/null +++ b/modules/hooks/clippy.nix @@ -0,0 +1,72 @@ +{ config, tools, lib, pkgs, settings, ... }: +let + inherit (lib) mkOption types; + inherit (settings.rust) cargoManifestPath; + + cargoManifestPathArg = + lib.optionalString + (cargoManifestPath != null) + "--manifest-path ${lib.escapeShellArg cargoManifestPath}"; + + wrapper = pkgs.symlinkJoin { + name = "clippy-wrapped"; + paths = [ config.packageOverrides.clippy ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/cargo-clippy \ + --prefix PATH : ${lib.makeBinPath [ config.packageOverrides.cargo ]} + ''; + }; +in +{ + options = { + settings = { + denyWarnings = mkOption { + type = types.bool; + description = "Fail when warnings are present"; + default = false; + }; + offline = mkOption { + type = types.bool; + description = "Run clippy offline"; + default = true; + }; + allFeatures = mkOption { + type = types.bool; + description = "Run clippy with --all-features"; + default = false; + }; + extraArgs = mkOption { + type = types.str; + description = "Additional arguments to pass to clippy"; + default = ""; + }; + }; + + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use"; + default = tools.cargo; + defaultText = "tools.cargo"; + }; + clippy = mkOption { + type = types.package; + description = "The clippy package to use"; + default = tools.clippy; + defaultText = "tools.clippy"; + }; + }; + }; + + config = { + package = wrapper; + entry = "${wrapper}/bin/cargo-clippy clippy ${cargoManifestPathArg} ${lib.optionalString config.settings.offline "--offline"} ${lib.optionalString config.settings.allFeatures "--all-features"} ${config.settings.extraArgs} -- ${lib.optionalString config.settings.denyWarnings "-D warnings"}"; + files = "\\.rs$"; + pass_filenames = false; + extraPackages = [ + config.packageOverrides.cargo + config.packageOverrides.clippy + ]; + }; +} diff --git a/modules/hooks/cljfmt.nix b/modules/hooks/cljfmt.nix new file mode 100644 index 00000000..aff5b8eb --- /dev/null +++ b/modules/hooks/cljfmt.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.cljfmt; + entry = "${config.package}/bin/cljfmt fix"; + types_or = [ "clojure" "clojurescript" "edn" ]; + }; +} diff --git a/modules/hooks/cmake-format.nix b/modules/hooks/cmake-format.nix new file mode 100644 index 00000000..b07350f6 --- /dev/null +++ b/modules/hooks/cmake-format.nix @@ -0,0 +1,28 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = mkOption { + type = types.str; + description = "Path to the configuration file (.json,.python,.yaml)"; + default = ""; + example = ".cmake-format.json"; + }; + }; + + config = { + package = tools.cmake-format; + entry = + let + maybeConfigPath = + if config.settings.configPath == "" + # Searches automatically for the config path. + then "" + else "-C ${config.settings.configPath}"; + in + "${config.package}/bin/cmake-format --check ${maybeConfigPath}"; + files = "\\.cmake$|CMakeLists.txt"; + }; +} diff --git a/modules/hooks/commitizen.nix b/modules/hooks/commitizen.nix new file mode 100644 index 00000000..7555ce3e --- /dev/null +++ b/modules/hooks/commitizen.nix @@ -0,0 +1,9 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.commitizen; + entry = "${config.package}/bin/cz check --allow-abort --commit-msg-file"; + stages = [ "commit-msg" ]; + }; +} + diff --git a/modules/hooks/conform.nix b/modules/hooks/conform.nix new file mode 100644 index 00000000..677e31d7 --- /dev/null +++ b/modules/hooks/conform.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.conform; + entry = "${config.package}/bin/conform enforce --commit-msg-file"; + stages = [ "commit-msg" ]; + }; +} diff --git a/modules/hooks/convco.nix b/modules/hooks/convco.nix new file mode 100644 index 00000000..444d776a --- /dev/null +++ b/modules/hooks/convco.nix @@ -0,0 +1,19 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.convco; + entry = + let + convco = config.package; + script = pkgs.writeShellScript "precommit-convco" '' + cat $1 | ${convco}/bin/convco check --from-stdin + ''; + # need version >= 0.4.0 for the --from-stdin flag + toolVersionCheck = lib.versionAtLeast convco.version "0.4.0"; + in + lib.throwIf (convco == null || !toolVersionCheck) "The version of Nixpkgs used by git-hooks.nix does not have the `convco` package (>=0.4.0). Please use a more recent version of Nixpkgs." + builtins.toString + script; + stages = [ "commit-msg" ]; + }; +} diff --git a/modules/hooks/credo.nix b/modules/hooks/credo.nix new file mode 100644 index 00000000..e821007c --- /dev/null +++ b/modules/hooks/credo.nix @@ -0,0 +1,22 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + strict = + mkOption { + type = types.bool; + description = "Whether to auto-promote the changes."; + default = true; + }; + }; + + config = { + package = tools.elixir; + entry = + let strict = if config.settings.strict then "--strict" else ""; + in "${config.package}/bin/mix credo ${strict}"; + files = "\\.exs?$"; + }; +} diff --git a/modules/hooks/crystal.nix b/modules/hooks/crystal.nix new file mode 100644 index 00000000..6a0ef778 --- /dev/null +++ b/modules/hooks/crystal.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.crystal; + entry = "${config.package}/bin/crystal tool format"; + files = "\.cr$"; + }; +} diff --git a/modules/hooks/cspell.nix b/modules/hooks/cspell.nix new file mode 100644 index 00000000..51e09c5f --- /dev/null +++ b/modules/hooks/cspell.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.cspell; + entry = "${config.package}/bin/cspell --no-summary"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/dart-analyze.nix b/modules/hooks/dart-analyze.nix new file mode 100644 index 00000000..71a5ee9f --- /dev/null +++ b/modules/hooks/dart-analyze.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.dart; + entry = "${config.package}/bin/dart analyze"; + types = [ "dart" ]; + }; +} diff --git a/modules/hooks/dart-format.nix b/modules/hooks/dart-format.nix new file mode 100644 index 00000000..cb15b560 --- /dev/null +++ b/modules/hooks/dart-format.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.dart; + entry = "${config.package}/bin/dart format"; + types = [ "dart" ]; + }; +} diff --git a/modules/hooks/deadnix.nix b/modules/hooks/deadnix.nix new file mode 100644 index 00000000..7f50cf06 --- /dev/null +++ b/modules/hooks/deadnix.nix @@ -0,0 +1,75 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + edit = + mkOption { + type = types.bool; + description = "Remove unused code and write to source file."; + default = false; + }; + + exclude = + mkOption { + type = types.listOf types.str; + description = "Files to exclude from analysis."; + default = [ ]; + }; + + hidden = + mkOption { + type = types.bool; + description = "Recurse into hidden subdirectories and process hidden .*.nix files."; + default = false; + }; + + noLambdaArg = + mkOption { + type = types.bool; + description = "Don't check lambda parameter arguments."; + default = false; + }; + + noLambdaPatternNames = + mkOption { + type = types.bool; + description = "Don't check lambda pattern names (don't break nixpkgs `callPackage`)."; + default = false; + }; + + noUnderscore = + mkOption { + type = types.bool; + description = "Don't check any bindings that start with a `_`."; + default = false; + }; + + quiet = + mkOption { + type = types.bool; + description = "Don't print a dead code report."; + default = false; + }; + }; + + config = { + package = tools.deadnix; + entry = + let + cmdArgs = + mkCmdArgs (with config.settings; [ + [ noLambdaArg "--no-lambda-arg" ] + [ noLambdaPatternNames "--no-lambda-pattern-names" ] + [ noUnderscore "--no-underscore" ] + [ quiet "--quiet" ] + [ hidden "--hidden" ] + [ edit "--edit" ] + [ (exclude != [ ]) "--exclude ${lib.escapeShellArgs exclude}" ] + ]); + in + "${config.package}/bin/deadnix ${cmdArgs} --fail"; + files = "\\.nix$"; + }; +} diff --git a/modules/hooks/denofmt.nix b/modules/hooks/denofmt.nix new file mode 100644 index 00000000..48d195a6 --- /dev/null +++ b/modules/hooks/denofmt.nix @@ -0,0 +1,36 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + write = + mkOption { + type = types.bool; + description = "Whether to edit files inplace."; + default = true; + }; + configPath = + mkOption { + type = types.str; + description = "Path to the configuration JSON file"; + # an empty string translates to use default configuration of the + # underlying deno binary (i.e deno.json or deno.jsonc) + default = ""; + }; + }; + + config = { + types_or = [ "javascript" "jsx" "ts" "tsx" "markdown" "json" ]; + package = tools.deno; + entry = + let + cmdArgs = + mkCmdArgs [ + [ (!config.settings.write) "--check" ] + [ (config.settings.configPath != "") "-c ${config.settings.configPath}" ] + ]; + in + "${config.package}/bin/deno fmt ${cmdArgs}"; + }; +} diff --git a/modules/hooks/denolint.nix b/modules/hooks/denolint.nix new file mode 100644 index 00000000..a0ad03d1 --- /dev/null +++ b/modules/hooks/denolint.nix @@ -0,0 +1,38 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + format = + mkOption { + type = types.enum [ "default" "compact" "json" ]; + description = "Output format."; + default = "default"; + }; + + configPath = + mkOption { + type = types.str; + description = "Path to the configuration JSON file"; + # an empty string translates to use default configuration of the + # underlying deno binary (i.e deno.json or deno.jsonc) + default = ""; + }; + }; + + config = { + types_or = [ "javascript" "jsx" "ts" "tsx" ]; + package = tools.deno; + entry = + let + cmdArgs = + mkCmdArgs [ + [ (config.settings.format == "compact") "--compact" ] + [ (config.settings.format == "json") "--json" ] + [ (config.settings.configPath != "") "-c ${config.settings.configPath}" ] + ]; + in + "${config.package}/bin/deno lint ${cmdArgs}"; + }; +} diff --git a/modules/hooks/detect-aws-credentials.nix b/modules/hooks/detect-aws-credentials.nix new file mode 100644 index 00000000..982e7a8e --- /dev/null +++ b/modules/hooks/detect-aws-credentials.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/detect-aws-credentials --allow-missing-credentials"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/detect-private-keys.nix b/modules/hooks/detect-private-keys.nix new file mode 100644 index 00000000..19130d02 --- /dev/null +++ b/modules/hooks/detect-private-keys.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/detect-private-key"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/dhall-format.nix b/modules/hooks/dhall-format.nix new file mode 100644 index 00000000..e5b37d4a --- /dev/null +++ b/modules/hooks/dhall-format.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.dhall; + entry = "${config.package}/bin/dhall format"; + files = "\.dhall$"; + }; +} diff --git a/modules/hooks/dialyzer.nix b/modules/hooks/dialyzer.nix new file mode 100644 index 00000000..227728d9 --- /dev/null +++ b/modules/hooks/dialyzer.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.elixir; + entry = "${config.package}/bin/mix dialyzer"; + files = "\.exs?$"; + }; +} diff --git a/modules/hooks/dune-fmt.nix b/modules/hooks/dune-fmt.nix new file mode 100644 index 00000000..37281f61 --- /dev/null +++ b/modules/hooks/dune-fmt.nix @@ -0,0 +1,35 @@ +{ config, lib, pkgs, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + auto-promote = mkOption { + type = types.bool; + description = "Whether to auto-promote the changes."; + default = true; + }; + + extraRuntimeInputs = mkOption { + type = types.listOf types.package; + description = "Extra runtimeInputs to add to the environment, eg. `ocamlformat`."; + default = [ ]; + }; + }; + + config = { + package = tools.dune-fmt; + entry = + let + auto-promote = if config.settings.auto-promote then "--auto-promote" else ""; + run-dune-fmt = pkgs.writeShellApplication { + name = "run-dune-fmt"; + runtimeInputs = config.settings.extraRuntimeInputs; + text = "${config.package}/bin/dune-fmt ${auto-promote}"; + }; + in + "${run-dune-fmt}/bin/run-dune-fmt"; + pass_filenames = false; + extraPackages = config.settings.extraRuntimeInputs; + }; +} diff --git a/modules/hooks/dune-opam-sync.nix b/modules/hooks/dune-opam-sync.nix new file mode 100644 index 00000000..07835651 --- /dev/null +++ b/modules/hooks/dune-opam-sync.nix @@ -0,0 +1,12 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.dune-build-opam-files; + entry = "${config.package}/bin/dune-build-opam-files"; + files = "(\.opam$)|(\.opam.template$)|((^|/)dune-project$)"; + ## We don't pass filenames because they can only be misleading. Indeed, + ## we need to re-run `dune build` for every `*.opam` file, but also when + ## the `dune-project` file has changed. + pass_filenames = false; + }; +} diff --git a/modules/hooks/eclint.nix b/modules/hooks/eclint.nix new file mode 100644 index 00000000..bc1a9be8 --- /dev/null +++ b/modules/hooks/eclint.nix @@ -0,0 +1,56 @@ +{ tools, config, lib, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + fix = + mkOption { + type = types.bool; + description = "Modify files in place rather than showing the errors."; + default = false; + }; + summary = + mkOption { + type = types.bool; + description = "Only show number of errors per file."; + default = false; + }; + color = + mkOption { + type = types.enum [ "auto" "always" "never" ]; + description = "When to generate colored output."; + default = "auto"; + }; + exclude = + mkOption { + type = types.listOf types.str; + description = "Filter to exclude files."; + default = [ ]; + }; + verbosity = + mkOption { + type = types.enum [ 0 1 2 3 4 ]; + description = "Log level verbosity"; + default = 0; + }; + }; + + config = { + types = [ "file" ]; + package = tools.eclint; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ fix "-fix" ] + [ summary "-summary" ] + [ (color != "auto") "-color ${color}" ] + [ (exclude != [ ]) "-exclude ${lib.escapeShellArgs exclude}" ] + [ (verbosity != 0) "-verbosity ${toString verbosity}" ] + ]); + in + "${config.package}/bin/eclint ${cmdArgs}"; + }; +} diff --git a/modules/hooks/editorconfig-checker.nix b/modules/hooks/editorconfig-checker.nix new file mode 100644 index 00000000..dd149ca9 --- /dev/null +++ b/modules/hooks/editorconfig-checker.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.editorconfig-checker; + entry = "${config.package}/bin/editorconfig-checker"; + types = [ "file" ]; + }; +} diff --git a/modules/hooks/elm-format.nix b/modules/hooks/elm-format.nix new file mode 100644 index 00000000..c2fb3a17 --- /dev/null +++ b/modules/hooks/elm-format.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.elm-format; + entry = "${config.package}/bin/elm-format --yes --elm-version=0.19"; + files = "\.elm$"; + }; +} diff --git a/modules/hooks/elm-review.nix b/modules/hooks/elm-review.nix new file mode 100644 index 00000000..cb003f7d --- /dev/null +++ b/modules/hooks/elm-review.nix @@ -0,0 +1,9 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.elm-review; + entry = "${config.package}/bin/elm-review"; + files = "\.elm$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/elm-test.nix b/modules/hooks/elm-test.nix new file mode 100644 index 00000000..bed84089 --- /dev/null +++ b/modules/hooks/elm-test.nix @@ -0,0 +1,9 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.elm-test; + entry = "${config.package}/bin/elm-test"; + files = "\.elm$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/end-of-file-fixer.nix b/modules/hooks/end-of-file-fixer.nix new file mode 100644 index 00000000..e15c6f72 --- /dev/null +++ b/modules/hooks/end-of-file-fixer.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/end-of-file-fixer"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/eslint.nix b/modules/hooks/eslint.nix new file mode 100644 index 00000000..e931157b --- /dev/null +++ b/modules/hooks/eslint.nix @@ -0,0 +1,42 @@ +{ tools, config, lib, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr (types.oneOf [ types.str types.path ]); + description = '' + `eslint` binary path. + For example, if you want to use the `eslint` binary from `node_modules`, use `"./node_modules/.bin/eslint"`. + Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. + ''; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/eslint" + ''; + example = lib.literalExpression '' + "./node_modules/.bin/eslint" + ''; + }; + + extensions = + mkOption { + type = types.str; + description = + "The pattern of files to run on, see [https://pre-commit.com/#hooks-files](https://pre-commit.com/#hooks-files)."; + default = "\\.js$"; + }; + }; + + config = { + package = tools.eslint; + entry = + let + binPath = migrateBinPathToPackage config "/bin/eslint"; + in + "${binPath} --fix"; + files = "${config.settings.extensions}"; + }; +} diff --git a/modules/hooks/fix-byte-order-marker.nix b/modules/hooks/fix-byte-order-marker.nix new file mode 100644 index 00000000..c86ee537 --- /dev/null +++ b/modules/hooks/fix-byte-order-marker.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/fix-byte-order-marker"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/fix-encoding-pragma.nix b/modules/hooks/fix-encoding-pragma.nix new file mode 100644 index 00000000..47900e15 --- /dev/null +++ b/modules/hooks/fix-encoding-pragma.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/fix-encoding-pragma"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/flake-checker.nix b/modules/hooks/flake-checker.nix new file mode 100644 index 00000000..e30f7f80 --- /dev/null +++ b/modules/hooks/flake-checker.nix @@ -0,0 +1,9 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.flake-checker; + entry = "${config.package}/bin/flake-checker -f"; + files = "(^flake\.nix$|^flake\.lock$)"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/flake8.nix b/modules/hooks/flake8.nix new file mode 100644 index 00000000..e4646819 --- /dev/null +++ b/modules/hooks/flake8.nix @@ -0,0 +1,45 @@ +{ tools, config, lib, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; + + extendIgnoreStr = + if lib.lists.length config.settings.extendIgnore > 0 + then "--extend-ignore " + builtins.concatStringsSep "," config.settings.extendIgnore + else ""; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "flake8 binary path. Should be used to specify flake8 binary from your Python environment."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/flake8" + ''; + }; + extendIgnore = + mkOption { + type = types.listOf types.str; + description = "List of additional ignore codes"; + default = [ ]; + example = [ "E501" ]; + }; + format = + mkOption { + type = types.str; + description = "Output format."; + default = "default"; + }; + }; + + config = { + package = tools.flake8; + entry = + let + binPath = migrateBinPathToPackage config "/bin/flake8"; + in + "${binPath} --format ${config.settings.format} ${extendIgnoreStr}"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/flynt.nix b/modules/hooks/flynt.nix new file mode 100644 index 00000000..50055312 --- /dev/null +++ b/modules/hooks/flynt.nix @@ -0,0 +1,97 @@ +{ tools, config, lib, mkCmdArgs, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + aggressive = + mkOption { + type = types.bool; + description = "Include conversions with potentially changed behavior."; + default = false; + }; + binPath = + mkOption { + type = types.nullOr types.str; + description = "flynt binary path. Can be used to specify the flynt binary from an existing Python environment."; + default = null; + }; + dry-run = + mkOption { + type = types.bool; + description = "Do not change files in-place and print diff instead."; + default = false; + }; + exclude = + mkOption { + type = types.listOf types.str; + description = "Ignore files with given strings in their absolute path."; + default = [ ]; + }; + fail-on-change = + mkOption { + type = types.bool; + description = "Fail when diff is not empty (for linting purposes)."; + default = true; + }; + line-length = + mkOption { + type = types.nullOr types.int; + description = "Convert expressions spanning multiple lines, only if the resulting single line will fit into this line length limit."; + default = null; + }; + no-multiline = + mkOption { + type = types.bool; + description = "Convert only single line expressions."; + default = false; + }; + quiet = + mkOption { + type = types.bool; + description = "Run without output."; + default = false; + }; + string = + mkOption { + type = types.bool; + description = "Interpret the input as a Python code snippet and print the converted version."; + default = false; + }; + transform-concats = + mkOption { + type = types.bool; + description = "Replace string concatenations with f-strings."; + default = false; + }; + verbose = + mkOption { + type = types.bool; + description = "Run with verbose output."; + default = false; + }; + }; + + config = { + package = tools.flynt; + entry = + let + binPath = migrateBinPathToPackage config "/bin/flynt"; + cmdArgs = + mkCmdArgs (with config.settings; [ + [ aggressive "--aggressive" ] + [ dry-run "--dry-run" ] + [ (exclude != [ ]) "--exclude ${lib.escapeShellArgs exclude}" ] + [ fail-on-change "--fail-on-change" ] + [ (line-length != null) "--line-length ${toString line-length}" ] + [ no-multiline "--no-multiline" ] + [ quiet "--quiet" ] + [ string "--string" ] + [ transform-concats "--transform-concats" ] + [ verbose "--verbose" ] + ]); + in + "${binPath} ${cmdArgs}"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/forbid-new-submodules.nix b/modules/hooks/forbid-new-submodules.nix new file mode 100644 index 00000000..b1b9b829 --- /dev/null +++ b/modules/hooks/forbid-new-submodules.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/forbid-new-submodules"; + types = [ "directory" ]; + }; +} diff --git a/modules/hooks/fourmolu.nix b/modules/hooks/fourmolu.nix new file mode 100644 index 00000000..927df030 --- /dev/null +++ b/modules/hooks/fourmolu.nix @@ -0,0 +1,22 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + defaultExtensions = mkOption { + type = types.listOf types.str; + description = "Haskell language extensions to enable."; + default = [ ]; + }; + }; + + config = { + package = tools.fourmolu; + entry = + "${config.package}/bin/fourmolu --mode inplace ${ + lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) config.settings.defaultExtensions) + }"; + files = "\\.l?hs(-boot)?$"; + }; +} diff --git a/modules/hooks/fprettify.nix b/modules/hooks/fprettify.nix new file mode 100644 index 00000000..f305c99d --- /dev/null +++ b/modules/hooks/fprettify.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + types = [ "fortran " ]; + package = tools.fprettify; + entry = "${config.package}/bin/fprettify"; + }; +} diff --git a/modules/hooks/gitlint.nix b/modules/hooks/gitlint.nix new file mode 100644 index 00000000..46be3125 --- /dev/null +++ b/modules/hooks/gitlint.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.gitlint; + entry = "${config.package}/bin/gitlint --staged --msg-filename"; + stages = [ "commit-msg" ]; + }; +} diff --git a/modules/hooks/gofmt.nix b/modules/hooks/gofmt.nix new file mode 100644 index 00000000..11e26483 --- /dev/null +++ b/modules/hooks/gofmt.nix @@ -0,0 +1,25 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.go; + entry = + let + script = pkgs.writeShellScript "precommit-gofmt" '' + set -e + failed=false + for file in "$@"; do + # redirect stderr so that violations and summaries are properly interleaved. + if ! ${config.package}/bin/gofmt -l -w "$file" 2>&1 + then + failed=true + fi + done + if [[ $failed == "true" ]]; then + exit 1 + fi + ''; + in + builtins.toString script; + files = "\\.go$"; + }; +} diff --git a/modules/hooks/golangci-lint.nix b/modules/hooks/golangci-lint.nix new file mode 100644 index 00000000..a26f0c17 --- /dev/null +++ b/modules/hooks/golangci-lint.nix @@ -0,0 +1,20 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.golangci-lint; + entry = + let + script = pkgs.writeShellScript "precommit-golangci-lint" '' + set -e + for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do + ${config.package}/bin/golangci-lint run ./"$dir" + done + ''; + in + builtins.toString script; + files = "\\.go$"; + # to avoid multiple invocations of the same directory input, provide + # all file names in a single run. + require_serial = true; + }; +} diff --git a/modules/hooks/golines.nix b/modules/hooks/golines.nix new file mode 100644 index 00000000..74c5977b --- /dev/null +++ b/modules/hooks/golines.nix @@ -0,0 +1,37 @@ +{ tools, config, lib, pkgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + flags = mkOption { + type = types.str; + description = "Flags passed to golines. See all available [here](https://github.com/segmentio/golines?tab=readme-ov-file#options)"; + default = ""; + example = "-m 120"; + }; + }; + + config = { + package = tools.golines; + entry = + let + script = pkgs.writeShellScript "precommit-golines" '' + set -e + failed=false + for file in "$@"; do + # redirect stderr so that violations and summaries are properly interleaved. + if ! ${config.package}/bin/golines ${config.settings.flags} -w "$file" 2>&1 + then + failed=true + fi + done + if [[ $failed == "true" ]]; then + exit 1 + fi + ''; + in + builtins.toString script; + files = "\\.go$"; + }; +} diff --git a/modules/hooks/gotest.nix b/modules/hooks/gotest.nix new file mode 100644 index 00000000..608271dc --- /dev/null +++ b/modules/hooks/gotest.nix @@ -0,0 +1,42 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.go; + entry = + let + script = pkgs.writeShellScript "precommit-gotest" '' + set -e + # find all directories that contain tests + dirs=() + for file in "$@"; do + # either the file is a test + if [[ "$file" = *_test.go ]]; then + dirs+=("$(dirname "$file")") + continue + fi + + # or the file has an associated test + filename="''${file%.go}" + test_file="''${filename}_test.go" + if [[ -f "$test_file" ]]; then + dirs+=("$(dirname "$test_file")") + continue + fi + done + + # ensure we are not duplicating dir entries + IFS=$'\n' sorted_dirs=($(sort -u <<<"''${dirs[*]}")); unset IFS + + # test each directory one by one + for dir in "''${sorted_dirs[@]}"; do + ${config.package}/bin/go test "./$dir" + done + ''; + in + builtins.toString script; + files = "\.go$"; + # to avoid multiple invocations of the same directory input, provide + # all file names in a single run. + require_serial = true; + }; +} diff --git a/modules/hooks/govet.nix b/modules/hooks/govet.nix new file mode 100644 index 00000000..4a3f73e2 --- /dev/null +++ b/modules/hooks/govet.nix @@ -0,0 +1,21 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.go; + entry = + let + # go vet requires package (directory) names as inputs. + script = pkgs.writeShellScript "precommit-govet" '' + set -e + for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do + ${config.package}/bin/go vet -C ./"$dir" + done + ''; + in + builtins.toString script; + # to avoid multiple invocations of the same directory input, provide + # all file names in a single run. + require_serial = true; + files = "\.go$"; + }; +} diff --git a/modules/hooks/gptcommit.nix b/modules/hooks/gptcommit.nix new file mode 100644 index 00000000..52a2f2ed --- /dev/null +++ b/modules/hooks/gptcommit.nix @@ -0,0 +1,17 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.gptcommit; + entry = + let + script = pkgs.writeShellScript "precommit-gptcomit" '' + ${config.package}/bin/gptcommit prepare-commit-msg --commit-source \ + "$PRE_COMMIT_COMMIT_MSG_SOURCE" --commit-msg-file "$1" + ''; + in + lib.throwIf (config.package == null) "The version of Nixpkgs used by git-hooks.nix does not have the `gptcommit` package. Please use a more recent version of Nixpkgs." + toString + script; + stages = [ "prepare-commit-msg" ]; + }; +} diff --git a/modules/hooks/hadolint.nix b/modules/hooks/hadolint.nix new file mode 100644 index 00000000..49fdc68e --- /dev/null +++ b/modules/hooks/hadolint.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.hadolint; + entry = "${config.package}/bin/hadolint"; + files = "Dockerfile$"; + }; +} diff --git a/modules/hooks/headache.nix b/modules/hooks/headache.nix new file mode 100644 index 00000000..789de7aa --- /dev/null +++ b/modules/hooks/headache.nix @@ -0,0 +1,27 @@ +{ tools, config, lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + header-file = mkOption { + type = types.str; + description = "Path to the header file."; + default = ".header"; + }; + }; + + config = { + ## NOTE: Supported `files` are taken from + ## https://github.com/Frama-C/headache/blob/master/config_builtin.txt + files = "(\\.ml[ily]?$)|(\\.fmli?$)|(\\.[chy]$)|(\\.tex$)|(Makefile)|(README)|(LICENSE)"; + package = tools.headache; + entry = + ## NOTE: `headache` made into in nixpkgs on 12 April 2023. At the + ## next NixOS release, the following code will become irrelevant. + lib.throwIf + (config.package == null) + "The version of nixpkgs used by git-hooks.nix does not have `ocamlPackages.headache`. Please use a more recent version of nixpkgs." + "${config.package}/bin/headache -h ${config.settings.header-file}"; + }; +} diff --git a/modules/hooks/hindent.nix b/modules/hooks/hindent.nix new file mode 100644 index 00000000..43eed284 --- /dev/null +++ b/modules/hooks/hindent.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.hindent; + entry = "${config.package}/bin/hindent"; + files = "\.l?hs$"; + }; +} diff --git a/modules/hooks/hlint.nix b/modules/hooks/hlint.nix new file mode 100644 index 00000000..78a5f293 --- /dev/null +++ b/modules/hooks/hlint.nix @@ -0,0 +1,19 @@ +{ config, lib, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + hintFile = + mkOption { + type = types.nullOr (types.oneOf [ types.str types.path ]); + description = "Path to hlint.yaml. By default, hlint searches for .hlint.yaml in the project root."; + default = null; + }; + }; + config = { + package = tools.hlint; + entry = "${config.package}/bin/hlint${if config.settings.hintFile == null then "" else " --hint=${config.settings.hintFile}"}"; + files = "\\.l?hs(-boot)?$"; + }; +} diff --git a/modules/hooks/hpack.nix b/modules/hooks/hpack.nix new file mode 100644 index 00000000..8854c795 --- /dev/null +++ b/modules/hooks/hpack.nix @@ -0,0 +1,29 @@ +{ config, lib, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + silent = + mkOption { + type = types.bool; + description = "Whether generation should be silent."; + default = false; + }; + }; + + config = { + package = tools.hpack-dir; + entry = "${config.package}/bin/hpack-dir --${if config.settings.silent then "silent" else "verbose"}"; + files = "(\\.l?hs(-boot)?$)|(\\.cabal$)|((^|/)package\\.yaml$)"; + # We don't pass filenames because they can only be misleading. + # Indeed, we need to rerun `hpack` in every directory: + # 1. In which there is a *.cabal file, or + # 2. Below which there are haskell files, or + # 3. In which there is a package.yaml that references haskell files + # that have been changed at arbitrary locations specified in that + # file. + # In other words: We have no choice but to always run `hpack` on every `package.yaml` directory. + pass_filenames = false; + }; +} diff --git a/modules/hooks/html-tidy.nix b/modules/hooks/html-tidy.nix new file mode 100644 index 00000000..4e78cbca --- /dev/null +++ b/modules/hooks/html-tidy.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.html-tidy; + entry = "${config.package}/bin/tidy -modify -indent -quiet"; + types = [ "html" ]; + }; +} diff --git a/modules/hooks/hunspell.nix b/modules/hooks/hunspell.nix new file mode 100644 index 00000000..5c9f97e9 --- /dev/null +++ b/modules/hooks/hunspell.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.hunspell; + entry = "${config.package}/bin/hunspell -l"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/isort.nix b/modules/hooks/isort.nix new file mode 100644 index 00000000..ab032f37 --- /dev/null +++ b/modules/hooks/isort.nix @@ -0,0 +1,34 @@ +{ config, lib, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + profile = + mkOption { + type = types.enum [ "" "black" "django" "pycharm" "google" "open_stack" "plone" "attrs" "hug" "wemake" "appnexus" ]; + description = "Built-in profiles to allow easy interoperability with common projects and code styles."; + default = ""; + }; + flags = + mkOption { + type = types.str; + description = "Flags passed to isort. See all available [here](https://pycqa.github.io/isort/docs/configuration/options.html)."; + default = ""; + }; + }; + + config = { + types = [ "file" "python" ]; + package = tools.isort; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (profile != "") " --profile ${profile}" ] + ]); + in + "${config.package}/bin/isort${cmdArgs} ${config.settings.flags}"; + }; +} diff --git a/modules/hooks/juliaformatter.nix b/modules/hooks/juliaformatter.nix new file mode 100644 index 00000000..67f7e813 --- /dev/null +++ b/modules/hooks/juliaformatter.nix @@ -0,0 +1,22 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.julia-bin; + entry = '' + ${config.package}/bin/julia -e ' + using Pkg + Pkg.activate(".") + using JuliaFormatter + format(ARGS) + out = Cmd(`git diff --name-only`) |> read |> String + if out == "" + exit(0) + else + @error "Some files have been formatted !!!" + write(stdout, out) + exit(1) + end' + ''; + files = "\\.jl$"; + }; +} diff --git a/modules/hooks/lacheck.nix b/modules/hooks/lacheck.nix new file mode 100644 index 00000000..b4b52f8f --- /dev/null +++ b/modules/hooks/lacheck.nix @@ -0,0 +1,34 @@ +{ config, lib, pkgs, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + checklevel = mkOption { + type = types.enum [ "Error" "Warning" "Information" "Hint" ]; + description = + "The diagnostic check level"; + default = "Warning"; + }; + configuration = mkOption { + type = types.attrs; + description = + "See https://github.com/LuaLS/lua-language-server/wiki/Configuration-File#luarcjson"; + default = { }; + }; + }; + + config = + let + script = pkgs.writeShellScript "precommit-mdsh" '' + for file in $(echo "$@"); do + "${config.package}/bin/lacheck" "$file" + done + ''; + in + { + types = [ "file" "tex" ]; + package = tools.lacheck; + entry = "${script}"; + }; +} diff --git a/modules/hooks/latexindent.nix b/modules/hooks/latexindent.nix new file mode 100644 index 00000000..99d95199 --- /dev/null +++ b/modules/hooks/latexindent.nix @@ -0,0 +1,20 @@ +{ config, lib, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + flags = + mkOption { + type = types.str; + description = "Flags passed to latexindent. See available flags [here](https://latexindentpl.readthedocs.io/en/latest/sec-how-to-use.html#from-the-command-line)"; + default = "--local --silent --overwriteIfDifferent"; + }; + }; + + config = { + types = [ "file" "tex" ]; + package = tools.latexindent; + entry = "${config.package}/bin/latexindent ${config.settings.flags}"; + }; +} diff --git a/modules/hooks/lua-ls.nix b/modules/hooks/lua-ls.nix new file mode 100644 index 00000000..95ae7b89 --- /dev/null +++ b/modules/hooks/lua-ls.nix @@ -0,0 +1,62 @@ +{ config, lib, pkgs, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + checklevel = mkOption { + type = types.enum [ "Error" "Warning" "Information" "Hint" ]; + description = + "The diagnostic check level"; + default = "Warning"; + }; + configuration = mkOption { + type = types.attrs; + description = + "See https://github.com/LuaLS/lua-language-server/wiki/Configuration-File#luarcjson"; + default = { }; + }; + }; + + config = + let + # .luarc.json has to be in a directory, + # or lua-language-server will hang forever. + luarc = pkgs.writeText ".luarc.json" (builtins.toJSON config.settings.configuration); + luarc-dir = pkgs.stdenv.mkDerivation { + name = "luarc"; + unpackPhase = "true"; + installPhase = '' + mkdir $out + cp ${luarc} $out/.luarc.json + ''; + }; + script = pkgs.writeShellApplication { + name = "lua-ls-lint"; + runtimeInputs = [ config.package pkgs.jq ]; + checkPhase = ""; # The default checkPhase depends on GHC + text = '' + set -e + export logpath="$(mktemp -d)" + lua-language-server --check $(realpath .) \ + --checklevel="${config.settings.checklevel}" \ + --configpath="${luarc-dir}/.luarc.json" \ + --logpath="$logpath" + if [[ -f $logpath/check.json ]]; then + echo "+++++++++++++++ lua-language-server diagnostics +++++++++++++++" + cat $logpath/check.json + diagnostic_count=$(jq 'length' $logpath/check.json) + if [ "$diagnostic_count" -gt 0 ]; then + exit 1 + fi + fi + ''; + }; + in + { + package = tools.lua-language-server; + entry = "${script}/bin/lua-ls-lint"; + files = "\\.lua$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/luacheck.nix b/modules/hooks/luacheck.nix new file mode 100644 index 00000000..b4d87acc --- /dev/null +++ b/modules/hooks/luacheck.nix @@ -0,0 +1,8 @@ +{ tools, config, lib, ... }: +{ + config = { + package = tools.luacheck; + entry = "${config.package}/bin/luacheck"; + types = [ "lua" ]; + }; +} diff --git a/modules/hooks/lychee.nix b/modules/hooks/lychee.nix new file mode 100644 index 00000000..552c1baa --- /dev/null +++ b/modules/hooks/lychee.nix @@ -0,0 +1,34 @@ +{ config, lib, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = + mkOption { + type = types.str; + description = "Path to the config file."; + default = ""; + }; + flags = + mkOption { + type = types.str; + description = "Flags passed to lychee. See all available [here](https://lychee.cli.rs/#/usage/cli)."; + default = ""; + }; + }; + + config = { + package = tools.lychee; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (configPath != "") " --config ${configPath}" ] + ]); + in + "${config.package}/bin/lychee${cmdArgs} ${config.settings.flags}"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/markdownlint.nix b/modules/hooks/markdownlint.nix new file mode 100644 index 00000000..7befcf72 --- /dev/null +++ b/modules/hooks/markdownlint.nix @@ -0,0 +1,21 @@ +{ config, lib, pkgs, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configuration = + mkOption { + type = types.attrs; + description = + "See https://github.com/DavidAnson/markdownlint/blob/main/schema/.markdownlint.jsonc"; + default = { }; + }; + }; + + config = { + package = tools.markdownlint-cli; + entry = "${config.package}/bin/markdownlint -c ${pkgs.writeText "markdownlint.json" (builtins.toJSON config.settings.configuration)}"; + files = "\\.md$"; + }; +} diff --git a/modules/hooks/mdformat.nix b/modules/hooks/mdformat.nix new file mode 100644 index 00000000..a99efd9d --- /dev/null +++ b/modules/hooks/mdformat.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.mdformat; + entry = "${config.package}/bin/mdformat"; + types = [ "markdown" ]; + }; +} diff --git a/modules/hooks/mdl.nix b/modules/hooks/mdl.nix new file mode 100644 index 00000000..7a0aa717 --- /dev/null +++ b/modules/hooks/mdl.nix @@ -0,0 +1,105 @@ +{ config, lib, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = + mkOption { + type = types.str; + description = "The configuration file to use."; + default = ""; + }; + git-recurse = + mkOption { + type = types.bool; + description = "Only process files known to git when given a directory."; + default = false; + }; + ignore-front-matter = + mkOption { + type = types.bool; + description = "Ignore YAML front matter."; + default = false; + }; + json = + mkOption { + type = types.bool; + description = "Format output as JSON."; + default = false; + }; + rules = + mkOption { + type = types.listOf types.str; + description = "Markdown rules to use for linting. Per default all rules are processed."; + default = [ ]; + }; + rulesets = + mkOption { + type = types.listOf types.str; + description = "Specify additional ruleset files to load."; + default = [ ]; + }; + show-aliases = + mkOption { + type = types.bool; + description = "Show rule alias instead of rule ID when viewing rules."; + default = false; + }; + warnings = + mkOption { + type = types.bool; + description = "Show Kramdown warnings."; + default = false; + }; + skip-default-ruleset = + mkOption { + type = types.bool; + description = "Do not load the default markdownlint ruleset. Use this option if you only want to load custom rulesets."; + default = false; + }; + style = + mkOption { + type = types.str; + description = "Select which style mdl uses."; + default = "default"; + }; + tags = + mkOption { + type = types.listOf types.str; + description = "Markdown rules to use for linting containing the given tags. Per default all rules are processed."; + default = [ ]; + }; + verbose = + mkOption { + type = types.bool; + description = "Increase verbosity."; + default = false; + }; + }; + + config = { + package = tools.mdl; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (configPath != "") "--config ${configPath}" ] + [ git-recurse "--git-recurse" ] + [ ignore-front-matter "--ignore-front-matter" ] + [ json "--json" ] + [ (rules != [ ]) "--rules ${lib.strings.concatStringsSep "," rules}" ] + [ (rulesets != [ ]) "--rulesets ${lib.strings.concatStringsSep "," rulesets}" ] + [ show-aliases "--show-aliases" ] + [ warnings "--warnings" ] + [ skip-default-ruleset "--skip-default-ruleset" ] + [ (style != "") "--style ${style}" ] + [ (tags != [ ]) "--tags ${lib.strings.concatStringsSep "," tags}" ] + [ verbose "--verbose" ] + ]); + in + "${config.package}/bin/mdl ${cmdArgs}"; + files = "\\.md$"; + }; +} diff --git a/modules/hooks/mdsh.nix b/modules/hooks/mdsh.nix new file mode 100644 index 00000000..e1fe791f --- /dev/null +++ b/modules/hooks/mdsh.nix @@ -0,0 +1,17 @@ +{ config, tools, lib, pkgs, ... }: +{ + config = { + package = tools.mdsh; + entry = + let + script = pkgs.writeShellScript "precommit-mdsh" '' + for file in $(echo "$@"); do + ${config.package}/bin/mdsh -i "$file" + done + ''; + in + toString script; + files = "\.md$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/mix-format.nix b/modules/hooks/mix-format.nix new file mode 100644 index 00000000..dc6a70f6 --- /dev/null +++ b/modules/hooks/mix-format.nix @@ -0,0 +1,8 @@ +{ config, tools, lib, ... }: +{ + config = { + package = tools.elixir; + entry = "${config.package}/bin/mix format"; + files = "\.exs?$"; + }; +} diff --git a/modules/hooks/mix-test.nix b/modules/hooks/mix-test.nix new file mode 100644 index 00000000..39230e63 --- /dev/null +++ b/modules/hooks/mix-test.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.elixir; + entry = "${config.package}/bin/mix test"; + files = "\.exs?$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/mixed-line-endings.nix b/modules/hooks/mixed-line-endings.nix new file mode 100644 index 00000000..2eee882a --- /dev/null +++ b/modules/hooks/mixed-line-endings.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/mixed-line-ending"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/mkdocs-linkcheck.nix b/modules/hooks/mkdocs-linkcheck.nix new file mode 100644 index 00000000..552902fb --- /dev/null +++ b/modules/hooks/mkdocs-linkcheck.nix @@ -0,0 +1,72 @@ +{ lib, config, tools, migrateBinPathToPackage, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr (types.oneOf [ types.str types.path ]); + description = "mkdocs-linkcheck binary path. Should be used to specify the mkdocs-linkcheck binary from your Python environment."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/mkdocs-linkcheck" + ''; + }; + + path = + mkOption { + type = types.str; + description = "Path to check"; + default = ""; + }; + + local-only = + mkOption { + type = types.bool; + description = "Whether to only check local links."; + default = false; + }; + + recurse = + mkOption { + type = types.bool; + description = "Whether to recurse directories under path."; + default = false; + }; + + extension = + mkOption { + type = types.str; + description = "File extension to scan for."; + default = ""; + }; + + method = + mkOption { + type = types.enum [ "get" "head" ]; + description = "HTTP method to use when checking external links."; + default = "get"; + }; + }; + + config = { + package = tools.mkdocs-linkcheck; + entry = + let + binPath = migrateBinPathToPackage config "/bin/mkdocs-linkcheck"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ local-only " --local" ] + [ recurse " --recurse" ] + [ (extension != "") " --ext ${extension}" ] + [ (method != "") " --method ${method}" ] + [ (path != "") " ${path}" ] + ]); + in + "${binPath}${cmdArgs}"; + types = [ "text" "markdown" ]; + }; + +} diff --git a/modules/hooks/mypy.nix b/modules/hooks/mypy.nix new file mode 100644 index 00000000..d3732469 --- /dev/null +++ b/modules/hooks/mypy.nix @@ -0,0 +1,17 @@ +{ lib, config, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "Mypy binary path. Should be used to specify the mypy executable in an environment containing your typing stubs."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/mypy" + ''; + }; + }; +} diff --git a/modules/hooks/name-tests-test.nix b/modules/hooks/name-tests-test.nix new file mode 100644 index 00000000..ae0fd800 --- /dev/null +++ b/modules/hooks/name-tests-test.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/tests_should_end_in_test.py"; + files = "(^|/)tests/\.+\\.py$"; + }; +} diff --git a/modules/hooks/nil.nix b/modules/hooks/nil.nix new file mode 100644 index 00000000..7692a2c0 --- /dev/null +++ b/modules/hooks/nil.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.nil; + entry = "${config.package}/bin/nil"; + files = "\.nix$"; + }; +} diff --git a/modules/hooks/nixfmt-classic.nix b/modules/hooks/nixfmt-classic.nix new file mode 100644 index 00000000..c0499a2c --- /dev/null +++ b/modules/hooks/nixfmt-classic.nix @@ -0,0 +1,14 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + width = + mkOption { + type = types.nullOr types.int; + description = "Line width."; + default = null; + }; + }; +} diff --git a/modules/hooks/nixfmt-rfc-style.nix b/modules/hooks/nixfmt-rfc-style.nix new file mode 100644 index 00000000..c0499a2c --- /dev/null +++ b/modules/hooks/nixfmt-rfc-style.nix @@ -0,0 +1,14 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + width = + mkOption { + type = types.nullOr types.int; + description = "Line width."; + default = null; + }; + }; +} diff --git a/modules/hooks/nixfmt.nix b/modules/hooks/nixfmt.nix new file mode 100644 index 00000000..c0499a2c --- /dev/null +++ b/modules/hooks/nixfmt.nix @@ -0,0 +1,14 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + width = + mkOption { + type = types.nullOr types.int; + description = "Line width."; + default = null; + }; + }; +} diff --git a/modules/hooks/nixpkgs-fmt.nix b/modules/hooks/nixpkgs-fmt.nix new file mode 100644 index 00000000..ca457f33 --- /dev/null +++ b/modules/hooks/nixpkgs-fmt.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.nixpkgs-fmt; + entry = "${config.package}/bin/nixpkgs-fmt"; + files = "\.nix$"; + }; +} diff --git a/modules/hooks/no-commit-to-branch.nix b/modules/hooks/no-commit-to-branch.nix new file mode 100644 index 00000000..0d876c8b --- /dev/null +++ b/modules/hooks/no-commit-to-branch.nix @@ -0,0 +1,22 @@ +{ lib, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + branch = + mkOption { + description = "Branches to disallow commits to."; + type = types.listOf types.str; + default = [ "main" ]; + example = [ "main" "master" ]; + }; + pattern = + mkOption { + description = "RegEx patterns for branch names to disallow commits to."; + type = types.listOf types.str; + default = [ ]; + example = [ "ma.*" ]; + }; + }; +} diff --git a/modules/hooks/ocp-indent.nix b/modules/hooks/ocp-indent.nix new file mode 100644 index 00000000..5d597764 --- /dev/null +++ b/modules/hooks/ocp-indent.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.ocp-indent; + entry = "${config.package}/bin/ocp-indent --inplace"; + files = "\.mli?$"; + }; +} diff --git a/modules/hooks/opam-lint.nix b/modules/hooks/opam-lint.nix new file mode 100644 index 00000000..21828cfd --- /dev/null +++ b/modules/hooks/opam-lint.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.opam; + entry = "${config.package}/bin/opam lint"; + files = "opam$"; + }; +} diff --git a/modules/hooks/openapi-spec-validator.nix b/modules/hooks/openapi-spec-validator.nix new file mode 100644 index 00000000..64951322 --- /dev/null +++ b/modules/hooks/openapi-spec-validator.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.openapi-spec-validator; + entry = "${config.package}/bin/openapi-spec-validator"; + files = "\.ya?ml$"; + }; +} diff --git a/modules/hooks/ormolu.nix b/modules/hooks/ormolu.nix new file mode 100644 index 00000000..b8c4d303 --- /dev/null +++ b/modules/hooks/ormolu.nix @@ -0,0 +1,33 @@ +{ lib, config, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + defaultExtensions = + mkOption { + type = types.listOf types.str; + description = "Haskell language extensions to enable."; + default = [ ]; + }; + cabalDefaultExtensions = + mkOption { + type = types.bool; + description = "Use `default-extensions` from `.cabal` files."; + default = false; + }; + }; + + config = { + package = tools.ormolu; + entry = + let + extensions = + lib.escapeShellArgs (lib.concatMap (ext: [ "--ghc-opt" "-X${ext}" ]) config.settings.defaultExtensions); + cabalExtensions = + if config.settings.cabalDefaultExtensions then "--cabal-default-extensions" else ""; + in + "${config.package}/bin/ormolu --mode inplace ${extensions} ${cabalExtensions}"; + files = "\\.l?hs(-boot)?$"; + }; +} diff --git a/modules/hooks/php-cs-fixer.nix b/modules/hooks/php-cs-fixer.nix new file mode 100644 index 00000000..9e9330ca --- /dev/null +++ b/modules/hooks/php-cs-fixer.nix @@ -0,0 +1,27 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "PHP-CS-Fixer binary path."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/php-cs-fixer" + ''; + }; + }; + + config = { + package = tools.php-cs-fixer; + entry = + let + binPath = migrateBinPathToPackage config "/bin/php-cs-fixer"; + in + "${binPath} fix"; + types = [ "php" ]; + }; +} diff --git a/modules/hooks/phpcbf.nix b/modules/hooks/phpcbf.nix new file mode 100644 index 00000000..25b96a64 --- /dev/null +++ b/modules/hooks/phpcbf.nix @@ -0,0 +1,17 @@ +{ lib, config, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "PHP_CodeSniffer binary path."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/phpcbf" + ''; + }; + }; +} diff --git a/modules/hooks/phpcs.nix b/modules/hooks/phpcs.nix new file mode 100644 index 00000000..a4d16bfd --- /dev/null +++ b/modules/hooks/phpcs.nix @@ -0,0 +1,23 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "PHP_CodeSniffer binary path."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/phpcs" + ''; + }; + }; + + config = { + package = tools.phpcs; + entry = migrateBinPathToPackage config "/bin/phpcs"; + types = [ "php" ]; + }; +} diff --git a/modules/hooks/phpstan.nix b/modules/hooks/phpstan.nix new file mode 100644 index 00000000..57f83714 --- /dev/null +++ b/modules/hooks/phpstan.nix @@ -0,0 +1,27 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "PHPStan binary path."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/phpstan" + ''; + }; + }; + + config = { + package = tools.phpstan; + entry = + let + binPath = migrateBinPathToPackage config "/bin/phpstan"; + in + "${binPath} analyse"; + types = [ "php" ]; + }; +} diff --git a/modules/hooks/poetry-check.nix b/modules/hooks/poetry-check.nix new file mode 100644 index 00000000..5c3c9b58 --- /dev/null +++ b/modules/hooks/poetry-check.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.poetry; + entry = "${config.package}/bin/poetry check"; + files = "^(poetry\\.lock$|pyproject\\.toml)$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/poetry-lock.nix b/modules/hooks/poetry-lock.nix new file mode 100644 index 00000000..cfcda27f --- /dev/null +++ b/modules/hooks/poetry-lock.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.poetry; + entry = "${config.package}/bin/poetry lock"; + files = "^(poetry\\.lock$|pyproject\\.toml)$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/pre-commit-hook-ensure-sops.nix b/modules/hooks/pre-commit-hook-ensure-sops.nix new file mode 100644 index 00000000..73eb623b --- /dev/null +++ b/modules/hooks/pre-commit-hook-ensure-sops.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hook-ensure-sops; + entry = "${config.package}/bin/pre-commit-hook-ensure-sops"; + files = "^secrets"; + }; +} diff --git a/modules/hooks/prettier.nix b/modules/hooks/prettier.nix new file mode 100644 index 00000000..1b0f3166 --- /dev/null +++ b/modules/hooks/prettier.nix @@ -0,0 +1,302 @@ +{ lib, config, tools, migrateBinPathToPackage, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + # See all CLI flags for prettier [here](https://prettier.io/docs/en/cli.html). + # See all options for prettier [here](https://prettier.io/docs/en/options.html). + options.settings = { + binPath = + mkOption { + description = '' + `prettier` binary path. + For example, if you want to use the `prettier` binary from `node_modules`, use `"./node_modules/.bin/prettier"`. + Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. + ''; + type = types.nullOr (types.oneOf [ types.str types.path ]); + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/prettier" + ''; + example = lib.literalExpression '' + "./node_modules/.bin/prettier" + ''; + }; + allow-parens = + mkOption { + description = "Include parentheses around a sole arrow function parameter."; + default = "always"; + type = types.enum [ "always" "avoid" ]; + }; + bracket-same-line = + mkOption { + description = "Put > of opening tags on the last line instead of on a new line."; + type = types.bool; + default = false; + }; + cache = + mkOption { + description = "Only format changed files."; + type = types.bool; + default = false; + }; + cache-location = + mkOption { + description = "Path to the cache file location used by `--cache` flag."; + type = types.str; + default = "./node_modules/.cache/prettier/.prettier-cache"; + }; + cache-strategy = + mkOption { + description = "Strategy for the cache to use for detecting changed files."; + type = types.nullOr (types.enum [ "metadata" "content" ]); + default = null; + }; + check = + mkOption { + description = "Output a human-friendly message and a list of unformatted files, if any."; + type = types.bool; + default = false; + }; + list-different = + mkOption { + description = "Print the filenames of files that are different from Prettier formatting."; + type = types.bool; + default = true; + }; + color = + mkOption { + description = "Colorize error messages."; + type = types.bool; + default = true; + }; + configPath = + mkOption { + description = "Path to a Prettier configuration file (.prettierrc, package.json, prettier.config.js)."; + type = types.str; + default = ""; + }; + config-precedence = + mkOption { + description = "Defines how config file should be evaluated in combination of CLI options."; + type = types.enum [ "cli-override" "file-override" "prefer-file" ]; + default = "cli-override"; + }; + embedded-language-formatting = + mkOption { + description = "Control how Prettier formats quoted code embedded in the file."; + type = types.enum [ "auto" "off" ]; + default = "auto"; + }; + end-of-line = + mkOption { + description = "Which end of line characters to apply."; + type = types.enum [ "lf" "crlf" "cr" "auto" ]; + default = "lf"; + }; + html-whitespace-sensitivity = + mkOption { + description = "How to handle whitespaces in HTML."; + type = types.enum [ "css" "strict" "ignore" ]; + default = "css"; + }; + ignore-path = + mkOption { + description = "Path to a file containing patterns that describe files to ignore. + By default, prettier looks for `./.gitignore` and `./.prettierignore`. + Multiple values are accepted."; + type = types.listOf (types.oneOf [ types.str types.path ]); + default = [ ]; + }; + ignore-unknown = + mkOption { + description = "Ignore unknown files."; + type = types.bool; + default = true; + }; + insert-pragma = + mkOption { + description = "Insert @format pragma into file's first docblock comment."; + type = types.bool; + default = false; + }; + jsx-single-quote = + mkOption { + description = "Use single quotes in JSX."; + type = types.bool; + default = false; + }; + log-level = + mkOption { + description = "What level of logs to report."; + type = types.enum [ "silent" "error" "warn" "log" "debug" ]; + default = "log"; + example = "debug"; + }; + no-bracket-spacing = + mkOption { + description = "Do not print spaces between brackets."; + type = types.bool; + default = false; + }; + no-config = + mkOption { + description = "Do not look for a configuration file."; + type = types.bool; + default = false; + }; + no-editorconfig = + mkOption { + description = "Don't take .editorconfig into account when parsing configuration."; + type = types.bool; + default = false; + }; + no-error-on-unmatched-pattern = + mkOption { + description = "Prevent errors when pattern is unmatched."; + type = types.bool; + default = false; + }; + no-semi = + mkOption { + description = "Do not print semicolons, except at the beginning of lines which may need them."; + type = types.bool; + default = false; + }; + parser = + mkOption { + description = "Which parser to use."; + type = types.enum [ "" "flow" "babel" "babel-flow" "babel-ts" "typescript" "acorn" "espree" "meriyah" "css" "less" "scss" "json" "json5" "json-stringify" "graphql" "markdown" "mdx" "vue" "yaml" "glimmer" "html" "angular" "lwc" ]; + default = ""; + }; + print-width = + mkOption { + type = types.int; + description = "Line length that the printer will wrap on."; + default = 80; + }; + prose-wrap = + mkOption { + description = "When to or if at all hard wrap prose to print width."; + type = types.enum [ "always" "never" "preserve" ]; + default = "preserve"; + }; + plugins = + mkOption { + description = "Add plugins from paths."; + type = types.listOf types.str; + default = [ ]; + }; + quote-props = + mkOption { + description = "Change when properties in objects are quoted."; + type = types.enum [ "as-needed" "consistent" "preserve" ]; + default = "as-needed"; + }; + require-pragma = + mkOption { + description = "Require either '@prettier' or '@format' to be present in the file's first docblock comment."; + type = types.bool; + default = false; + }; + single-attribute-per-line = + mkOption { + description = "Enforce single attribute per line in HTML, Vue andJSX."; + type = types.bool; + default = false; + }; + single-quote = + mkOption { + description = "Number of spaces per indentation-level."; + type = types.bool; + default = false; + }; + tab-width = + mkOption { + description = "Line length that the printer will wrap on."; + type = types.int; + default = 2; + }; + trailing-comma = + mkOption { + description = "Print trailing commas wherever possible in multi-line comma-separated syntactic structures."; + type = types.enum [ "all" "es5" "none" ]; + default = "all"; + }; + use-tabs = + mkOption { + type = types.bool; + description = "Indent with tabs instead of spaces."; + default = false; + }; + vue-indent-script-and-style = + mkOption { + description = "Indent script and style tags in Vue files."; + type = types.bool; + default = false; + }; + with-node-modules = + mkOption { + type = types.bool; + description = "Process files inside 'node_modules' directory."; + default = false; + }; + write = + mkOption { + description = "Edit files in-place."; + type = types.bool; + default = true; + }; + }; + + config = { + types = [ "text" ]; + package = tools.prettier; + entry = + let + binPath = migrateBinPathToPackage config "/bin/prettier"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (allow-parens != "always") "--allow-parens ${allow-parens}" ] + [ bracket-same-line "--bracket-same-line" ] + [ cache "--cache" ] + [ (cache-location != "./node_modules/.cache/prettier/.prettier-cache") "--cache-location ${cache-location}" ] + [ (cache-strategy != null) "--cache-strategy ${cache-strategy}" ] + [ check "--check" ] + [ (!color) "--no-color" ] + [ (configPath != "") "--config ${configPath}" ] + [ (config-precedence != "cli-override") "--config-precedence ${config-precedence}" ] + [ (embedded-language-formatting != "auto") "--embedded-language-formatting ${embedded-language-formatting}" ] + [ (end-of-line != "lf") "--end-of-line ${end-of-line}" ] + [ (html-whitespace-sensitivity != "css") "--html-whitespace-sensitivity ${html-whitespace-sensitivity}" ] + [ (ignore-path != [ ]) "--ignore-path ${lib.escapeShellArgs ignore-path}" ] + [ ignore-unknown "--ignore-unknown" ] + [ insert-pragma "--insert-pragma" ] + [ jsx-single-quote "--jsx-single-quote" ] + [ list-different "--list-different" ] + [ (log-level != "log") "--log-level ${log-level}" ] + [ no-bracket-spacing "--no-bracket-spacing" ] + [ no-config "--no-config" ] + [ no-editorconfig "--no-editorconfig" ] + [ no-error-on-unmatched-pattern "--no-error-on-unmatched-pattern" ] + [ no-semi "--no-semi" ] + [ (parser != "") "--parser ${parser}" ] + [ (print-width != 80) "--print-width ${toString print-width}" ] + [ (prose-wrap != "preserve") "--prose-wrap ${prose-wrap}" ] + [ (plugins != [ ]) "--plugin ${lib.strings.concatStringsSep " --plugin " plugins}" ] + [ (quote-props != "as-needed") "--quote-props ${quote-props}" ] + [ require-pragma "--require-pragma" ] + [ single-attribute-per-line "--single-attribute-per-line" ] + [ single-quote "--single-quote" ] + [ (tab-width != 2) "--tab-width ${toString tab-width}" ] + [ (trailing-comma != "all") "--trailing-comma ${trailing-comma}" ] + [ use-tabs "--use-tabs" ] + [ vue-indent-script-and-style "--vue-indent-script-and-style" ] + [ with-node-modules "--with-node-modules" ] + [ write "--write" ] + ]); + in + "${binPath} ${cmdArgs}"; + }; +} diff --git a/modules/hooks/pretty-format-json.nix b/modules/hooks/pretty-format-json.nix new file mode 100644 index 00000000..5e1bafb6 --- /dev/null +++ b/modules/hooks/pretty-format-json.nix @@ -0,0 +1,55 @@ +{ lib, config, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + autofix = + mkOption { + type = types.bool; + description = "Automatically format JSON files."; + default = false; + }; + indent = + mkOption { + type = types.nullOr (types.oneOf [ types.int types.str ]); + description = "Control the indentation (either a number for a number of spaces or a string of whitespace). Defaults to 2 spaces."; + default = null; + }; + no-ensure-ascii = + mkOption { + type = types.bool; + description = "Preserve unicode characters instead of converting to escape sequences."; + default = false; + }; + no-sort-keys = + mkOption { + type = types.bool; + description = "When autofixing, retain the original key ordering (instead of sorting the keys)."; + default = false; + }; + top-keys = + mkOption { + type = types.listOf types.str; + description = "Keys to keep at the top of mappings."; + default = [ ]; + }; + }; + + config = { + package = tools.pre-commit-hooks; + entry = + let + binPath = "${config.package}/bin/pretty-format-json"; + cmdArgs = mkCmdArgs (with config.settings; [ + [ autofix "--autofix" ] + [ (indent != null) "--indent ${toString indent}" ] + [ no-ensure-ascii "--no-ensure-ascii" ] + [ no-sort-keys "--no-sort-keys" ] + [ (top-keys != [ ]) "--top-keys ${lib.strings.concatStringsSep "," top-keys}" ] + ]); + in + "${binPath} ${cmdArgs}"; + types = [ "json" ]; + }; +} diff --git a/modules/hooks/proselint.nix b/modules/hooks/proselint.nix new file mode 100644 index 00000000..6f2cf1ee --- /dev/null +++ b/modules/hooks/proselint.nix @@ -0,0 +1,49 @@ +{ lib, config, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + config = + mkOption { + type = types.str; + description = "Multiline-string configuration passed as config file."; + default = ""; + example = '' + { + "checks": { + "typography.diacritical_marks": false + } + } + ''; + }; + configPath = + mkOption { + type = types.str; + description = "Path to the config file."; + default = ""; + }; + flags = + mkOption { + type = types.str; + description = "Flags passed to proselint."; + default = ""; + }; + }; + + config = { + types = [ "text" ]; + package = tools.proselint; + entry = + let + configFile = builtins.toFile "proselint-config.json" "${config.settings.config}"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (configPath != "") " --config ${configPath}" ] + [ (config != "" && configPath == "") " --config ${configFile}" ] + ]); + in + "${config.package}/bin/proselint${cmdArgs} ${config.settings.flags}"; + }; +} diff --git a/modules/hooks/psalm.nix b/modules/hooks/psalm.nix new file mode 100644 index 00000000..e191a1b3 --- /dev/null +++ b/modules/hooks/psalm.nix @@ -0,0 +1,23 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "Psalm binary path."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/psalm" + ''; + }; + }; + + config = { + package = tools.psalm; + entry = migrateBinPathToPackage config "/bin/psalm"; + types = [ "php" ]; + }; +} diff --git a/modules/hooks/purs-tidy.nix b/modules/hooks/purs-tidy.nix new file mode 100644 index 00000000..a441e43f --- /dev/null +++ b/modules/hooks/purs-tidy.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.purs-tidy; + entry = "${config.package}/bin/purs-tidy format-in-place"; + files = "\.purs$"; + }; +} diff --git a/modules/hooks/purty.nix b/modules/hooks/purty.nix new file mode 100644 index 00000000..6a8ee99e --- /dev/null +++ b/modules/hooks/purty.nix @@ -0,0 +1,8 @@ +{ tools, config, lib, ... }: +{ + config = { + package = tools.purty; + entry = "${config.package}/bin/purty"; + files = "\\.purs$"; + }; +} diff --git a/modules/hooks/pylint.nix b/modules/hooks/pylint.nix new file mode 100644 index 00000000..8c65078b --- /dev/null +++ b/modules/hooks/pylint.nix @@ -0,0 +1,45 @@ +{ lib, config, tools, migrateBinPathToPackage, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "Pylint binary path. Should be used to specify Pylint binary from your Python environment."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/pylint" + ''; + }; + reports = + mkOption { + type = types.bool; + description = "Whether to display a full report."; + default = false; + }; + score = + mkOption { + type = types.bool; + description = "Whether to activate the evaluation score."; + default = true; + }; + }; + + config = { + package = tools.pylint; + entry = + let + binPath = migrateBinPathToPackage config "/bin/pylint"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ reports "-ry" ] + [ (! score) "-sn" ] + ]); + in + "${binPath} ${cmdArgs}"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/pyright.nix b/modules/hooks/pyright.nix new file mode 100644 index 00000000..10f5f2d3 --- /dev/null +++ b/modules/hooks/pyright.nix @@ -0,0 +1,23 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "Pyright binary path. Should be used to specify the pyright executable in an environment containing your typing stubs."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/pyright" + ''; + }; + }; + + config = { + package = tools.pyright; + entry = migrateBinPathToPackage config "/bin/pyright"; + files = "\\.py$"; + }; +} diff --git a/modules/hooks/python-debug-statements.nix b/modules/hooks/python-debug-statements.nix new file mode 100644 index 00000000..3eaa9d93 --- /dev/null +++ b/modules/hooks/python-debug-statements.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/debug-statement-hook"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/pyupgrade.nix b/modules/hooks/pyupgrade.nix new file mode 100644 index 00000000..d5e16ff1 --- /dev/null +++ b/modules/hooks/pyupgrade.nix @@ -0,0 +1,23 @@ +{ lib, config, tools, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr types.str; + description = "pyupgrade binary path. Should be used to specify the pyupgrade binary from your Python environment."; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/pyupgrade" + ''; + }; + }; + + config = { + package = tools.pyupgrade; + entry = migrateBinPathToPackage config "/bin/pyupgrade"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/reuse.nix b/modules/hooks/reuse.nix new file mode 100644 index 00000000..5b24fd57 --- /dev/null +++ b/modules/hooks/reuse.nix @@ -0,0 +1,21 @@ +{ lib, config, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + flags = mkOption { + type = types.str; + description = "Flags passed to reuse. For available options run 'reuse lint --help'"; + default = ""; + example = "--json"; + }; + }; + + config = { + package = tools.reuse; + entry = "${config.package}/bin/reuse lint ${config.settings.flags}"; + types = [ "file" ]; + pass_filenames = false; + }; +} diff --git a/modules/hooks/revive.nix b/modules/hooks/revive.nix new file mode 100644 index 00000000..ffc690b3 --- /dev/null +++ b/modules/hooks/revive.nix @@ -0,0 +1,43 @@ +{ lib, config, pkgs, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = + mkOption { + type = types.str; + description = "Path to the configuration TOML file."; + # an empty string translates to use default configuration of the + # underlying revive binary + default = ""; + }; + }; + + config = { + package = tools.revive; + entry = + let + cmdArgs = + mkCmdArgs [ + [ true "-set_exit_status" ] + [ (config.settings.configPath != "") "-config ${config.settings.configPath}" ] + ]; + # revive works with both files and directories; however some lints + # may fail (e.g. package-comment) if they run on an individual file + # rather than a package/directory scope; given this let's get the + # directories from each individual file. + script = pkgs.writeShellScript "precommit-revive" '' + set -e + for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do + ${config.package}/bin/revive ${cmdArgs} ./"$dir" + done + ''; + in + builtins.toString script; + files = "\\.go$"; + # to avoid multiple invocations of the same directory input, provide + # all file names in a single run. + require_serial = true; + }; +} diff --git a/modules/hooks/ripsecrets.nix b/modules/hooks/ripsecrets.nix new file mode 100644 index 00000000..41ab6a67 --- /dev/null +++ b/modules/hooks/ripsecrets.nix @@ -0,0 +1,32 @@ +{ lib, config, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + additionalPatterns = + mkOption { + type = types.listOf types.str; + description = "Additional regex patterns used to find secrets. If there is a matching group in the regex the matched group will be tested for randomness before being reported as a secret."; + default = [ ]; + }; + }; + + config = { + package = tools.ripsecrets; + entry = + let + cmdArgs = mkCmdArgs ( + with config.settings; [ + [ true "--strict-ignore" ] + [ + (additionalPatterns != [ ]) + "--additional-pattern ${lib.strings.concatStringsSep " --additional-pattern " additionalPatterns}" + ] + ] + ); + in + "${config.package}/bin/ripsecrets ${cmdArgs}"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/rome.nix b/modules/hooks/rome.nix new file mode 100644 index 00000000..6c7977d9 --- /dev/null +++ b/modules/hooks/rome.nix @@ -0,0 +1,54 @@ +{ config, lib, tools, mkCmdArgs, migrateBinPathToPackage, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binPath = + mkOption { + type = types.nullOr (types.oneOf [ types.str types.path ]); + description = '' + `rome` binary path. + For example, if you want to use the `rome` binary from `node_modules`, use `"./node_modules/.bin/rome"`. + Use a string instead of a path to avoid having to Git track the file in projects that use Nix flakes. + ''; + default = null; + defaultText = lib.literalExpression '' + "''${config.package}/bin/rome" + ''; + example = lib.literalExpression '' + "./node_modules/.bin/rome" + ''; + }; + + write = + mkOption { + type = types.bool; + description = "Whether to edit files inplace."; + default = true; + }; + + configPath = mkOption { + type = types.str; + description = "Path to the configuration JSON file"; + # an empty string translates to use default configuration of the + # underlying biome binary (i.e biome.json if exists) + default = ""; + }; + }; + + config = { + types_or = [ "javascript" "jsx" "ts" "tsx" "json" ]; + package = tools.biome; + entry = + let + binPath = migrateBinPathToPackage config "/bin/biome"; + cmdArgs = + mkCmdArgs [ + [ (config.settings.write) "--apply" ] + [ (config.settings.configPath != "") "--config-path ${config.settings.configPath}" ] + ]; + in + "${binPath} check ${cmdArgs}"; + }; +} diff --git a/modules/hooks/ruff-format.nix b/modules/hooks/ruff-format.nix new file mode 100644 index 00000000..ae2eede2 --- /dev/null +++ b/modules/hooks/ruff-format.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.ruff; + entry = "${config.package}/bin/ruff format"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/ruff.nix b/modules/hooks/ruff.nix new file mode 100644 index 00000000..0cc4c0ef --- /dev/null +++ b/modules/hooks/ruff.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.ruff; + entry = "${config.package}/bin/ruff check --fix"; + types = [ "python" ]; + require_serial = true; + }; +} diff --git a/modules/hooks/rustfmt.nix b/modules/hooks/rustfmt.nix new file mode 100644 index 00000000..472cf165 --- /dev/null +++ b/modules/hooks/rustfmt.nix @@ -0,0 +1,119 @@ +{ lib, config, settings, pkgs, tools, ... }: +let + inherit (lib) mkOption types; + nameType = types.strMatching "[][*?!0-9A-Za-z_-]+"; +in +{ + options = { + packageOverrides = { + cargo = mkOption { + type = types.package; + description = "The cargo package to use."; + }; + rustfmt = mkOption { + type = types.package; + description = "The rustfmt package to use."; + }; + }; + + settings = { + all = mkOption { + type = types.bool; + description = "Format all packages, and also their local path-based dependencies"; + default = true; + }; + check = mkOption { + type = types.bool; + description = "Run rustfmt in check mode"; + default = false; + }; + color = mkOption { + type = types.enum [ "auto" "always" "never" ]; + description = "Coloring the output"; + default = "always"; + }; + config = mkOption { + type = types.attrs; + description = "Override configuration values"; + default = { }; + apply = config: + let + config' = lib.mapAttrsToList + (key: value: "${key}=${toString value}") + config; + in + if config == { } then "" else "--config=${lib.concatStringsSep "," config'}"; + }; + config-path = mkOption { + type = types.nullOr types.str; + description = "Path to the configuration file"; + default = null; + }; + emit = mkOption { + type = types.enum [ "files" "stdout" "coverage" "checkstyle" "json" ]; + description = "What data to emit and how"; + default = "files"; + }; + files-with-diff = mkOption { + type = types.bool; + description = "Print the names of mismatched files that were formatted. Prints the names of files that would be formatted when used with `--check` mode"; + default = config.settings.message-format == "short"; + }; + manifest-path = mkOption { + type = types.nullOr types.str; + description = "Path to Cargo.toml"; + default = settings.rust.cargoManifestPath; + }; + message-format = mkOption { + type = types.nullOr (types.enum [ "human" "short" ]); + description = "The output format of diagnostic messages"; + default = null; + }; + package = mkOption { + type = types.listOf nameType; + description = "Package(s) to check"; + default = [ ]; + }; + verbose = mkOption { + type = types.bool; + description = "Use verbose output"; + default = false; + }; + }; + }; + + config = + let + mkAdditionalArgs = args: lib.optionalString (args != "") " -- ${args}"; + + wrapper = pkgs.symlinkJoin { + name = "rustfmt-wrapped"; + paths = [ config.packageOverrides.rustfmt ]; + nativeBuildInputs = [ pkgs.makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/cargo-fmt \ + --prefix PATH : ${lib.makeBinPath (builtins.attrValues config.packageOverrides)} + ''; + }; + in + { + package = wrapper; + packageOverrides = { inherit (tools) cargo rustfmt; }; + entry = + let + cargoArgs = lib.cli.toGNUCommandLineShell { } { + inherit (config.settings) all package verbose manifest-path; + }; + rustfmtArgs = lib.cli.toGNUCommandLineShell { } { + inherit (config.settings) check emit config-path color files-with-diff config verbose; + }; + in + "${config.package}/bin/cargo-fmt fmt ${cargoArgs}${mkAdditionalArgs rustfmtArgs}"; + files = "\\.rs$"; + pass_filenames = false; + extraPackages = [ + config.packageOverrides.cargo + config.packageOverrides.rustfmt + ]; + }; +} diff --git a/modules/hooks/selene.nix b/modules/hooks/selene.nix new file mode 100644 index 00000000..3b76ba27 --- /dev/null +++ b/modules/hooks/selene.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.selene; + entry = "${config.package}/bin/selene"; + types = [ "lua" ]; + }; +} diff --git a/modules/hooks/shellcheck.nix b/modules/hooks/shellcheck.nix new file mode 100644 index 00000000..85491c3b --- /dev/null +++ b/modules/hooks/shellcheck.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.shellcheck; + entry = "${config.package}/bin/shellcheck"; + types = [ "shell" ]; + }; +} diff --git a/modules/hooks/shfmt.nix b/modules/hooks/shfmt.nix new file mode 100644 index 00000000..2d063925 --- /dev/null +++ b/modules/hooks/shfmt.nix @@ -0,0 +1,23 @@ +{ lib, config, tools, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + simplify = mkOption { + type = types.bool; + description = "Simplify the code."; + default = true; + }; + }; + + config = { + types = [ "shell" ]; + package = tools.shfmt; + entry = + let + simplify = if config.settings.simplify then "-s" else ""; + in + "${config.package}/bin/shfmt -w -l ${simplify}"; + }; +} diff --git a/modules/hooks/single-quoted-strings.nix b/modules/hooks/single-quoted-strings.nix new file mode 100644 index 00000000..1a76dcd1 --- /dev/null +++ b/modules/hooks/single-quoted-strings.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/double-quote-string-fixer"; + types = [ "python" ]; + }; +} diff --git a/modules/hooks/sort-file-contents.nix b/modules/hooks/sort-file-contents.nix new file mode 100644 index 00000000..07a72eb6 --- /dev/null +++ b/modules/hooks/sort-file-contents.nix @@ -0,0 +1,36 @@ +{ lib, config, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + ignore-case = + mkOption { + type = types.bool; + description = "Fold lower case to upper case characters."; + default = false; + }; + unique = + mkOption { + type = types.bool; + description = "Ensure each line is unique."; + default = false; + }; + }; + + config = { + types = [ "text" ]; + package = tools.pre-commit-hooks; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; + [ + [ ignore-case "--ignore-case" ] + [ unique "--unique" ] + ]); + in + "${config.package}/bin/file-contents-sorter ${cmdArgs}"; + }; +} diff --git a/modules/hooks/sort-requirements-txt.nix b/modules/hooks/sort-requirements-txt.nix new file mode 100644 index 00000000..912a62c6 --- /dev/null +++ b/modules/hooks/sort-requirements-txt.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/requirements-txt-fixer"; + files = "\\.*(requirements|constraints)\\.*\\.txt$"; + }; +} diff --git a/modules/hooks/sort-simple-yaml.nix b/modules/hooks/sort-simple-yaml.nix new file mode 100644 index 00000000..793b39fd --- /dev/null +++ b/modules/hooks/sort-simple-yaml.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/sort-simple-yaml"; + files = "(\\.yaml$)|(\\.yml$)"; + }; +} diff --git a/modules/hooks/staticcheck.nix b/modules/hooks/staticcheck.nix new file mode 100644 index 00000000..8afdd4d6 --- /dev/null +++ b/modules/hooks/staticcheck.nix @@ -0,0 +1,26 @@ +{ tools, lib, config, pkgs, ... }: +{ + config = { + package = tools.go-tools; + # staticheck works with directories. + entry = + let + script = pkgs.writeShellScript "precommit-staticcheck" '' + err=0 + for dir in $(echo "$@" | xargs -n1 dirname | sort -u); do + ${config.package}/bin/staticcheck ./"$dir" + code="$?" + if [[ "$err" -eq 0 ]]; then + err="$code" + fi + done + exit $err + ''; + in + builtins.toString script; + files = "\\.go$"; + # to avoid multiple invocations of the same directory input, provide + # all file names in a single run. + require_serial = true; + }; +} diff --git a/modules/hooks/statix.nix b/modules/hooks/statix.nix new file mode 100644 index 00000000..78ad834a --- /dev/null +++ b/modules/hooks/statix.nix @@ -0,0 +1,59 @@ +{ lib, tools, config, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + config = + mkOption { + type = types.nullOr types.str; + description = "Path to statix.toml or its parent directory."; + default = null; + }; + + format = + mkOption { + type = types.enum [ "stderr" "errfmt" "json" ]; + description = "Error Output format."; + default = "errfmt"; + }; + + ignore = + mkOption { + type = types.listOf types.str; + description = "Globs of file patterns to skip."; + default = [ ]; + example = [ "flake.nix" "_*" ]; + }; + + unrestricted = + mkOption { + type = types.bool; + description = "Don't respect .gitignore files."; + default = false; + example = true; + }; + }; + + config = { + package = tools.statix; + entry = + let + inherit (config) package settings; + mkOptionName = k: + if builtins.stringLength k == 1 + then "-${k}" + else "--${k}"; + options = lib.cli.toGNUCommandLineShell + { + # instead of repeating the option name for each element, + # create a single option with a space-separated list of unique values. + mkList = k: v: if v == [ ] then [ ] else [ (mkOptionName k) ] ++ lib.unique v; + } + settings; + in + "${package}/bin/statix check ${options}"; + files = "\\.nix$"; + pass_filenames = false; + }; +} diff --git a/modules/hooks/stylish-haskell.nix b/modules/hooks/stylish-haskell.nix new file mode 100644 index 00000000..8f2592bf --- /dev/null +++ b/modules/hooks/stylish-haskell.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.stylish-haskell; + entry = "${config.package}/bin/stylish-haskell --inplace"; + files = "\\.l?hs(-boot)?$"; + }; +} diff --git a/modules/hooks/stylua.nix b/modules/hooks/stylua.nix new file mode 100644 index 00000000..25e2b7e0 --- /dev/null +++ b/modules/hooks/stylua.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.stylua; + entry = "${config.package}/bin/stylua --respect-ignores"; + types = [ "file" "lua" ]; + }; +} diff --git a/modules/hooks/tagref.nix b/modules/hooks/tagref.nix new file mode 100644 index 00000000..be509bd2 --- /dev/null +++ b/modules/hooks/tagref.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.tagref; + entry = "${config.package}/bin/tagref"; + types = [ "text" ]; + pass_filenames = false; + }; +} diff --git a/modules/hooks/taplo.nix b/modules/hooks/taplo.nix new file mode 100644 index 00000000..a7a244b7 --- /dev/null +++ b/modules/hooks/taplo.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.taplo; + entry = "${config.package}/bin/taplo fmt"; + types = [ "toml" ]; + }; +} diff --git a/modules/hooks/terraform-format.nix b/modules/hooks/terraform-format.nix new file mode 100644 index 00000000..33f6da10 --- /dev/null +++ b/modules/hooks/terraform-format.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.opentofu; + entry = "${lib.getExe config.package} fmt -check -diff"; + files = "\\.tf$"; + }; +} diff --git a/modules/hooks/terraform-validate.nix b/modules/hooks/terraform-validate.nix new file mode 100644 index 00000000..e17ba788 --- /dev/null +++ b/modules/hooks/terraform-validate.nix @@ -0,0 +1,10 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.terraform-validate; + entry = "${config.package}/bin/terraform-validate"; + files = "\\.(tf(vars)?|terraform\\.lock\\.hcl)$"; + excludes = [ "\\.terraform/.*$" ]; + require_serial = true; + }; +} diff --git a/modules/hooks/tflint.nix b/modules/hooks/tflint.nix new file mode 100644 index 00000000..3c1df742 --- /dev/null +++ b/modules/hooks/tflint.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.tflint; + entry = "${config.package}/bin/tflint"; + files = "\\.tf$"; + }; +} diff --git a/modules/hooks/topiary.nix b/modules/hooks/topiary.nix new file mode 100644 index 00000000..56078353 --- /dev/null +++ b/modules/hooks/topiary.nix @@ -0,0 +1,19 @@ +{ tools, lib, config, pkgs, ... }: +{ + config = { + package = tools.topiary; + entry = + let + topiary-inplace = pkgs.writeShellApplication { + name = "topiary-inplace"; + text = '' + for file; do + ${config.package}/bin/topiary --in-place --input-file "$file" + done + ''; + }; + in + "${topiary-inplace}/bin/topiary-inplace"; + files = "(\\.json$)|(\\.toml$)|(\\.mli?$)"; + }; +} diff --git a/modules/hooks/treefmt.nix b/modules/hooks/treefmt.nix new file mode 100644 index 00000000..655d5a2a --- /dev/null +++ b/modules/hooks/treefmt.nix @@ -0,0 +1,66 @@ +{ config, lib, pkgs, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options = { + packageOverrides = { + treefmt = mkOption { + type = types.package; + description = "The treefmt package to use"; + }; + }; + + settings = { + fail-on-change = mkOption { + type = types.bool; + description = "Fail if some files require re-formatting."; + default = true; + }; + no-cache = mkOption { + type = types.bool; + description = "Ignore the evaluation cache entirely."; + default = true; + }; + formatters = mkOption { + type = types.listOf types.package; + description = "The formatter packages configured by treefmt"; + default = [ ]; + }; + }; + }; + + config = + let + inherit (config) packageOverrides settings; + wrapper = + pkgs.writeShellApplication { + name = "treefmt"; + runtimeInputs = [ + packageOverrides.treefmt + ] ++ settings.formatters; + + text = + '' + exec treefmt "$@" + ''; + }; + in + { + types = [ "file" ]; + pass_filenames = true; + package = wrapper; + packageOverrides = { inherit (tools) treefmt; }; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ fail-on-change "--fail-on-change" ] + [ no-cache "--no-cache" ] + ]); + in + "${config.package}/bin/treefmt ${cmdArgs}"; + extraPackages = config.settings.formatters; + }; +} diff --git a/modules/hooks/trim-trailing-whitespace.nix b/modules/hooks/trim-trailing-whitespace.nix new file mode 100644 index 00000000..9dd3cbeb --- /dev/null +++ b/modules/hooks/trim-trailing-whitespace.nix @@ -0,0 +1,9 @@ +{ tools, lib, config, ... }: +{ + config = { + types = [ "text" ]; + stages = [ "pre-commit" "pre-push" "manual" ]; + package = tools.pre-commit-hooks; + entry = "${config.package}/bin/trailing-whitespace-fixer"; + }; +} diff --git a/modules/hooks/trufflehog.nix b/modules/hooks/trufflehog.nix new file mode 100644 index 00000000..9903eb70 --- /dev/null +++ b/modules/hooks/trufflehog.nix @@ -0,0 +1,16 @@ +{ tools, lib, pkgs, config, ... }: +{ + config = { + package = tools.trufflehog; + entry = + let + script = pkgs.writeShellScript "precommit-trufflehog" '' + set -e + ${config.package}/bin/trufflehog --no-update git "file://$(git rev-parse --show-toplevel)" --since-commit HEAD --only-verified --fail + ''; + in + builtins.toString script; + # trufflehog expects to run across the whole repo, not particular files + pass_filenames = false; + }; +} diff --git a/modules/hooks/typos.nix b/modules/hooks/typos.nix new file mode 100644 index 00000000..7e2aaba1 --- /dev/null +++ b/modules/hooks/typos.nix @@ -0,0 +1,164 @@ +{ lib, tools, config, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + binary = + mkOption { + type = types.bool; + description = "Whether to search binary files."; + default = false; + }; + color = + mkOption { + type = types.enum [ "auto" "always" "never" ]; + description = "When to use generate output."; + default = "auto"; + }; + configuration = + mkOption { + type = types.str; + description = "Multiline-string configuration passed as config file. If set, config set in `typos.settings.configPath` gets ignored."; + default = ""; + example = '' + [files] + ignore-dot = true + + [default] + binary = false + + [type.py] + extend-glob = [] + ''; + }; + + configPath = + mkOption { + type = types.str; + description = "Path to a custom config file."; + default = ""; + example = ".typos.toml"; + }; + + diff = + mkOption { + type = types.bool; + description = "Print a diff of what would change."; + default = false; + }; + + exclude = + mkOption { + type = types.str; + description = "Ignore files and directories matching the glob."; + default = ""; + example = "*.nix"; + }; + + format = + mkOption { + type = types.enum [ "silent" "brief" "long" "json" ]; + description = "Output format to use."; + default = "long"; + }; + + hidden = + mkOption { + type = types.bool; + description = "Search hidden files and directories."; + default = false; + }; + + ignored-words = + mkOption { + type = types.listOf types.str; + description = "Spellings and words to ignore."; + default = [ ]; + example = [ + "MQTT" + "mosquitto" + ]; + }; + + locale = + mkOption { + type = types.enum [ "en" "en-us" "en-gb" "en-ca" "en-au" ]; + description = "Which language to use for spell checking."; + default = "en"; + }; + + no-check-filenames = + mkOption { + type = types.bool; + description = "Skip verifying spelling in file names."; + default = false; + }; + + no-check-files = + mkOption { + type = types.bool; + description = "Skip verifying spelling in files."; + default = false; + }; + + no-unicode = + mkOption { + type = types.bool; + description = "Only allow ASCII characters in identifiers."; + default = false; + }; + + quiet = + mkOption { + type = types.bool; + description = "Less output per occurrence."; + default = false; + }; + + verbose = + mkOption { + type = types.bool; + description = "More output per occurrence."; + default = false; + }; + + write = + mkOption { + type = types.bool; + description = "Fix spelling in files by writing them. Cannot be used with `typos.settings.diff`."; + default = false; + }; + }; + + config = { + package = tools.typos; + entry = + let + # Concatenate config in config file with section for ignoring words generated from list of words to ignore + configuration = "${config.settings.configuration}" + lib.strings.optionalString (config.settings.ignored-words != [ ]) "\n\[default.extend-words\]" + lib.strings.concatMapStrings (x: "\n${x} = \"${x}\"") config.settings.ignored-words; + configFile = builtins.toFile "typos-config.toml" configuration; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ binary "--binary" ] + [ (color != "auto") "--color ${color}" ] + [ (configuration != "") "--config ${configFile}" ] + [ (configPath != "" && configuration == "") "--config ${configPath}" ] + [ diff "--diff" ] + [ (exclude != "") "--exclude ${exclude} --force-exclude" ] + [ (format != "long") "--format ${format}" ] + [ hidden "--hidden" ] + [ (locale != "en") "--locale ${locale}" ] + [ no-check-filenames "--no-check-filenames" ] + [ no-check-files "--no-check-files" ] + [ no-unicode "--no-unicode" ] + [ quiet "--quiet" ] + [ verbose "--verbose" ] + [ (write && !diff) "--write-changes" ] + ]); + in + "${config.package}/bin/typos ${cmdArgs}"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/typstfmt.nix b/modules/hooks/typstfmt.nix new file mode 100644 index 00000000..be30daf1 --- /dev/null +++ b/modules/hooks/typstfmt.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.typstfmt; + entry = "${config.package}/bin/typstfmt"; + files = "\\.typ$"; + }; +} diff --git a/modules/hooks/typstyle.nix b/modules/hooks/typstyle.nix new file mode 100644 index 00000000..46c52652 --- /dev/null +++ b/modules/hooks/typstyle.nix @@ -0,0 +1,12 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.typstyle; + entry = + lib.throwIf + (config.package == null) + "The version of nixpkgs used by git-hooks.nix must contain typstyle" + "${config.package}/bin/typstyle -i"; + files = "\\.typ$"; + }; +} diff --git a/modules/hooks/vale.nix b/modules/hooks/vale.nix new file mode 100644 index 00000000..a28326c3 --- /dev/null +++ b/modules/hooks/vale.nix @@ -0,0 +1,48 @@ +{ lib, tools, config, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configuration = + mkOption { + type = types.str; + description = "Multiline-string configuration passed as config file."; + default = ""; + example = '' + MinAlertLevel = suggestion + [*] + BasedOnStyles = Vale + ''; + }; + configPath = + mkOption { + type = types.str; + description = "Path to the config file."; + default = ""; + }; + flags = + mkOption { + type = types.str; + description = "Flags passed to vale."; + default = ""; + }; + }; + + config = { + package = tools.vale; + entry = + let + # TODO: was .vale.ini, threw error in Nix + configFile = builtins.toFile "vale.ini" "${config.settings.configuration}"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + [ (configPath != "") " --config ${configPath}" ] + [ (configuration != "" && configPath == "") " --config ${configFile}" ] + ]); + in + "${config.package}/bin/vale${cmdArgs} ${config.settings.flags}"; + types = [ "text" ]; + }; +} diff --git a/modules/hooks/yamlfmt.nix b/modules/hooks/yamlfmt.nix new file mode 100644 index 00000000..34693737 --- /dev/null +++ b/modules/hooks/yamlfmt.nix @@ -0,0 +1,44 @@ +{ lib, config, tools, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + configPath = + mkOption { + type = types.str; + description = "Path to a custom configuration file."; + # An empty string translates to yamlfmt looking for a configuration file in the + # following locations (by order of preference): + # a file named .yamlfmt, yamlfmt.yml, yamlfmt.yaml, .yamlfmt.yaml or .yamlfmt.yml in the current working directory + # See details [here](https://github.com/google/yamlfmt/blob/main/docs/config-file.md#config-file-discovery) + default = ""; + example = ".yamlfmt"; + }; + lint-only = + mkOption { + type = types.bool; + description = "Only lint the files, do not format them in place."; + default = true; + }; + }; + + config = { + types = [ "file" "yaml" ]; + package = tools.yamlfmt; + entry = + let + cmdArgs = + mkCmdArgs + (with config.settings; [ + # Exit with non-zero status if the file is not formatted + [ lint-only "-lint" ] + # Do not print the diff + [ lint-only "-quiet" ] + # See https://github.com/google/yamlfmt/blob/main/docs/config-file.md#config-file-discovery + [ (configPath != "") "-conf ${configPath}" ] + ]); + in + "${config.package}/bin/yamlfmt ${cmdArgs}"; + }; +} diff --git a/modules/hooks/yamllint.nix b/modules/hooks/yamllint.nix new file mode 100644 index 00000000..e4ea340b --- /dev/null +++ b/modules/hooks/yamllint.nix @@ -0,0 +1,76 @@ +{ lib, tools, config, mkCmdArgs, ... }: +let + inherit (lib) mkOption types; +in +{ + options.settings = { + # `list-files` is not useful for a pre-commit hook as it always exits with exit code 0 + # `no-warnings` is not useful for a pre-commit hook as it exits with exit code 2 and the hook + # therefore fails when warnings level problems are detected but there is no output + configuration = mkOption { + type = types.str; + description = "Multiline-string configuration passed as config file. If set, configuration file set in `yamllint.settings.configPath` gets ignored."; + default = ""; + example = '' + --- + + extends: relaxed + + rules: + indentation: enable + ''; + }; + configData = mkOption { + type = types.str; + description = "Serialized YAML object describing the configuration."; + default = ""; + example = "{extends: relaxed, rules: {line-length: {max: 120}}}"; + }; + configPath = mkOption { + type = types.str; + description = "Path to a custom configuration file."; + # An empty string translates to yamllint looking for a configuration file in the + # following locations (by order of preference): + # a file named .yamllint, .yamllint.yaml or .yamllint.yml in the current working directory + # a filename referenced by $YAMLLINT_CONFIG_FILE, if set + # a file named $XDG_CONFIG_HOME/yamllint/config or ~/.config/yamllint/config, if present + default = ""; + }; + format = mkOption { + type = types.enum [ "parsable" "standard" "colored" "github" "auto" ]; + description = "Format for parsing output."; + default = "auto"; + }; + preset = mkOption { + type = types.enum [ "default" "relaxed" ]; + description = "The configuration preset to use."; + default = "default"; + }; + strict = mkOption { + type = types.bool; + description = "Return non-zero exit code on warnings as well as errors."; + default = true; + }; + }; + + config = { + types = [ "file" "yaml" ]; + package = tools.yamllint; + entry = + let + configFile = builtins.toFile "yamllint.yaml" "${config.settings.configuration}"; + cmdArgs = + mkCmdArgs + (with config.settings; [ + # Priorize multiline configuration over serialized configuration and configuration file + [ (configuration != "") "--config-file ${configFile}" ] + [ (configData != "" && configuration == "") "--config-data \"${configData}\"" ] + [ (configPath != "" && configData == "" && configuration == "" && preset == "default") "--config-file ${configPath}" ] + [ (format != "auto") "--format ${format}" ] + [ (preset != "default" && configuration == "") "--config-data ${preset}" ] + [ strict "--strict" ] + ]); + in + "${config.package}/bin/yamllint ${cmdArgs}"; + }; +} diff --git a/modules/hooks/zprint.nix b/modules/hooks/zprint.nix new file mode 100644 index 00000000..17b8fa0d --- /dev/null +++ b/modules/hooks/zprint.nix @@ -0,0 +1,8 @@ +{ tools, lib, config, ... }: +{ + config = { + package = tools.zprint; + entry = "${config.package}/bin/zprint '{:search-config? true}' -w"; + types_or = [ "clojure" "clojurescript" "edn" ]; + }; +}