From 450f410e3c7246b5f35b66434f621394921aa279 Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 1 Aug 2023 17:22:03 +0300 Subject: [PATCH 1/5] Support repo-specific configuration file --- pr_agent/git_providers/github_provider.py | 20 ++++++++++++++++++++ pr_agent/git_providers/gitlab_provider.py | 20 ++++++++++++++++++++ pr_agent/settings/configuration.toml | 1 + 3 files changed, 41 insertions(+) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index ae3eaebae..a985b1d21 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -1,4 +1,6 @@ import logging +import os +import tempfile from datetime import datetime from typing import Optional, Tuple from urllib.parse import urlparse @@ -31,6 +33,17 @@ def __init__(self, pr_url: Optional[str] = None, incremental=IncrementalPR(False if pr_url: self.set_pr(pr_url) self.last_commit_id = list(self.pr.get_commits())[-1] + if get_settings().config.use_repo_settings_file: + repo_settings = self.get_repo_settings() + if repo_settings: + repo_settings_file = None + try: + fd, repo_settings_file = tempfile.mkstemp(suffix='.toml') + os.write(fd, repo_settings) + get_settings().load_file(repo_settings_file) + finally: + if repo_settings_file: + os.remove(repo_settings_file) def is_supported(self, capability: str) -> bool: return True @@ -251,6 +264,13 @@ def get_notifications(self, since: datetime): def get_issue_comments(self): return self.pr.get_issue_comments() + def get_repo_settings(self): + try: + contents = self.repo_obj.get_contents(".pr_agent.yaml", ref=self.pr.head.sha).decoded_content + return contents + except Exception: + return "" + @staticmethod def _parse_pr_url(pr_url: str) -> Tuple[str, int]: parsed_url = urlparse(pr_url) diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 10363ec18..01cf7666e 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -1,5 +1,7 @@ import logging +import os import re +import tempfile from typing import Optional, Tuple from urllib.parse import urlparse @@ -35,6 +37,17 @@ def __init__(self, merge_request_url: Optional[str] = None, incremental: Optiona self.RE_HUNK_HEADER = re.compile( r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)") self.incremental = incremental + if get_settings().config.use_repo_settings_file: + repo_settings = self.get_repo_settings() + if repo_settings: + repo_settings_file = None + try: + fd, repo_settings_file = tempfile.mkstemp(suffix='.yaml') + os.write(fd, repo_settings.encode()) + get_settings().load_file(repo_settings_file) + finally: + if repo_settings_file: + os.remove(repo_settings_file) def is_supported(self, capability: str) -> bool: if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']: @@ -253,6 +266,13 @@ def get_pr_description(self): def get_issue_comments(self): raise NotImplementedError("GitLab provider does not support issue comments yet") + def get_repo_settings(self): + try: + contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.yaml', ref=self.mr.source_branch).decode() + return contents + except Exception: + return "" + def _parse_merge_request_url(self, merge_request_url: str) -> Tuple[str, int]: parsed_url = urlparse(merge_request_url) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 5d9c08467..fe7f1ec29 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -6,6 +6,7 @@ publish_output=true publish_output_progress=true verbosity_level=0 # 0,1,2 use_extra_bad_extensions=false +use_repo_settings_file=true [pr_reviewer] # /review # require_focused_review=true From 696e2bd6ffab2823dbbbe7af54b41b32b73c4529 Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 1 Aug 2023 17:27:25 +0300 Subject: [PATCH 2/5] Support repo-specific configuration file --- pr_agent/git_providers/github_provider.py | 2 +- pr_agent/git_providers/gitlab_provider.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index a985b1d21..b4b31a5c7 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -266,7 +266,7 @@ def get_issue_comments(self): def get_repo_settings(self): try: - contents = self.repo_obj.get_contents(".pr_agent.yaml", ref=self.pr.head.sha).decoded_content + contents = self.repo_obj.get_contents(".pr_agent.toml", ref=self.pr.head.sha).decoded_content return contents except Exception: return "" diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 01cf7666e..d6a2d5911 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -42,7 +42,7 @@ def __init__(self, merge_request_url: Optional[str] = None, incremental: Optiona if repo_settings: repo_settings_file = None try: - fd, repo_settings_file = tempfile.mkstemp(suffix='.yaml') + fd, repo_settings_file = tempfile.mkstemp(suffix='.toml') os.write(fd, repo_settings.encode()) get_settings().load_file(repo_settings_file) finally: @@ -268,7 +268,7 @@ def get_issue_comments(self): def get_repo_settings(self): try: - contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.yaml', ref=self.mr.source_branch).decode() + contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.toml', ref=self.mr.source_branch).decode() return contents except Exception: return "" From e12874b6968dc120d35b60411d236e393982671a Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 1 Aug 2023 17:44:08 +0300 Subject: [PATCH 3/5] Support repo-specific configuration file --- pr_agent/agent/pr_agent.py | 20 ++++++++++++++++++++ pr_agent/git_providers/github_provider.py | 15 +-------------- pr_agent/git_providers/gitlab_provider.py | 15 +-------------- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index d0037c95c..9347d188a 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -1,7 +1,10 @@ +import os import shlex +import tempfile from pr_agent.algo.utils import update_settings_from_args from pr_agent.config_loader import get_settings +from pr_agent.git_providers import get_git_provider from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_information_from_user import PRInformationFromUser @@ -31,11 +34,28 @@ def __init__(self): pass async def handle_request(self, pr_url, request) -> bool: + # First, apply repo specific settings if exists + if get_settings().config.use_repo_settings_file: + repo_settings_file = None + try: + git_provider = get_git_provider()(pr_url) + repo_settings = git_provider.get_repo_settings() + if repo_settings: + repo_settings_file = None + fd, repo_settings_file = tempfile.mkstemp(suffix='.toml') + os.write(fd, repo_settings) + get_settings().load_file(repo_settings_file) + finally: + if repo_settings_file: + os.remove(repo_settings_file) + + # Then, apply user specific settings if exists request = request.replace("'", "\\'") lexer = shlex.shlex(request, posix=True) lexer.whitespace_split = True action, *args = list(lexer) args = update_settings_from_args(args) + action = action.lstrip("/").lower() if action == "reflect_and_review" and not get_settings().pr_reviewer.ask_and_reflect: action = "review" diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index b4b31a5c7..4869ca692 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -1,6 +1,4 @@ import logging -import os -import tempfile from datetime import datetime from typing import Optional, Tuple from urllib.parse import urlparse @@ -9,11 +7,11 @@ from retry import retry from starlette_context import context +from .git_provider import FilePatchInfo, GitProvider, IncrementalPR from ..algo.language_handler import is_valid_file from ..algo.utils import load_large_diff from ..config_loader import get_settings from ..servers.utils import RateLimitExceeded -from .git_provider import FilePatchInfo, GitProvider, IncrementalPR class GithubProvider(GitProvider): @@ -33,17 +31,6 @@ def __init__(self, pr_url: Optional[str] = None, incremental=IncrementalPR(False if pr_url: self.set_pr(pr_url) self.last_commit_id = list(self.pr.get_commits())[-1] - if get_settings().config.use_repo_settings_file: - repo_settings = self.get_repo_settings() - if repo_settings: - repo_settings_file = None - try: - fd, repo_settings_file = tempfile.mkstemp(suffix='.toml') - os.write(fd, repo_settings) - get_settings().load_file(repo_settings_file) - finally: - if repo_settings_file: - os.remove(repo_settings_file) def is_supported(self, capability: str) -> bool: return True diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index d6a2d5911..170b356e6 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -1,7 +1,5 @@ import logging -import os import re -import tempfile from typing import Optional, Tuple from urllib.parse import urlparse @@ -37,17 +35,6 @@ def __init__(self, merge_request_url: Optional[str] = None, incremental: Optiona self.RE_HUNK_HEADER = re.compile( r"^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@[ ]?(.*)") self.incremental = incremental - if get_settings().config.use_repo_settings_file: - repo_settings = self.get_repo_settings() - if repo_settings: - repo_settings_file = None - try: - fd, repo_settings_file = tempfile.mkstemp(suffix='.toml') - os.write(fd, repo_settings.encode()) - get_settings().load_file(repo_settings_file) - finally: - if repo_settings_file: - os.remove(repo_settings_file) def is_supported(self, capability: str) -> bool: if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments']: @@ -268,7 +255,7 @@ def get_issue_comments(self): def get_repo_settings(self): try: - contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.toml', ref=self.mr.source_branch).decode() + contents = self.gl.projects.get(self.id_project).files.get(file_path='.pr_agent.toml', ref=self.mr.source_branch) return contents except Exception: return "" From bfe73044497426d42101df3b7cca7f04fe4c322c Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 1 Aug 2023 18:04:52 +0300 Subject: [PATCH 4/5] Support repo-specific configuration file --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6164830ba..6dda62bb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,14 @@ ## 2023-08-01 +2023-08-01 ### Enhanced - Introduced the ability to retrieve commit messages from pull requests across different git providers. - Implemented commit messages retrieval for GitHub and GitLab providers. - Updated the PR description template to include a section for commit messages if they exist. +- Added support for repository-specific configuration files (.pr_agent.yaml) for the PR Agent. +- Implemented this feature for both GitHub and GitLab providers. +- Added a new configuration option 'use_repo_settings_file' to enable or disable the use of a repo-specific settings file. + ## 2023-07-30 From ab607d74be4a3d41a6d2419325f0ad8b806fdc49 Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 1 Aug 2023 18:36:20 +0300 Subject: [PATCH 5/5] Support repo-specific configuration file --- pr_agent/agent/pr_agent.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 9347d188a..cdc8eb0cf 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -1,3 +1,4 @@ +import logging import os import shlex import tempfile @@ -47,7 +48,10 @@ async def handle_request(self, pr_url, request) -> bool: get_settings().load_file(repo_settings_file) finally: if repo_settings_file: - os.remove(repo_settings_file) + try: + os.remove(repo_settings_file) + except Exception as e: + logging.error(f"Failed to remove temporary settings file {repo_settings_file}", e) # Then, apply user specific settings if exists request = request.replace("'", "\\'")