From b190b1879eaf51529599d8080ee0b3b517abe1ad Mon Sep 17 00:00:00 2001 From: mrT23 Date: Tue, 6 Feb 2024 08:31:36 +0200 Subject: [PATCH] auto approval --- README.md | 3 +++ docs/REVIEW.md | 29 ++++++++++++++++++++ pr_agent/agent/pr_agent.py | 8 ++++++ pr_agent/git_providers/git_provider.py | 6 ++++- pr_agent/git_providers/github_provider.py | 10 +++++++ pr_agent/servers/help.py | 26 ++++++++++++++++++ pr_agent/settings/configuration.toml | 4 +++ pr_agent/tools/pr_reviewer.py | 33 +++++++++++++++++++++++ 8 files changed, 118 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f64bb2f6..f01bcc3d 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,9 @@ Making pull requests less painful with an AI agent - [Why use PR-Agent?](#why-use-pr-agent) ## News and Updates +### Feb 6, 2024 +A new feature was added to the `review` tool - [Auto-approved PRs](./docs/REVIEW.md#auto-approved-prs). If enabled, this feature enables to automatically approve PRs that meet specific criteria, by commenting on a PR: `/review auto_approve`. + ### Feb 2, 2024 We are excited to introduce "PR Actions" 💎: diff --git a/docs/REVIEW.md b/docs/REVIEW.md index 5a35d8b9..e37b9c24 100644 --- a/docs/REVIEW.md +++ b/docs/REVIEW.md @@ -11,6 +11,7 @@ - [Automation](#automation) - [Auto-labels](#auto-labels) - [Extra instructions](#extra-instructions) + - [Auto-approval](#auto-approval) ## Overview The `review` tool scans the PR code changes, and automatically generates a PR review. @@ -50,6 +51,9 @@ This sub-tool checks if the PR description properly contains a ticket to a proje #### Adding PR labels - `enable_review_labels_security`: if set to true, the tool will publish a 'possible security issue' label if it detects a security issue. Default is true. - `enable_review_labels_effort`: if set to true, the tool will publish a 'Review effort [1-5]: x' label. Default is true. +#### Auto-approval +- `enable_auto_approval`: if set to true, the tool will approve the PR when invoked with the 'auto_approve' command. Default is false. This flag can be changed only from configuration file. +- `maximal_review_effort`: maximal effort level for auto-approval. If the PR's estimated review effort is above this threshold, the auto-approval will not run. Default is 5. ### Incremental Mode Incremental review only considers changes since the last PR-Agent review. This can be useful when working on the PR in an iterative manner, and you want to focus on the changes since the last review instead of reviewing the entire PR again. @@ -97,6 +101,7 @@ ___ 3) [Automation](#automation) 4) [Auto-labels](#auto-labels) 5) [Extra instructions](#extra-instructions) +6) [Auto-approval](#auto-approval) ### General guidelines The `review` tool provides a collection of possible feedbacks about a PR. @@ -146,3 +151,27 @@ In the code feedback section, emphasize the following: ``` Use triple quotes to write multi-line instructions. Use bullet points to make the instructions more readable. + +### Auto-approval +PR-Agent can approve a PR when a specific comment is invoked, given that this ability was enabled in the configuration file. + +To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following: +``` +[pr_reviewer] +enable_auto_approval = true +``` +(this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository) + + +After enabling, by invoking: +``` +/review auto_approve +``` +The tool will automatically approve the PR, and add a comment with the approval. + + +You can also enable auto-approval only if the PR meets certain requirements, such as that the `estimated_review_effort` is equal or below a certain threshold, by adjusting the flag: +``` +[pr_reviewer] +maximal_review_effort = 5 +``` diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 3eb26841..770317fb 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -45,6 +45,7 @@ commands = list(command2class.keys()) class PRAgent: def __init__(self, ai_handler: partial[BaseAiHandler,] = LiteLLMAIHandler): self.ai_handler = ai_handler # will be initialized in run_action + self.forbidden_cli_args = ['enable_auto_approval'] async def handle_request(self, pr_url, request, notify=None) -> bool: # First, apply repo specific settings if exists @@ -58,6 +59,13 @@ class PRAgent: action, *args = list(lexer) else: action, *args = request + + if args: + for forbidden_arg in self.forbidden_cli_args: + for arg in args: + if forbidden_arg in arg: + get_logger().error(f"CLI argument '{forbidden_arg}' is forbidden") + return False args = update_settings_from_args(args) action = action.lstrip("/").lower() diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index ae2109b5..a3237dac 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -171,6 +171,11 @@ class GitProvider(ABC): def get_latest_commit_url(self) -> str: return "" + def auto_approve(self) -> bool: + return False + + + def get_main_pr_language(languages, files) -> str: """ Get the main language of the commit. Return an empty string if cannot determine. @@ -239,7 +244,6 @@ def get_main_pr_language(languages, files) -> str: return main_language_str - class IncrementalPR: def __init__(self, is_incremental: bool = False): self.is_incremental = is_incremental diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 9b89973d..8dd8a87f 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -643,3 +643,13 @@ class GithubProvider(GitProvider): return pr_id except: return "" + + def auto_approve(self) -> bool: + try: + res = self.pr.create_review(event="APPROVE") + if res.state == "APPROVED": + return True + return False + except Exception as e: + get_logger().exception(f"Failed to auto-approve, error: {e}") + return False \ No newline at end of file diff --git a/pr_agent/servers/help.py b/pr_agent/servers/help.py index 1d48be59..36bd0db5 100644 --- a/pr_agent/servers/help.py +++ b/pr_agent/servers/help.py @@ -99,6 +99,31 @@ Some of the feature that are disabled by default are quite useful, and should be """ output += "\n\n\n\n" + output += "
Auto-approve PRs
\n\n" + output += '''\ +By invoking: +``` +/review auto_approve +``` +The tool will automatically approve the PR, and add a comment with the approval. + + +To ensure safety, the auto-approval feature is disabled by default. To enable auto-approval, you need to actively set in a pre-defined configuration file the following: +``` +[pr_reviewer] +enable_auto_approval = true +``` +(this specific flag cannot be set with a command line argument, only in the configuration file, committed to the repository) + + +You can also enable auto-approval only if the PR meets certain requirements, such as that the `estimated_review_effort` is equal or below a certain threshold, by adjusting the flag: +``` +[pr_reviewer] +maximal_review_effort = 5 +``` +''' + output += "\n\n
\n\n" + # general output += "\n\n
More PR-Agent commands
\n\n" output += HelpMessage.get_general_bot_help_text() @@ -186,6 +211,7 @@ To enable inline file summary, set `pr_description.inline_file_summary` in the c - `true`: A collapsable file comment with changes title and a changes summary for each file in the PR. - `false` (default): File changes walkthrough will be added only to the "Conversation" tab. """ + # extra instructions output += "
Utilizing extra instructions
\n\n" output += '''\ diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 7ab2f8cb..eea41a7f 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -43,6 +43,10 @@ require_all_thresholds_for_incremental_review=false minimal_commits_for_incremental_review=0 minimal_minutes_for_incremental_review=0 enable_help_text=true # Determines whether to include help text in the PR review. Enabled by default. +# auto approval +enable_auto_approval=false +maximal_review_effort=5 + [pr_description] # /describe # publish_labels=true diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 31cc1c43..e714b194 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -36,6 +36,7 @@ class PRReviewer: ai_handler (BaseAiHandler): The AI handler to be used for the review. Defaults to None. args (list, optional): List of arguments passed to the PRReviewer class. Defaults to None. """ + self.args = args self.parse_args(args) # -i command self.git_provider = get_git_provider()(pr_url, incremental=self.incremental) @@ -102,6 +103,11 @@ class PRReviewer: if self.incremental.is_incremental and not self._can_run_incremental_review(): return None + if isinstance(self.args, list) and self.args and self.args[0] == 'auto_approve': + get_logger().info(f'Auto approve flow PR: {self.pr_url} ...') + self.auto_approve_logic() + return None + get_logger().info(f'Reviewing PR: {self.pr_url} ...') if get_settings().config.publish_output: @@ -392,3 +398,30 @@ class PRReviewer: self.git_provider.publish_labels(review_labels + current_labels_filtered) except Exception as e: get_logger().error(f"Failed to set review labels, error: {e}") + + def auto_approve_logic(self): + """ + Auto-approve a pull request if it meets the conditions for auto-approval. + """ + if get_settings().pr_reviewer.enable_auto_approval: + maximal_review_effort = get_settings().pr_reviewer.maximal_review_effort + if maximal_review_effort < 5: + current_labels = self.git_provider.get_pr_labels() + for label in current_labels: + if label.lower().startswith('review effort [1-5]:'): + effort = int(label.split(':')[1].strip()) + if effort > maximal_review_effort: + get_logger().info( + f"Auto-approve error: PR review effort ({effort}) is higher than the maximal review effort " + f"({maximal_review_effort}) allowed") + self.git_provider.publish_comment( + f"Auto-approve error: PR review effort ({effort}) is higher than the maximal review effort " + f"({maximal_review_effort}) allowed") + return + is_auto_approved = self.git_provider.auto_approve() + if is_auto_approved: + get_logger().info("Auto-approved PR") + self.git_provider.publish_comment("Auto-approved PR") + else: + get_logger().info("Auto-approval option is disabled") + self.git_provider.publish_comment("Auto-approval option for PR-Agent is disabled") \ No newline at end of file