Skip to content

Commit

Permalink
Merge pull request #205 from python-poetry/1.1-ensure-windows-git-exe…
Browse files Browse the repository at this point in the history
…cutable

[1.0] Ensure the full path to git is used on Windows
  • Loading branch information
sdispater committed Sep 17, 2021
2 parents 9a29df4 + fa9cb6f commit d77c702
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 2 deletions.
49 changes: 47 additions & 2 deletions poetry/core/vcs/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from typing import Any
from typing import Optional

from poetry.core.utils._compat import PY36
from poetry.core.utils._compat import WINDOWS
from poetry.core.utils._compat import Path
from poetry.core.utils._compat import decode

Expand Down Expand Up @@ -154,14 +156,55 @@ def __str__(self): # type: () -> str
GitUrl = namedtuple("GitUrl", ["url", "revision"])


_executable = None


def executable():
global _executable

if _executable is not None:
return _executable

if WINDOWS and PY36:
# Finding git via where.exe
where = "%WINDIR%\\System32\\where.exe"
paths = decode(
subprocess.check_output([where, "git"], shell=True, encoding="oem")
).split("\n")
for path in paths:
if not path:
continue

path = Path(path.strip())
try:
path.relative_to(Path.cwd())
except ValueError:
_executable = str(path)

break
else:
_executable = "git"

if _executable is None:
raise RuntimeError("Unable to find a valid git executable")

return _executable


def _reset_executable():
global _executable

_executable = None


class GitConfig:
def __init__(self, requires_git_presence=False): # type: (bool) -> None
self._config = {}

try:
config_list = decode(
subprocess.check_output(
["git", "config", "-l"], stderr=subprocess.STDOUT
[executable(), "config", "-l"], stderr=subprocess.STDOUT
)
)

Expand Down Expand Up @@ -310,7 +353,9 @@ def run(self, *args, **kwargs): # type: (*Any, **Any) -> str
) + args

return decode(
subprocess.check_output(["git"] + list(args), stderr=subprocess.STDOUT)
subprocess.check_output(
[executable()] + list(args), stderr=subprocess.STDOUT
)
).strip()

def _check_parameter(self, parameter): # type: (str) -> None
Expand Down
45 changes: 45 additions & 0 deletions tests/vcs/test_vcs.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import subprocess

import pytest

from poetry.core.utils._compat import PY36
from poetry.core.utils._compat import WINDOWS
from poetry.core.utils._compat import Path
from poetry.core.vcs.git import Git
from poetry.core.vcs.git import GitError
from poetry.core.vcs.git import GitUrl
from poetry.core.vcs.git import ParsedUrl
from poetry.core.vcs.git import _reset_executable


@pytest.mark.parametrize(
Expand Down Expand Up @@ -276,3 +281,43 @@ def test_git_checkout_raises_error_on_invalid_repository():
def test_git_rev_parse_raises_error_on_invalid_repository():
with pytest.raises(GitError):
Git().rev_parse("-u./payload")


@pytest.mark.skipif(
not WINDOWS or not PY36,
reason="Retrieving the complete path to git is only necessary on Windows, for security reasons",
)
def test_ensure_absolute_path_to_git(mocker):
_reset_executable()

def checkout_output(cmd, *args, **kwargs):
if Path(cmd[0]).name == "where.exe":
return "\n".join(
[str(Path.cwd().joinpath("git.exe")), "C:\\Git\\cmd\\git.exe"]
)

return b""

mock = mocker.patch.object(subprocess, "check_output", side_effect=checkout_output)

Git().run("config")

assert mock.call_args_list[-1][0][0] == [
"C:\\Git\\cmd\\git.exe",
"config",
]


@pytest.mark.skipif(
not WINDOWS or not PY36,
reason="Retrieving the complete path to git is only necessary on Windows, for security reasons",
)
def test_ensure_existing_git_executable_is_found(mocker):
mock = mocker.patch.object(subprocess, "check_output", return_value=b"")

Git().run("config")

cmd = Path(mock.call_args_list[-1][0][0][0])

assert cmd.is_absolute()
assert cmd.name == "git.exe"

0 comments on commit d77c702

Please sign in to comment.