From bf7f096d69cd2fb8184a75dc05b84e3744343650 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 19:01:17 +0000 Subject: [PATCH 1/2] fix: empty sibling dir no longer reported when peers have visible files - Add `_has_visible_files` helper to check whether a directory directly contains at least one visible (non-ignored, non-ontrack.yml) file. - In `_find_reporting_directories`, after collecting results from all subdirectories, filter out any empty-leaf entries when the result set also contains directories that have visible files. An empty directory is still reported when it is the only (or deepest) reachable leaf, but is silently dropped when it would otherwise appear alongside real content. - Add `test_find_reporting_directories_empty_sibling_not_reported` to reproduce the bug and verify the fix. Agent-Logs-Url: https://github.com/FertigLab/ontrack/sessions/9d81e458-efda-4b1c-892b-4525bad9e81e Co-authored-by: dimalvovs <1246862+dimalvovs@users.noreply.github.com> --- ontrack.py | 47 +++++++++++++++++++++++++++++++++++++++---- tests/test_ontrack.py | 28 ++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/ontrack.py b/ontrack.py index a1f9155..7135790 100644 --- a/ontrack.py +++ b/ontrack.py @@ -188,8 +188,37 @@ def _is_on_track( return True +def _has_visible_files(directory: str, patterns: list[str]) -> bool: + """Return True iff *directory* directly contains at least one visible file. + + A "visible file" is a regular file whose name is not ``ontrack.yml`` and + does not match any pattern in *patterns*. Entries that cannot be stat'd + are silently skipped. + + Args: + directory: Path to the directory to inspect. + patterns: Shell-style glob patterns (see :func:`_is_ignored`). Files + whose names match any pattern are not considered visible. + """ + try: + for entry in os.scandir(directory): + try: + if ( + entry.is_file(follow_symlinks=False) + and entry.name != _ONTRACK_YML + and not _is_ignored(entry.name, patterns) + ): + return True + except OSError: + pass + except OSError: + pass + return False + + def _find_reporting_directories( - directory: str, ignore_patterns: list[str] | None = None + directory: str, + ignore_patterns: list[str] | None = None, ) -> list[str]: """Return reporting directories within *directory*. @@ -198,9 +227,11 @@ def _find_reporting_directories( If *directory* contains only ignored files and subdirectories, or contains no files at all, the search recurses into each non-ignored subdirectory. An empty directory (no files, no subdirectories) is itself treated as a - reporting directory. Entries that cannot be stat'd are silently skipped. - Subdirectories whose names match *ignore_patterns* are not descended into - and are not considered reporting directories. + reporting directory, unless the result set also contains directories that do + have visible files — in that case the empty directories are dropped so that + they are not reported alongside real content. Entries that cannot be stat'd + are silently skipped. Subdirectories whose names match *ignore_patterns* + are not descended into and are not considered reporting directories. **ontrack.yml special handling:** When an ``ontrack.yml`` file is found in *directory*, descent stops @@ -252,6 +283,14 @@ def _find_reporting_directories( result: list[str] = [] for subdir in subdirs: result.extend(_find_reporting_directories(subdir, patterns)) + + # If some of the collected reporting directories have visible files and + # others are empty leaves, discard the empty ones. An empty directory + # should not be reported alongside real content at the same level. + non_empty = [p for p in result if _has_visible_files(p, patterns)] + if non_empty: + result = non_empty + # Fall back to the current directory if all recursive calls returned nothing # (e.g. every subdirectory raised OSError and could not be scanned). return result if result else [directory] diff --git a/tests/test_ontrack.py b/tests/test_ontrack.py index e47fb30..f89d6a2 100644 --- a/tests/test_ontrack.py +++ b/tests/test_ontrack.py @@ -766,6 +766,34 @@ def test_find_reporting_directories_files_and_subdir(tmp_path): assert str(dir012) not in result +def test_find_reporting_directories_empty_sibling_not_reported(tmp_path): + """An empty subdirectory is not reported when a sibling directory contains files. + + Structure: + parent/ + empty_subdir/ <- empty (no files, no subdirs) + project_dir/ + data.txt <- has content + + Expected: project_dir is reported; empty_subdir is NOT reported because it + has a sibling directory with actual content. The parent directory itself + should not appear either. + """ + parent = tmp_path / "parent" + parent.mkdir() + empty_subdir = parent / "empty_subdir" + empty_subdir.mkdir() + project_dir = parent / "project_dir" + project_dir.mkdir() + (project_dir / "data.txt").write_text("content") + + result = _find_reporting_directories(str(parent)) + + assert str(project_dir) in result + assert str(empty_subdir) not in result + assert str(parent) not in result + + # --------------------------------------------------------------------------- # main – group from config file # --------------------------------------------------------------------------- From 6bcea8150e9a2e54afe9435a2b2115fa111c609b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 20:00:53 +0000 Subject: [PATCH 2/2] chore: bump version to 0.2.1 Agent-Logs-Url: https://github.com/FertigLab/ontrack/sessions/101e0071-7411-4437-a466-286653c5aab8 Co-authored-by: dimalvovs <1246862+dimalvovs@users.noreply.github.com> --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f2721d8..9cf7b4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "ontrack" -version = "0.2.0" +version = "0.2.1" description = "Scan directory trees and report file statistics from YAML configuration." readme = "README.md" requires-python = ">=3.10"