Skip to content

Commit a208a33

Browse files
authored
Add pattern matching for gitignore files in ddev size (DataDog#22838)
* Add pattern matching to ignore gitignore files * Add changelog
1 parent 46ce508 commit a208a33

3 files changed

Lines changed: 52 additions & 18 deletions

File tree

ddev/changelog.d/22838.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed `ddev size` ignore filtering to correctly interpret gitignore entries, ensuring excluded files are omitted from size calculations.

ddev/src/ddev/cli/size/utils/common_funcs.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# Licensed under a 3-clause BSD style license (see LICENSE)
44
from __future__ import annotations
55

6+
import fnmatch
67
import json
78
import os
89
import re
@@ -160,7 +161,7 @@ def is_valid_integration_file(
160161
included_folder = "datadog_checks" + os.sep
161162

162163
if git_ignore is None:
163-
git_ignore = get_gitignore_files(repo_path)
164+
git_ignore = get_gitignore_files(Path(repo_path))
164165
# It is not an integration
165166
if path.startswith("."):
166167
return False
@@ -171,20 +172,34 @@ def is_valid_integration_file(
171172
elif any(ignore in path for ignore in ignored_files):
172173
return False
173174
# This file is contained in .gitignore
174-
elif any(ignore in path for ignore in git_ignore):
175+
elif _matches_gitignore(path, git_ignore):
175176
return False
176177
else:
177178
return True
178179

179180

180-
def get_gitignore_files(repo_path: str | Path) -> list[str]:
181-
gitignore_path = os.path.join(repo_path, ".gitignore")
182-
with open(gitignore_path, "r", encoding="utf-8") as file:
183-
gitignore_content = file.read()
184-
ignored_patterns = [
185-
line.strip() for line in gitignore_content.splitlines() if line.strip() and not line.startswith("#")
186-
]
187-
return ignored_patterns
181+
def _matches_gitignore(path: str, patterns: list[str]) -> bool:
182+
parts = path.replace(os.sep, "/").split("/")
183+
for pattern in patterns:
184+
norm = pattern.rstrip("/")
185+
if fnmatch.fnmatch(path, norm):
186+
return True
187+
if fnmatch.fnmatch(os.path.basename(path), norm):
188+
return True
189+
if any(fnmatch.fnmatch(part, norm) for part in parts):
190+
return True
191+
return False
192+
193+
194+
def get_gitignore_files(repo_path: Path) -> list[str]:
195+
gitignore_path = repo_path / ".gitignore"
196+
if not gitignore_path.is_file():
197+
return []
198+
199+
with gitignore_path.open(mode="r", encoding="utf-8") as f:
200+
lines = [line.strip() for line in f.read().splitlines() if line.strip() and not line.startswith("#")]
201+
202+
return lines
188203

189204

190205
def convert_to_human_readable_size(size_bytes: float) -> str:

ddev/tests/size/test_common.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import pytest
88

99
from ddev.cli.size.utils.common_funcs import (
10+
_matches_gitignore,
1011
check_python_version,
1112
compress,
1213
convert_to_human_readable_size,
@@ -115,14 +116,34 @@ def test_convert_to_human_readable_size(size_bytes, expected_string):
115116
pytest.param("__pycache__/file.py", False, id="pycache"),
116117
pytest.param("datadog_checks_dev/example.py", False, id="checks_dev"),
117118
pytest.param(".git/config", False, id="git"),
119+
pytest.param("datadog_checks/module/cache.pyc", False, id="gitignore_glob_ext"),
120+
pytest.param("datadog_checks/module/__pycache__/foo.py", False, id="gitignore_glob_dir"),
118121
],
119122
)
120123
def test_is_valid_integration_file(file_path, expected):
121124
repo_path = "fake_repo"
122-
with patch("ddev.cli.size.utils.common_funcs.get_gitignore_files", return_value=set()):
125+
gitignore_patterns = ["*.pyc", "__pycache__"]
126+
with patch("ddev.cli.size.utils.common_funcs.get_gitignore_files", return_value=gitignore_patterns):
123127
assert is_valid_integration_file(to_native_path(file_path), repo_path) is expected
124128

125129

130+
@pytest.mark.parametrize(
131+
"path, patterns, expected",
132+
[
133+
pytest.param("foo/bar/baz.pyc", ["*.pyc"], True, id="glob_extension_match"),
134+
pytest.param("foo/bar/baz.py", ["*.pyc"], False, id="glob_extension_no_match"),
135+
pytest.param("foo/__pycache__/module.py", ["__pycache__"], True, id="dir_segment_match"),
136+
pytest.param("foo/bar/module.py", ["__pycache__"], False, id="dir_segment_no_match"),
137+
pytest.param("foo/bar/notes.log", ["*.log"], True, id="glob_log_match"),
138+
pytest.param("foo/bar/notes.txt", ["*.log"], False, id="glob_log_no_match"),
139+
pytest.param("foo/bar/baz.py", ["*.pyc", "__pycache__", "*.log"], False, id="no_pattern_matches"),
140+
pytest.param("foo/__pycache__/baz.pyc", ["*.pyc", "__pycache__"], True, id="multiple_patterns_first_matches"),
141+
],
142+
)
143+
def test_matches_gitignore(path, patterns, expected):
144+
assert _matches_gitignore(to_native_path(path), patterns) is expected
145+
146+
126147
def test_get_dependencies_list():
127148
file_content = "dependency1 @ https://example.com/dependency1/dependency1-1.1.1-.whl\ndependency2 @ https://example.com/dependency2/dependency2-1.1.1-.whl"
128149
mock_open_obj = mock_open(read_data=file_content)
@@ -267,13 +288,10 @@ def test_check_version(py_version, expected):
267288
assert check_python_version("fake_repo", "integration1", py_version) is expected
268289

269290

270-
def test_get_gitignore_files():
271-
mock_gitignore = f"__pycache__{os.sep}\n*.log\n" # Sample .gitignore file
272-
repo_path = "fake_repo"
273-
with patch("builtins.open", mock_open(read_data=mock_gitignore)):
274-
with patch("ddev.cli.size.utils.common_funcs.os.path.exists", return_value=True):
275-
ignored_patterns = get_gitignore_files(repo_path)
276-
assert ignored_patterns == ["__pycache__" + os.sep, "*.log"]
291+
def test_get_gitignore_files(tmp_path):
292+
gitignore = tmp_path / ".gitignore"
293+
gitignore.write_text(f"__pycache__{os.sep}\n*.log\n")
294+
assert get_gitignore_files(tmp_path) == ["__pycache__" + os.sep, "*.log"]
277295

278296

279297
def test_compress():

0 commit comments

Comments
 (0)