Skip to content

Commit

Permalink
Add git file lister.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfontein committed Aug 30, 2024
1 parent c43a47b commit 4083c5b
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 25 deletions.
48 changes: 45 additions & 3 deletions src/antsibull_fileutils/vcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
import subprocess
import typing as t

if t.TYPE_CHECKING:
from _typeshed import StrPath


def detect_vcs(
path: str,
path: StrPath,
*,
git_bin_path: str = "git",
git_bin_path: StrPath = "git",
log_debug: t.Callable[[str], None] | None = None,
log_info: t.Callable[[str], None] | None = None,
) -> t.Literal["none", "git"]:
Expand All @@ -40,7 +43,7 @@ def do_log_info(msg: str, *args: t.Any) -> None:
do_log_debug("Trying to determine whether {!r} is a Git repository", path)
try:
result = subprocess.check_output(
[git_bin_path, "-C", path, "rev-parse", "--is-inside-work-tree"],
[str(git_bin_path), "-C", path, "rev-parse", "--is-inside-work-tree"],
text=True,
encoding="utf-8",
).strip()
Expand All @@ -58,3 +61,42 @@ def do_log_info(msg: str, *args: t.Any) -> None:
# Fallback: no VCS detected
do_log_debug("Cannot identify VCS")
return "none"


def list_git_files(
directory: StrPath,
*,
git_bin_path: StrPath = "git",
log_debug: t.Callable[[str], None] | None = None,
) -> list[bytes]:
"""
List all files not ignored by git in a directory and subdirectories.
Raises ``ValueError`` in case of errors.
"""

def do_log_debug(msg: str, *args) -> None:
if log_debug:
log_debug(msg, *args)

do_log_debug("Identifying files not ignored by Git in {!r}", directory)
try:
result = subprocess.check_output(
[
str(git_bin_path),
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
"--deduplicate",
],
cwd=directory,
).strip(b"\x00")
if result == b"":
return []
return result.split(b"\x00")
except subprocess.CalledProcessError as exc:
raise ValueError("Error while running git") from exc
except FileNotFoundError as exc:
raise ValueError("Cannot find git executable") from exc
Empty file added tests/units/__init__.py
Empty file.
94 changes: 72 additions & 22 deletions tests/units/test_vcs.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Author: Felix Fontein <[email protected]>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2020, Ansible Project
# SPDX-FileCopyrightText: 2024, Ansible Project

"""
Test utils module.
Test vcs module.
"""

from __future__ import annotations
Expand All @@ -14,33 +14,16 @@

import pytest

from antsibull_fileutils.vcs import detect_vcs
from antsibull_fileutils.vcs import detect_vcs, list_git_files

from .utils import collect_log


def test_detect_vcs():
path = "/path/to/dir"
git_bin_path = "/path/to/git"
git_command = [git_bin_path, "-C", path, "rev-parse", "--is-inside-work-tree"]

def collect_log():
debug = []
info = []

def log_debug(msg: str, *args) -> None:
debug.append((msg, args))

def log_info(msg: str, *args) -> None:
info.append((msg, args))

return (
{
"log_debug": log_debug,
"log_info": log_info,
},
debug,
info,
)

with mock.patch(
"subprocess.check_output",
return_value="true\n",
Expand Down Expand Up @@ -83,3 +66,70 @@ def log_info(msg: str, *args) -> None:
) as m:
assert detect_vcs(path, git_bin_path=git_bin_path) == "none"
m.assert_called_with(git_command, text=True, encoding="utf-8")


TEST_LIST_GIT_FILES = [
(b"", [], False),
(b"", [], True),
(b"foo\nbar", [b"foo\nbar"], False),
(b"foo\x00", [b"foo"], True),
(b"foo\x00bar", [b"foo", b"bar"], True),
(
b"link\x00foobar\x00dir/binary_file",
[b"link", b"foobar", b"dir/binary_file"],
False,
),
]


@pytest.mark.parametrize("stdout, expected, with_logging", TEST_LIST_GIT_FILES)
def test_list_git_files(stdout: bytes, expected: list[str], with_logging: bool):
path = "/path/to/dir"
git_bin_path = "/path/to/git"
git_command = [
git_bin_path,
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
"--deduplicate",
]

with mock.patch(
"subprocess.check_output",
return_value=stdout,
) as m:
kwargs, debug, info = collect_log(with_debug=with_logging, with_info=False)
assert list_git_files(path, git_bin_path=git_bin_path, **kwargs) == expected
m.assert_called_with(git_command, cwd=path)
if with_logging:
assert debug == [("Identifying files not ignored by Git in {!r}", (path,))]


def test_list_git_files_fail():
path = "/path/to/dir"
git_bin_path = "/path/to/git"
git_command = [
git_bin_path,
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
"--deduplicate",
]

with mock.patch(
"subprocess.check_output",
side_effect=subprocess.CalledProcessError(128, path),
) as m:
with pytest.raises(ValueError, match="^Error while running git$") as exc:
list_git_files(path, git_bin_path=git_bin_path)

with mock.patch(
"subprocess.check_output",
side_effect=FileNotFoundError(),
) as m:
with pytest.raises(ValueError, match="^Cannot find git executable$") as exc:
list_git_files(path, git_bin_path=git_bin_path)
32 changes: 32 additions & 0 deletions tests/units/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Author: Felix Fontein <[email protected]>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# SPDX-FileCopyrightText: 2024, Ansible Project

"""
Utilities
"""

from __future__ import annotations


def collect_log(with_debug: bool = True, with_info: bool = True):
debug = []
info = []

def log_debug(msg: str, *args) -> None:
debug.append((msg, args))

def log_info(msg: str, *args) -> None:
info.append((msg, args))

kwargs = {}
if with_debug:
kwargs["log_debug"] = log_debug
if with_info:
kwargs["log_info"] = log_info
return (
kwargs,
debug,
info,
)

0 comments on commit 4083c5b

Please sign in to comment.