2023-07-06 00:21:08 +03:00
import copy
2023-11-01 21:02:25 +02:00
import datetime
2023-07-19 01:03:47 +03:00
from collections import OrderedDict
2023-12-17 16:52:03 +02:00
from functools import partial
2023-08-01 14:43:26 +03:00
from typing import List , Tuple
2023-07-06 00:21:08 +03:00
from jinja2 import Environment , StrictUndefined
2023-12-12 23:03:38 +08:00
from pr_agent . algo . ai_handlers . base_ai_handler import BaseAiHandler
2023-12-14 09:00:14 +02:00
from pr_agent . algo . ai_handlers . litellm_ai_handler import LiteLLMAIHandler
2023-12-09 16:39:25 +00:00
from pr_agent . algo . pr_processing import get_pr_diff , retry_with_fallback_models
2023-07-06 00:21:08 +03:00
from pr_agent . algo . token_handler import TokenHandler
2024-02-24 16:47:23 +02:00
from pr_agent . algo . utils import convert_to_markdown , load_yaml , ModelType
2023-08-01 14:43:26 +03:00
from pr_agent . config_loader import get_settings
2023-07-18 10:17:09 +03:00
from pr_agent . git_providers import get_git_provider
2023-08-01 14:43:26 +03:00
from pr_agent . git_providers . git_provider import IncrementalPR , get_main_pr_language
2023-10-16 14:56:00 +03:00
from pr_agent . log import get_logger
2024-01-07 09:56:09 +02:00
from pr_agent . servers . help import HelpMessage
2023-07-06 00:21:08 +03:00
class PRReviewer :
2023-07-26 17:24:03 +03:00
"""
The PRReviewer class is responsible for reviewing a pull request and generating feedback using an AI model .
"""
2023-12-14 09:00:14 +02:00
def __init__ ( self , pr_url : str , is_answer : bool = False , is_auto : bool = False , args : list = None ,
2023-12-17 16:52:03 +02:00
ai_handler : partial [ BaseAiHandler , ] = LiteLLMAIHandler ) :
2023-07-26 17:24:03 +03:00
"""
Initialize the PRReviewer object with the necessary attributes and objects to review a pull request .
Args :
pr_url ( str ) : The URL of the pull request to be reviewed .
is_answer ( bool , optional ) : Indicates whether the review is being done in answer mode . Defaults to False .
2023-12-13 08:16:02 +08:00
is_auto ( bool , optional ) : Indicates whether the review is being done in automatic mode . Defaults to False .
ai_handler ( BaseAiHandler ) : The AI handler to be used for the review . Defaults to None .
2023-07-26 17:24:03 +03:00
args ( list , optional ) : List of arguments passed to the PRReviewer class . Defaults to None .
"""
2024-02-06 08:31:36 +02:00
self . args = args
2023-07-30 11:43:44 +03:00
self . parse_args ( args ) # -i command
2023-07-06 00:21:08 +03:00
2023-07-19 14:22:34 +03:00
self . git_provider = get_git_provider ( ) ( pr_url , incremental = self . incremental )
2023-07-07 16:31:28 +03:00
self . main_language = get_main_pr_language (
2023-07-07 16:10:33 +03:00
self . git_provider . get_languages ( ) , self . git_provider . get_files ( )
)
2023-07-19 01:03:47 +03:00
self . pr_url = pr_url
2023-07-17 15:49:29 +03:00
self . is_answer = is_answer
2023-08-16 16:17:00 -04:00
self . is_auto = is_auto
2023-07-26 17:24:03 +03:00
2023-07-18 10:17:09 +03:00
if self . is_answer and not self . git_provider . is_supported ( " get_issue_comments " ) :
2023-08-01 14:43:26 +03:00
raise Exception ( f " Answer mode is not supported for { get_settings ( ) . config . git_provider } for now " )
2023-12-17 16:52:03 +02:00
self . ai_handler = ai_handler ( )
2023-07-06 00:21:08 +03:00
self . patches_diff = None
self . prediction = None
2023-07-26 17:24:03 +03:00
answer_str , question_str = self . _get_user_answers ( )
2023-07-06 00:21:08 +03:00
self . vars = {
" title " : self . git_provider . pr . title ,
" branch " : self . git_provider . get_pr_branch ( ) ,
2023-07-07 15:02:40 +03:00
" description " : self . git_provider . get_pr_description ( ) ,
2023-07-06 17:34:40 +03:00
" language " : self . main_language ,
2023-07-06 00:21:08 +03:00
" diff " : " " , # empty diff for initial calculation
2023-08-01 14:43:26 +03:00
" require_score " : get_settings ( ) . pr_reviewer . require_score_review ,
" require_tests " : get_settings ( ) . pr_reviewer . require_tests_review ,
" require_focused " : get_settings ( ) . pr_reviewer . require_focused_review ,
2023-09-17 16:31:58 +03:00
" require_estimate_effort_to_review " : get_settings ( ) . pr_reviewer . require_estimate_effort_to_review ,
2023-08-01 14:43:26 +03:00
' num_code_suggestions ' : get_settings ( ) . pr_reviewer . num_code_suggestions ,
2023-07-17 15:49:29 +03:00
' question_str ' : question_str ,
' answer_str ' : answer_str ,
2023-08-01 14:43:26 +03:00
" extra_instructions " : get_settings ( ) . pr_reviewer . extra_instructions ,
2023-08-02 18:26:39 +03:00
" commit_messages_str " : self . git_provider . get_commit_messages ( ) ,
2023-10-27 21:12:58 +03:00
" custom_labels " : " " ,
2023-10-29 08:58:12 +02:00
" enable_custom_labels " : get_settings ( ) . config . enable_custom_labels ,
2023-07-06 00:21:08 +03:00
}
2023-07-26 17:24:03 +03:00
self . token_handler = TokenHandler (
self . git_provider . pr ,
self . vars ,
2023-08-01 14:43:26 +03:00
get_settings ( ) . pr_review_prompt . system ,
get_settings ( ) . pr_review_prompt . user
2023-07-26 17:24:03 +03:00
)
def parse_args ( self , args : List [ str ] ) - > None :
"""
Parse the arguments passed to the PRReviewer class and set the ' incremental ' attribute accordingly .
Args :
args : A list of arguments passed to the PRReviewer class .
Returns :
None
"""
2023-07-19 14:22:34 +03:00
is_incremental = False
2023-07-19 18:18:18 +03:00
if args and len ( args ) > = 1 :
2023-07-19 14:22:34 +03:00
arg = args [ 0 ]
if arg == " -i " :
is_incremental = True
self . incremental = IncrementalPR ( is_incremental )
2023-08-01 14:43:26 +03:00
async def run ( self ) - > None :
2023-09-20 07:39:56 +03:00
try :
2023-11-06 09:21:22 +02:00
if self . incremental . is_incremental and not self . _can_run_incremental_review ( ) :
2023-09-20 07:39:56 +03:00
return None
2024-02-06 08:31:36 +02:00
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
2023-10-16 14:56:00 +03:00
get_logger ( ) . info ( f ' Reviewing PR: { self . pr_url } ... ' )
2024-02-24 16:47:23 +02:00
relevant_configs = { ' pr_reviewer ' : dict ( get_settings ( ) . pr_reviewer ) ,
' config ' : dict ( get_settings ( ) . config ) }
2024-02-25 10:45:15 +02:00
get_logger ( ) . debug ( " Relevant configs " , artifacts = relevant_configs )
2024-03-10 17:15:25 +02:00
if self . incremental . is_incremental and hasattr ( self . git_provider , " file_set " ) and not self . git_provider . file_set :
get_logger ( ) . info ( f " Incremental review is enabled for { self . pr_url } but there are no new files " )
if hasattr ( self . git_provider , " previous_review " ) :
previous_review_url = self . git_provider . previous_review . html_url
if get_settings ( ) . config . publish_output :
self . git_provider . publish_comment ( f " Incremental Review Skipped \n No files were changed since the [previous PR Review]( { previous_review_url } ) " , is_temporary = True )
return None
2023-09-20 07:39:56 +03:00
if get_settings ( ) . config . publish_output :
self . git_provider . publish_comment ( " Preparing review... " , is_temporary = True )
2024-02-08 17:08:42 +02:00
await retry_with_fallback_models ( self . _prepare_prediction , model_type = ModelType . TURBO )
2024-01-18 17:01:25 +02:00
if not self . prediction :
self . git_provider . remove_initial_comment ( )
return None
2023-09-20 07:39:56 +03:00
2024-02-24 16:47:23 +02:00
pr_review = self . _prepare_pr_review ( )
2024-02-25 10:45:15 +02:00
get_logger ( ) . debug ( f " PR output " , artifact = pr_review )
2023-09-20 07:39:56 +03:00
if get_settings ( ) . config . publish_output :
2023-11-08 19:27:16 +02:00
# publish the review
if get_settings ( ) . pr_reviewer . persistent_comment and not self . incremental . is_incremental :
2024-03-05 17:29:17 +02:00
final_update_message = get_settings ( ) . pr_reviewer . final_update_message
2024-02-24 16:47:23 +02:00
self . git_provider . publish_persistent_comment ( pr_review ,
2024-02-08 15:25:43 +02:00
initial_header = " ## PR Review " ,
2024-03-05 17:29:17 +02:00
update_header = True ,
final_update_message = final_update_message , )
2023-11-08 19:27:16 +02:00
else :
2024-02-24 16:47:23 +02:00
self . git_provider . publish_comment ( pr_review )
2023-11-08 19:27:16 +02:00
2023-09-20 07:39:56 +03:00
self . git_provider . remove_initial_comment ( )
if get_settings ( ) . pr_reviewer . inline_code_comments :
self . _publish_inline_code_comments ( )
except Exception as e :
2023-10-16 14:56:00 +03:00
get_logger ( ) . error ( f " Failed to review PR: { e } " )
2023-07-06 00:21:08 +03:00
2023-07-26 17:24:03 +03:00
async def _prepare_prediction ( self , model : str ) - > None :
2023-07-23 16:16:36 +03:00
self . patches_diff = get_pr_diff ( self . git_provider , self . token_handler , model )
2024-01-18 17:01:25 +02:00
if self . patches_diff :
2024-02-24 16:47:23 +02:00
get_logger ( ) . debug ( f " PR diff " , diff = self . patches_diff )
2024-01-18 17:01:25 +02:00
self . prediction = await self . _get_prediction ( model )
else :
get_logger ( ) . error ( f " Error getting PR diff " )
self . prediction = None
2023-07-23 16:16:36 +03:00
2023-07-26 17:24:03 +03:00
async def _get_prediction ( self , model : str ) - > str :
"""
Generate an AI prediction for the pull request review .
Args :
model : A string representing the AI model to be used for the prediction .
Returns :
A string representing the AI prediction for the pull request review .
"""
2023-07-06 00:21:08 +03:00
variables = copy . deepcopy ( self . vars )
variables [ " diff " ] = self . patches_diff # update diff
2023-07-26 17:24:03 +03:00
2023-07-06 00:21:08 +03:00
environment = Environment ( undefined = StrictUndefined )
2023-08-01 14:43:26 +03:00
system_prompt = environment . from_string ( get_settings ( ) . pr_review_prompt . system ) . render ( variables )
user_prompt = environment . from_string ( get_settings ( ) . pr_review_prompt . user ) . render ( variables )
2023-07-26 17:24:03 +03:00
response , finish_reason = await self . ai_handler . chat_completion (
model = model ,
temperature = 0.2 ,
system = system_prompt ,
user = user_prompt
)
2023-07-11 17:32:48 +03:00
2023-07-06 00:21:08 +03:00
return response
def _prepare_pr_review ( self ) - > str :
2023-07-26 17:24:03 +03:00
"""
2023-08-01 14:43:26 +03:00
Prepare the PR review by processing the AI prediction and generating a markdown - formatted text that summarizes
the feedback .
2023-07-26 17:24:03 +03:00
"""
2024-03-10 07:50:12 +02:00
data = load_yaml ( self . prediction . strip ( ) ,
keys_fix_yaml = [ " estimated_effort_to_review_[1-5]: " , " security_concerns: " , " possible_issues: " ,
" relevant_file: " , " relevant_line: " , " suggestion: " ] )
2023-07-06 12:49:10 +03:00
2024-02-08 17:08:42 +02:00
if ' code_feedback ' in data :
code_feedback = data [ ' code_feedback ' ]
2023-08-05 10:34:09 +03:00
# Filter out code suggestions that can be submitted as inline comments
if get_settings ( ) . pr_reviewer . inline_code_comments :
2024-02-08 17:08:42 +02:00
del data [ ' code_feedback ' ]
2023-08-05 10:34:09 +03:00
else :
for suggestion in code_feedback :
2024-02-08 17:08:42 +02:00
if ( ' relevant_file ' in suggestion ) and ( not suggestion [ ' relevant_file ' ] . startswith ( ' `` ' ) ) :
suggestion [ ' relevant_file ' ] = f " `` { suggestion [ ' relevant_file ' ] } `` "
2023-08-09 08:50:15 +03:00
2024-02-08 17:08:42 +02:00
if ' relevant_line ' not in suggestion :
suggestion [ ' relevant_line ' ] = ' '
2023-08-09 08:50:15 +03:00
2024-02-08 17:08:42 +02:00
relevant_line_str = suggestion [ ' relevant_line ' ] . split ( ' \n ' ) [ 0 ]
2023-08-05 10:34:09 +03:00
# removing '+'
2024-02-08 17:08:42 +02:00
suggestion [ ' relevant_line ' ] = relevant_line_str . lstrip ( ' + ' ) . strip ( )
2023-08-05 10:34:09 +03:00
# try to add line numbers link to code suggestions
if hasattr ( self . git_provider , ' generate_link_to_relevant_line_number ' ) :
link = self . git_provider . generate_link_to_relevant_line_number ( suggestion )
if link :
2024-02-08 17:08:42 +02:00
suggestion [ ' relevant_line ' ] = f " [ { suggestion [ ' relevant_line ' ] } ]( { link } ) "
2023-10-05 17:48:36 +03:00
else :
2023-10-05 17:59:08 +03:00
pass
2023-10-05 17:48:36 +03:00
2023-07-13 09:44:33 +03:00
2024-03-10 14:18:29 +02:00
incremental_review_markdown_text = None
2023-07-26 17:24:03 +03:00
# Add incremental review section
2023-07-19 14:22:34 +03:00
if self . incremental . is_incremental :
2023-08-01 14:43:26 +03:00
last_commit_url = f " { self . git_provider . get_pr_url ( ) } /commits/ " \
f " { self . git_provider . incremental . first_new_commit_sha } "
2023-10-25 11:15:23 +03:00
incremental_review_markdown_text = f " Starting from commit { last_commit_url } "
2023-07-19 01:03:47 +03:00
2024-03-10 14:18:29 +02:00
markdown_text = convert_to_markdown ( data , self . git_provider . is_supported ( " gfm_markdown " ) ,
incremental_review_markdown_text )
2023-07-06 17:53:52 +03:00
2023-12-26 09:25:15 +02:00
# Add help text if gfm_markdown is supported
2023-12-27 10:17:08 +07:00
if self . git_provider . is_supported ( " gfm_markdown " ) and get_settings ( ) . pr_reviewer . enable_help_text :
2024-02-20 08:06:33 +02:00
markdown_text + = " <hr> \n \n <details> <summary><strong>✨ Review tool usage guide:</strong></summary><hr> \n \n "
2024-01-07 09:56:09 +02:00
markdown_text + = HelpMessage . get_review_usage_guide ( )
2023-12-26 08:26:18 +02:00
markdown_text + = " \n </details> \n "
2023-07-06 12:58:05 +03:00
2023-11-15 14:04:17 +02:00
# Add custom labels from the review prediction (effort, security)
self . set_review_labels ( data )
2023-07-06 12:58:05 +03:00
2023-08-03 16:05:46 -07:00
if markdown_text == None or len ( markdown_text ) == 0 :
2023-08-09 08:50:15 +03:00
markdown_text = " "
2023-08-06 18:09:09 +03:00
2023-07-13 09:44:33 +03:00
return markdown_text
2023-07-26 17:24:03 +03:00
def _publish_inline_code_comments ( self ) - > None :
"""
Publishes inline comments on a pull request with code suggestions generated by the AI model .
"""
2023-08-01 14:43:26 +03:00
if get_settings ( ) . pr_reviewer . num_code_suggestions == 0 :
2023-07-17 08:18:42 +03:00
return
2024-02-09 11:11:03 +02:00
data = load_yaml ( self . prediction . strip ( ) ,
keys_fix_yaml = [ " estimated_effort_to_review_[1-5]: " , " security_concerns: " , " possible_issues: " ,
" relevant_file: " , " relevant_line: " , " suggestion: " ] )
2023-07-26 17:24:03 +03:00
comments : List [ str ] = [ ]
2024-03-10 07:50:12 +02:00
for suggestion in data . get ( ' code_feedback ' , [ ] ) :
2024-02-08 17:08:42 +02:00
relevant_file = suggestion . get ( ' relevant_file ' , ' ' ) . strip ( )
relevant_line_in_file = suggestion . get ( ' relevant_line ' , ' ' ) . strip ( )
2023-08-05 10:34:09 +03:00
content = suggestion . get ( ' suggestion ' , ' ' )
2023-07-17 10:41:02 +03:00
if not relevant_file or not relevant_line_in_file or not content :
2023-10-16 14:56:00 +03:00
get_logger ( ) . info ( " Skipping inline comment with missing file/line/content " )
2023-07-17 10:41:02 +03:00
continue
2023-07-18 12:26:49 +03:00
if self . git_provider . is_supported ( " create_inline_comment " ) :
2023-07-17 10:41:02 +03:00
comment = self . git_provider . create_inline_comment ( content , relevant_file , relevant_line_in_file )
if comment :
comments . append ( comment )
else :
self . git_provider . publish_inline_comment ( content , relevant_file , relevant_line_in_file )
2023-07-17 16:53:38 +03:00
2023-07-17 10:41:02 +03:00
if comments :
2023-08-24 11:52:20 +05:30
self . git_provider . publish_inline_comments ( comments )
2023-07-17 15:49:29 +03:00
2023-07-26 17:24:03 +03:00
def _get_user_answers ( self ) - > Tuple [ str , str ] :
"""
Retrieves the question and answer strings from the discussion messages related to a pull request .
Returns :
A tuple containing the question and answer strings .
"""
question_str = " "
answer_str = " "
2023-07-17 15:49:29 +03:00
if self . is_answer :
2023-07-18 10:17:09 +03:00
discussion_messages = self . git_provider . get_issue_comments ( )
2023-07-26 17:24:03 +03:00
fix TypeError when iterating discussion_messages
When `pr-agent` is reviewing a long list of messages, a TypeError is thrown on the line
```python
for message in reversed(discussion_messages):
```
When reviewing the PyGithub library, the recommend an alternate syntax for iterating a paginated list in reverse.
https://github.com/PyGithub/PyGithub/blob/v1.59.0/github/PaginatedList.py#L122-L125
```
If you want to iterate in reversed order, just do::
for repo in user.get_repos().reversed:
print(repo.name)
```
And here's a copy of the actual traceback
```
Traceback (most recent call last):
File "/app/pr_agent/servers/github_action_runner.py", line 68, in <module>
asyncio.run(run_action())
File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
return future.result()
File "/app/pr_agent/servers/github_action_runner.py", line 64, in run_action
await PRAgent().handle_request(pr_url, body)
File "/app/pr_agent/agent/pr_agent.py", line 19, in handle_request
await PRReviewer(pr_url, is_answer=True).review()
File "/app/pr_agent/tools/pr_reviewer.py", line 49, in __init__
answer_str, question_str = self._get_user_answers()
File "/app/pr_agent/tools/pr_reviewer.py", line 253, in _get_user_answers
for message in reversed(discussion_messages):
TypeError: object of type 'PaginatedList' has no len()
```
2023-07-27 20:49:45 +02:00
for message in discussion_messages . reversed :
2023-07-17 15:49:29 +03:00
if " Questions to better understand the PR: " in message . body :
question_str = message . body
elif ' /answer ' in message . body :
answer_str = message . body
2023-07-26 17:24:03 +03:00
2023-07-17 15:49:29 +03:00
if answer_str and question_str :
break
2023-07-26 17:24:03 +03:00
2023-07-17 15:49:29 +03:00
return question_str , answer_str
2023-10-26 11:24:24 +03:00
def _get_previous_review_comment ( self ) :
"""
Get the previous review comment if it exists .
"""
try :
2024-03-03 11:24:30 +02:00
if hasattr ( self . git_provider , " get_previous_review " ) :
2023-10-26 11:24:24 +03:00
return self . git_provider . get_previous_review (
full = not self . incremental . is_incremental ,
incremental = self . incremental . is_incremental ,
)
except Exception as e :
get_logger ( ) . exception ( f " Failed to get previous review comment, error: { e } " )
def _remove_previous_review_comment ( self , comment ) :
"""
2023-10-26 17:07:16 +03:00
Remove the previous review comment if it exists .
2023-10-26 11:24:24 +03:00
"""
try :
2024-03-03 11:24:30 +02:00
if comment :
2023-10-26 11:24:24 +03:00
self . git_provider . remove_comment ( comment )
except Exception as e :
get_logger ( ) . exception ( f " Failed to remove previous review comment, error: { e } " )
2023-11-06 09:21:22 +02:00
def _can_run_incremental_review ( self ) - > bool :
""" Checks if we can run incremental review according the various configurations and previous review """
# checking if running is auto mode but there are no new commits
if self . is_auto and not self . incremental . first_new_commit_sha :
get_logger ( ) . info ( f " Incremental review is enabled for { self . pr_url } but there are no new commits " )
return False
# checking if there are enough commits to start the review
num_new_commits = len ( self . incremental . commits_range )
num_commits_threshold = get_settings ( ) . pr_reviewer . minimal_commits_for_incremental_review
not_enough_commits = num_new_commits < num_commits_threshold
# checking if the commits are not too recent to start the review
recent_commits_threshold = datetime . datetime . now ( ) - datetime . timedelta (
minutes = get_settings ( ) . pr_reviewer . minimal_minutes_for_incremental_review
)
last_seen_commit_date = (
self . incremental . last_seen_commit . commit . author . date if self . incremental . last_seen_commit else None
)
all_commits_too_recent = (
last_seen_commit_date > recent_commits_threshold if self . incremental . last_seen_commit else False
)
# check all the thresholds or just one to start the review
condition = any if get_settings ( ) . pr_reviewer . require_all_thresholds_for_incremental_review else all
if condition ( ( not_enough_commits , all_commits_too_recent ) ) :
get_logger ( ) . info (
f " Incremental review is enabled for { self . pr_url } but didn ' t pass the threshold check to run: "
f " \n * Number of new commits = { num_new_commits } (threshold is { num_commits_threshold } ) "
f " \n * Last seen commit date = { last_seen_commit_date } (threshold is { recent_commits_threshold } ) "
)
return False
return True
2023-11-15 14:04:17 +02:00
def set_review_labels ( self , data ) :
if ( get_settings ( ) . pr_reviewer . enable_review_labels_security or
get_settings ( ) . pr_reviewer . enable_review_labels_effort ) :
try :
review_labels = [ ]
if get_settings ( ) . pr_reviewer . enable_review_labels_effort :
2024-02-08 17:08:42 +02:00
estimated_effort = data [ ' review ' ] [ ' estimated_effort_to_review_[1-5] ' ]
2023-11-15 14:04:17 +02:00
estimated_effort_number = int ( estimated_effort . split ( ' , ' ) [ 0 ] )
2023-11-15 14:12:59 +02:00
if 1 < = estimated_effort_number < = 5 : # 1, because ...
2023-11-15 14:04:17 +02:00
review_labels . append ( f ' Review effort [1-5]: { estimated_effort_number } ' )
if get_settings ( ) . pr_reviewer . enable_review_labels_security :
2024-02-08 17:08:42 +02:00
security_concerns = data [ ' review ' ] [ ' security_concerns ' ] # yes, because ...
2023-11-15 14:04:17 +02:00
security_concerns_bool = ' yes ' in security_concerns . lower ( ) or ' true ' in security_concerns . lower ( )
if security_concerns_bool :
review_labels . append ( ' Possible security concern ' )
2023-12-11 16:47:38 +02:00
current_labels = self . git_provider . get_pr_labels ( )
2023-12-26 08:26:18 +02:00
if current_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 ' ) ]
else :
current_labels_filtered = [ ]
2023-11-28 17:03:55 +02:00
if current_labels or review_labels :
2024-02-24 16:47:23 +02:00
get_logger ( ) . debug ( f " Current labels: \n { current_labels } " )
get_logger ( ) . info ( f " Setting review labels: \n { review_labels + current_labels_filtered } " )
2023-11-15 14:04:17 +02:00
self . git_provider . publish_labels ( review_labels + current_labels_filtered )
2023-11-15 14:07:32 +02:00
except Exception as e :
get_logger ( ) . error ( f " Failed to set review labels, error: { e } " )
2024-02-06 08:31:36 +02:00
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 " )
2024-02-13 11:21:59 +02:00
self . git_provider . publish_comment ( " Auto-approval option for PR-Agent is disabled. "
" You can enable it via a [configuration file](https://github.com/Codium-ai/pr-agent/blob/main/docs/REVIEW.md#auto-approval-1) " )