Skip to content

Commit

Permalink
Make githooks possible to run local hook from global hooks (#901)
Browse files Browse the repository at this point in the history
  • Loading branch information
kachick authored Oct 31, 2024
1 parent 1bef4a5 commit 1814ed1
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .envrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#!/usr/bin/env -S bash

use flake

# See GH-545
export GIT_HOOKS_TRUST_REPOS="${GIT_HOOKS_TRUST_REPOS}:${PWD}"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ rclone.conf

# `local` files may have similar problems: https://github.com/github/gitignore/pull/3651
*local*
!*local_hook*

dist/
tmp/
Expand Down
2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ script = [
[tasks.setup]
dependencies = ['build']
script = [
"git config --local core.hooksPath .githooks",
"cp .githooks/* .git/hooks/",
"git config --local core.whitespace cr-at-eol",
]

Expand Down
29 changes: 28 additions & 1 deletion home-manager/git.nix
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@
#
# tig cannot be used as a standard UNIX filter tools, it prints with ncurses, not to STDOUT

let
mkPassthruHook = (
hook_name:
pkgs.writeShellApplication {
name = "passthru-hook-for-the-local-hook";
text = ''
run_local_hook '${hook_name}' "$@"
'';
meta.description = "GH-545";
runtimeInputs = [ (import ../pkgs/run_local_hook { inherit pkgs; }) ];
}
);
in
{
home.file."repos/.keep".text = "Put repositories here";

Expand Down Expand Up @@ -40,12 +53,26 @@
resolve-conflict = "!${lib.getExe homemade-pkgs.git-resolve-conflict}";
};

# TODO: They will be overridden by local hooks, Fixes in #545
# Required to provide all global hooks to respect local hooks even if it is empty. See GH-545 for detail
# Candidates: https://github.com/git/git/tree/v2.44.1/templates
hooks = {
commit-msg = lib.getExe homemade-pkgs.git-hooks-commit-msg;

# Git does not provide hooks for renaming branch, so using in checkout phase is not enough
pre-push = lib.getExe homemade-pkgs.git-hooks-pre-push;

pre-merge-commit = lib.getExe (mkPassthruHook "pre-merge-commit");
pre-applypatch = lib.getExe (mkPassthruHook "pre-applypatch");
post-update = lib.getExe (mkPassthruHook "post-update");
pre-receive = lib.getExe (mkPassthruHook "pre-receive");
push-to-checkout = lib.getExe (mkPassthruHook "push-to-checkout");
pre-commit = lib.getExe (mkPassthruHook "pre-commit");
prepare-commit-msg = lib.getExe (mkPassthruHook "prepare-commit-msg");
fsmonitor-watchman = lib.getExe (mkPassthruHook "fsmonitor-watchman");
update = lib.getExe (mkPassthruHook "update");
applypatch-msg = lib.getExe (mkPassthruHook "applypatch-msg");
pre-rebase = lib.getExe (mkPassthruHook "pre-rebase");
sendemail-validate = lib.getExe (mkPassthruHook "sendemail-validate");
};

extraConfig = {
Expand Down
2 changes: 2 additions & 0 deletions pkgs/git-hooks-commit-msg/commit-msg.bash
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
typos --config "$TYPOS_CONFIG_PATH" "$1"

run_local_hook 'commit-msg' "$@"
5 changes: 4 additions & 1 deletion pkgs/git-hooks-commit-msg/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ pkgs.writeShellApplication rec {
name = "commit-msg";
text = builtins.readFile ./${name}.bash;
meta.description = "#325";
runtimeInputs = with pkgs; [ typos ];
runtimeInputs = with pkgs; [
typos
(import ../run_local_hook { inherit pkgs; })
];
runtimeEnv = {
TYPOS_CONFIG_PATH = "${../../typos.toml}";
};
Expand Down
1 change: 1 addition & 0 deletions pkgs/git-hooks-pre-push/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pkgs.writeShellApplication rec {
runtimeInputs = with pkgs; [
typos
coreutils # `basename`
(import ../run_local_hook { inherit pkgs; })
];
runtimeEnv = {
TYPOS_CONFIG_PATH = "${../../typos.toml}";
Expand Down
2 changes: 2 additions & 0 deletions pkgs/git-hooks-pre-push/pre-push.bash
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ while read -r _local_ref _local_oid remote_ref _remote_oid; do
# https://github.com/crate-ci/typos/issues/758
basename "$remote_ref" | typos --config "$TYPOS_CONFIG_PATH" -
done

run_local_hook 'pre-push' "$@"
10 changes: 10 additions & 0 deletions pkgs/run_local_hook/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{ pkgs, ... }:
pkgs.writeShellApplication rec {
name = "run_local_hook";
text = builtins.readFile ./${name}.bash;
meta.description = "GH-545";
runtimeInputs = with pkgs; [
git
coreutils # `cat`
];
}
29 changes: 29 additions & 0 deletions pkgs/run_local_hook/run_local_hook.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# You should remove local hooks as `git config --local --unset core.hooksPath` to prefer global hooks for the entry point

readonly hook_name="$1"
shift

repository_path="$(git rev-parse --show-toplevel)"
readonly repository_path

# Avoiding -o error only at here: https://stackoverflow.com/a/7832158
TRUST_PATH=${GIT_HOOKS_TRUST_REPOS:-}

if [[ -x "${repository_path}/.git/hooks/${hook_name}" ]]; then
# Why using `case`: https://unix.stackexchange.com/a/32054
case ":$TRUST_PATH:" in
*:$repository_path:*)
exec "${repository_path}/.git/hooks/${hook_name}" "$@"
;;
*)
cat <<'EOF'
Found an ignored local hook.
You can allow it as
```bash
export GIT_HOOKS_TRUST_REPOS="${GIT_HOOKS_TRUST_REPOS}:${PWD}"
```
EOF
;;
esac
fi

0 comments on commit 1814ed1

Please sign in to comment.