From 498f345dd9d7d5f54bb817b51ae7f71f7ae8270c Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Fri, 13 Feb 2026 02:34:16 +0000 Subject: [PATCH 1/6] Add hook stage option --- modules/pre-commit.nix | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index cf807acf..3bba4bcb 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -100,14 +100,13 @@ let git config --global user.email "you@example.com" git config --global user.name "Your Name" git commit -m "init" -q - if [[ ${toString (compare cfg.installStages [ "manual" ])} -eq 0 ]] - then - echo "Running: $ pre-commit run --hook-stage manual --all-files" - ${lib.getExe cfg.package} run -c ${cfg.configPath} --hook-stage manual --all-files - else + ${if cfg.runHookStage != null then '' + echo "Running: $ pre-commit run --hook-stage ${cfg.runHookStage} --all-files" + ${lib.getExe cfg.package} run -c ${cfg.configPath} --hook-stage ${cfg.runHookStage} --all-files + '' else '' echo "Running: $ pre-commit run --all-files" - ${lib.getExe cfg.package} run -c ${cfg.configPath} --all-files - fi + ${lib.getExe cfg.package} run -c ${cfg.configPath} --all-files + ''} exitcode=$? git --no-pager diff --color # Derivations must produce an output @@ -278,6 +277,21 @@ in defaultText = lib.literalExpression ""; }; + runHookStage = + mkOption { + type = types.nullOr types.str; + description = + '' + The hook stage to run in the `run` derivation. + + If set to `null` (default), pre-commit will run all hooks. + If set to a specific stage (e.g., "manual", "pre-commit", "pre-push"), + only hooks configured for that stage will run. + ''; + default = null; + example = "manual"; + }; + shellHook = mkOption { type = types.str; From ee4ddbd7e955bd8fcf97497317173a9c41647393 Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Fri, 13 Feb 2026 02:34:45 +0000 Subject: [PATCH 2/6] Update installationScript to match option description and provide helper functions --- modules/pre-commit.nix | 112 ++++++++++++++++++++++++++--------------- 1 file changed, 72 insertions(+), 40 deletions(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index 3bba4bcb..ccaee7f8 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -476,10 +476,73 @@ in installationScript = '' - if ${boolToString cfg.install.enable}; then + # Define helper functions for managing git hooks + _git_hooks_uninstall() { if ! ${cfg.gitPackage}/bin/git rev-parse --git-dir &> /dev/null; then - echo 1>&2 "WARNING: git-hooks.nix: .git not found; skipping installation." + echo 1>&2 "WARNING: git-hooks.nix: .git not found; cannot uninstall hooks." + return 1 + fi + + echo 1>&2 "git-hooks.nix: uninstalling hooks" + # Remove any previously installed hooks (since pre-commit itself has no convergent design) + hooks="${concatStringsSep " " (remove "manual" supportedHooksLib.supportedHooks )}" + for hook in $hooks; do + ${lib.getExe cfg.package} uninstall -t $hook + done + ${lib.getExe cfg.gitPackage} config --local core.hooksPath "" + } + + _git_hooks_install() { + if ! ${cfg.gitPackage}/bin/git rev-parse --git-dir &> /dev/null; then + echo 1>&2 "WARNING: git-hooks.nix: .git not found; cannot install hooks." + return 1 + fi + + GIT_WC=`${lib.getExe cfg.gitPackage} rev-parse --show-toplevel` + + if [ ! -L "''${GIT_WC}/${cfg.configPath}" ] || [[ $(readlink "''${GIT_WC}/${cfg.configPath}") != ${cfg.configFile} ]]; then + echo 1>&2 "WARNING: git-hooks.nix: config not properly set up. Please ensure the config symlink exists." + return 1 + fi + + echo 1>&2 "git-hooks.nix: installing hooks" + # Add hooks for configured stages (only) ... + if [ ! -z "${concatStringsSep " " install_stages}" ]; then + for stage in ${concatStringsSep " " install_stages}; do + case $stage in + manual) + ;; + # if you amend these switches please also review $hooks above + commit | merge-commit | push) + stage="pre-"$stage + ${lib.getExe cfg.package} install -c ${cfg.configPath} -t $stage + ;; + ${concatStringsSep "|" supportedHooksLib.supportedHooks}) + ${lib.getExe cfg.package} install -c ${cfg.configPath} -t $stage + ;; + *) + echo 1>&2 "ERROR: git-hooks.nix: either $stage is not a valid stage or git-hooks.nix doesn't yet support it." + return 1 + ;; + esac + done + # ... or default 'pre-commit' hook else + ${lib.getExe cfg.package} install -c ${cfg.configPath} + fi + + # Fetch the absolute path to the git common directory. This will normally point to $GIT_WC/.git. + common_dir=''$(${cfg.gitPackage}/bin/git rev-parse --path-format=absolute --git-common-dir) + + # Convert the absolute path to a path relative to the toplevel working directory. + common_dir=''${common_dir#''$GIT_WC/} + + ${lib.getExe cfg.gitPackage} config --local core.hooksPath "''$common_dir/hooks" + } + + if ! ${cfg.gitPackage}/bin/git rev-parse --git-dir &> /dev/null; then + echo 1>&2 "WARNING: git-hooks.nix: .git not found; skipping installation." + else GIT_WC=`${lib.getExe cfg.gitPackage} rev-parse --show-toplevel` # These update procedures compare before they write, to avoid @@ -505,48 +568,17 @@ in else ln -fs ${cfg.configFile} "''${GIT_WC}/${cfg.configPath}" fi - # Remove any previously installed hooks (since pre-commit itself has no convergent design) - hooks="${concatStringsSep " " (remove "manual" supportedHooksLib.supportedHooks )}" - for hook in $hooks; do - ${lib.getExe cfg.package} uninstall -t $hook - done - ${lib.getExe cfg.gitPackage} config --local core.hooksPath "" - # Add hooks for configured stages (only) ... - if [ ! -z "${concatStringsSep " " install_stages}" ]; then - for stage in ${concatStringsSep " " install_stages}; do - case $stage in - manual) - ;; - # if you amend these switches please also review $hooks above - commit | merge-commit | push) - stage="pre-"$stage - ${lib.getExe cfg.package} install -c ${cfg.configPath} -t $stage - ;; - ${concatStringsSep "|" supportedHooksLib.supportedHooks}) - ${lib.getExe cfg.package} install -c ${cfg.configPath} -t $stage - ;; - *) - echo 1>&2 "ERROR: git-hooks.nix: either $stage is not a valid stage or git-hooks.nix doesn't yet support it." - exit 1 - ;; - esac - done - # ... or default 'pre-commit' hook - else - ${lib.getExe cfg.package} install -c ${cfg.configPath} - fi - - # Fetch the absolute path to the git common directory. This will normally point to $GIT_WC/.git. - common_dir=''$(${cfg.gitPackage}/bin/git rev-parse --path-format=absolute --git-common-dir) - - # Convert the absolute path to a path relative to the toplevel working directory. - common_dir=''${common_dir#''$GIT_WC/} - ${lib.getExe cfg.gitPackage} config --local core.hooksPath "''$common_dir/hooks" + # Only manage hooks if install.enable is true + if ${boolToString cfg.install.enable}; then + # Uninstall any previously installed hooks + _git_hooks_uninstall + # Install new hooks + _git_hooks_install + fi fi fi fi - fi ''; }; } From 6e94219bcbed5b6dea191d925b4f9d423ac36024 Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Thu, 12 Feb 2026 18:47:08 -0800 Subject: [PATCH 3/6] Docs --- modules/pre-commit.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index ccaee7f8..b4035ce7 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -284,7 +284,7 @@ in '' The hook stage to run in the `run` derivation. - If set to `null` (default), pre-commit will run all hooks. + If set to `null` (default), pre-commit will run the default hook stage. If set to a specific stage (e.g., "manual", "pre-commit", "pre-push"), only hooks configured for that stage will run. ''; From 1127ce5265ece395fe225eb1aee0d0f0f2e23d37 Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Wed, 25 Feb 2026 14:22:15 -0800 Subject: [PATCH 4/6] Address review comments --- modules/pre-commit.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index b4035ce7..68032e41 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -279,12 +279,12 @@ in runHookStage = mkOption { - type = types.nullOr types.str; + type = types.nullOr supportedHooksLib.supportedHooksType; description = '' The hook stage to run in the `run` derivation. - If set to `null` (default), pre-commit will run the default hook stage. + If set to `null` (default), pre-commit will run the default hook stage (pre-commit). If set to a specific stage (e.g., "manual", "pre-commit", "pre-push"), only hooks configured for that stage will run. ''; From 0cf55edf27cf8db6b9476c55ea8836a31f8b0bac Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Wed, 25 Feb 2026 14:33:36 -0800 Subject: [PATCH 5/6] Exit from new bash function for consistency with previous impl --- modules/pre-commit.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index 68032e41..63b7ae63 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -522,7 +522,7 @@ in ;; *) echo 1>&2 "ERROR: git-hooks.nix: either $stage is not a valid stage or git-hooks.nix doesn't yet support it." - return 1 + exit 1 ;; esac done From 1e2c27cd43277a4a671f2fb7e11f13c232708599 Mon Sep 17 00:00:00 2001 From: Mitchell Allain Date: Wed, 25 Feb 2026 14:37:14 -0800 Subject: [PATCH 6/6] Remove extra log messages for consistency with prev impl --- modules/pre-commit.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/pre-commit.nix b/modules/pre-commit.nix index 63b7ae63..27562d03 100644 --- a/modules/pre-commit.nix +++ b/modules/pre-commit.nix @@ -483,7 +483,6 @@ in return 1 fi - echo 1>&2 "git-hooks.nix: uninstalling hooks" # Remove any previously installed hooks (since pre-commit itself has no convergent design) hooks="${concatStringsSep " " (remove "manual" supportedHooksLib.supportedHooks )}" for hook in $hooks; do @@ -505,7 +504,6 @@ in return 1 fi - echo 1>&2 "git-hooks.nix: installing hooks" # Add hooks for configured stages (only) ... if [ ! -z "${concatStringsSep " " install_stages}" ]; then for stage in ${concatStringsSep " " install_stages}; do