Add optional pre-commit hook for code formatting #5178
4 issues
find-bugs: Found 4 issues (1 high, 2 medium, 1 low)
High
dotnet format mutates the working tree, leaving format edits behind on failure and corrupting stash pop - `.githooks/pre-commit:28-29`
The hook runs dotnet format without --verify-no-changes, so it actively rewrites files in the working tree instead of just checking them (contradicting the PR's stated 'check-only mode'). When formatting issues exist, the script exits 1 and the EXIT trap runs git stash pop, but the working tree now contains dotnet format's edits on top of the staged content. Restoring the previously stashed unstaged changes will frequently conflict, and even when it doesn't, the developer is left with unrequested formatting modifications applied to their working tree (and possibly their stash dropped). On a successful commit the hook also leaves these working-tree mutations in place, since nothing resets them before exit.
Medium
`set -e` causes the hook to abort silently if dotnet format exits non-zero for any reason - `.githooks/pre-commit:28-29`
With set -e enabled at the top of the script, any non-zero exit from dotnet format (e.g., build/restore error, invalid argument, dotnet not installed) will terminate the script immediately. The EXIT trap then runs git stash pop and the script exits non-zero, blocking the commit without printing the helpful 'formatting issues found' message — and without surfacing the actual dotnet error, since stdout/stderr are redirected to /dev/null. Developers will see a failed commit with no diagnostic output.
`./**/*OptionsSetup.cs` is expanded by bash before reaching dotnet format, producing wrong --exclude arguments - `.githooks/pre-commit:29`
The script passes ./**/*OptionsSetup.cs unquoted to dotnet format. Bash will perform pathname expansion on this token before invoking dotnet. Without shopt -s globstar, ** behaves like * and matches only one directory level; with globstar enabled it expands recursively. Either way, dotnet format receives a list of concrete paths (or the literal pattern if no match) — not the intended glob. As a result the exclude list differs from CI, so the hook may flag (or miss) files that CI treats differently, defeating the 'matches CI command exactly' goal.
Low
Setup script does not ensure pre-commit hook is executable - `scripts/setup-hooks.sh:7`
The setup script configures core.hooksPath to .githooks but does not chmod +x .githooks/pre-commit. If the file is checked out without the executable bit (e.g., on systems where git's filemode tracking is disabled, or if the file was committed without it), git will silently skip the hook, defeating the formatting check. Developers may believe formatting is being verified when it is not.
Duration: 59.0s · Tokens: 63.4k in / 4.3k out · Cost: $1.30 (+merge: $0.00)
Annotations
Check failure on line 29 in .githooks/pre-commit
sentry-warden / warden: find-bugs
dotnet format mutates the working tree, leaving format edits behind on failure and corrupting stash pop
The hook runs `dotnet format` without `--verify-no-changes`, so it actively rewrites files in the working tree instead of just checking them (contradicting the PR's stated 'check-only mode'). When formatting issues exist, the script exits 1 and the EXIT trap runs `git stash pop`, but the working tree now contains dotnet format's edits on top of the staged content. Restoring the previously stashed unstaged changes will frequently conflict, and even when it doesn't, the developer is left with unrequested formatting modifications applied to their working tree (and possibly their stash dropped). On a successful commit the hook also leaves these working-tree mutations in place, since nothing resets them before exit.
Check warning on line 29 in .githooks/pre-commit
sentry-warden / warden: find-bugs
`set -e` causes the hook to abort silently if dotnet format exits non-zero for any reason
With `set -e` enabled at the top of the script, any non-zero exit from `dotnet format` (e.g., build/restore error, invalid argument, dotnet not installed) will terminate the script immediately. The EXIT trap then runs `git stash pop` and the script exits non-zero, blocking the commit without printing the helpful 'formatting issues found' message — and without surfacing the actual dotnet error, since stdout/stderr are redirected to /dev/null. Developers will see a failed commit with no diagnostic output.
Check warning on line 29 in .githooks/pre-commit
sentry-warden / warden: find-bugs
`./**/*OptionsSetup.cs` is expanded by bash before reaching dotnet format, producing wrong --exclude arguments
The script passes `./**/*OptionsSetup.cs` unquoted to `dotnet format`. Bash will perform pathname expansion on this token before invoking dotnet. Without `shopt -s globstar`, `**` behaves like `*` and matches only one directory level; with globstar enabled it expands recursively. Either way, dotnet format receives a list of concrete paths (or the literal pattern if no match) — not the intended glob. As a result the exclude list differs from CI, so the hook may flag (or miss) files that CI treats differently, defeating the 'matches CI command exactly' goal.