Skip to content

Commit

Permalink
Use bash to open extensionless hooks on windows
Browse files Browse the repository at this point in the history
Fix #971. Partly resolve #703.

If the hook doesn't have a file extension, then Windows won't know how
to run it and you'll get "[WinError 193] %1 is not a valid Win32
application". It's very likely that it's a shell script of some kind, so
use bash.exe (commonly installed via Windows Subsystem for Linux). We
don't want to run all hooks with bash because they could be .bat files.

Update tests to get several hook ones working. More work necessary to
get commit-msg hook working. The hook writes to the wrong file because
it's not using forward slashes in the path:
C:\Users\idbrii\AppData\Local\Temp\bare_test_commit_msg_hook_successy5fo00du\CUsersidbriiAppDataLocalTempbare_test_commit_msg_hook_successy5fo00duCOMMIT_EDITMSG
  • Loading branch information
idbrii authored and Byron committed Jan 14, 2022
1 parent fac6037 commit b719e18
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 4 deletions.
15 changes: 14 additions & 1 deletion git/index/fun.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# NOTE: Autodoc hates it if this is a docstring

from io import BytesIO
from pathlib import Path
import os
from stat import (
S_IFDIR,
Expand All @@ -21,6 +22,7 @@
force_text,
force_bytes,
is_posix,
is_win,
safe_decode,
)
from git.exc import (
Expand Down Expand Up @@ -76,6 +78,10 @@ def hook_path(name: str, git_dir: PathLike) -> str:
return osp.join(git_dir, 'hooks', name)


def _has_file_extension(path):
return osp.splitext(path)[1]


def run_commit_hook(name: str, index: 'IndexFile', *args: str) -> None:
"""Run the commit hook of the given name. Silently ignores hooks that do not exist.
:param name: name of hook, like 'pre-commit'
Expand All @@ -89,8 +95,15 @@ def run_commit_hook(name: str, index: 'IndexFile', *args: str) -> None:
env = os.environ.copy()
env['GIT_INDEX_FILE'] = safe_decode(str(index.path))
env['GIT_EDITOR'] = ':'
cmd = [hp]
try:
cmd = subprocess.Popen([hp] + list(args),
if is_win and not _has_file_extension(hp):
# Windows only uses extensions to determine how to open files
# (doesn't understand shebangs). Try using bash to run the hook.
relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
cmd = ["bash.exe", relative_hp]

cmd = subprocess.Popen(cmd + list(args),
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
Expand Down
9 changes: 6 additions & 3 deletions test/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
)
import tempfile
from unittest import skipIf
import shutil

from git import (
IndexFile,
Expand Down Expand Up @@ -52,8 +53,9 @@

HOOKS_SHEBANG = "#!/usr/bin/env sh\n"

is_win_without_bash = is_win and not shutil.which('bash.exe')


@skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
def _make_hook(git_dir, name, content, make_exec=True):
"""A helper to create a hook"""
hp = hook_path(name, git_dir)
Expand Down Expand Up @@ -881,7 +883,7 @@ def test_pre_commit_hook_fail(self, rw_repo):
try:
index.commit("This should fail")
except HookExecutionError as err:
if is_win:
if is_win_without_bash:
self.assertIsInstance(err.status, OSError)
self.assertEqual(err.command, [hp])
self.assertEqual(err.stdout, '')
Expand All @@ -896,6 +898,7 @@ def test_pre_commit_hook_fail(self, rw_repo):
else:
raise AssertionError("Should have caught a HookExecutionError")

@skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
@with_rw_repo('HEAD', bare=True)
def test_commit_msg_hook_success(self, rw_repo):
commit_message = "commit default head by Frèderic Çaufl€"
Expand All @@ -920,7 +923,7 @@ def test_commit_msg_hook_fail(self, rw_repo):
try:
index.commit("This should fail")
except HookExecutionError as err:
if is_win:
if is_win_without_bash:
self.assertIsInstance(err.status, OSError)
self.assertEqual(err.command, [hp])
self.assertEqual(err.stdout, '')
Expand Down

0 comments on commit b719e18

Please sign in to comment.