diff --git a/README.md b/README.md index f64bb2f6..3c592dd1 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-approve PRs](./docs/REVIEW.md#auto-approval-1). 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..19c4880b 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-1) ## 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. + +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 commenting on a PR: +``` +/review auto_approve +``` +PR-Agent 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` label 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 += "