Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.0] Ensure the full path to git is used on Windows #205

Merged
merged 1 commit into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"