From a043eb939b853cf792c4a089dec93d55fadef33d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 7 Dec 2023 08:42:18 +0200 Subject: [PATCH 01/15] feat: Apply repo settings on push trigger in github_app.py --- pr_agent/servers/github_app.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/servers/github_app.py b/pr_agent/servers/github_app.py index 36cc3e88..32b3305d 100644 --- a/pr_agent/servers/github_app.py +++ b/pr_agent/servers/github_app.py @@ -125,11 +125,15 @@ async def handle_request(body: Dict[str, Any], event: str): await _perform_commands("pr_commands", agent, body, api_url, log_context) # handle pull_request event with synchronize action - "push trigger" for new commits - elif event == 'pull_request' and action == 'synchronize' and get_settings().github_app.handle_push_trigger: + elif event == 'pull_request' and action == 'synchronize': pull_request, api_url = _check_pull_request_event(action, body, log_context, bot_user) if not (pull_request and api_url): return {} + apply_repo_settings(api_url) + if not get_settings().github_app.handle_push_trigger: + return {} + # TODO: do we still want to get the list of commits to filter bot/merge commits? before_sha = body.get("before") after_sha = body.get("after") From 654938f27c837d131c70f54fec25b4854dd4534a Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 10 Dec 2023 17:30:27 +0200 Subject: [PATCH 02/15] feat: Enhance code feedback formatting in utils.py --- pr_agent/algo/utils.py | 58 +++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index d5e1a3c6..50505e24 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -59,7 +59,7 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: if key.lower() == 'code feedback': if gfm_supported: markdown_text += f"\n\n- " - markdown_text += f"
{ emoji } Code feedback:\n\n" + markdown_text += f"
{ emoji } Code feedback:" else: markdown_text += f"\n\n- **{emoji} Code feedback:**\n\n" else: @@ -91,24 +91,52 @@ def parse_code_suggestion(code_suggestions: dict, gfm_supported: bool=True) -> s str: A string containing the markdown formatted text generated from the input dictionary. """ markdown_text = "" - for sub_key, sub_value in code_suggestions.items(): - if isinstance(sub_value, dict): # "code example" - markdown_text += f" - **{sub_key}:**\n" - for code_key, code_value in sub_value.items(): # 'before' and 'after' code - code_str = f"```\n{code_value}\n```" - code_str_indented = textwrap.indent(code_str, ' ') - markdown_text += f" - **{code_key}:**\n{code_str_indented}\n" - else: - if "relevant file" in sub_key.lower(): - markdown_text += f"\n - **{sub_key}:** {sub_value} \n" + if gfm_supported and 'relevant line' in code_suggestions: + # markdown_text=markdown_text.strip() + markdown_text += '' + relevant_file = '' + for sub_key, sub_value in code_suggestions.items(): + try: + if sub_key.lower() == 'relevant file': + relevant_file = sub_value.strip('`').strip('"').strip("'") + markdown_text += f"" + # continue + elif sub_key.lower() == 'suggestion': + markdown_text += f"" + elif sub_key.lower() == 'relevant line': + markdown_text += f"" + sub_value_list = sub_value.split('](') + relevant_line = sub_value_list[0].lstrip('`').lstrip('[') + if len(sub_value_list) > 1: + link = sub_value_list[1].rstrip(')').strip('`') + markdown_text += f"" + else: + markdown_text += f"" + markdown_text += "" + except Exception as e: + get_logger().exception(f"Failed to parse code suggestion: {e}") + pass + markdown_text += '
{sub_key}{relevant_file}
{sub_key}      {sub_value}
relevant line{relevant_line}{relevant_line}
' + markdown_text += "
" + else: + for sub_key, sub_value in code_suggestions.items(): + if isinstance(sub_value, dict): # "code example" + markdown_text += f" - **{sub_key}:**\n" + for code_key, code_value in sub_value.items(): # 'before' and 'after' code + code_str = f"```\n{code_value}\n```" + code_str_indented = textwrap.indent(code_str, ' ') + markdown_text += f" - **{code_key}:**\n{code_str_indented}\n" else: - markdown_text += f" **{sub_key}:** {sub_value} \n" - if not gfm_supported: - if "relevant line" not in sub_key.lower(): # nicer presentation + if "relevant file" in sub_key.lower(): + markdown_text += f"\n - **{sub_key}:** {sub_value} \n" + else: + markdown_text += f" **{sub_key}:** {sub_value} \n" + if not gfm_supported: + if "relevant line" not in sub_key.lower(): # nicer presentation # markdown_text = markdown_text.rstrip('\n') + "\\\n" # works for gitlab markdown_text = markdown_text.rstrip('\n') + " \n" # works for gitlab and bitbucker - markdown_text += "\n" + markdown_text += "\n" return markdown_text From a247fc326342fb818a41d3b30eb7141b658e59c5 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 10 Dec 2023 17:46:49 +0200 Subject: [PATCH 03/15] s --- pr_agent/algo/utils.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 50505e24..c68e7f6f 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -64,9 +64,9 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: markdown_text += f"\n\n- **{emoji} Code feedback:**\n\n" else: markdown_text += f"- {emoji} **{key}:**\n\n" - for item in value: + for i, item in enumerate(value): if isinstance(item, dict) and key.lower() == 'code feedback': - markdown_text += parse_code_suggestion(item, gfm_supported) + markdown_text += parse_code_suggestion(item, i, gfm_supported) elif item: markdown_text += f" - {item}\n" if key.lower() == 'code feedback': @@ -80,7 +80,7 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: return markdown_text -def parse_code_suggestion(code_suggestions: dict, gfm_supported: bool=True) -> str: +def parse_code_suggestion(code_suggestions: dict, i:int, gfm_supported: bool=True) -> str: """ Convert a dictionary of data into markdown format. @@ -91,6 +91,8 @@ def parse_code_suggestion(code_suggestions: dict, gfm_supported: bool=True) -> s str: A string containing the markdown formatted text generated from the input dictionary. """ markdown_text = "" + if i == 0: + markdown_text += "
" if gfm_supported and 'relevant line' in code_suggestions: # markdown_text=markdown_text.strip() markdown_text += '' From 2b1e841ef15b5b88673ddff1d0487fe992cfb81d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 10 Dec 2023 19:45:54 +0200 Subject: [PATCH 04/15] s --- pr_agent/algo/utils.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index c68e7f6f..8b2f21d7 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -80,7 +80,7 @@ def convert_to_markdown(output_data: dict, gfm_supported: bool=True) -> str: return markdown_text -def parse_code_suggestion(code_suggestions: dict, i:int, gfm_supported: bool=True) -> str: +def parse_code_suggestion(code_suggestions: dict, i: int = 0, gfm_supported: bool = True) -> str: """ Convert a dictionary of data into markdown format. @@ -91,12 +91,10 @@ def parse_code_suggestion(code_suggestions: dict, i:int, gfm_supported: bool=Tru str: A string containing the markdown formatted text generated from the input dictionary. """ markdown_text = "" - if i == 0: - markdown_text += "
" if gfm_supported and 'relevant line' in code_suggestions: - # markdown_text=markdown_text.strip() + if i == 0: + markdown_text += "
" markdown_text += '
' - relevant_file = '' for sub_key, sub_value in code_suggestions.items(): try: if sub_key.lower() == 'relevant file': From b8cc110cbe7179b983a609eea128f0da2ddacf80 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Sun, 10 Dec 2023 19:51:08 +0200 Subject: [PATCH 05/15] s --- tests/unittest/test_convert_to_markdown.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittest/test_convert_to_markdown.py b/tests/unittest/test_convert_to_markdown.py index b03c4fde..800d3ada 100644 --- a/tests/unittest/test_convert_to_markdown.py +++ b/tests/unittest/test_convert_to_markdown.py @@ -71,7 +71,7 @@ class TestConvertToMarkdown: - 📌 **Type of PR:** Test type\n\ - 🧪 **Relevant tests added:** no\n\ - ✨ **Focused PR:** Yes\n\ -- **General PR suggestions:** general suggestion...\n\n\n-
🤖 Code feedback:\n\n - **Code example:**\n - **Before:**\n ```\n Code before\n ```\n - **After:**\n ```\n Code after\n ```\n\n - **Code example:**\n - **Before:**\n ```\n Code before 2\n ```\n - **After:**\n ```\n Code after 2\n ```\n\n
\ +- **General PR suggestions:** general suggestion...\n\n\n-
🤖 Code feedback: - **Code example:**\n - **Before:**\n ```\n Code before\n ```\n - **After:**\n ```\n Code after\n ```\n\n - **Code example:**\n - **Before:**\n ```\n Code before 2\n ```\n - **After:**\n ```\n Code after 2\n ```\n\n
\ """ assert convert_to_markdown(input_data).strip() == expected_output.strip() From 02c0c89b13628c558adfeae882e593218512a8ba Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 11 Dec 2023 08:29:09 +0200 Subject: [PATCH 06/15] feat: Add exception handling for discussion creation in gitlab_provider.py --- pr_agent/git_providers/gitlab_provider.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index 7b11ef54..c5e77d07 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -211,7 +211,11 @@ class GitLabProvider(GitProvider): pos_obj['new_line'] = target_line_no - 1 pos_obj['old_line'] = source_line_no - 1 get_logger().debug(f"Creating comment in {self.id_mr} with body {body} and position {pos_obj}") - self.mr.discussions.create({'body': body, 'position': pos_obj}) + try: + self.mr.discussions.create({'body': body, 'position': pos_obj}) + except Exception as e: + get_logger().debug( + f"Failed to create comment in {self.id_mr} with position {pos_obj} (probably not a '+' line)") def get_relevant_diff(self, relevant_file: str, relevant_line_in_file: int) -> Optional[dict]: changes = self.mr.changes() # Retrieve the changes for the merge request once From 2c1dfe7f3f7697c5b9f599ed866dd3840f90cdb5 Mon Sep 17 00:00:00 2001 From: Itamar Friedman <108689937+coditamar@users.noreply.github.com> Date: Mon, 11 Dec 2023 10:58:30 +0200 Subject: [PATCH 07/15] Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a33fa7cf..11700d32 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Making pull requests less painful with an AI agent
-CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull requests faster and more efficiently. It automatically analyzes the pull request and can provide several types of commands: +CodiumAI `PR-Agent` is an open-source tool aiming to help developers review and handle pull requests faster and more efficiently. It automatically analyzes the pull request and can provide several types of commands: ‣ **Auto Description ([`/describe`](./docs/DESCRIBE.md))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. \ @@ -33,17 +33,17 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull \ ‣ **Update Changelog ([`/update_changelog`](./docs/UPDATE_CHANGELOG.md))**: Automatically updating the CHANGELOG.md file with the PR changes. \ -‣ **Find Similar Issue ([`/similar_issue`](./docs/SIMILAR_ISSUE.md))**: Automatically retrieves and presents similar issues +‣ **Find Similar Issue ([`/similar_issue`](./docs/SIMILAR_ISSUE.md))**: Automatically retrieves and presents similar issues. \ ‣ **Add Documentation ([`/add_docs`](./docs/ADD_DOCUMENTATION.md))**: Automatically adds documentation to un-documented functions/classes in the PR. \ ‣ **Generate Custom Labels ([`/generate_labels`](./docs/GENERATE_CUSTOM_LABELS.md))**: Automatically suggests custom labels based on the PR code changes. -See the [Installation Guide](./INSTALL.md) for instructions how to install and run the tool on different platforms. +See the [Installation Guide](./INSTALL.md) for instructions how to install and run the tool on different git platforms. -See the [Usage Guide](./Usage.md) for instructions how to run the different tools from _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. +See the [Usage Guide](./Usage.md) for instructions how to run the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. -See the [Tools Guide](./docs/TOOLS_GUIDE.md) for detailed description of the different tools. +See the [Tools Guide](./docs/TOOLS_GUIDE.md) for detailed description of the different tools (tools are run via the commands).

Example results:

@@ -140,7 +140,7 @@ Review the [usage guide](./Usage.md) section for detailed instructions how to us ## Try it now -You can try GPT-4 powered PR-Agent, on your public GitHub repository, instantly. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. +You can try GPT-4 powered PR-Agent, on _your public GitHub repository_, instantly. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. For example, add a comment to any pull request with the following text: ``` @CodiumAI-Agent /review @@ -151,6 +151,7 @@ and the agent will respond with a review of your PR To set up your own PR-Agent, see the [Installation](#installation) section below. +Note that when you set your own PR-Agent or use CodiumAI hosted PR-Agent, you do not need to mention `@CodiumAI-Agent ...`. Instead, directly start with the command, e.g., `/ask ...`. --- From 2ce4af16cb048c21e6aecec8b5f8f6f9baaef334 Mon Sep 17 00:00:00 2001 From: Itamar Friedman <108689937+coditamar@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:10:27 +0200 Subject: [PATCH 08/15] Update README.md fix grammar according to PR-Agent suggestions --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 11700d32..95399c1e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Making pull requests less painful with an AI agent
-CodiumAI `PR-Agent` is an open-source tool aiming to help developers review and handle pull requests faster and more efficiently. It automatically analyzes the pull request and can provide several types of commands: +CodiumAI `PR-Agent` is an open-source tool for efficient pull request reviewing and handling. It automatically analyzes the pull request and can provide several types of commands: ‣ **Auto Description ([`/describe`](./docs/DESCRIBE.md))**: Automatically generating PR description - title, type, summary, code walkthrough and labels. \ @@ -41,7 +41,7 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review and See the [Installation Guide](./INSTALL.md) for instructions how to install and run the tool on different git platforms. -See the [Usage Guide](./Usage.md) for instructions how to run the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. +See the [Usage Guide](./Usage.md) for on running the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. See the [Tools Guide](./docs/TOOLS_GUIDE.md) for detailed description of the different tools (tools are run via the commands). @@ -140,7 +140,7 @@ Review the [usage guide](./Usage.md) section for detailed instructions how to us ## Try it now -You can try GPT-4 powered PR-Agent, on _your public GitHub repository_, instantly. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. +Try the GPT-4 powered PR-Agent instantly on _your public GitHub repository_. Just mention `@CodiumAI-Agent` and add the desired command in any PR comment. The agent will generate a response based on your command. For example, add a comment to any pull request with the following text: ``` @CodiumAI-Agent /review @@ -151,7 +151,7 @@ and the agent will respond with a review of your PR To set up your own PR-Agent, see the [Installation](#installation) section below. -Note that when you set your own PR-Agent or use CodiumAI hosted PR-Agent, you do not need to mention `@CodiumAI-Agent ...`. Instead, directly start with the command, e.g., `/ask ...`. +Note that when you set your own PR-Agent or use CodiumAI hosted PR-Agent, there is no need to mention `@CodiumAI-Agent ...`. Instead, directly start with the command, e.g., `/ask ...`. --- From e9bf8574a88609e810405e715d1205f95c2e1348 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 11 Dec 2023 11:52:36 +0200 Subject: [PATCH 09/15] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 95399c1e..5de410b1 100644 --- a/README.md +++ b/README.md @@ -39,9 +39,9 @@ CodiumAI `PR-Agent` is an open-source tool for efficient pull request reviewing \ ‣ **Generate Custom Labels ([`/generate_labels`](./docs/GENERATE_CUSTOM_LABELS.md))**: Automatically suggests custom labels based on the PR code changes. -See the [Installation Guide](./INSTALL.md) for instructions how to install and run the tool on different git platforms. +See the [Installation Guide](./INSTALL.md) for instructions on installing and running the tool on different git platforms. -See the [Usage Guide](./Usage.md) for on running the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. +See the [Usage Guide](./Usage.md) for running the PR-Agent commands via different interfaces, including _CLI_, _online usage_, or by _automatically triggering_ them when a new PR is opened. See the [Tools Guide](./docs/TOOLS_GUIDE.md) for detailed description of the different tools (tools are run via the commands). From 9b97073174fc9dd98a3807f0b6204f0522623232 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 11 Dec 2023 12:00:44 +0200 Subject: [PATCH 10/15] s --- pr_agent/tools/pr_description.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 64acaab3..bd623d5a 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -315,7 +315,6 @@ class PRDescription: if not self.git_provider.is_supported("gfm_markdown"): get_logger().info(f"Disabling semantic files types for {self.pr_id} since gfm_markdown is not supported") return pr_body - try: pr_body += "
" header = f"Relevant files" From 480e2ee6783540cdb7a52b7bfab3d5d03731438c Mon Sep 17 00:00:00 2001 From: mrT23 Date: Mon, 11 Dec 2023 15:55:04 +0200 Subject: [PATCH 11/15] feat: Improve PR description formatting in pr_description.py --- pr_agent/tools/pr_description.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index bd623d5a..34626fa3 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -290,7 +290,7 @@ class PRDescription: value = ', '.join(v for v in value) pr_body += f"{value}\n" if idx < len(self.data) - 1: - pr_body += "\n___\n" + pr_body += "\n\n___\n\n" if get_settings().config.verbosity_level >= 2: get_logger().info(f"title:\n{title}\n{pr_body}") From fdc776887d79360d17b3d9748fcf1d2c06ef033c Mon Sep 17 00:00:00 2001 From: "Hussam.lawen" Date: Mon, 11 Dec 2023 16:47:38 +0200 Subject: [PATCH 12/15] Refactor labels --- pr_agent/algo/utils.py | 9 +++------ pr_agent/git_providers/bitbucket_provider.py | 2 +- pr_agent/git_providers/bitbucket_server_provider.py | 2 +- pr_agent/git_providers/codecommit_provider.py | 2 +- pr_agent/git_providers/gerrit_provider.py | 2 +- pr_agent/git_providers/git_provider.py | 5 ++++- pr_agent/git_providers/github_provider.py | 6 +++++- pr_agent/git_providers/gitlab_provider.py | 2 +- pr_agent/git_providers/local_git_provider.py | 2 +- pr_agent/tools/pr_description.py | 4 ++-- pr_agent/tools/pr_generate_labels.py | 4 ++-- pr_agent/tools/pr_reviewer.py | 2 +- 12 files changed, 23 insertions(+), 19 deletions(-) diff --git a/pr_agent/algo/utils.py b/pr_agent/algo/utils.py index 8b2f21d7..6d0d3731 100644 --- a/pr_agent/algo/utils.py +++ b/pr_agent/algo/utils.py @@ -364,7 +364,7 @@ def try_fix_yaml(response_text: str) -> dict: pass -def set_custom_labels(variables): +def set_custom_labels(variables, git_provider=None): if not get_settings().config.enable_custom_labels: return @@ -376,11 +376,8 @@ def set_custom_labels(variables): labels_list = f" - {labels_list}" if labels_list else "" variables["custom_labels"] = labels_list return - #final_labels = "" - #for k, v in labels.items(): - # final_labels += f" - {k} ({v['description']})\n" - #variables["custom_labels"] = final_labels - #variables["custom_labels_examples"] = f" - {list(labels.keys())[0]}" + + # Set custom labels variables["custom_labels_class"] = "class Label(str, Enum):" for k, v in labels.items(): description = v['description'].strip('\n').replace('\n', '\\n') diff --git a/pr_agent/git_providers/bitbucket_provider.py b/pr_agent/git_providers/bitbucket_provider.py index ee8ad48f..23173f8e 100644 --- a/pr_agent/git_providers/bitbucket_provider.py +++ b/pr_agent/git_providers/bitbucket_provider.py @@ -354,5 +354,5 @@ class BitbucketProvider(GitProvider): pass # bitbucket does not support labels - def get_labels(self): + def get_pr_labels(self): pass diff --git a/pr_agent/git_providers/bitbucket_server_provider.py b/pr_agent/git_providers/bitbucket_server_provider.py index 44347850..902beb16 100644 --- a/pr_agent/git_providers/bitbucket_server_provider.py +++ b/pr_agent/git_providers/bitbucket_server_provider.py @@ -344,7 +344,7 @@ class BitbucketServerProvider(GitProvider): pass # bitbucket does not support labels - def get_labels(self): + def get_pr_labels(self): pass def _get_pr_comments_url(self): diff --git a/pr_agent/git_providers/codecommit_provider.py b/pr_agent/git_providers/codecommit_provider.py index 64cfc70a..286444c5 100644 --- a/pr_agent/git_providers/codecommit_provider.py +++ b/pr_agent/git_providers/codecommit_provider.py @@ -216,7 +216,7 @@ class CodeCommitProvider(GitProvider): def publish_labels(self, labels): return [""] # not implemented yet - def get_labels(self): + def get_pr_labels(self): return [""] # not implemented yet def remove_initial_comment(self): diff --git a/pr_agent/git_providers/gerrit_provider.py b/pr_agent/git_providers/gerrit_provider.py index d286b1bf..dbdbe82f 100644 --- a/pr_agent/git_providers/gerrit_provider.py +++ b/pr_agent/git_providers/gerrit_provider.py @@ -207,7 +207,7 @@ class GerritProvider(GitProvider): Comment = namedtuple('Comment', ['body']) return Comments([Comment(c['message']) for c in reversed(comments)]) - def get_labels(self): + def get_pr_labels(self): raise NotImplementedError( 'Getting labels is not implemented for the gerrit provider') diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index deb5df3d..4c4684c3 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -135,7 +135,10 @@ class GitProvider(ABC): pass @abstractmethod - def get_labels(self): + def get_pr_labels(self): + pass + + def get_repo_labels(self): pass @abstractmethod diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 3ae97742..f365db84 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -461,13 +461,17 @@ class GithubProvider(GitProvider): except Exception as e: get_logger().exception(f"Failed to publish labels, error: {e}") - def get_labels(self): + def get_pr_labels(self): try: return [label.name for label in self.pr.labels] except Exception as e: get_logger().exception(f"Failed to get labels, error: {e}") return [] + def get_repo_labels(self): + labels = self.repo_obj.get_labels() + return [label for label in labels] + def get_commit_messages(self): """ Retrieves the commit messages of a pull request. diff --git a/pr_agent/git_providers/gitlab_provider.py b/pr_agent/git_providers/gitlab_provider.py index c5e77d07..618cebc0 100644 --- a/pr_agent/git_providers/gitlab_provider.py +++ b/pr_agent/git_providers/gitlab_provider.py @@ -408,7 +408,7 @@ class GitLabProvider(GitProvider): def publish_inline_comments(self, comments: list[dict]): pass - def get_labels(self): + def get_pr_labels(self): return self.mr.labels def get_commit_messages(self): diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index 0ef11413..b3fad772 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -178,5 +178,5 @@ class LocalGitProvider(GitProvider): def get_issue_comments(self): raise NotImplementedError('Getting issue comments is not implemented for the local git provider') - def get_labels(self): + def get_pr_labels(self): raise NotImplementedError('Getting labels is not implemented for the local git provider') diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 64acaab3..05fb63f8 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -106,7 +106,7 @@ class PRDescription: else: self.git_provider.publish_description(pr_title, pr_body) if get_settings().pr_description.publish_labels and self.git_provider.is_supported("get_labels"): - current_labels = self.git_provider.get_labels() + current_labels = self.git_provider.get_pr_labels() user_labels = get_user_labels(current_labels) self.git_provider.publish_labels(pr_labels + user_labels) @@ -158,7 +158,7 @@ class PRDescription: variables["diff"] = self.patches_diff # update diff environment = Environment(undefined=StrictUndefined) - set_custom_labels(variables) + set_custom_labels(variables, self.git_provider) system_prompt = environment.from_string(get_settings().pr_description_prompt.system).render(variables) user_prompt = environment.from_string(get_settings().pr_description_prompt.user).render(variables) diff --git a/pr_agent/tools/pr_generate_labels.py b/pr_agent/tools/pr_generate_labels.py index 6ea322a4..fc90ed44 100644 --- a/pr_agent/tools/pr_generate_labels.py +++ b/pr_agent/tools/pr_generate_labels.py @@ -82,7 +82,7 @@ class PRGenerateLabels: if get_settings().config.publish_output: get_logger().info(f"Pushing labels {self.pr_id}") - current_labels = self.git_provider.get_labels() + current_labels = self.git_provider.get_pr_labels() user_labels = get_user_labels(current_labels) pr_labels = pr_labels + user_labels @@ -132,7 +132,7 @@ class PRGenerateLabels: variables["diff"] = self.patches_diff # update diff environment = Environment(undefined=StrictUndefined) - set_custom_labels(variables) + set_custom_labels(variables, self.git_provider) system_prompt = environment.from_string(get_settings().pr_custom_labels_prompt.system).render(variables) user_prompt = environment.from_string(get_settings().pr_custom_labels_prompt.user).render(variables) diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 13fc1717..5a6f720a 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -392,7 +392,7 @@ class PRReviewer: if security_concerns_bool: review_labels.append('Possible security concern') - current_labels = self.git_provider.get_labels() + current_labels = self.git_provider.get_pr_labels() current_labels_filtered = [label for label in current_labels if not label.lower().startswith('review effort [1-5]:') and not label.lower().startswith( 'possible security concern')] From 81da328ae32663f55893525fefd39fe7a096ba6b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 12 Dec 2023 08:06:20 +0200 Subject: [PATCH 13/15] feat: Add automatic tool configuration for Bitbucket app in bitbucket_app.py and configuration.toml, update Usage.md --- Usage.md | 14 ++++++++++++++ pr_agent/servers/bitbucket_app.py | 21 +++++++++++++++++++-- pr_agent/settings/configuration.toml | 7 +++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/Usage.md b/Usage.md index a7c64fbc..887bf22a 100644 --- a/Usage.md +++ b/Usage.md @@ -239,6 +239,20 @@ inline_code_comments = true Each time you invoke a `/review` tool, it will use inline code comments. +#### BitBucket Self-Hosted App automatic tools +You can configure in your local `.pr_agent.toml` file conditions for which tools will **run automatically**. + +Specifically, start by setting the following environment variables: +```yaml +[bitbucket_app] +auto_review = true # set as config var in .pr_agent.toml +auto_describe = true # set as config var in .pr_agent.toml +auto_improve = true # set as config var in .pr_agent.toml +``` + +`bitbucket_app.auto_review`, `bitbucket_app.auto_describe` and `bitbucket_app.auto_improve` are used to enable/disable automatic tools that run when a new PR is opened. +If not set, the default option is that only the `review` tool will run automatically when a new PR is opened. + ### Changing a model See [here](pr_agent/algo/__init__.py) for the list of available models. diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index e147fbdd..7ca0bcf0 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -16,8 +16,13 @@ from starlette_context.middleware import RawContextMiddleware from pr_agent.agent.pr_agent import PRAgent from pr_agent.config_loader import get_settings, global_settings +from pr_agent.git_providers.utils import apply_repo_settings from pr_agent.log import LoggingFormat, get_logger, setup_logger from pr_agent.secret_providers import get_secret_provider +from pr_agent.servers.github_action_runner import get_setting_or_env, is_true +from pr_agent.tools.pr_code_suggestions import PRCodeSuggestions +from pr_agent.tools.pr_description import PRDescription +from pr_agent.tools.pr_reviewer import PRReviewer setup_logger(fmt=LoggingFormat.JSON) router = APIRouter() @@ -89,8 +94,20 @@ async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Req pr_url = data["data"]["pullrequest"]["links"]["html"]["href"] log_context["api_url"] = pr_url log_context["event"] = "pull_request" - with get_logger().contextualize(**log_context): - await agent.handle_request(pr_url, "review") + if pr_url: + with get_logger().contextualize(**log_context): + apply_repo_settings(pr_url) + auto_review = get_setting_or_env("BITBUCKET_APP.AUTO_REVIEW", None) + if auto_review is None or is_true(auto_review): # by default, auto review is enabled + await PRReviewer(pr_url).run() + auto_describe = get_setting_or_env("BITBUCKET_APP.AUTO_DESCRIBE", None) + if is_true(auto_describe): # by default, auto describe is disabled + await PRDescription(pr_url).run() + auto_improve = get_setting_or_env("BITBUCKET_APP.AUTO_IMPROVE", None) + if is_true(auto_improve): # by default, auto improve is disabled + await PRCodeSuggestions(pr_url).run() + # with get_logger().contextualize(**log_context): + # await agent.handle_request(pr_url, "review") elif event == "pullrequest:comment_created": pr_url = data["data"]["pullrequest"]["links"]["html"]["href"] log_context["api_url"] = pr_url diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 259383d7..30fc97f8 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -143,6 +143,12 @@ magic_word = "AutoReview" # Polling interval polling_interval_seconds = 30 +[bitbucket_app] +#auto_review = true # set as config var in .pr_agent.toml +#auto_describe = true # set as config var in .pr_agent.toml +#auto_improve = true # set as config var in .pr_agent.toml + + [local] # LocalGitProvider settings - uncomment to use paths other than default # description_path= "path/to/description.md" @@ -170,3 +176,4 @@ max_issues_to_scan = 500 # fill and place in .secrets.toml #api_key = ... # environment = "gcp-starter" + From afb633811f3da5e66246802ad8c7b1e2775ad213 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 12 Dec 2023 09:18:51 +0200 Subject: [PATCH 14/15] remove bad default --- pr_agent/servers/bitbucket_app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index 7ca0bcf0..dfb945c9 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -154,7 +154,6 @@ async def handle_uninstalled_webhooks(request: Request, response: Response): def start(): get_settings().set("CONFIG.PUBLISH_OUTPUT_PROGRESS", False) get_settings().set("CONFIG.GIT_PROVIDER", "bitbucket") - get_settings().set("PR_DESCRIPTION.PUBLISH_DESCRIPTION_AS_COMMENT", True) middleware = [Middleware(RawContextMiddleware)] app = FastAPI(middleware=middleware) app.include_router(router) From 2f10b4f3c5e3c71808e3cd9b9efc106ff8b826f1 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 12 Dec 2023 09:59:26 +0200 Subject: [PATCH 15/15] feat: Refactor auto tool execution order in bitbucket_app.py, add logging in pr_description.py, and update tool configuration instructions in Usage.md --- Usage.md | 6 +++--- pr_agent/servers/bitbucket_app.py | 6 +++--- pr_agent/tools/pr_description.py | 1 + 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Usage.md b/Usage.md index 887bf22a..db9874d2 100644 --- a/Usage.md +++ b/Usage.md @@ -240,9 +240,9 @@ inline_code_comments = true Each time you invoke a `/review` tool, it will use inline code comments. #### BitBucket Self-Hosted App automatic tools -You can configure in your local `.pr_agent.toml` file conditions for which tools will **run automatically**. +You can configure in your local `.pr_agent.toml` file which tools will **run automatically** when a new PR is opened. -Specifically, start by setting the following environment variables: +Specifically, set the following values: ```yaml [bitbucket_app] auto_review = true # set as config var in .pr_agent.toml @@ -250,7 +250,7 @@ auto_describe = true # set as config var in .pr_agent.toml auto_improve = true # set as config var in .pr_agent.toml ``` -`bitbucket_app.auto_review`, `bitbucket_app.auto_describe` and `bitbucket_app.auto_improve` are used to enable/disable automatic tools that run when a new PR is opened. +`bitbucket_app.auto_review`, `bitbucket_app.auto_describe` and `bitbucket_app.auto_improve` are used to enable/disable automatic tools. If not set, the default option is that only the `review` tool will run automatically when a new PR is opened. ### Changing a model diff --git a/pr_agent/servers/bitbucket_app.py b/pr_agent/servers/bitbucket_app.py index dfb945c9..b5e5e7d7 100644 --- a/pr_agent/servers/bitbucket_app.py +++ b/pr_agent/servers/bitbucket_app.py @@ -100,12 +100,12 @@ async def handle_github_webhooks(background_tasks: BackgroundTasks, request: Req auto_review = get_setting_or_env("BITBUCKET_APP.AUTO_REVIEW", None) if auto_review is None or is_true(auto_review): # by default, auto review is enabled await PRReviewer(pr_url).run() - auto_describe = get_setting_or_env("BITBUCKET_APP.AUTO_DESCRIBE", None) - if is_true(auto_describe): # by default, auto describe is disabled - await PRDescription(pr_url).run() auto_improve = get_setting_or_env("BITBUCKET_APP.AUTO_IMPROVE", None) if is_true(auto_improve): # by default, auto improve is disabled await PRCodeSuggestions(pr_url).run() + auto_describe = get_setting_or_env("BITBUCKET_APP.AUTO_DESCRIBE", None) + if is_true(auto_describe): # by default, auto describe is disabled + await PRDescription(pr_url).run() # with get_logger().contextualize(**log_context): # await agent.handle_request(pr_url, "review") elif event == "pullrequest:comment_created": diff --git a/pr_agent/tools/pr_description.py b/pr_agent/tools/pr_description.py index 64acaab3..92b71af5 100644 --- a/pr_agent/tools/pr_description.py +++ b/pr_agent/tools/pr_description.py @@ -102,6 +102,7 @@ class PRDescription: if get_settings().config.publish_output: get_logger().info(f"Pushing answer {self.pr_id}") if get_settings().pr_description.publish_description_as_comment: + get_logger().info(f"Publishing answer as comment") self.git_provider.publish_comment(full_markdown_description) else: self.git_provider.publish_description(pr_title, pr_body)