Skip to content

driftcheck.sh: Stop hook has no way to exempt intentionally non-executable scripts — add .driftcheckignore support #56

Description

@ulises-c

Problem

The driftcheck.sh Stop hook flags every git-tracked *.sh file that has a shebang but no execute permission. Some repos have files like that on purpose — e.g. in navecho_monorepo, the workstation_install settings.sh/ros_config.sh files are only ever sourced by deploy_install.sh, and the run scripts get chmod'd at their install destination, so their execute bits were deliberately removed (repo commit f979acd6).

Because it's a Stop hook that exits 2, there is no way to acknowledge the exception: it blocks the end of every Claude Code session in that repo, and the only escapes are flipping the exec bits back (reverting an intentional commit) or stripping shebangs (breaking the scripts at their install destination).

Fix

Add per-repo opt-out support: an optional .driftcheckignore file at the repo root, one glob pattern per line (matched with bash [[ == ]] against the git-relative path), blank lines and # comments allowed. Files matching a pattern are skipped; everything else is still checked.

diff --git a/agentic-ai/Claude/hooks/driftcheck.sh b/agentic-ai/Claude/hooks/driftcheck.sh
index d0eadd4..4c372ef 100755
--- a/agentic-ai/Claude/hooks/driftcheck.sh
+++ b/agentic-ai/Claude/hooks/driftcheck.sh
@@ -6,15 +6,30 @@
 #   - has shebang but not executable → flag (meant to run but can't)
 #   - is executable but no shebang  → flag (can run but no interpreter declared)
 # Library/sourced files (no shebang, not executable) are intentionally skipped.
+# Repos can opt files out via .driftcheckignore at the repo root: one glob
+# pattern per line (matched against the git-relative path), # for comments.
 set -euo pipefail
 trap 'exit 2' ERR
 
 git rev-parse --git-dir &>/dev/null || exit 0
 
+ignore_file="$(git rev-parse --show-toplevel)/.driftcheckignore"
+ignored() {
+  local f=$1 pat
+  [[ -f "$ignore_file" ]] || return 1
+  while IFS= read -r pat; do
+    [[ -z "$pat" || "$pat" == '#'* ]] && continue
+    # shellcheck disable=SC2053
+    [[ "$f" == $pat ]] && return 0
+  done < "$ignore_file"
+  return 1
+}
+
 issues=()
 
 while IFS= read -r f; do
   [[ -f "$f" ]] || continue
+  ignored "$f" && continue
   read -r first_line < "$f" || first_line=""
   has_shebang=false; is_exec=false
   [[ "$first_line" == '#!'* ]] && has_shebang=true

Example .driftcheckignore:

# Intentionally non-executable despite shebangs:
# settings.sh/config.sh are sourced; run scripts are chmod'd at install.
docker/local_account.sh
workstation_install/global_run.sh
workstation_install/workstations/*/settings.sh
workstation_install/workstations/*/config.sh

Status

The fix is already applied to the working copy at agentic-ai/Claude/hooks/driftcheck.sh (uncommitted) and verified against test_repo: with the ignore file present the hook exits 0; other violations are still reported. Needs commit.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions