mirror of
https://github.com/qodo-ai/pr-agent.git
synced 2025-07-05 05:10:38 +08:00
Compare commits
1 Commits
idavidov/g
...
ok/multi_c
Author | SHA1 | Date | |
---|---|---|---|
36f7a7b17a |
@ -92,7 +92,6 @@ pip install -r requirements.txt
|
|||||||
|
|
||||||
```
|
```
|
||||||
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
|
cp pr_agent/settings/.secrets_template.toml pr_agent/settings/.secrets.toml
|
||||||
chmod 600 pr_agent/settings/.secrets.toml
|
|
||||||
# Edit .secrets.toml file
|
# Edit .secrets.toml file
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -129,7 +128,6 @@ Allowing you to automate the review process on your private or public repositori
|
|||||||
- Pull requests: Read & write
|
- Pull requests: Read & write
|
||||||
- Issue comment: Read & write
|
- Issue comment: Read & write
|
||||||
- Metadata: Read-only
|
- Metadata: Read-only
|
||||||
- Contents: Read-only
|
|
||||||
- Set the following events:
|
- Set the following events:
|
||||||
- Issue comment
|
- Issue comment
|
||||||
- Pull request
|
- Pull request
|
||||||
|
@ -79,7 +79,7 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull
|
|||||||
|-------|---------------------------------------------|:------:|:------:|:---------:|
|
|-------|---------------------------------------------|:------:|:------:|:---------:|
|
||||||
| TOOLS | Review | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
| TOOLS | Review | :white_check_mark: | :white_check_mark: | :white_check_mark: |
|
||||||
| | ⮑ Inline review | :white_check_mark: | :white_check_mark: | |
|
| | ⮑ Inline review | :white_check_mark: | :white_check_mark: | |
|
||||||
| | Ask | :white_check_mark: | :white_check_mark: | :white_check_mark:
|
| | Ask | :white_check_mark: | :white_check_mark: | |
|
||||||
| | Auto-Description | :white_check_mark: | :white_check_mark: | |
|
| | Auto-Description | :white_check_mark: | :white_check_mark: | |
|
||||||
| | Improve Code | :white_check_mark: | :white_check_mark: | |
|
| | Improve Code | :white_check_mark: | :white_check_mark: | |
|
||||||
| | Reflect and Review | :white_check_mark: | | |
|
| | Reflect and Review | :white_check_mark: | | |
|
||||||
|
@ -29,6 +29,7 @@ class AiHandler:
|
|||||||
self.azure = False
|
self.azure = False
|
||||||
if get_settings().get("OPENAI.ORG", None):
|
if get_settings().get("OPENAI.ORG", None):
|
||||||
litellm.organization = get_settings().openai.org
|
litellm.organization = get_settings().openai.org
|
||||||
|
self.deployment_id = get_settings().get("OPENAI.DEPLOYMENT_ID", None)
|
||||||
if get_settings().get("OPENAI.API_TYPE", None):
|
if get_settings().get("OPENAI.API_TYPE", None):
|
||||||
if get_settings().openai.api_type == "azure":
|
if get_settings().openai.api_type == "azure":
|
||||||
self.azure = True
|
self.azure = True
|
||||||
@ -46,13 +47,6 @@ class AiHandler:
|
|||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
raise ValueError("OpenAI key is required") from e
|
raise ValueError("OpenAI key is required") from e
|
||||||
|
|
||||||
@property
|
|
||||||
def deployment_id(self):
|
|
||||||
"""
|
|
||||||
Returns the deployment ID for the OpenAI API.
|
|
||||||
"""
|
|
||||||
return get_settings().get("OPENAI.DEPLOYMENT_ID", None)
|
|
||||||
|
|
||||||
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError),
|
@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError),
|
||||||
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
|
tries=OPENAI_RETRIES, delay=2, backoff=2, jitter=(1, 3))
|
||||||
async def chat_completion(self, model: str, temperature: float, system: str, user: str):
|
async def chat_completion(self, model: str, temperature: float, system: str, user: str):
|
||||||
@ -76,15 +70,9 @@ class AiHandler:
|
|||||||
TryAgain: If there is an attribute error during OpenAI inference.
|
TryAgain: If there is an attribute error during OpenAI inference.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
deployment_id = self.deployment_id
|
|
||||||
if get_settings().config.verbosity_level >= 2:
|
|
||||||
logging.debug(
|
|
||||||
f"Generating completion with {model}"
|
|
||||||
f"{(' from deployment ' + deployment_id) if deployment_id else ''}"
|
|
||||||
)
|
|
||||||
response = await acompletion(
|
response = await acompletion(
|
||||||
model=model,
|
model=model,
|
||||||
deployment_id=deployment_id,
|
deployment_id=self.deployment_id,
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": system},
|
{"role": "system", "content": system},
|
||||||
{"role": "user", "content": user}
|
{"role": "user", "content": user}
|
||||||
|
@ -208,45 +208,18 @@ def pr_generate_compressed_diff(top_langs: list, token_handler: TokenHandler, mo
|
|||||||
|
|
||||||
|
|
||||||
async def retry_with_fallback_models(f: Callable):
|
async def retry_with_fallback_models(f: Callable):
|
||||||
all_models = _get_all_models()
|
|
||||||
all_deployments = _get_all_deployments(all_models)
|
|
||||||
# try each (model, deployment_id) pair until one is successful, otherwise raise exception
|
|
||||||
for i, (model, deployment_id) in enumerate(zip(all_models, all_deployments)):
|
|
||||||
try:
|
|
||||||
get_settings().set("openai.deployment_id", deployment_id)
|
|
||||||
return await f(model)
|
|
||||||
except Exception as e:
|
|
||||||
logging.warning(
|
|
||||||
f"Failed to generate prediction with {model}"
|
|
||||||
f"{(' from deployment ' + deployment_id) if deployment_id else ''}: "
|
|
||||||
f"{traceback.format_exc()}"
|
|
||||||
)
|
|
||||||
if i == len(all_models) - 1: # If it's the last iteration
|
|
||||||
raise # Re-raise the last exception
|
|
||||||
|
|
||||||
|
|
||||||
def _get_all_models() -> List[str]:
|
|
||||||
model = get_settings().config.model
|
model = get_settings().config.model
|
||||||
fallback_models = get_settings().config.fallback_models
|
fallback_models = get_settings().config.fallback_models
|
||||||
if not isinstance(fallback_models, list):
|
if not isinstance(fallback_models, list):
|
||||||
fallback_models = [m.strip() for m in fallback_models.split(",")]
|
fallback_models = [fallback_models]
|
||||||
all_models = [model] + fallback_models
|
all_models = [model] + fallback_models
|
||||||
return all_models
|
for i, model in enumerate(all_models):
|
||||||
|
try:
|
||||||
|
return await f(model)
|
||||||
def _get_all_deployments(all_models: List[str]) -> List[str]:
|
except Exception as e:
|
||||||
deployment_id = get_settings().get("openai.deployment_id", None)
|
logging.warning(f"Failed to generate prediction with {model}: {traceback.format_exc()}")
|
||||||
fallback_deployments = get_settings().get("openai.fallback_deployments", [])
|
if i == len(all_models) - 1: # If it's the last iteration
|
||||||
if not isinstance(fallback_deployments, list) and fallback_deployments:
|
raise # Re-raise the last exception
|
||||||
fallback_deployments = [d.strip() for d in fallback_deployments.split(",")]
|
|
||||||
if fallback_deployments:
|
|
||||||
all_deployments = [deployment_id] + fallback_deployments
|
|
||||||
if len(all_deployments) < len(all_models):
|
|
||||||
raise ValueError(f"The number of deployments ({len(all_deployments)}) "
|
|
||||||
f"is less than the number of models ({len(all_models)})")
|
|
||||||
else:
|
|
||||||
all_deployments = [deployment_id] * len(all_models)
|
|
||||||
return all_deployments
|
|
||||||
|
|
||||||
|
|
||||||
def find_line_number_of_relevant_line_in_file(diff_files: List[FilePatchInfo],
|
def find_line_number_of_relevant_line_in_file(diff_files: List[FilePatchInfo],
|
||||||
|
@ -261,7 +261,7 @@ def update_settings_from_args(args: List[str]) -> List[str]:
|
|||||||
|
|
||||||
|
|
||||||
def load_yaml(review_text: str) -> dict:
|
def load_yaml(review_text: str) -> dict:
|
||||||
review_text = review_text.removeprefix('```yaml').rstrip('`')
|
review_text = review_text.lstrip('```yaml').rstrip('`')
|
||||||
try:
|
try:
|
||||||
data = yaml.load(review_text, Loader=yaml.SafeLoader)
|
data = yaml.load(review_text, Loader=yaml.SafeLoader)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -26,13 +26,6 @@ class BitbucketProvider:
|
|||||||
if pr_url:
|
if pr_url:
|
||||||
self.set_pr(pr_url)
|
self.set_pr(pr_url)
|
||||||
|
|
||||||
def get_repo_settings(self):
|
|
||||||
try:
|
|
||||||
contents = self.repo_obj.get_contents(".pr_agent.toml", ref=self.pr.head.sha).decoded_content
|
|
||||||
return contents
|
|
||||||
except Exception:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def is_supported(self, capability: str) -> bool:
|
def is_supported(self, capability: str) -> bool:
|
||||||
if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels']:
|
if capability in ['get_issue_comments', 'create_inline_comment', 'publish_inline_comments', 'get_labels']:
|
||||||
return False
|
return False
|
||||||
@ -100,13 +93,6 @@ class BitbucketProvider:
|
|||||||
def get_issue_comments(self):
|
def get_issue_comments(self):
|
||||||
raise NotImplementedError("Bitbucket provider does not support issue comments yet")
|
raise NotImplementedError("Bitbucket provider does not support issue comments yet")
|
||||||
|
|
||||||
def get_repo_settings(self):
|
|
||||||
try:
|
|
||||||
contents = self.repo_obj.get_contents(".pr_agent.toml", ref=self.pr.head.sha).decoded_content
|
|
||||||
return contents
|
|
||||||
except Exception:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def add_eyes_reaction(self, issue_comment_id: int) -> Optional[int]:
|
def add_eyes_reaction(self, issue_comment_id: int) -> Optional[int]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -118,7 +104,7 @@ class BitbucketProvider:
|
|||||||
parsed_url = urlparse(pr_url)
|
parsed_url = urlparse(pr_url)
|
||||||
|
|
||||||
if 'bitbucket.org' not in parsed_url.netloc:
|
if 'bitbucket.org' not in parsed_url.netloc:
|
||||||
raise ValueError("The provided URL is not a valid Bitbucket URL")
|
raise ValueError("The provided URL is not a valid GitHub URL")
|
||||||
|
|
||||||
path_parts = parsed_url.path.strip('/').split('/')
|
path_parts = parsed_url.path.strip('/').split('/')
|
||||||
|
|
||||||
|
@ -89,10 +89,6 @@ class GitProvider(ABC):
|
|||||||
def get_issue_comments(self):
|
def get_issue_comments(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def get_repo_settings(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def add_eyes_reaction(self, issue_comment_id: int) -> Optional[int]:
|
def add_eyes_reaction(self, issue_comment_id: int) -> Optional[int]:
|
||||||
pass
|
pass
|
||||||
|
@ -14,9 +14,6 @@ from .git_provider import EDIT_TYPE, FilePatchInfo, GitProvider
|
|||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
class DiffNotFoundError(Exception):
|
|
||||||
"""Raised when the diff for a merge request cannot be found."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
class GitLabProvider(GitProvider):
|
class GitLabProvider(GitProvider):
|
||||||
|
|
||||||
@ -59,7 +56,7 @@ class GitLabProvider(GitProvider):
|
|||||||
self.last_diff = self.mr.diffs.list(get_all=True)[-1]
|
self.last_diff = self.mr.diffs.list(get_all=True)[-1]
|
||||||
except IndexError as e:
|
except IndexError as e:
|
||||||
logger.error(f"Could not get diff for merge request {self.id_mr}")
|
logger.error(f"Could not get diff for merge request {self.id_mr}")
|
||||||
raise DiffNotFoundError(f"Could not get diff for merge request {self.id_mr}") from e
|
raise ValueError(f"Could not get diff for merge request {self.id_mr}") from e
|
||||||
|
|
||||||
|
|
||||||
def _get_pr_file_content(self, file_path: str, branch: str) -> str:
|
def _get_pr_file_content(self, file_path: str, branch: str) -> str:
|
||||||
@ -153,20 +150,16 @@ class GitLabProvider(GitProvider):
|
|||||||
def create_inline_comments(self, comments: list[dict]):
|
def create_inline_comments(self, comments: list[dict]):
|
||||||
raise NotImplementedError("Gitlab provider does not support publishing inline comments yet")
|
raise NotImplementedError("Gitlab provider does not support publishing inline comments yet")
|
||||||
|
|
||||||
def send_inline_comment(self,body: str,edit_type: str,found: bool,relevant_file: str,relevant_line_in_file: int,
|
def send_inline_comment(self, body, edit_type, found, relevant_file, relevant_line_in_file, source_line_no,
|
||||||
source_line_no: int, target_file: str,target_line_no: int) -> None:
|
target_file, target_line_no):
|
||||||
if not found:
|
if not found:
|
||||||
logging.info(f"Could not find position for {relevant_file} {relevant_line_in_file}")
|
logging.info(f"Could not find position for {relevant_file} {relevant_line_in_file}")
|
||||||
else:
|
else:
|
||||||
# in order to have exact sha's we have to find correct diff for this change
|
d = self.last_diff
|
||||||
diff = self.get_relevant_diff(relevant_file, relevant_line_in_file)
|
|
||||||
if diff is None:
|
|
||||||
logger.error(f"Could not get diff for merge request {self.id_mr}")
|
|
||||||
raise DiffNotFoundError(f"Could not get diff for merge request {self.id_mr}")
|
|
||||||
pos_obj = {'position_type': 'text',
|
pos_obj = {'position_type': 'text',
|
||||||
'new_path': target_file.filename,
|
'new_path': target_file.filename,
|
||||||
'old_path': target_file.old_filename if target_file.old_filename else target_file.filename,
|
'old_path': target_file.old_filename if target_file.old_filename else target_file.filename,
|
||||||
'base_sha': diff.base_commit_sha, 'start_sha': diff.start_commit_sha, 'head_sha': diff.head_commit_sha}
|
'base_sha': d.base_commit_sha, 'start_sha': d.start_commit_sha, 'head_sha': d.head_commit_sha}
|
||||||
if edit_type == 'deletion':
|
if edit_type == 'deletion':
|
||||||
pos_obj['old_line'] = source_line_no - 1
|
pos_obj['old_line'] = source_line_no - 1
|
||||||
elif edit_type == 'addition':
|
elif edit_type == 'addition':
|
||||||
@ -178,23 +171,6 @@ class GitLabProvider(GitProvider):
|
|||||||
self.mr.discussions.create({'body': body,
|
self.mr.discussions.create({'body': body,
|
||||||
'position': pos_obj})
|
'position': pos_obj})
|
||||||
|
|
||||||
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
|
|
||||||
if not changes:
|
|
||||||
logging.error('No changes found for the merge request.')
|
|
||||||
return None
|
|
||||||
all_diffs = self.mr.diffs.list(get_all=True)
|
|
||||||
if not all_diffs:
|
|
||||||
logging.error('No diffs found for the merge request.')
|
|
||||||
return None
|
|
||||||
for diff in all_diffs:
|
|
||||||
for change in changes['changes']:
|
|
||||||
if change['new_path'] == relevant_file and relevant_line_in_file in change['diff']:
|
|
||||||
return diff
|
|
||||||
logging.debug(
|
|
||||||
f'No relevant diff found for {relevant_file} {relevant_line_in_file}. Falling back to last diff.')
|
|
||||||
return self.last_diff # fallback to last_diff if no relevant diff is found
|
|
||||||
|
|
||||||
def publish_code_suggestions(self, code_suggestions: list):
|
def publish_code_suggestions(self, code_suggestions: list):
|
||||||
for suggestion in code_suggestions:
|
for suggestion in code_suggestions:
|
||||||
try:
|
try:
|
||||||
|
@ -2,9 +2,8 @@ commands_text = "> **/review [-i]**: Request a review of your Pull Request. For
|
|||||||
"considers changes since the last review, include the '-i' option.\n" \
|
"considers changes since the last review, include the '-i' option.\n" \
|
||||||
"> **/describe**: Modify the PR title and description based on the contents of the PR.\n" \
|
"> **/describe**: Modify the PR title and description based on the contents of the PR.\n" \
|
||||||
"> **/improve**: Suggest improvements to the code in the PR. \n" \
|
"> **/improve**: Suggest improvements to the code in the PR. \n" \
|
||||||
"> **/ask \\<QUESTION\\>**: Pose a question about the PR.\n" \
|
"> **/ask \\<QUESTION\\>**: Pose a question about the PR.\n\n" \
|
||||||
"> **/update_changelog**: Update the changelog based on the PR's contents.\n\n" \
|
">To edit any configuration parameter from 'configuration.toml', add --config_path=new_value\n" \
|
||||||
">To edit any configuration parameter from **configuration.toml**, add --config_path=new_value\n" \
|
|
||||||
">For example: /review --pr_reviewer.extra_instructions=\"focus on the file: ...\" \n" \
|
">For example: /review --pr_reviewer.extra_instructions=\"focus on the file: ...\" \n" \
|
||||||
">To list the possible configuration parameters, use the **/config** command.\n" \
|
">To list the possible configuration parameters, use the **/config** command.\n" \
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ key = "" # Acquire through https://platform.openai.com
|
|||||||
#api_version = '2023-05-15' # Check Azure documentation for the current API version
|
#api_version = '2023-05-15' # Check Azure documentation for the current API version
|
||||||
#api_base = "" # The base URL for your Azure OpenAI resource. e.g. "https://<your resource name>.openai.azure.com"
|
#api_base = "" # The base URL for your Azure OpenAI resource. e.g. "https://<your resource name>.openai.azure.com"
|
||||||
#deployment_id = "" # The deployment name you chose when you deployed the engine
|
#deployment_id = "" # The deployment name you chose when you deployed the engine
|
||||||
#fallback_deployments = [] # For each fallback model specified in configuration.toml in the [config] section, specify the appropriate deployment_id
|
|
||||||
|
|
||||||
[anthropic]
|
[anthropic]
|
||||||
key = "" # Optional, uncomment if you want to use Anthropic. Acquire through https://www.anthropic.com/
|
key = "" # Optional, uncomment if you want to use Anthropic. Acquire through https://www.anthropic.com/
|
||||||
|
@ -3,7 +3,7 @@ system="""You are CodiumAI-PR-Reviewer, a language model designed to review git
|
|||||||
Your task is to provide full description of the PR content.
|
Your task is to provide full description of the PR content.
|
||||||
- Make sure not to focus the new PR code (the '+' lines).
|
- Make sure not to focus the new PR code (the '+' lines).
|
||||||
- Notice that the 'Previous title', 'Previous description' and 'Commit messages' sections may be partial, simplistic, non-informative or not up-to-date. Hence, compare them to the PR diff code, and use them only as a reference.
|
- Notice that the 'Previous title', 'Previous description' and 'Commit messages' sections may be partial, simplistic, non-informative or not up-to-date. Hence, compare them to the PR diff code, and use them only as a reference.
|
||||||
- If needed, each YAML output should be in block scalar format ('|-')
|
|
||||||
{%- if extra_instructions %}
|
{%- if extra_instructions %}
|
||||||
|
|
||||||
Extra instructions from the user:
|
Extra instructions from the user:
|
||||||
@ -33,7 +33,7 @@ PR Description:
|
|||||||
PR Main Files Walkthrough:
|
PR Main Files Walkthrough:
|
||||||
type: array
|
type: array
|
||||||
maxItems: 10
|
maxItems: 10
|
||||||
description: |-
|
description: >-
|
||||||
a walkthrough of the PR changes. Review main files, and shortly describe the changes in each file (up to 10 most important files).
|
a walkthrough of the PR changes. Review main files, and shortly describe the changes in each file (up to 10 most important files).
|
||||||
items:
|
items:
|
||||||
filename:
|
filename:
|
||||||
@ -46,12 +46,10 @@ PR Main Files Walkthrough:
|
|||||||
|
|
||||||
Example output:
|
Example output:
|
||||||
```yaml
|
```yaml
|
||||||
PR Title: |-
|
PR Title: ...
|
||||||
...
|
|
||||||
PR Type:
|
PR Type:
|
||||||
- Bug fix
|
- Bug fix
|
||||||
PR Description: |-
|
PR Description: ...
|
||||||
...
|
|
||||||
PR Main Files Walkthrough:
|
PR Main Files Walkthrough:
|
||||||
- ...
|
- ...
|
||||||
- ...
|
- ...
|
||||||
|
@ -7,7 +7,6 @@ Your task is to provide constructive and concise feedback for the PR, and also p
|
|||||||
- Suggestions should focus on improving the new added code lines.
|
- Suggestions should focus on improving the new added code lines.
|
||||||
- Make sure not to provide suggestions repeating modifications already implemented in the new PR code (the '+' lines).
|
- Make sure not to provide suggestions repeating modifications already implemented in the new PR code (the '+' lines).
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
- If needed, each YAML output should be in block scalar format ('|-')
|
|
||||||
|
|
||||||
{%- if extra_instructions %}
|
{%- if extra_instructions %}
|
||||||
|
|
||||||
@ -79,7 +78,7 @@ PR Feedback:
|
|||||||
description: the relevant file full path
|
description: the relevant file full path
|
||||||
suggestion:
|
suggestion:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: >-
|
||||||
a concrete suggestion for meaningfully improving the new PR code. Also
|
a concrete suggestion for meaningfully improving the new PR code. Also
|
||||||
describe how, specifically, the suggestion can be applied to new PR
|
describe how, specifically, the suggestion can be applied to new PR
|
||||||
code. Add tags with importance measure that matches each suggestion
|
code. Add tags with importance measure that matches each suggestion
|
||||||
@ -87,10 +86,10 @@ PR Feedback:
|
|||||||
adding docstrings, renaming PR title and description, or linter like.
|
adding docstrings, renaming PR title and description, or linter like.
|
||||||
relevant line:
|
relevant line:
|
||||||
type: string
|
type: string
|
||||||
description: |
|
description: >-
|
||||||
a single code line taken from the relevant file, to which the suggestion applies.
|
a single code line taken from the relevant file, to which the
|
||||||
The line should be a '+' line.
|
suggestion applies. The line should be a '+' line. Make sure to output
|
||||||
Make sure to output the line exactly as it appears in the relevant file
|
the line exactly as it appears in the relevant file
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{%- if require_security %}
|
{%- if require_security %}
|
||||||
Security concerns:
|
Security concerns:
|
||||||
|
@ -93,10 +93,6 @@ class PRCodeSuggestions:
|
|||||||
|
|
||||||
def push_inline_code_suggestions(self, data):
|
def push_inline_code_suggestions(self, data):
|
||||||
code_suggestions = []
|
code_suggestions = []
|
||||||
|
|
||||||
if not data['Code suggestions']:
|
|
||||||
return self.git_provider.publish_comment('No suggestions found to improve this PR.')
|
|
||||||
|
|
||||||
for d in data['Code suggestions']:
|
for d in data['Code suggestions']:
|
||||||
try:
|
try:
|
||||||
if get_settings().config.verbosity_level >= 2:
|
if get_settings().config.verbosity_level >= 2:
|
||||||
|
@ -237,7 +237,7 @@ class PRReviewer:
|
|||||||
return
|
return
|
||||||
|
|
||||||
review_text = self.prediction.strip()
|
review_text = self.prediction.strip()
|
||||||
review_text = review_text.removeprefix('```yaml').rstrip('`')
|
review_text = review_text.lstrip('```yaml').rstrip('`')
|
||||||
try:
|
try:
|
||||||
data = yaml.load(review_text, Loader=SafeLoader)
|
data = yaml.load(review_text, Loader=SafeLoader)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
from pr_agent.git_providers.bitbucket_provider import BitbucketProvider
|
|
||||||
|
|
||||||
|
|
||||||
class TestBitbucketProvider:
|
|
||||||
def test_parse_pr_url(self):
|
|
||||||
url = "https://bitbucket.org/WORKSPACE_XYZ/MY_TEST_REPO/pull-requests/321"
|
|
||||||
workspace_slug, repo_slug, pr_number = BitbucketProvider._parse_pr_url(url)
|
|
||||||
assert workspace_slug == "WORKSPACE_XYZ"
|
|
||||||
assert repo_slug == "MY_TEST_REPO"
|
|
||||||
assert pr_number == 321
|
|
Reference in New Issue
Block a user