Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

auto approval feature #641

Merged
merged 5 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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" 💎:

Expand Down
29 changes: 29 additions & 0 deletions docs/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 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` label is equal or below a certain threshold, by adjusting the flag:
```
[pr_reviewer]
maximal_review_effort = 5
```
8 changes: 8 additions & 0 deletions pr_agent/agent/pr_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
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
Expand All @@ -58,6 +59,13 @@ async def handle_request(self, pr_url, request, notify=None) -> bool:
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()
Expand Down
6 changes: 5 additions & 1 deletion pr_agent/git_providers/git_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ def get_commit_messages(self):
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.
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions pr_agent/git_providers/github_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,3 +643,13 @@ def get_pr_id(self):
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
26 changes: 26 additions & 0 deletions pr_agent/servers/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,31 @@ def get_review_usage_guide():
"""
output += "\n\n</details></td></tr>\n\n"

output += "<tr><td><details> <summary><strong> Auto-approve PRs</strong></summary><hr>\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</details></td></tr>\n\n"

# general
output += "\n\n<tr><td><details> <summary><strong> More PR-Agent commands</strong></summary><hr> \n\n"
output += HelpMessage.get_general_bot_help_text()
Expand Down Expand Up @@ -186,6 +211,7 @@ def get_describe_usage_guide():
- `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 += "<tr><td><details> <summary><strong> Utilizing extra instructions</strong></summary><hr>\n\n"
output += '''\
Expand Down
4 changes: 4 additions & 0 deletions pr_agent/settings/configuration.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pr_agent/tools/pr_description.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ async def run(self):
latest_commit_url = self.git_provider.get_latest_commit_url()
if latest_commit_url:
self.git_provider.publish_comment(
f"**[PR Description]({self.git_provider.pr_url})** updated to latest commit ({latest_commit_url})")
f"**[PR Description]({self.git_provider.get_pr_url()})** updated to latest commit ({latest_commit_url})")
self.git_provider.remove_initial_comment()
except Exception as e:
get_logger().error(f"Error generating PR description {self.pr_id}: {e}")
Expand Down
33 changes: 33 additions & 0 deletions pr_agent/tools/pr_reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ def __init__(self, pr_url: str, is_answer: bool = False, is_auto: bool = False,
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)
Expand Down Expand Up @@ -102,6 +103,11 @@ async def run(self) -> None:
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:
Expand Down Expand Up @@ -392,3 +398,30 @@ def set_review_labels(self, data):
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")
Loading