Skip to content

Commit c17c39b

Browse files
authored
fix: anchor kind discovery to project root (#4763) (#4889)
1 parent cfd927f commit c17c39b

File tree

2 files changed

+64
-3
lines changed

2 files changed

+64
-3
lines changed

src/ansiblelint/file_utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,9 +134,17 @@ def kind_from_path(path: Path, *, base: bool = False) -> FileType:
134134
When called with base=True, it will return the base file type instead
135135
of the explicit one. That is expected to return 'yaml' for any yaml files.
136136
"""
137-
# pathlib.Path.match patterns are very limited, they do not support *a*.yml
138-
# glob.glob supports **/foo.yml but not multiple extensions
139-
pathex = wcmatch.pathlib.PurePath(str(path.absolute().resolve()))
137+
# We attempt to use a relative path to the project root for glob matching.
138+
# This prevents parent directory names (like 'tasks') from triggering
139+
# false positives in kind discovery. See #4763.
140+
try:
141+
project_root, _ = find_project_root([str(path)])
142+
# .resolve() ensures we handle symlinks and double-dots correctly
143+
rel_path = path.resolve().relative_to(project_root.resolve())
144+
pathex = wcmatch.pathlib.PurePath(str(rel_path))
145+
except (ValueError, RuntimeError):
146+
# Fallback to absolute if the file is outside the project root or can't be found
147+
pathex = wcmatch.pathlib.PurePath(str(path.absolute().resolve()))
140148
kinds = options.kinds if not base else BASE_KINDS
141149
for entry in kinds:
142150
for k, v in entry.items():

test/test_file_utils.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
expand_path_vars,
1919
expand_paths_vars,
2020
find_project_root,
21+
kind_from_path,
2122
normpath,
2223
normpath_path,
2324
)
@@ -543,3 +544,55 @@ def test_bug_2513(
543544
results = Runner(filename, rules=default_rules_collection).run()
544545
assert len(results) == 1
545546
assert results[0].rule.id == "name"
547+
548+
549+
def test_kind_from_path_parent_collision(tmp_path: Path) -> None:
550+
"""Verify that a parent directory named 'tasks' doesn't cause false positives.
551+
552+
See https://github.com/ansible/ansible-lint/issues/4763
553+
"""
554+
tasks_parent = tmp_path / "tasks"
555+
project_dir = tasks_parent / "my_project"
556+
project_dir.mkdir(parents=True)
557+
558+
(project_dir / ".git").mkdir()
559+
560+
playbook_path = project_dir / "site.yml"
561+
playbook_path.touch()
562+
563+
kind = kind_from_path(playbook_path)
564+
565+
assert kind != "tasks", f"File {playbook_path} should not be identified as 'tasks'"
566+
567+
568+
def test_kind_from_path_valid_tasks(tmp_path: Path) -> None:
569+
"""Verify that legitimate tasks directories are still correctly identified."""
570+
project_dir = tmp_path / "my_project"
571+
project_dir.mkdir()
572+
(project_dir / ".git").mkdir()
573+
574+
task_dir = project_dir / "tasks"
575+
task_dir.mkdir()
576+
task_path = task_dir / "main.yml"
577+
task_path.touch()
578+
579+
kind = kind_from_path(task_path)
580+
581+
assert kind == "tasks", f"File {task_path} should be identified as 'tasks'"
582+
583+
584+
def test_kind_from_path_outside_project_root(tmp_path: Path) -> None:
585+
"""Verify fallback to absolute path when file is outside the project root.
586+
587+
This triggers the except block in kind_from_path
588+
"""
589+
project_dir = tmp_path / "actual_project"
590+
project_dir.mkdir()
591+
(project_dir / ".git").mkdir()
592+
593+
outside_file = tmp_path / "external_file.yml"
594+
outside_file.touch()
595+
596+
kind = kind_from_path(outside_file)
597+
598+
assert kind == "yaml"

0 commit comments

Comments
 (0)