Install Nix packages as Git hooks from your Nix shell environment.
Heavily inspired by a hack to get treefmt into a pre-commit hook.
nix-shell -p npins
npins init
npins add github fricklerhandwerk git-hooks -b main
## default.nix
let
sources = import ./npins;
in
{
pkgs ? import sources.nixpkgs { inherit system; config = { }; overlays = [ ]; },
git-hooks ? pkgs.callPackage sources.git-hooks { },
system ? builtins.currentSystem,
}:
let
inherit (git-hooks) lib;
in
pkgs.mkShellNoCC {
shellHook = ''
# # add Git hooks here
'';
}
pre-commit :: Derivation -> Path
pre-commit
takes a derivaton with a pre-commit hook, and returns a path to an executable that will install the hook.
The derivation must have meta.mainProgram
set to the name of the executable in $out/bin/
that implements the hook.
The hook is installed in the Git repository that surrounds the working directory of the Nix invocation, and will get run roughly like this:
git stash push --keep-index
hook
git stash pop
Example
Entering this shell environment will install a Git hook that prints
Hello, world!
to the console before each commit:pkgs.mkShellNoCC { shellHook = '' ${lib.git-hooks.pre-commit pkgs.hello} ''; }
abort-on-change :: Derivation -> Derivation
Wrap a hook such that the commit is aborted if the hook changes staged files.
Example
The
cursed
hook will add an empty line to each staged file. Wrapping it inabort-on-change
will prevent files thus changed from being committed.let cursed = pkgs.writeShellApplication { name = "pre-commit-hook"; runtimeInputs = with pkgs; [ git ]; text = '' for f in $(git diff --name-only --cached); do echo >> "$f" done ''; }; in pkgs.mkShellNoCC { shellHook = '' ${with lib.git-hooks; pre-commit (wrap.abort-on-change cursed)} ''; }