Merge pull request #1898 from isExample/feat/ignore-language-framework

feat: support ignoring auto-generated files by language/framework
This commit is contained in:
Tal
2025-06-29 10:19:37 +03:00
committed by GitHub
6 changed files with 126 additions and 1 deletions

View File

@ -250,3 +250,15 @@ Where the `ignore_pr_authors` is a list of usernames that you want to ignore.
!!! note
There is one specific case where bots will receive an automatic response - when they generated a PR with a _failed test_. In that case, the [`ci_feedback`](https://qodo-merge-docs.qodo.ai/tools/ci_feedback/) tool will be invoked.
### Ignoring Generated Files by Language/Framework
To automatically exclude files generated by specific languages or frameworks, you can add the following to your `configuration.toml` file:
```
[config]
ignore_language_framework = ['protobuf', ...]
```
You can view the list of auto-generated file patterns in `generated_code_ignore.toml`.
Files matching these glob patterns will be automatically excluded from PR Agent analysis.

View File

@ -2,6 +2,7 @@ import fnmatch
import re
from pr_agent.config_loader import get_settings
from pr_agent.log import get_logger
def filter_ignored(files, platform = 'github'):
@ -17,7 +18,17 @@ def filter_ignored(files, platform = 'github'):
glob_setting = get_settings().ignore.glob
if isinstance(glob_setting, str): # --ignore.glob=[.*utils.py], --ignore.glob=.*utils.py
glob_setting = glob_setting.strip('[]').split(",")
patterns += [fnmatch.translate(glob) for glob in glob_setting]
patterns += translate_globs_to_regexes(glob_setting)
code_generators = get_settings().config.get('ignore_language_framework', [])
if isinstance(code_generators, str):
get_logger().warning("'ignore_language_framework' should be a list. Skipping language framework filtering.")
code_generators = []
for cg in code_generators:
glob_patterns = get_settings().generated_code.get(cg, [])
if isinstance(glob_patterns, str):
glob_patterns = [glob_patterns]
patterns += translate_globs_to_regexes(glob_patterns)
# compile all valid patterns
compiled_patterns = []
@ -66,3 +77,11 @@ def filter_ignored(files, platform = 'github'):
print(f"Could not filter file list: {e}")
return files
def translate_globs_to_regexes(globs: list):
regexes = []
for pattern in globs:
regexes.append(fnmatch.translate(pattern))
if pattern.startswith("**/"): # cover root-level files
regexes.append(fnmatch.translate(pattern[3:]))
return regexes

View File

@ -14,6 +14,7 @@ global_settings = Dynaconf(
settings_files=[join(current_dir, f) for f in [
"settings/configuration.toml",
"settings/ignore.toml",
"settings/generated_code_ignore.toml",
"settings/language_extensions.toml",
"settings/pr_reviewer_prompts.toml",
"settings/pr_questions_prompts.toml",

View File

@ -56,6 +56,7 @@ ignore_pr_source_branches = [] # a list of regular expressions of source branche
ignore_pr_labels = [] # labels to ignore from PR agent when an PR is created
ignore_pr_authors = [] # authors to ignore from PR agent when an PR is created
ignore_repositories = [] # a list of regular expressions of repository full names (e.g. "org/repo") to ignore from PR agent processing
ignore_language_framework = [] # a list of code-generation languages or frameworks (e.g. 'protobuf', 'go_gen') whose auto-generated source files will be excluded from analysis
#
is_auto_command = false # will be auto-set to true if the command is triggered by an automation
enable_ai_metadata = false # will enable adding ai metadata

View File

@ -0,0 +1,42 @@
[generated_code]
# Protocol Buffers
protobuf = [
"**/*.pb.go",
"**/*.pb.cc",
"**/*_pb2.py",
"**/*.pb.swift",
"**/*.pb.rb",
"**/*.pb.php",
"**/*.pb.h"
]
# OpenAPI / Swagger stubs
openapi = [
"**/__generated__/**",
"**/openapi_client/**",
"**/openapi_server/**"
]
swagger = [
"**/swagger.json",
"**/swagger.yaml"
]
# GraphQL codegen
graphql = [
"**/*.graphql.ts",
"**/*.generated.ts",
"**/*.graphql.js"
]
# RPC / gRPC Generators
grpc_python = ["**/*_grpc.py"]
grpc_java = ["**/*Grpc.java"]
grpc_csharp = ["**/*Grpc.cs"]
grpc_typescript = ["**/*_grpc.ts", "**/*_grpc.js"]
# Go code generators
go_gen = [
"**/*_gen.go",
"**/*generated.go"
]

View File

@ -80,3 +80,53 @@ class TestIgnoreFilter:
filtered_files = filter_ignored(files)
assert filtered_files == expected, f"Expected {[file.filename for file in expected]}, but got {[file.filename for file in filtered_files]}."
def test_language_framework_ignores(self, monkeypatch):
"""
Test files are ignored based on language/framework mapping (e.g., protobuf).
"""
monkeypatch.setattr(global_settings.config, 'ignore_language_framework', ['protobuf', 'go_gen'])
files = [
type('', (object,), {'filename': 'main.go'})(),
type('', (object,), {'filename': 'dir1/service.pb.go'})(),
type('', (object,), {'filename': 'dir1/dir/data_pb2.py'})(),
type('', (object,), {'filename': 'file.py'})(),
type('', (object,), {'filename': 'dir2/file_gen.go'})(),
type('', (object,), {'filename': 'file.generated.go'})()
]
expected = [
files[0],
files[3]
]
filtered = filter_ignored(files)
assert filtered == expected, (
f"Expected {[f.filename for f in expected]}, "
f"but got {[f.filename for f in filtered]}"
)
def test_skip_invalid_ignore_language_framework(self, monkeypatch):
"""
Test skipping of generated code filtering when ignore_language_framework is not a list
"""
monkeypatch.setattr(global_settings.config, 'ignore_language_framework', 'protobuf')
files = [
type('', (object,), {'filename': 'main.go'})(),
type('', (object,), {'filename': 'file.py'})(),
type('', (object,), {'filename': 'dir1/service.pb.go'})(),
type('', (object,), {'filename': 'file_pb2.py'})()
]
expected = [
files[0],
files[1],
files[2],
files[3]
]
filtered = filter_ignored(files)
assert filtered == expected, (
f"Expected {[f.filename for f in expected]}, "
f"but got {[f.filename for f in filtered]}"
)