Skip to content

Commit 9a9feb4

Browse files
authored
Merge pull request #1786 from qodo-ai/pr-1736
Pr 1736
2 parents c15fb16 + 52ce74a commit 9a9feb4

File tree

6 files changed

+119
-2
lines changed

6 files changed

+119
-2
lines changed

docs/docs/usage-guide/additional_configurations.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ Qodo Merge allows you to automatically ignore certain PRs based on various crite
164164

165165
- PRs with specific titles (using regex matching)
166166
- PRs between specific branches (using regex matching)
167+
- PRs from specific repositories (using regex matching)
167168
- PRs not from specific folders
168169
- PRs containing specific labels
169170
- PRs opened by specific users
@@ -172,7 +173,7 @@ Qodo Merge allows you to automatically ignore certain PRs based on various crite
172173

173174
To ignore PRs with a specific title such as "[Bump]: ...", you can add the following to your `configuration.toml` file:
174175

175-
```
176+
```toml
176177
[config]
177178
ignore_pr_title = ["\\[Bump\\]"]
178179
```
@@ -183,7 +184,7 @@ Where the `ignore_pr_title` is a list of regex patterns to match the PR title yo
183184

184185
To ignore PRs from specific source or target branches, you can add the following to your `configuration.toml` file:
185186

186-
```
187+
```toml
187188
[config]
188189
ignore_pr_source_branches = ['develop', 'main', 'master', 'stage']
189190
ignore_pr_target_branches = ["qa"]
@@ -192,6 +193,18 @@ ignore_pr_target_branches = ["qa"]
192193
Where the `ignore_pr_source_branches` and `ignore_pr_target_branches` are lists of regex patterns to match the source and target branches you want to ignore.
193194
They are not mutually exclusive, you can use them together or separately.
194195

196+
### Ignoring PRs from specific repositories
197+
198+
To ignore PRs from specific repositories, you can add the following to your `configuration.toml` file:
199+
200+
```toml
201+
[config]
202+
ignore_repositories = ["my-org/my-repo1", "my-org/my-repo2"]
203+
```
204+
205+
Where the `ignore_repositories` is a list of regex patterns to match the repositories you want to ignore. This is useful when you have multiple repositories and want to exclude certain ones from analysis.
206+
207+
195208
### Ignoring PRs not from specific folders
196209

197210
To allow only specific folders (often needed in large monorepos), set:

pr_agent/servers/bitbucket_app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ def should_process_pr_logic(data) -> bool:
127127
source_branch = pr_data.get("source", {}).get("branch", {}).get("name", "")
128128
target_branch = pr_data.get("destination", {}).get("branch", {}).get("name", "")
129129
sender = _get_username(data)
130+
repo_full_name = pr_data.get("destination", {}).get("repository", {}).get("full_name", "")
131+
132+
# logic to ignore PRs from specific repositories
133+
ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", [])
134+
if repo_full_name and ignore_repos:
135+
if any(re.search(regex, repo_full_name) for regex in ignore_repos):
136+
get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting")
137+
return False
130138

131139
# logic to ignore PRs from specific users
132140
ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", [])

pr_agent/servers/github_app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,14 @@ def should_process_pr_logic(body) -> bool:
258258
source_branch = pull_request.get("head", {}).get("ref", "")
259259
target_branch = pull_request.get("base", {}).get("ref", "")
260260
sender = body.get("sender", {}).get("login")
261+
repo_full_name = body.get("repository", {}).get("full_name", "")
262+
263+
# logic to ignore PRs from specific repositories
264+
ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", [])
265+
if ignore_repos and repo_full_name:
266+
if any(re.search(regex, repo_full_name) for regex in ignore_repos):
267+
get_logger().info(f"Ignoring PR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting")
268+
return False
261269

262270
# logic to ignore PRs from specific users
263271
ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", [])

pr_agent/servers/gitlab_webhook.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,14 @@ def should_process_pr_logic(data) -> bool:
113113
return False
114114
title = data['object_attributes'].get('title')
115115
sender = data.get("user", {}).get("username", "")
116+
repo_full_name = data.get('project', {}).get('path_with_namespace', "")
117+
118+
# logic to ignore PRs from specific repositories
119+
ignore_repos = get_settings().get("CONFIG.IGNORE_REPOSITORIES", [])
120+
if ignore_repos and repo_full_name:
121+
if any(re.search(regex, repo_full_name) for regex in ignore_repos):
122+
get_logger().info(f"Ignoring MR from repository '{repo_full_name}' due to 'config.ignore_repositories' setting")
123+
return False
116124

117125
# logic to ignore PRs from specific users
118126
ignore_pr_users = get_settings().get("CONFIG.IGNORE_PR_AUTHORS", [])

pr_agent/settings/configuration.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ ignore_pr_target_branches = [] # a list of regular expressions of target branche
5555
ignore_pr_source_branches = [] # a list of regular expressions of source branches to ignore from PR agent when an PR is created
5656
ignore_pr_labels = [] # labels to ignore from PR agent when an PR is created
5757
ignore_pr_authors = [] # authors to ignore from PR agent when an PR is created
58+
ignore_repositories = [] # a list of regular expressions of repository full names (e.g. "org/repo") to ignore from PR agent processing
5859
#
5960
is_auto_command = false # will be auto-set to true if the command is triggered by an automation
6061
enable_ai_metadata = false # will enable adding ai metadata
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import pytest
2+
from pr_agent.servers.github_app import should_process_pr_logic as github_should_process_pr_logic
3+
from pr_agent.servers.bitbucket_app import should_process_pr_logic as bitbucket_should_process_pr_logic
4+
from pr_agent.servers.gitlab_webhook import should_process_pr_logic as gitlab_should_process_pr_logic
5+
from pr_agent.config_loader import get_settings
6+
7+
def make_bitbucket_payload(full_name):
8+
return {
9+
"data": {
10+
"pullrequest": {
11+
"title": "Test PR",
12+
"source": {"branch": {"name": "feature/test"}},
13+
"destination": {
14+
"branch": {"name": "main"},
15+
"repository": {"full_name": full_name}
16+
}
17+
},
18+
"actor": {"username": "user", "type": "user"}
19+
}
20+
}
21+
22+
def make_github_body(full_name):
23+
return {
24+
"pull_request": {},
25+
"repository": {"full_name": full_name},
26+
"sender": {"login": "user"}
27+
}
28+
29+
def make_gitlab_body(full_name):
30+
return {
31+
"object_attributes": {"title": "Test MR"},
32+
"project": {"path_with_namespace": full_name}
33+
}
34+
35+
PROVIDERS = [
36+
("github", github_should_process_pr_logic, make_github_body),
37+
("bitbucket", bitbucket_should_process_pr_logic, make_bitbucket_payload),
38+
("gitlab", gitlab_should_process_pr_logic, make_gitlab_body),
39+
]
40+
41+
class TestIgnoreRepositories:
42+
def setup_method(self):
43+
get_settings().set("CONFIG.IGNORE_REPOSITORIES", [])
44+
45+
@pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS)
46+
def test_should_ignore_matching_repository(self, provider_name, provider_func, body_func):
47+
get_settings().set("CONFIG.IGNORE_REPOSITORIES", ["org/repo-to-ignore"])
48+
body = {
49+
"pull_request": {},
50+
"repository": {"full_name": "org/repo-to-ignore"},
51+
"sender": {"login": "user"}
52+
}
53+
result = provider_func(body_func(body["repository"]["full_name"]))
54+
# print(f"DEBUG: Provider={provider_name}, test_should_ignore_matching_repository, result={result}")
55+
assert result is False, f"{provider_name}: PR from ignored repository should be ignored (return False)"
56+
57+
@pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS)
58+
def test_should_not_ignore_non_matching_repository(self, provider_name, provider_func, body_func):
59+
get_settings().set("CONFIG.IGNORE_REPOSITORIES", ["org/repo-to-ignore"])
60+
body = {
61+
"pull_request": {},
62+
"repository": {"full_name": "org/other-repo"},
63+
"sender": {"login": "user"}
64+
}
65+
result = provider_func(body_func(body["repository"]["full_name"]))
66+
# print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_non_matching_repository, result={result}")
67+
assert result is True, f"{provider_name}: PR from non-ignored repository should not be ignored (return True)"
68+
69+
@pytest.mark.parametrize("provider_name, provider_func, body_func", PROVIDERS)
70+
def test_should_not_ignore_when_config_empty(self, provider_name, provider_func, body_func):
71+
get_settings().set("CONFIG.IGNORE_REPOSITORIES", [])
72+
body = {
73+
"pull_request": {},
74+
"repository": {"full_name": "org/repo-to-ignore"},
75+
"sender": {"login": "user"}
76+
}
77+
result = provider_func(body_func(body["repository"]["full_name"]))
78+
# print(f"DEBUG: Provider={provider_name}, test_should_not_ignore_when_config_empty, result={result}")
79+
assert result is True, f"{provider_name}: PR should not be ignored if ignore_repositories config is empty"

0 commit comments

Comments
 (0)