fix(hooks): run hooks through sh on Windows when in a POSIX shell env - #120
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #120 +/- ##
==========================================
+ Coverage 36.89% 37.71% +0.82%
==========================================
Files 28 28
Lines 3201 3216 +15
==========================================
+ Hits 1181 1213 +32
+ Misses 1934 1913 -21
- Partials 86 90 +4
🚀 New features to boost your workflow:
|
CI verified — counts, not the tickAll checks green. Reading the leg summaries as #118 asks, since a scenario that silently keeps skipping is indistinguishable from a passing one in the check status:
The absolute numbers differ from the 96→101 and 71/34, 72/33 predicted in the issue — that baseline predates #117 adding All five scenarios pass by name in
|
wt spawns hook commands itself, so the shell the user is sitting in never had a say in what ran them: on Windows every hook went through `cmd /c`. That breaks essentially every hook we document, since `cmd` expands neither `$WT_PATH` nor provides `test`, `cp` or `&&`. Post-hooks warned to stderr and reported success, so a Git Bash user's .env was silently not copied; pre-hooks aborted the operation naming a command that looks perfectly valid. Reuse isPOSIXShellEnv() from #113: on Windows, use `sh -c` only when we can see a POSIX environment and find an sh to run it with, otherwise keep `cmd /c`. PowerShell and cmd users who wrote %WT_PATH% hooks are unaffected. Choosing sh is not enough on its own — the hook vars hold native paths, and `cd $WT_PATH` in bash eats the backslashes as escapes. WT_PATH and WT_MAIN are therefore converted to mixed form (C:\a\b -> C:/a/b) when the hook shell is POSIX. Mixed form over /c/a/b so it also works under Cygwin (which mounts drives at /cygdrive) and survives being passed to a native tool the hook invokes, e.g. `code $WT_PATH`. The five hooks.yaml scenarios skipped for this exact cause are enabled, with their assertions moved to $WORKTREE_ROOT_POSIX so they read paths in the harness shell's own form. Fixes #118
b1d0474 to
afa04b2
Compare
Fixes #118.
The decision
Both open questions from the issue, resolved as recommended there:
Policy — option 1 (POSIX shell only when we are demonstrably in one). On Windows,
sh -cis used whenisPOSIXShellEnv()(from #113) reports a POSIX environment andshresolves onPATH; otherwisecmd /cstays. Nothing changes for PowerShell/cmd users, so anyone with%WT_PATH%-flavoured hooks is unaffected. A missingshfalls back tocmdrather than failing every hook at exec time.The wrinkle — convert in-process, to mixed form
C:/a/b, not/c/a/b. The issue is right that pickingshfixes nothing on its own:cd $WT_PATHin bash eats the backslashes as escapes.WT_PATHandWT_MAINare converted when the hook shell is POSIX. Mixed form rather than the/c/a/bsketched in the issue, because it is strictly wider:C:/a/b/c/a/b/cygdrive/ccode $WT_PATH,npm)Backslashes are what actually break the hook, so swapping the separator is the whole fix — no drive-letter special-casing, and already-POSIX paths pass through unchanged.
WT_BRANCHis deliberately left alone: a branch may legitimately contain a backslash and is not a path.No
cygpathdependency, and nohooks.shellconfig key — not needed to close this.Changes
cmd/hooks.go—hookShell()picks the interpreter;adaptHookEnv()+toPOSIXPath()adapt the path-valued vars. Both takegoosexplicitly, matching thedetectShell(args, goos)convention, andhookShelltakeslookPathso the "POSIX env but nosh" branch is testable off Windows.cmd/hooks_test.go— new: interpreter selection across Git Bash / Cygwin / PowerShell / no-sh, path conversion, and env adaptation (including that the caller's map is not mutated).e2e/scenarios/hooks.yaml— the five scenarios loseskip_os: [windows]forskip_shells: [powershell, pwsh].docs/configuration.md— the "sh -c(orcmd /con Windows)" footnote replaced with a table of which shell runs a hook where, the exact rule, the path form, and a cmd-syntax counterexample.docs/examples.mdgets a note at the top pointing there.Verifying
One thing the issue did not anticipate: the five scenarios also needed their assertion steps moved from
$WORKTREE_ROOTto$WORKTREE_ROOT_POSIX. Those steps runtest -f/catin the harness shell, and on Windows$WORKTREE_ROOTis the native form — so they would have failed on backslashes even with the fix in place, exactly the native/POSIX split #116 introduced.Per the issue, read the counts rather than the tick —
E2E (windows, bash)should go 96 passed / 9 skipped → 101 / 4, with the PowerShell legs unchanged at 71/34 and 72/33.copy_env_file_when_existsis the scenario that actually proves the fix; its siblingcopy_env_file_skipped_when_missingwould pass either way.Locally on macOS: 310 e2e passed / 0 failed / 23 skipped.
go test ./...passes exceptTestBashTabCompletionForCheckoutBranchandTestBashTabCompletionForCommands, which fail identically on a cleanmainhere (bash 3.2).