Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,16 +88,7 @@ Given the following `flake.nix` example:
{
# Run the hooks with `nix fmt`.
formatter = forEachSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
config = self.checks.${system}.pre-commit-check.config;
inherit (config) package configFile;
script = ''
${pkgs.lib.getExe package} run --all-files --config ${configFile}
'';
in
pkgs.writeShellScriptBin "pre-commit-run" script
system: self.checks.${system}.pre-commit-check.formatter
);

# Run the hooks in a sandbox with `nix flake check`.
Expand Down Expand Up @@ -162,7 +153,7 @@ nix fmt

### flake-parts

If your flake uses [flake-parts](https://flake.parts/), we provide a flake-parts module as well. Checkout [`./template/flake.nix`](https://github.com/cachix/git-hooks.nix/blob/master/template/flake.nix) for an example.
If your flake uses [flake-parts](https://flake.parts/), we provide a flake-parts module as well. It automatically configures `checks.<system>.pre-commit` and `formatter.<system>` to format your code using the enabled hooks when running `nix fmt`. Checkout [`./template/flake.nix`](https://github.com/cachix/git-hooks.nix/blob/master/template/flake.nix) for an example.

## Nix

Expand Down
8 changes: 8 additions & 0 deletions flake-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,18 @@ in
description = "A development shell with the git hooks installed and all the packages made available.";
readOnly = true;
};
formatter = mkOption {
type = types.package;
description = "A formatter package that runs pre-commit hooks, suitable for flake-parts `formatter`.";
default = cfg.settings.formatter;
defaultText = lib.literalExpression "config.pre-commit.settings.formatter";
readOnly = true;
};
};
};
config = {
checks = lib.optionalAttrs cfg.check.enable { pre-commit = cfg.settings.run; };
formatter = lib.mkDefault cfg.settings.formatter;
pre-commit.settings = { pkgs, ... }: {
rootSrc = self.outPath;
package = lib.mkDefault pkgs.pre-commit;
Expand Down
43 changes: 43 additions & 0 deletions modules/pre-commit.nix
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,36 @@ let
'';
};

formatter =
pkgs.writeShellScriptBin "pre-commit-fmt" ''
set -euo pipefail
export PATH="${lib.makeBinPath ([ cfg.gitPackage cfg.package ] ++ enabledExtraPackages)}:$PATH"

exitcode=0
if [ "$#" -gt 0 ]; then
${lib.getExe cfg.package} run -c ${cfg.configFile} --files "$@" || exitcode=$?
else
if [ -n "''${PRJ_ROOT:-}" ]; then
cd "$PRJ_ROOT"
fi
${lib.getExe cfg.package} run -c ${cfg.configFile} --all-files || exitcode=$?
fi

# pre-commit returns 1 when files were modified by hooks.
# For a formatter (`nix fmt`), modifying files is the intended outcome.
# If exit code was 1, re-run to distinguish between successful formatting changes (clean on 2nd pass)
# and actual errors/syntax failures (fails again on 2nd pass).
if [ "$exitcode" -eq 1 ]; then
if [ "$#" -gt 0 ]; then
${lib.getExe cfg.package} run -c ${cfg.configFile} --files "$@"
else
${lib.getExe cfg.package} run -c ${cfg.configFile} --all-files
fi
else
exit "$exitcode"
fi
'';

failedAssertions = builtins.map (x: x.message) (builtins.filter (x: !x.assertion) config.assertions);

performAssertions =
Expand Down Expand Up @@ -279,6 +309,19 @@ in
defaultText = lib.literalExpression "<derivation>";
};

formatter =
mkOption {
type = types.package;
description =
''
A wrapper script that runs pre-commit on all files or specified files,
suitable for use as the flake's `formatter` output (`nix fmt`).
'';
readOnly = true;
default = formatter;
defaultText = lib.literalExpression "<derivation>";
};

shellHook =
mkOption {
type = types.str;
Expand Down
2 changes: 1 addition & 1 deletion nix/run.nix
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ let
in
project.config.run // {
inherit (project) config extendModules;
inherit (project.config) enabledPackages shellHook;
inherit (project.config) enabledPackages shellHook formatter;
}
2 changes: 2 additions & 0 deletions nix/tests/hook-config.nix
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ runCommand "hook-config-test" { } (
lib.concatStrings (lib.mapAttrsToList runHookTest hookTests) +
lib.concatStrings (lib.mapAttrsToList runAssertionTest assertionTests) +
''
${lib.optionalString (!lib.isDerivation (run { src = null; addGcRoot = false; }).formatter) "echo 'FAILED: res.formatter is not a derivation'; exit 1"}
${lib.optionalString (!lib.isDerivation (eval { }).formatter) "echo 'FAILED: (eval {}).formatter is not a derivation'; exit 1"}
echo "All hook config tests passed" > $out
''
)
2 changes: 2 additions & 0 deletions template/flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
# Equivalent to inputs'.nixpkgs.legacyPackages.hello;
packages.hello = pkgs.hello;
pre-commit.settings.hooks.nixpkgs-fmt.enable = true;
# NOTE: `formatter` is automatically configured to run enabled hooks via `nix fmt`.
# You can also use `config.pre-commit.formatter` explicitly if needed.
# NOTE: You can also use `config.pre-commit.devShell`
devShells.default = pkgs.mkShell {
shellHook = ''
Expand Down