Skip to content

Commit 28ef9b7

Browse files
committed
fix: do not require git to start up
1 parent a157352 commit 28ef9b7

File tree

5 files changed

+58
-5
lines changed

5 files changed

+58
-5
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Fixed
2+
3+
- `ggshield` no longer crashes when it can't find git.

ggshield/core/env_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from dotenv import dotenv_values, load_dotenv
77

88
from ggshield.core import ui
9-
from ggshield.utils.git_shell import get_git_root, is_git_dir
9+
from ggshield.utils.git_shell import get_git_root, is_git_available, is_git_dir
1010
from ggshield.utils.os import getenv_bool
1111

1212

@@ -37,7 +37,7 @@ def _find_dot_env() -> Optional[Path]:
3737
return env
3838

3939
# If we are in a git checkout, look for a .env at the root of the checkout
40-
if is_git_dir(os.getcwd()):
40+
if is_git_available() and is_git_dir(os.getcwd()):
4141
env = get_git_root() / ".env"
4242
if env.is_file():
4343
return env

ggshield/utils/git_shell.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ class Filemode(Enum):
5959
UNKNOWN = "unknown"
6060

6161

62+
def is_git_available() -> bool:
63+
try:
64+
_get_git_path()
65+
return True
66+
except GitExecutableNotFound:
67+
return False
68+
69+
6270
@lru_cache(None)
6371
def _get_git_path() -> str:
6472
git_path = which("git")
@@ -362,11 +370,11 @@ def get_last_commit_sha_of_branch(branch_name: str) -> Optional[str]:
362370
def get_repository_url_from_path(wd: Path) -> Optional[str]:
363371
"""
364372
Returns one of the repository remote urls. Returns None if no remote are found,
365-
or the directory is not a repository.
373+
or the directory is not a repository or we don't have git so we can't know if the
374+
directory is a repository.
366375
"""
367-
remotes_raw: List[str] = []
368376
try:
369-
if not is_git_dir(wd):
377+
if not is_git_available() or not is_git_dir(wd):
370378
return None
371379
remotes_raw = git(["remote", "-v"], cwd=wd).splitlines()
372380
except (subprocess.CalledProcessError, OSError):

tests/functional/secret/test_scan_path.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import json
2+
import shutil
3+
import sys
24
from pathlib import Path
35

46
import jsonschema
@@ -123,3 +125,35 @@ def test_scan_path_ignore_known_secrets(tmp_path: Path) -> None:
123125
# THEN only the unknown secret is reported
124126
assert f"KNOWN_SECRET='{KNOWN_SECRET}'" not in result.stdout
125127
assert f"UNKNOWN_SECRET='{UNKNOWN_SECRET}'" in result.stdout
128+
129+
130+
def create_fake_path(tmp_path: Path) -> Path:
131+
# Create a bin dir
132+
bin_path = (tmp_path / "bin").absolute()
133+
bin_path.mkdir()
134+
135+
# Copy the binary of our interpreter to it
136+
shutil.copy2(sys.executable, bin_path)
137+
138+
return bin_path
139+
140+
141+
def test_scan_path_works_if_git_not_found(monkeypatch, tmp_path: Path) -> None:
142+
# GIVEN a test file with no secret
143+
test_file = tmp_path / "hello"
144+
test_file.write_text("harmless")
145+
146+
# AND a fake PATH containing only Python
147+
fake_path = create_fake_path(tmp_path)
148+
monkeypatch.setenv("PATH", str(fake_path))
149+
150+
# the name of the interpreter might be python3, not just python
151+
python_filename = Path(sys.executable).name
152+
assert shutil.which(python_filename) is not None
153+
154+
# AND git cannot be found
155+
assert shutil.which("git") is None
156+
157+
# WHEN ggshield scans the test file
158+
# THEN it does not fail
159+
run_ggshield_scan("path", "--debug", str(test_file), cwd=tmp_path, expected_code=0)

tests/unit/utils/test_git_shell.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from ggshield.core.tar_utils import tar_from_ref_and_filepaths
1212
from ggshield.utils.git_shell import (
13+
GitExecutableNotFound,
1314
InvalidGitRefError,
1415
NotAGitDirectory,
1516
check_git_dir,
@@ -22,6 +23,7 @@
2223
get_staged_filepaths,
2324
git,
2425
git_ls_unstaged,
26+
is_git_available,
2527
is_git_dir,
2628
is_valid_git_commit_ref,
2729
simplify_git_url,
@@ -47,6 +49,12 @@ def _create_repository_with_remote(
4749
return local_repo
4850

4951

52+
@patch("ggshield.utils.git_shell._get_git_path")
53+
def test_is_git_available(_get_git_path_mock):
54+
_get_git_path_mock.side_effect = GitExecutableNotFound()
55+
assert not is_git_available()
56+
57+
5058
def test_git_shell():
5159
assert "usage: git" in git(["help"])
5260

0 commit comments

Comments
 (0)