Skip to content

fix(hooks): source nix profile in pre-push when nix not in PATH#62

Open
Castrozan wants to merge 3 commits intomainfrom
fix-prepush-nix-path
Open

fix(hooks): source nix profile in pre-push when nix not in PATH#62
Castrozan wants to merge 3 commits intomainfrom
fix-prepush-nix-path

Conversation

@Castrozan
Copy link
Owner

Problem

Git hooks run in a restricted environment that does not inherit the user's shell profile. The ~/.githooks/pre-push hook calls nix run nixpkgs#statix, nix run nixpkgs#deadnix, nix run nixpkgs#nixfmt-rfc-style, and nix shell nixpkgs#bats — but nix is not on PATH when git invokes hooks.

Symptom: every git push on the dotfiles repo failed with:

==> statix
/home/lucas.zanoni/.githooks/pre-push: linha 28: nix: command not found
error: failed to push some refs

The workaround was SKIP_HOOKS=1 git push, which bypasses all quality checks.

Fix

Add _source_nix_profile_if_not_in_path() at the top of the hook (before any nix invocation):

  1. If nix is already in PATH — no-op (fast path, zero cost)
  2. Sources /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh for multi-user installations
  3. Falls back to ~/.nix-profile/etc/profile.d/nix.sh for single-user installations
  4. Safe if neither file exists (no set -e failure)

Testing

Smoke-tested by running the hook with env -i PATH=/usr/bin:/bin — nix was successfully found after sourcing and all checks (statix, deadnix, nixfmt, bats) ran to completion.

Notes

  • The SC1090 shellcheck warnings on the . (source) lines are suppressed with # shellcheck source=/dev/null since the sourced paths are runtime-determined
  • The pre-existing SC2155 warning on readonly REPO_ROOT=$(…) is out of scope for this fix
  • shfmt applied (tabs, consistent style)

Git hooks run in a restricted environment that does not inherit the
user's shell profile. When nix is installed via the daemon (multi-user)
or single-user mode, the nix binary is not on PATH unless the profile
scripts are sourced explicitly.

Add _source_nix_profile_if_not_in_path() that checks for nix before
doing anything, then sources whichever profile script exists:
  1. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh (multi-user)
  2. ~/.nix-profile/etc/profile.d/nix.sh (single-user fallback)

Without this, every git push required SKIP_HOOKS=1 to bypass the
nix-dependent checks (statix, deadnix, nixfmt, bats).
…ypes on Ubuntu

Git hooks, VS Code terminals, and systemd user units all inherit PATH
from different ancestors. /etc/profile.d/nix.sh only covers login bash
shells, leaving non-login bash and non-shell processes without nix.

Three-layer fix via new home/modules/nix-path-bootstrap.nix:

Layer 1 — home.sessionPath
  Adds nix binaries to ~/.profile. Covers bash/sh login shells (SSH,
  su -, desktop session managers that source /etc/profile).

Layer 2 — ~/.config/environment.d/10-nix-path.conf
  Injected into the systemd user session environment at login. Every
  process started from the user session manager inherits this PATH:
  terminal emulators, VS Code, GUI apps, and all their children
  (including git hooks). $PATH is expanded by systemd from the
  inherited system environment.

Layer 3 — programs.bash.initExtra
  Sources /etc/profile.d/nix.sh (or single-user fallback) in
  non-login interactive bash sessions — terminal emulators that start
  bash without --login skip /etc/profile.d entirely. Guard prevents
  double-sourcing when nix is already present.

programs.bash.enable = true required so home-manager actually writes
the initExtra content to ~/.bashrc (without enable the option is
silently ignored).
@Castrozan Castrozan force-pushed the main branch 14 times, most recently from 61ea5bb to a17ae27 Compare March 2, 2026 22:48
@Castrozan Castrozan force-pushed the main branch 3 times, most recently from 6227bc7 to b84e24d Compare March 11, 2026 01:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant