Skip to content

GSoC Module A : week 4 : feat(harvester): implement repository file filtering (stacked on top of #985)#986

Open
ParthAggarwal16 wants to merge 23 commits into
OWASP:mainfrom
ParthAggarwal16:week_4-clean
Open

GSoC Module A : week 4 : feat(harvester): implement repository file filtering (stacked on top of #985)#986
ParthAggarwal16 wants to merge 23 commits into
OWASP:mainfrom
ParthAggarwal16:week_4-clean

Conversation

@ParthAggarwal16

Copy link
Copy Markdown
Contributor

Summary

(this PR is stacked on top of #985 )
This PR adds the file filtering layer for the harvester pipeline, ensuring that only relevant documentation files continue through the harvesting process.

Note: This PR depends on the previous stacked PRs (Week 2 and Week 3) and should be reviewed after them.

Added

  • File filtering based on supported documentation extensions
  • Regex-based exclusion rules for common non-documentation paths
  • Configurable allowlist of supported file extensions
  • Configurable exclude pattern support
  • Filtering metrics collector
  • Filtering benchmark utilities
  • Filtering benchmark result model
  • Unit tests covering filtering behavior and metrics

Filtering Behavior

Allowed extensions

  • .md
  • .mdx
  • .rst
  • .txt
  • .adoc

Default excluded paths

  • .github/
  • .git/
  • node_modules/
  • dist/
  • build/
  • coverage/
  • vendor/
  • package-lock.json
  • yarn.lock
  • pnpm-lock.yaml

Validation Coverage

File Filtering

  • Extension allowlist filtering
  • Regex-based path exclusion
  • Combined extension + regex filtering

Metrics

  • Retained file counting
  • Filtered file counting
  • Total file counting

Benchmark

  • Retention rate calculation
  • Filtering rate calculation
  • Benchmark summary generation

Test Plan

Executed:

python3 -m unittest discover \
    -s application/tests/harvester_test \
    -p "*_test.py"

make test

All tests passing.

Notes for Reviewers

  • Filtering is intentionally separated from change detection to keep responsibilities isolated.
  • Default filtering rules are configurable through the FileFilter constructor.
  • Benchmark and metrics utilities are lightweight helpers intended for future pipeline instrumentation.
  1. Class Diagram
image
  1. Sequence Diagram — filter_files() call flow
image
  1. Sequence Diagram — FilteringBenchmark.run() call flow
image
  1. Data Flow Diagram — Benchmark metrics aggregation
image
  1. High-Level Architecture (Component Diagram) — simplified
image
  1. Data Flow Diagram — end-to-end filtering pipeline — simplified
image

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: c2d9b121-4953-462c-8d28-e436d353b3a1

📥 Commits

Reviewing files that changed from the base of the PR and between 853f34e and 10b8f2f.

📒 Files selected for processing (4)
  • application/tests/harvester_test/filtering_benchmark_test.py
  • application/tests/harvester_test/git_repository_client_test.py
  • application/utils/harvester/filtering_benchmark.py
  • application/utils/harvester/git_repository_client.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • application/tests/harvester_test/git_repository_client_test.py

Summary by CodeRabbit

  • New Features
    • Added GitHub repository harvesting configuration with chunking, include/exclude path rules, and polling.
    • Added repository sync, change detection, checkpoint persistence, and configurable file filtering with metrics/benchmarking.
    • Added default harvesting exclude patterns and bundled harvesting utilities/models for public use.
  • Tests
    • Added unit tests and fixtures covering configuration loading/validation, repository operations, change detection parsing, checkpoint storage, file filtering, cache pathing, and metrics/benchmarking.

Walkthrough

Adds harvester configuration schemas and validation, Git repository synchronization, change detection, checkpoint persistence, file filtering, metrics, repository configuration, and comprehensive unit tests with YAML fixtures.

Changes

Harvester configuration and validation

Layer / File(s) Summary
Configuration contracts and loading
application/utils/harvester/schemas.py, application/utils/harvester/config_loader.py
Defines repository, path, chunking, and polling schemas, then loads and validates YAML configurations.
Repository validation and fixtures
application/utils/harvester/repos_validator.py, application/utils/harvester/repos.yaml, application/tests/harvester_test/fixtures/*, application/tests/harvester_test/config_loader_test.py, application/tests/harvester_test/repos_validator_test.py
Rejects duplicate IDs, repository sources, and include paths; adds valid and invalid fixtures and tests.
Package exports
application/utils/harvester/__init__.py, application/tests/harvester_test/__init__.py
Exports harvester APIs and adds test package documentation.

Repository lifecycle and change tracking

Layer / File(s) Summary
Repository contracts and Git cache operations
application/utils/harvester/repository_client.py, application/utils/harvester/repository_cache.py, application/utils/harvester/git_repository_client.py, application/tests/harvester_test/git_repository_client_test.py, application/tests/harvester_test/repository_cache_test.py
Adds repository interfaces, normalized cache paths, Git operations, integrity checks, and tests.
Change detection and checkpoints
application/utils/harvester/models.py, application/utils/harvester/change_detector.py, application/utils/harvester/checkpoint_store.py, application/tests/harvester_test/change_detector_test.py, application/tests/harvester_test/checkpoint_store_test.py
Adds repository state models, Git diff/log queries, and atomic JSON checkpoint persistence with tests.

File filtering and metrics

Layer / File(s) Summary
Filtering implementation and measurement
application/utils/harvester/file_filter.py, application/utils/harvester/exclude_patterns.txt, application/utils/harvester/filtering_metrics.py, application/utils/harvester/filtering_benchmark.py, application/utils/harvester/models.py, application/tests/harvester_test/*filter*test.py
Filters paths by exclusions and extensions, collects counts, computes benchmark rates, and tests the resulting behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • OWASP/OpenCRE#983: Overlaps with the same harvester modules, tests, and configuration fixtures.
  • OWASP/OpenCRE#985: Directly overlaps with ChangeDetector and CheckpointStore implementations and tests.

Suggested reviewers: robvanderveer, northdpole, paoga87, pa04rth

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding repository file filtering for the harvester.
Description check ✅ Passed The description matches the implemented file filtering, metrics, benchmark, and test additions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (7)
application/tests/harvester_test/file_filter_test.py (1)

25-38: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Isolate regex filtering in this test.

This test currently uses .github/workflows/test.yml to test the regex exclusion path. However, because .yml is not an allowed extension, this file would be excluded by the extension check even if the regex missed it. To strictly verify that the regex logic works independently, consider using a file path that has an allowed extension (e.g., .md) but is excluded by the regex.

💡 Proposed adjustment
     def test_regex_filtering(self):
         file_filter = FileFilter()
 
         result = file_filter.filter_files(
             [
-                ".github/workflows/test.yml",
+                ".github/workflows/readme.md",
                 "docs/setup.md",
             ]
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/tests/harvester_test/file_filter_test.py` around lines 25 - 38,
Update test_regex_filtering in FileFilter so the regex-excluded fixture uses an
allowed file extension, such as a .md path matching the exclusion pattern, while
retaining a separate allowed .md path that should remain in the result. Ensure
the assertion verifies exclusion by regex rather than by the extension filter.
application/utils/harvester/file_filter.py (1)

31-35: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Pre-compile the exclusion regexes. filter_files runs this check for every path, so compiling exclude_patterns once in __init__ avoids repeated regex parsing on the hot path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/harvester/file_filter.py` around lines 31 - 35, Pre-compile
each exclusion pattern during __init__ and store the compiled regex objects on
the harvester instance, then update is_excluded_by_pattern to use those compiled
patterns without calling re.search for every path. Preserve the existing
exclude_patterns defaults and matching behavior.
application/tests/harvester_test/config_loader_test.py (1)

55-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add test cases for the remaining configuration fixtures.

The invalid_chunking_strategy.yaml and invalid_polling_mode.yaml fixtures were added in this PR but are currently unused in the test suite. Consider adding tests to verify that these invalid enumerations are correctly caught and rejected by the schema validation.

♻️ Proposed tests
     def test_empty_include_paths(self):
         config_path = FIXTURES_DIR / "empty_include_paths.yaml"
 
         with self.assertRaises(ConfigLoaderError):
             load_repo_config(config_path)
+
+    def test_invalid_chunking_strategy(self):
+        config_path = FIXTURES_DIR / "invalid_chunking_strategy.yaml"
+
+        with self.assertRaisesRegex(ConfigLoaderError, "strategy"):
+            load_repo_config(config_path)
+
+    def test_invalid_polling_mode(self):
+        config_path = FIXTURES_DIR / "invalid_polling_mode.yaml"
+
+        with self.assertRaisesRegex(ConfigLoaderError, "mode"):
+            load_repo_config(config_path)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/tests/harvester_test/config_loader_test.py` around lines 55 - 60,
Add tests alongside test_empty_include_paths that load
invalid_chunking_strategy.yaml and invalid_polling_mode.yaml through
load_repo_config, asserting each raises ConfigLoaderError during schema
validation. Use the existing FIXTURES_DIR pattern and preserve the current test
structure.
application/utils/harvester/__init__.py (1)

27-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Sort __all__ list.

To maintain consistency and comply with ruff rules (RUF022), it is recommended to keep the __all__ list alphabetically sorted.

♻️ Proposed fix
 __all__ = [
-    "build_repository_cache_path",
     "ChunkingConfig",
     "ConfigLoaderError",
-    "GitRepositoryClient",
     "FileFilter",
-    "FilteringMetricsCollector",
     "FilteringBenchmark",
     "FilteringBenchmarkResult",
+    "FilteringMetricsCollector",
+    "GitRepositoryClient",
     "PathRules",
     "PollingConfig",
     "RepositoryClient",
     "RepositoryConfig",
     "RepositoryValidationError",
     "ReposFile",
+    "build_repository_cache_path",
     "load_repo_config",
     "validate_repositories",
 ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/harvester/__init__.py` around lines 27 - 44, Sort the
exported names in the __all__ list alphabetically to satisfy the RUF022 ordering
rule, without changing the listed symbols or their exports.

Source: Linters/SAST tools

application/tests/harvester_test/git_repository_client_test.py (1)

95-104: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Assert exact arguments in subprocess.run mocks.

Currently, tests only assert that subprocess.run was called. To prevent regressions where critical flags (like branch references or timeouts) are accidentally removed, consider asserting the exact list of arguments that are dispatched.

💡 Example improvement
     def test_checkout_runs_git_command(self, mock_run):
         client = GitRepositoryClient(
             owner="OWASP",
             repository="ASVS",
         )
 
         client.checkout("main")
 
-        mock_run.assert_called_once()
+        mock_run.assert_called_once_with(
+            [
+                "git",
+                "-C",
+                str(client.get_local_path()),
+                "checkout",
+                "--",
+                "main",
+            ],
+            check=True,
+            capture_output=True,
+            text=True,
+            timeout=300,
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/tests/harvester_test/git_repository_client_test.py` around lines
95 - 104, Update test_checkout_runs_git_command for GitRepositoryClient.checkout
to assert the exact arguments passed to the mocked subprocess.run, including the
repository checkout command, branch reference, and any required execution
options such as timeout or check settings, rather than only verifying that it
was called.
application/utils/harvester/git_repository_client.py (1)

42-46: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Clear existing corrupted directories before cloning.

If the integrity check fails but self.local_path still exists (e.g., from a previously aborted clone or incomplete cleanup), the subsequent git clone command will fail because the target directory already exists and is not empty. Clear the directory beforehand to ensure the clone operation succeeds and the pipeline can auto-recover.

♻️ Proposed fix
+        if self.local_path.exists():
+            import shutil
+            shutil.rmtree(self.local_path)
+
         self.local_path.parent.mkdir(
             parents=True,
             exist_ok=True,
         )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/harvester/git_repository_client.py` around lines 42 - 46,
Update the directory setup in the Git repository cloning flow around
self.local_path to remove any existing corrupted or incomplete target directory
before recreating it. Preserve parent-directory creation, then ensure
self.local_path is clean and ready for git clone while retaining the existing
behavior when it does not exist.
application/utils/harvester/change_detector.py (1)

19-43: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider using -z to safely handle filenames with spaces.

By default, git diff may wrap filenames containing spaces or special characters in quotes. Passing the -z flag outputs NUL-terminated strings instead of newlines, which ensures parsing works reliably regardless of the repository's file naming patterns or core.quotePath configuration.

(Note: If you apply this refactor, remember to update the corresponding mocked output in change_detector_test.py to use \0 instead of \n).

♻️ Proposed fix
         try:
             result = subprocess.run(
                 [
                     "git",
                     "-C",
                     str(self.repository_client.get_local_path()),
                     "diff",
                     "--name-only",
+                    "-z",
                     commit_sha,
                     "HEAD",
                 ],
                 capture_output=True,
                 text=True,
                 check=True,
                 timeout=60,
             )
         except subprocess.CalledProcessError as exc:
             logger.error("Git command failed: %s", exc.stderr)
             raise
 
-        files = [
-            file_path for file_path in result.stdout.splitlines() if file_path.strip()
-        ]
+        files = [
+            file_path for file_path in result.stdout.split("\0") if file_path.strip()
+        ]
 
         return sorted(set(files))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@application/utils/harvester/change_detector.py` around lines 19 - 43, Update
the git diff invocation in the change-detection method to include the `-z`
option, then parse `result.stdout` using NUL delimiters while retaining
filtering and deduplication. Update the corresponding mocked output in
`change_detector_test.py` to use `\0` separators.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@application/tests/harvester_test/filtering_benchmark_test.py`:
- Around line 11-46: Update FilteringBenchmarkTests.test_filtering_benchmark to
instantiate FilteringBenchmark and invoke its run() method with the sample
files, then assert the returned metrics. Remove the direct
FileFilter.filter_files() call and manual FilteringMetrics construction so the
test exercises the actual benchmark implementation.

In `@application/utils/harvester/checkpoint_store.py`:
- Around line 55-64: Update the checkpoint-saving logic around
self.checkpoint_file and os.replace to generate a unique temporary filename per
save, using a UUID or process/thread-specific identifier instead of the
hardcoded ".tmp" suffix. Keep the existing JSON serialization and atomic
replacement behavior unchanged.

In `@application/utils/harvester/filtering_benchmark.py`:
- Around line 17-23: Remove the unused metrics parameter from
FilteringBenchmark.__init__ and delete the corresponding self.metrics
assignment. Update all call sites to construct FilteringBenchmark with only the
required file_filter argument, preserving any metrics computed dynamically by
run().

In `@application/utils/harvester/git_repository_client.py`:
- Around line 140-151: Update sync so that after verify_repository_integrity()
succeeds and fetch() completes, the local working tree and HEAD are reset to the
fetched remote branch before change detection runs. Preserve the existing clone
path for invalid repositories and use the repository’s configured branch and
remote-tracking reference.

---

Nitpick comments:
In `@application/tests/harvester_test/config_loader_test.py`:
- Around line 55-60: Add tests alongside test_empty_include_paths that load
invalid_chunking_strategy.yaml and invalid_polling_mode.yaml through
load_repo_config, asserting each raises ConfigLoaderError during schema
validation. Use the existing FIXTURES_DIR pattern and preserve the current test
structure.

In `@application/tests/harvester_test/file_filter_test.py`:
- Around line 25-38: Update test_regex_filtering in FileFilter so the
regex-excluded fixture uses an allowed file extension, such as a .md path
matching the exclusion pattern, while retaining a separate allowed .md path that
should remain in the result. Ensure the assertion verifies exclusion by regex
rather than by the extension filter.

In `@application/tests/harvester_test/git_repository_client_test.py`:
- Around line 95-104: Update test_checkout_runs_git_command for
GitRepositoryClient.checkout to assert the exact arguments passed to the mocked
subprocess.run, including the repository checkout command, branch reference, and
any required execution options such as timeout or check settings, rather than
only verifying that it was called.

In `@application/utils/harvester/__init__.py`:
- Around line 27-44: Sort the exported names in the __all__ list alphabetically
to satisfy the RUF022 ordering rule, without changing the listed symbols or
their exports.

In `@application/utils/harvester/change_detector.py`:
- Around line 19-43: Update the git diff invocation in the change-detection
method to include the `-z` option, then parse `result.stdout` using NUL
delimiters while retaining filtering and deduplication. Update the corresponding
mocked output in `change_detector_test.py` to use `\0` separators.

In `@application/utils/harvester/file_filter.py`:
- Around line 31-35: Pre-compile each exclusion pattern during __init__ and
store the compiled regex objects on the harvester instance, then update
is_excluded_by_pattern to use those compiled patterns without calling re.search
for every path. Preserve the existing exclude_patterns defaults and matching
behavior.

In `@application/utils/harvester/git_repository_client.py`:
- Around line 42-46: Update the directory setup in the Git repository cloning
flow around self.local_path to remove any existing corrupted or incomplete
target directory before recreating it. Preserve parent-directory creation, then
ensure self.local_path is clean and ready for git clone while retaining the
existing behavior when it does not exist.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 62a102b9-a5e0-4bd5-ae8d-34913933cba9

📥 Commits

Reviewing files that changed from the base of the PR and between 2d1aa47 and 853f34e.

📒 Files selected for processing (37)
  • application/tests/harvester_test/__init__.py
  • application/tests/harvester_test/change_detector_test.py
  • application/tests/harvester_test/checkpoint_store_test.py
  • application/tests/harvester_test/config_loader_test.py
  • application/tests/harvester_test/file_filter_test.py
  • application/tests/harvester_test/filtering_benchmark_test.py
  • application/tests/harvester_test/filtering_metrics_test.py
  • application/tests/harvester_test/fixtures/duplicate_include_paths.yaml
  • application/tests/harvester_test/fixtures/duplicate_repo_ids.yaml
  • application/tests/harvester_test/fixtures/duplicate_repositories.yaml
  • application/tests/harvester_test/fixtures/empty_include_paths.yaml
  • application/tests/harvester_test/fixtures/empty_owner.yaml
  • application/tests/harvester_test/fixtures/invalid_chunk_size.yaml
  • application/tests/harvester_test/fixtures/invalid_chunking_strategy.yaml
  • application/tests/harvester_test/fixtures/invalid_missing_id.yaml
  • application/tests/harvester_test/fixtures/invalid_polling_interval.yaml
  • application/tests/harvester_test/fixtures/invalid_polling_mode.yaml
  • application/tests/harvester_test/fixtures/invalid_yaml.yaml
  • application/tests/harvester_test/fixtures/valid_repos.yaml
  • application/tests/harvester_test/git_repository_client_test.py
  • application/tests/harvester_test/repos_validator_test.py
  • application/tests/harvester_test/repository_cache_test.py
  • application/utils/harvester/__init__.py
  • application/utils/harvester/change_detector.py
  • application/utils/harvester/checkpoint_store.py
  • application/utils/harvester/config_loader.py
  • application/utils/harvester/exclude_patterns.txt
  • application/utils/harvester/file_filter.py
  • application/utils/harvester/filtering_benchmark.py
  • application/utils/harvester/filtering_metrics.py
  • application/utils/harvester/git_repository_client.py
  • application/utils/harvester/models.py
  • application/utils/harvester/repos.yaml
  • application/utils/harvester/repos_validator.py
  • application/utils/harvester/repository_cache.py
  • application/utils/harvester/repository_client.py
  • application/utils/harvester/schemas.py

Comment thread application/tests/harvester_test/filtering_benchmark_test.py
Comment thread application/utils/harvester/checkpoint_store.py
Comment thread application/utils/harvester/filtering_benchmark.py Outdated
Comment thread application/utils/harvester/git_repository_client.py
self.allowed_extensions = allowed_extensions or DEFAULT_ALLOWED_EXTENSIONS

def is_excluded_by_pattern(self, file_path: str) -> bool:
return any(re.search(pattern, file_path) for pattern in self.exclude_patterns)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocker: this API receives glob patterns but interprets them as regular expressions. PathRules.exclude, repos.yaml, and exclude_patterns.txt define values such as **/archive/**; feeding that value to re.search() can raise re.error, so the expected config-to-filter path is unusable. Apply the fix by making globs the single public contract: normalize each repository-relative path to POSIX separators, validate exclusion globs during configuration loading/construction, and match them with one glob-aware helper rather than re.search(). Wire repository.paths.exclude through that helper and add a regression test using the actual **/archive/** configuration. Do not maintain a second regex syntax for the same setting.

".adoc",
}

DEFAULT_EXCLUDE_PATTERNS = [

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These defaults duplicate—but do not match—the existing exclusion configuration. Root anchors miss nested directories, and the list omits configured paths such as archive, .cursor, and .claude. Apply the fix by defining one canonical immutable set of default globs, then combine it with each repository's configured exclusions through the same matching path. Ensure directory exclusions apply at repository root and any nesting depth. Add allowed-extension cases for .github/README.md, packages/site/node_modules/README.md, docs/archive/old.md, and .cursor/rules/project.md. The partial nested-pattern adjustment in #987 does not resolve the duplicated contract or all omitted paths.

exclude_patterns: list[str] | None = None,
allowed_extensions: set[str] | None = None,
):
self.exclude_patterns = exclude_patterns or DEFAULT_EXCLUDE_PATTERNS

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using or discards intentional empty overrides, while assigning the module-level list/set directly shares mutable state between instances. Apply the fix by distinguishing absence explicitly: use a copy of defaults only when the argument is None; otherwise copy the supplied collection even when it is empty. Do the same for allowed_extensions (for example, list(defaults) if exclude_patterns is None else list(exclude_patterns) and the equivalent set(...)). Add tests proving empty overrides remain empty and mutations in one FileFilter instance cannot affect another.


result = file_filter.filter_files(
[
".github/workflows/test.yml",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not test exclusion matching because .yml is already rejected by the extension allowlist; the combined test has the same problem with .js. Replace excluded examples with allowed documentation extensions so removing exclusion matching would make the test fail, e.g. .github/workflows/README.md and node_modules/react/README.md. Add focused cases for nested paths, the real **/archive/** custom glob, invalid glob handling, explicit empty exclusions/extensions, and default-instance isolation. Keep extension and path-filter behavior in separate tests so each assertion has one reason to pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants