Skip to content

Commit 136ad7e

Browse files
committed
added handling for extremely weird edge case behavior for dockerignore
1 parent b50f80e commit 136ad7e

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

src/runloop_api_client/lib/_ignore.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,6 @@ class DockerIgnoreMatcher(IgnoreMatcher):
322322

323323
def iter_paths(self, root: Path) -> Iterable[Path]:
324324
"""Yield non-ignored files under ``root`` honoring Docker-style patterns."""
325-
326325
root = root.resolve()
327326

328327
all_patterns: list[str] = []
@@ -338,9 +337,25 @@ def iter_paths(self, root: Path) -> Iterable[Path]:
338337
raise FileNotFoundError(f"Ignore file does not exist: {ignore_path}")
339338
all_patterns.extend(read_ignorefile(ignore_path))
340339

341-
# 3) Optional inline patterns appended last.
340+
# 3) Optional inline patterns appended last using same rules as .dockerignore
341+
# Some extra handling here for trailing slashes that is different from .gitignore.
342342
if self.patterns:
343-
all_patterns.extend(self.patterns)
343+
for raw in self.patterns:
344+
if not raw:
345+
continue
346+
347+
invert = raw[0] == "!"
348+
pattern = raw[1:].strip() if invert else raw.strip()
349+
350+
if pattern:
351+
pattern = os.path.normpath(pattern)
352+
pattern = pattern.replace(os.sep, "/")
353+
if len(pattern) > 1 and pattern[0] == "/":
354+
pattern = pattern[1:]
355+
356+
normalized = f"!{pattern}" if invert else pattern
357+
if normalized:
358+
all_patterns.append(normalized)
344359

345360
compiled: list[IgnorePattern] = compile_ignore(all_patterns)
346361
return iter_included_files(root, patterns=compiled)

tests/test_utils/test_context_loader.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,31 @@ def test_iter_build_context_files_respects_dockerignore(tmp_path: Path):
7979
assert "build/ignored.txt" not in files
8080

8181

82+
def test_optional_patterns_trailing_slash_matches_file_in_context(tmp_path: Path) -> None:
83+
"""Inline ignore patterns should mirror .dockerignore trailing-slash behavior.
84+
85+
Patterns like '/foo/bar/' provided via the optional ``ignore=`` parameter
86+
should exclude a file at 'foo/bar' in the build context, matching Docker's
87+
patternmatcher semantics where trailing slashes are not directory-only.
88+
"""
89+
90+
root = tmp_path
91+
foo = root / "foo"
92+
foo.mkdir()
93+
(foo / "bar").write_text("ignored", encoding="utf-8")
94+
(root / "keep.txt").write_text("keep", encoding="utf-8")
95+
96+
# Use build_docker_context_tar with an inline pattern that includes a
97+
# trailing slash; this should still exclude the file at 'foo/bar'.
98+
tar_bytes = build_docker_context_tar(root, ignore=["/foo/bar/"])
99+
100+
with tarfile.open(fileobj=io.BytesIO(tar_bytes), mode="r:gz") as tf:
101+
names = {m.name for m in tf.getmembers()}
102+
103+
assert "keep.txt" in names
104+
assert "foo/bar" not in names
105+
106+
82107
def test_is_ignored_directory_pattern_affects_directory_entry_only() -> None:
83108
"""Directory patterns apply directly to directory entries, not to children."""
84109

0 commit comments

Comments
 (0)