From 48c368fda214e02762b9d33c22519d32963ed4c5 Mon Sep 17 00:00:00 2001 From: Castrozan Date: Sun, 22 Feb 2026 16:15:59 -0300 Subject: [PATCH 1/2] fix(hooks): source nix profile in pre-push when nix not in PATH 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). --- .githooks/pre-push.sh | 101 ++++++++++++++++++++++++------------------ 1 file changed, 58 insertions(+), 43 deletions(-) diff --git a/.githooks/pre-push.sh b/.githooks/pre-push.sh index 012b7721..cb4e0ba3 100755 --- a/.githooks/pre-push.sh +++ b/.githooks/pre-push.sh @@ -2,6 +2,21 @@ set -Eeuo pipefail +_source_nix_profile_if_not_in_path() { + command -v nix &>/dev/null && return + local nixMultiUserDaemonProfile="/nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh" + local nixSingleUserProfile="${HOME}/.nix-profile/etc/profile.d/nix.sh" + if [[ -f "$nixMultiUserDaemonProfile" ]]; then + # shellcheck source=/dev/null + . "$nixMultiUserDaemonProfile" + elif [[ -f "$nixSingleUserProfile" ]]; then + # shellcheck source=/dev/null + . "$nixSingleUserProfile" + fi +} + +_source_nix_profile_if_not_in_path + [ "${SKIP_HOOKS:-0}" = "1" ] && exit 0 readonly REPO_ROOT=$(git rev-parse --show-toplevel) @@ -11,61 +26,61 @@ readonly REPO_ROOT=$(git rev-parse --show-toplevel) cd "$REPO_ROOT" main() { - _run_check "statix" nix run nixpkgs#statix -- check . --ignore 'result*' - _run_check "deadnix" nix run nixpkgs#deadnix -- . - _run_check "nixfmt" bash -c "find . -name '*.nix' -not -path './result*' -not -path './.worktrees/*' -exec nix run nixpkgs#nixfmt-rfc-style -- --check {} +" - _run_check "validate-skill-frontmatter" ./tests/validate-skill-frontmatter.sh agents/skills - _run_quick_bats_tests - _remind_nix_tests_if_openclaw_changed - - echo "All pre-push checks passed." + _run_check "statix" nix run nixpkgs#statix -- check . --ignore 'result*' + _run_check "deadnix" nix run nixpkgs#deadnix -- . + _run_check "nixfmt" bash -c "find . -name '*.nix' -not -path './result*' -not -path './.worktrees/*' -exec nix run nixpkgs#nixfmt-rfc-style -- --check {} +" + _run_check "validate-skill-frontmatter" ./tests/validate-skill-frontmatter.sh agents/skills + _run_quick_bats_tests + _remind_nix_tests_if_openclaw_changed + + echo "All pre-push checks passed." } _run_check() { - local checkName="$1" - shift - echo "==> $checkName" - "$@" - echo "" + local checkName="$1" + shift + echo "==> $checkName" + "$@" + echo "" } _run_quick_bats_tests() { - echo "==> bats (quick)" - local testFiles=() - for testFile in tests/bin-scripts/*.bats; do - [[ "$(basename "$testFile")" == *-docker.bats ]] && continue - testFiles+=("$testFile") - done - nix shell nixpkgs#bats --command bats "${testFiles[@]}" - echo "" + echo "==> bats (quick)" + local testFiles=() + for testFile in tests/bin-scripts/*.bats; do + [[ "$(basename "$testFile")" == *-docker.bats ]] && continue + testFiles+=("$testFile") + done + nix shell nixpkgs#bats --command bats "${testFiles[@]}" + echo "" } _remind_nix_tests_if_openclaw_changed() { - if _openclaw_files_changed; then - echo "" - echo "NOTE: OpenClaw files changed. Run 'tests/run-all.sh --nix' to verify nix eval tests." - echo "" - fi + if _openclaw_files_changed; then + echo "" + echo "NOTE: OpenClaw files changed. Run 'tests/run-all.sh --nix' to verify nix eval tests." + echo "" + fi } _openclaw_files_changed() { - local watchedPaths=( - "home/modules/openclaw" - "users/zanoni/home/openclaw" - "users/lucas.zanoni/home/openclaw" - "tests/openclaw" - ) - - local changedFiles - changedFiles=$(git diff --name-only origin/main...HEAD 2>/dev/null || true) - - for watchedPath in "${watchedPaths[@]}"; do - if echo "$changedFiles" | grep -q "^$watchedPath"; then - return 0 - fi - done - - return 1 + local watchedPaths=( + "home/modules/openclaw" + "users/zanoni/home/openclaw" + "users/lucas.zanoni/home/openclaw" + "tests/openclaw" + ) + + local changedFiles + changedFiles=$(git diff --name-only origin/main...HEAD 2>/dev/null || true) + + for watchedPath in "${watchedPaths[@]}"; do + if echo "$changedFiles" | grep -q "^$watchedPath"; then + return 0 + fi + done + + return 1 } main "$@" From 46ab5515ff3fc23b450dad970d2fb816de1e5d40 Mon Sep 17 00:00:00 2001 From: Castrozan Date: Sun, 22 Feb 2026 16:32:57 -0300 Subject: [PATCH 2/2] feat(nix-path)(lucas.zanoni): bootstrap nix in PATH for all process types on Ubuntu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- home/modules/nix-path-bootstrap.nix | 29 +++++++++++++++++++++++++++++ users/lucas.zanoni/home.nix | 2 ++ 2 files changed, 31 insertions(+) create mode 100644 home/modules/nix-path-bootstrap.nix diff --git a/home/modules/nix-path-bootstrap.nix b/home/modules/nix-path-bootstrap.nix new file mode 100644 index 00000000..0d8b8774 --- /dev/null +++ b/home/modules/nix-path-bootstrap.nix @@ -0,0 +1,29 @@ +{ config, ... }: +let + nixMultiUserDaemonBinPath = "/nix/var/nix/profiles/default/bin"; + nixUserProfileBinPath = "${config.home.homeDirectory}/.nix-profile/bin"; + nixDaemonProfileScript = "/etc/profile.d/nix.sh"; + nixSingleUserProfileScript = "${config.home.homeDirectory}/.nix-profile/etc/profile.d/nix.sh"; +in +{ + home.sessionPath = [ + nixMultiUserDaemonBinPath + nixUserProfileBinPath + ]; + + home.file.".config/environment.d/10-nix-path.conf".text = '' + PATH=${nixMultiUserDaemonBinPath}:${nixUserProfileBinPath}:$PATH + ''; + + programs.bash.enable = true; + + programs.bash.initExtra = '' + if ! command -v nix &>/dev/null; then + if [[ -f ${nixDaemonProfileScript} ]]; then + source ${nixDaemonProfileScript} + elif [[ -f ${nixSingleUserProfileScript} ]]; then + source ${nixSingleUserProfileScript} + fi + fi + ''; +} diff --git a/users/lucas.zanoni/home.nix b/users/lucas.zanoni/home.nix index d8b61bf3..06f0a808 100644 --- a/users/lucas.zanoni/home.nix +++ b/users/lucas.zanoni/home.nix @@ -11,6 +11,8 @@ ../../home/core.nix ../../home/scripts + ../../home/modules/nix-path-bootstrap.nix + ../../home/modules/agenix.nix ../../home/modules/atuin.nix ../../home/modules/bad-apple.nix