From 34096059ff746cccddf3a42a58b9960a786dec11 Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 13:05:56 +0300 Subject: [PATCH 01/33] quick and dirty response for github API ratelimit, until some smart solution will be implemented --- pr_agent/algo/pr_processing.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 20933d51..bfe72387 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -10,6 +10,7 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider +from github import GithubException DELETED_FILES_ = "Deleted files:\n" @@ -40,7 +41,10 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s global PATCH_EXTRA_LINES PATCH_EXTRA_LINES = 0 - diff_files = list(git_provider.get_diff_files()) + try: + diff_files = list(git_provider.get_diff_files()) + except GithubException.RateLimitExceededException as e: + logging.warning("Rate limit exceeded for GitHub API.") # get pr languages pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files) From 8f482cd41ab2c2b5f2a83f82e8440cff4f7b0748 Mon Sep 17 00:00:00 2001 From: Ilya Davidov Date: Tue, 25 Jul 2023 13:23:19 +0300 Subject: [PATCH 02/33] show how much time until rate limit reset Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- pr_agent/algo/pr_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index bfe72387..c55ef138 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -44,7 +44,7 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s try: diff_files = list(git_provider.get_diff_files()) except GithubException.RateLimitExceededException as e: - logging.warning("Rate limit exceeded for GitHub API.") + logging.warning(f"Rate limit exceeded for GitHub API. The rate limit will be reset at {e.reset}.") # get pr languages pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files) From d1a8a610e942457a9cff22afb34af1c7ad4aadab Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 13:38:55 +0300 Subject: [PATCH 03/33] Revert "show how much time until rate limit reset" This reverts commit 8f482cd41ab2c2b5f2a83f82e8440cff4f7b0748. --- pr_agent/algo/pr_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index c55ef138..bfe72387 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -44,7 +44,7 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s try: diff_files = list(git_provider.get_diff_files()) except GithubException.RateLimitExceededException as e: - logging.warning(f"Rate limit exceeded for GitHub API. The rate limit will be reset at {e.reset}.") + logging.warning("Rate limit exceeded for GitHub API.") # get pr languages pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files) From 404cc0a00e84d50dc8cc8b5162fe0003362c15fd Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 14:20:20 +0300 Subject: [PATCH 04/33] small change to show message and fail --- pr_agent/algo/pr_processing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index bfe72387..491d3e92 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -44,7 +44,7 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s try: diff_files = list(git_provider.get_diff_files()) except GithubException.RateLimitExceededException as e: - logging.warning("Rate limit exceeded for GitHub API.") + logging.error(f"Rate limit exceeded for GitHub API. original message {e}") # get pr languages pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files) From 55637a5620af65d813eaeb7d3ffbd97fde0d7eb8 Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 14:42:54 +0300 Subject: [PATCH 05/33] added retry decorator similar to used in ai_handler following @okotek suggestion --- pr_agent/algo/pr_processing.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 491d3e92..17ea2356 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -11,6 +11,7 @@ from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider from github import GithubException +from retry import retry DELETED_FILES_ = "Deleted files:\n" @@ -20,7 +21,10 @@ OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD = 1000 OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD = 600 PATCH_EXTRA_LINES = 3 +GITHUB_RETRIES=1 +@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError, GithubException.RateLimitExceededException), + tries=GITHUB_RETRIES, delay=2, backoff=2, jitter=(1, 3)) def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: str, add_line_numbers_to_hunks: bool = False, disable_extra_lines: bool = False) -> str: """ @@ -45,6 +49,7 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s diff_files = list(git_provider.get_diff_files()) except GithubException.RateLimitExceededException as e: logging.error(f"Rate limit exceeded for GitHub API. original message {e}") + raise # get pr languages pr_languages = sort_files_by_main_languages(git_provider.get_languages(), diff_files) From b6f6c903a042f2bf65d91af940d03017960ae6df Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 15:12:02 +0300 Subject: [PATCH 06/33] moved @retry to github_provider.py and fetch number of retries from settings --- pr_agent/algo/pr_processing.py | 7 +------ pr_agent/git_providers/github_provider.py | 6 ++++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 17ea2356..1e96d2f1 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -10,8 +10,7 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider -from github import GithubException -from retry import retry + DELETED_FILES_ = "Deleted files:\n" @@ -21,10 +20,6 @@ OUTPUT_BUFFER_TOKENS_SOFT_THRESHOLD = 1000 OUTPUT_BUFFER_TOKENS_HARD_THRESHOLD = 600 PATCH_EXTRA_LINES = 3 -GITHUB_RETRIES=1 - -@retry(exceptions=(APIError, Timeout, TryAgain, AttributeError, RateLimitError, GithubException.RateLimitExceededException), - tries=GITHUB_RETRIES, delay=2, backoff=2, jitter=(1, 3)) def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: str, add_line_numbers_to_hunks: bool = False, disable_extra_lines: bool = False) -> str: """ diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 10a50412..64e4ab46 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -3,14 +3,14 @@ from datetime import datetime from typing import Optional, Tuple from urllib.parse import urlparse -from github import AppAuthentication, Github, Auth +from github import AppAuthentication, Github, Auth, GithubException from pr_agent.config_loader import settings from .git_provider import FilePatchInfo, GitProvider, IncrementalPR from ..algo.language_handler import is_valid_file from ..algo.utils import load_large_diff - +from retry import retry class GithubProvider(GitProvider): def __init__(self, pr_url: Optional[str] = None, incremental=IncrementalPR(False)): @@ -78,6 +78,8 @@ class GithubProvider(GitProvider): return self.file_set.values() return self.pr.get_files() + @retry(exceptions=(GithubException.RateLimitExceededException), + tries=settings.github.ratelimit_retries, delay=2, backoff=2, jitter=(1, 3)) def get_diff_files(self) -> list[FilePatchInfo]: files = self.get_files() diff_files = [] From 3b334805eec8870df0eb7dbd1758fdee37f776be Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 15:14:56 +0300 Subject: [PATCH 07/33] still need GithubException.RateLimitExceededException in pr_processing.py for correct exception catch --- pr_agent/algo/pr_processing.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 1e96d2f1..59b342a0 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -10,6 +10,7 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider +from github import GithubException DELETED_FILES_ = "Deleted files:\n" From f6036e936ee0dbdad268314064af8586f8d6cdbc Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 15:23:40 +0300 Subject: [PATCH 08/33] + settings.github.ratelimit_retries setup in configuration.toml --- pr_agent/settings/configuration.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 3861df13..186b0855 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -27,6 +27,7 @@ num_code_suggestions=4 [github] # The type of deployment to create. Valid values are 'app' or 'user'. deployment_type = "user" +ratelimit_retries = 5 [gitlab] # URL to the gitlab service From 1229fba346664d5d347a15571a0066f8e55ea9e6 Mon Sep 17 00:00:00 2001 From: idavidov Date: Tue, 25 Jul 2023 16:37:13 +0300 Subject: [PATCH 09/33] + settings.github.ratelimit_retries setup in configuration.toml --- pr_agent/algo/pr_processing.py | 7 ++-- pr_agent/git_providers/git_provider.py | 5 +++ pr_agent/git_providers/github_provider.py | 44 +++++++++++++---------- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 59b342a0..94cc1a00 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -10,8 +10,7 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider -from github import GithubException - +from git_provider import RateLimitExceeded DELETED_FILES_ = "Deleted files:\n" @@ -43,8 +42,8 @@ def get_pr_diff(git_provider: GitProvider, token_handler: TokenHandler, model: s try: diff_files = list(git_provider.get_diff_files()) - except GithubException.RateLimitExceededException as e: - logging.error(f"Rate limit exceeded for GitHub API. original message {e}") + except RateLimitExceededException as e: + logging.error(f"Rate limit exceeded for git provider API. original message {e}") raise # get pr languages diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 3f7c1ef2..97dbe8ab 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -136,3 +136,8 @@ class IncrementalPR: self.commits_range = None self.first_new_commit_sha = None self.last_seen_commit_sha = None + + +class RateLimitExceeded(Exception): + """Raised when the git provider API rate limit has been exceeded.""" + pass \ No newline at end of file diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index 64e4ab46..a9979a78 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -4,6 +4,7 @@ from typing import Optional, Tuple from urllib.parse import urlparse from github import AppAuthentication, Github, Auth, GithubException +from git_provider import RateLimitExceeded from pr_agent.config_loader import settings @@ -81,26 +82,31 @@ class GithubProvider(GitProvider): @retry(exceptions=(GithubException.RateLimitExceededException), tries=settings.github.ratelimit_retries, delay=2, backoff=2, jitter=(1, 3)) def get_diff_files(self) -> list[FilePatchInfo]: - files = self.get_files() - diff_files = [] - for file in files: - if is_valid_file(file.filename): - new_file_content_str = self._get_pr_file_content(file, self.pr.head.sha) - patch = file.patch - if self.incremental.is_incremental and self.file_set: - original_file_content_str = self._get_pr_file_content(file, self.incremental.last_seen_commit_sha) - patch = load_large_diff(file, - new_file_content_str, - original_file_content_str, - None) - self.file_set[file.filename] = patch - else: - original_file_content_str = self._get_pr_file_content(file, self.pr.base.sha) + try: + files = self.get_files() + diff_files = [] + for file in files: + if is_valid_file(file.filename): + new_file_content_str = self._get_pr_file_content(file, self.pr.head.sha) + patch = file.patch + if self.incremental.is_incremental and self.file_set: + original_file_content_str = self._get_pr_file_content(file, + self.incremental.last_seen_commit_sha) + patch = load_large_diff(file, + new_file_content_str, + original_file_content_str, + None) + self.file_set[file.filename] = patch + else: + original_file_content_str = self._get_pr_file_content(file, self.pr.base.sha) - diff_files.append( - FilePatchInfo(original_file_content_str, new_file_content_str, patch, file.filename)) - self.diff_files = diff_files - return diff_files + diff_files.append( + FilePatchInfo(original_file_content_str, new_file_content_str, patch, file.filename)) + self.diff_files = diff_files + return diff_files + except GithubException.RateLimitExceededException as e: + logging.error(f"Rate limit exceeded for GitHub API. Original message: {e}") + raise RateLimitExceeded("Rate limit exceeded for GitHub API.") from e def publish_description(self, pr_title: str, pr_body: str): self.pr.edit(title=pr_title, body=pr_body) From 8ae5faca530a62910525f1fa69071183af6d42e3 Mon Sep 17 00:00:00 2001 From: Ori Kotek Date: Tue, 25 Jul 2023 16:52:18 +0300 Subject: [PATCH 10/33] Fix cyclic dependency --- pr_agent/algo/pr_processing.py | 3 ++- pr_agent/git_providers/git_provider.py | 4 ---- pr_agent/git_providers/github_provider.py | 11 ++++++----- pr_agent/servers/utils.py | 4 ++++ 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/pr_agent/algo/pr_processing.py b/pr_agent/algo/pr_processing.py index 94cc1a00..45ef40b2 100644 --- a/pr_agent/algo/pr_processing.py +++ b/pr_agent/algo/pr_processing.py @@ -3,6 +3,8 @@ from __future__ import annotations import logging from typing import Tuple, Union, Callable, List +from github import RateLimitExceededException + from pr_agent.algo import MAX_TOKENS from pr_agent.algo.git_patch_processing import convert_to_hunks_with_lines_numbers, extend_patch, handle_patch_deletions from pr_agent.algo.language_handler import sort_files_by_main_languages @@ -10,7 +12,6 @@ from pr_agent.algo.token_handler import TokenHandler from pr_agent.algo.utils import load_large_diff from pr_agent.config_loader import settings from pr_agent.git_providers.git_provider import GitProvider -from git_provider import RateLimitExceeded DELETED_FILES_ = "Deleted files:\n" diff --git a/pr_agent/git_providers/git_provider.py b/pr_agent/git_providers/git_provider.py index 97dbe8ab..677c2eb1 100644 --- a/pr_agent/git_providers/git_provider.py +++ b/pr_agent/git_providers/git_provider.py @@ -137,7 +137,3 @@ class IncrementalPR: self.first_new_commit_sha = None self.last_seen_commit_sha = None - -class RateLimitExceeded(Exception): - """Raised when the git provider API rate limit has been exceeded.""" - pass \ No newline at end of file diff --git a/pr_agent/git_providers/github_provider.py b/pr_agent/git_providers/github_provider.py index a9979a78..7f617937 100644 --- a/pr_agent/git_providers/github_provider.py +++ b/pr_agent/git_providers/github_provider.py @@ -3,15 +3,16 @@ from datetime import datetime from typing import Optional, Tuple from urllib.parse import urlparse -from github import AppAuthentication, Github, Auth, GithubException -from git_provider import RateLimitExceeded +from github import AppAuthentication, Auth, Github, GithubException +from retry import retry from pr_agent.config_loader import settings -from .git_provider import FilePatchInfo, GitProvider, IncrementalPR from ..algo.language_handler import is_valid_file from ..algo.utils import load_large_diff -from retry import retry +from .git_provider import FilePatchInfo, GitProvider, IncrementalPR +from ..servers.utils import RateLimitExceeded + class GithubProvider(GitProvider): def __init__(self, pr_url: Optional[str] = None, incremental=IncrementalPR(False)): @@ -79,7 +80,7 @@ class GithubProvider(GitProvider): return self.file_set.values() return self.pr.get_files() - @retry(exceptions=(GithubException.RateLimitExceededException), + @retry(exceptions=RateLimitExceeded, tries=settings.github.ratelimit_retries, delay=2, backoff=2, jitter=(1, 3)) def get_diff_files(self) -> list[FilePatchInfo]: try: diff --git a/pr_agent/servers/utils.py b/pr_agent/servers/utils.py index 942ac449..c24b880c 100644 --- a/pr_agent/servers/utils.py +++ b/pr_agent/servers/utils.py @@ -21,3 +21,7 @@ def verify_signature(payload_body, secret_token, signature_header): if not hmac.compare_digest(expected_signature, signature_header): raise HTTPException(status_code=403, detail="Request signatures didn't match!") + +class RateLimitExceeded(Exception): + """Raised when the git provider API rate limit has been exceeded.""" + pass From e3846a480e9de360733132aef4fc5a949c388169 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 09:21:31 +0300 Subject: [PATCH 11/33] s --- CHANGELOG.md | 8 ++ pr_agent/cli.py | 12 ++- pr_agent/config_loader.py | 1 + pr_agent/settings/configuration.toml | 9 +- pr_agent/settings/pr_update_changelog.toml | 30 ++++++ pr_agent/tools/pr_update_changelog.py | 103 +++++++++++++++++++++ 6 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 pr_agent/settings/pr_update_changelog.toml create mode 100644 pr_agent/tools/pr_update_changelog.py diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..12dfefea --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +## [Unreleased] - 2023-07-23 + +### Added +- '/describe' operation now updates also the label of the PR + +### Changed + +### Fixed \ No newline at end of file diff --git a/pr_agent/cli.py b/pr_agent/cli.py index 4477016c..2bae6bd4 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -8,6 +8,7 @@ from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_information_from_user import PRInformationFromUser from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer +from pr_agent.tools.pr_update_changelog import PRUpdateChangelog def run(args=None): @@ -27,13 +28,15 @@ ask / ask_question [question] - Ask a question about the PR. describe / describe_pr - Modify the PR title and description based on the PR's contents. improve / improve_code - Suggest improvements to the code in the PR as pull request comments ready to commit. reflect - Ask the PR author questions about the PR. +update_changelog - Update the changelog based on the PR's contents. """) parser.add_argument('--pr_url', type=str, help='The URL of the PR to review', required=True) parser.add_argument('command', type=str, help='The', choices=['review', 'review_pr', 'ask', 'ask_question', 'describe', 'describe_pr', 'improve', 'improve_code', - 'reflect', 'review_after_reflect'], + 'reflect', 'review_after_reflect', + 'update_changelog'], default='review') parser.add_argument('rest', nargs=argparse.REMAINDER, default=[]) args = parser.parse_args(args) @@ -49,7 +52,8 @@ reflect - Ask the PR author questions about the PR. 'review': _handle_review_command, 'review_pr': _handle_review_command, 'reflect': _handle_reflect_command, - 'review_after_reflect': _handle_review_after_reflect_command + 'review_after_reflect': _handle_review_after_reflect_command, + 'update_changelog': _handle_update_changelog, } if command in commands: commands[command](args.pr_url, args.rest) @@ -96,6 +100,10 @@ def _handle_review_after_reflect_command(pr_url: str, rest: list): reviewer = PRReviewer(pr_url, cli_mode=True, is_answer=True) asyncio.run(reviewer.review()) +def _handle_update_changelog(pr_url: str, rest: list): + print(f"Updating changlog for: {pr_url}") + reviewer = PRUpdateChangelog(pr_url, cli_mode=True) + asyncio.run(reviewer.update_changelog()) if __name__ == '__main__': run() diff --git a/pr_agent/config_loader.py b/pr_agent/config_loader.py index 7841f0b7..69d20d88 100644 --- a/pr_agent/config_loader.py +++ b/pr_agent/config_loader.py @@ -19,6 +19,7 @@ settings = Dynaconf( "settings/pr_description_prompts.toml", "settings/pr_code_suggestions_prompts.toml", "settings/pr_information_from_user_prompts.toml", + "settings/pr_update_changelog.toml", "settings_prod/.secrets.toml" ]] ) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index fbf8ffec..33b84f79 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -1,10 +1,10 @@ [config] model="gpt-4" -fallback-models=["gpt-3.5-turbo-16k", "gpt-3.5-turbo"] +fallback-models=["gpt-3.5-turbo-16k"] git_provider="github" -publish_output=true +publish_output=false publish_output_progress=true -verbosity_level=0 # 0,1,2 +verbosity_level=2 # 0,1,2 use_extra_bad_extensions=false [pr_reviewer] @@ -24,6 +24,9 @@ publish_description_as_comment=false [pr_code_suggestions] num_code_suggestions=4 +[pr_update_changelog] +push_changelog_changes=false + [github] # The type of deployment to create. Valid values are 'app' or 'user'. deployment_type = "user" diff --git a/pr_agent/settings/pr_update_changelog.toml b/pr_agent/settings/pr_update_changelog.toml new file mode 100644 index 00000000..9fb386ea --- /dev/null +++ b/pr_agent/settings/pr_update_changelog.toml @@ -0,0 +1,30 @@ +[pr_update_changelog_prompt] +system="""You are a language model called CodiumAI-PR-Code-Reviewer. +Your task is to update the CHANGELOG.md file of the project, based on the PR diff. +The update should be short and concise. It should match the existing CHANGELOG.md format. + +Note that the output should be only the added lines to the CHANGELOG.md file, and nothing else. + +""" + +user="""PR Info: +Title: '{{title}}' +Branch: '{{branch}}' +Description: '{{description}}' +{%- if language %} +Main language: {{language}} +{%- endif %} + + +The PR Diff: +``` +{{diff}} +``` + +The current CHANGELOG.md: +``` +{{changelog_file}} +``` + +Response: +""" diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py new file mode 100644 index 00000000..71e5e6e7 --- /dev/null +++ b/pr_agent/tools/pr_update_changelog.py @@ -0,0 +1,103 @@ +import copy +import json +import logging +import textwrap +from typing import Tuple + +from jinja2 import Environment, StrictUndefined + +from pr_agent.algo.ai_handler import AiHandler +from pr_agent.algo.pr_processing import get_pr_diff, retry_with_fallback_models +from pr_agent.algo.token_handler import TokenHandler +from pr_agent.config_loader import settings +from pr_agent.git_providers import get_git_provider, GithubProvider +from pr_agent.git_providers.git_provider import get_main_pr_language + + +class PRUpdateChangelog: + def __init__(self, pr_url: str, cli_mode=False): + + self.git_provider = get_git_provider()(pr_url) + self.main_language = get_main_pr_language( + self.git_provider.get_languages(), self.git_provider.get_files() + ) + max_lines=50 + try: + self.changelog_file = self.git_provider.repo_obj.get_contents("CHANGELOG.md", ref=self.git_provider.get_pr_branch()) + changelog_file_lines = self.changelog_file.decoded_content.decode().splitlines() + changelog_file_lines = changelog_file_lines[:max_lines] + self.changelog_file_str = "\n".join(changelog_file_lines) + except: + raise Exception("No CHANGELOG.md file found in the repository") + + self.ai_handler = AiHandler() + self.patches_diff = None + self.prediction = None + self.cli_mode = cli_mode + self.vars = { + "title": self.git_provider.pr.title, + "branch": self.git_provider.get_pr_branch(), + "description": self.git_provider.get_pr_description(), + "language": self.main_language, + "diff": "", # empty diff for initial calculation + "changelog_file": self.changelog_file_str, + } + self.token_handler = TokenHandler(self.git_provider.pr, + self.vars, + settings.pr_update_changelog_prompt.system, + settings.pr_update_changelog_prompt.user) + + async def update_changelog(self): + assert type(self.git_provider) == GithubProvider, "Currently only Github is supported" + + logging.info('Updating the changelog...') + if settings.config.publish_output: + self.git_provider.publish_comment("Preparing changelog updates...", is_temporary=True) + await retry_with_fallback_models(self._prepare_prediction) + logging.info('Preparing PR changelog updates...') + new_file_content, answer = self._prepare_changelog_update() + if settings.config.publish_output or True: + self.git_provider.remove_initial_comment() + logging.info('publishing changelog updates...') + self.git_provider.publish_comment(f"**Changelog updates:**\n\n{answer}") + if settings.pr_update_changelog_prompt.push_changelog_changes: + logging.info('Pushing PR changelog updates...') + self.push_changelog_update(new_file_content) + + async def _prepare_prediction(self, model: str): + logging.info('Getting PR diff...') + # we are using extended hunk with line numbers for code suggestions + self.patches_diff = get_pr_diff(self.git_provider, + self.token_handler, + model, + add_line_numbers_to_hunks=True, + disable_extra_lines=True) + logging.info('Getting AI prediction...') + self.prediction = await self._get_prediction(model) + + async def _get_prediction(self, model: str): + variables = copy.deepcopy(self.vars) + variables["diff"] = self.patches_diff # update diff + environment = Environment(undefined=StrictUndefined) + system_prompt = environment.from_string(settings.pr_update_changelog_prompt.system).render(variables) + user_prompt = environment.from_string(settings.pr_update_changelog_prompt.user).render(variables) + if settings.config.verbosity_level >= 2: + logging.info(f"\nSystem prompt:\n{system_prompt}") + logging.info(f"\nUser prompt:\n{user_prompt}") + response, finish_reason = await self.ai_handler.chat_completion(model=model, temperature=0.2, + system=system_prompt, user=user_prompt) + + return response + + def _prepare_changelog_update(self) -> Tuple[str,str]: + answer = self.prediction.strip().strip("```").strip() + new_file_content = answer.strip().strip("```").strip() + "\n\n" + self.changelog_file.decoded_content.decode() + + return new_file_content, answer + + def push_changelog_update(self, new_file_content): + self.git_provider.repo_obj.update_file(path=self.changelog_file.path, + message="Update CHANGELOG.md", + content=new_file_content, + sha=self.changelog_file.sha, + branch=self.git_provider.get_pr_branch()) \ No newline at end of file From 99d53af28d70344c1967b72ac6e3874d4b4bf90b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 09:50:21 +0300 Subject: [PATCH 12/33] Update CHANGELOG.md --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12dfefea..09d1463d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,14 @@ +## [Unreleased] - 2023-07-26 + +### Added +- New feature to update the changelog based on the contents of a pull request for the Github provider. +- Added 'update_changelog' command to the list of supported commands in the CLI. +- New configuration settings and prompts for the changelog update feature. +- New class `PRUpdateChangelog` in `pr_update_changelog.py` for handling changelog updates. + +### Changed +- Updated configuration settings in `configuration.toml` to include settings for the new feature. + ## [Unreleased] - 2023-07-23 ### Added From ccde68293f2a98a86058bfcbc161745d66506692 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 10:09:01 +0300 Subject: [PATCH 13/33] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 173b7844..6985d50d 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull | | Auto-Description | :white_check_mark: | :white_check_mark: | | | | Improve Code | :white_check_mark: | :white_check_mark: | | | | Reflect and Review | :white_check_mark: | | | +| | Update CHANGELOG.md | :white_check_mark: | | | | | | | | | | USAGE | CLI | :white_check_mark: | :white_check_mark: | :white_check_mark: | | | App / webhook | :white_check_mark: | :white_check_mark: | | @@ -98,6 +99,7 @@ Examples for invoking the different tools via the CLI: - **Improve**: python cli.py --pr-url= improve - **Ask**: python cli.py --pr-url= ask "Write me a poem about this PR" - **Reflect**: python cli.py --pr-url= reflect +- **Update changelog**: python cli.py --pr-url= update_changelog "" is the url of the relevant PR (for example: https://github.com/Codium-ai/pr-agent/pull/50). From ea6e1811c156f61761e92ef6f312e372833acac0 Mon Sep 17 00:00:00 2001 From: Patryk Kowalski Date: Wed, 26 Jul 2023 14:15:50 +0200 Subject: [PATCH 14/33] Fixed PR title - should be feature branch name, not target branch name --- pr_agent/git_providers/local_git_provider.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index 4a7775ac..9c826777 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -167,7 +167,7 @@ class LocalGitProvider(GitProvider): """ Substitutes the branch-name as the PR-mimic title. """ - return self.target_branch_name + return self.head_branch_name def get_issue_comments(self): raise NotImplementedError('Getting issue comments is not implemented for the local git provider') From 3b19827ae212c8031574a41f05ca5c6c86573acd Mon Sep 17 00:00:00 2001 From: Patryk Kowalski Date: Wed, 26 Jul 2023 15:29:09 +0200 Subject: [PATCH 15/33] Add validation for repository path --- pr_agent/git_providers/local_git_provider.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pr_agent/git_providers/local_git_provider.py b/pr_agent/git_providers/local_git_provider.py index 9c826777..304417ea 100644 --- a/pr_agent/git_providers/local_git_provider.py +++ b/pr_agent/git_providers/local_git_provider.py @@ -30,6 +30,8 @@ class LocalGitProvider(GitProvider): def __init__(self, target_branch_name, incremental=False): self.repo_path = _find_repository_root() + if self.repo_path is None: + raise ValueError('Could not find repository root') self.repo = Repo(self.repo_path) self.head_branch_name = self.repo.head.ref.name self.target_branch_name = target_branch_name From 7531ccd31fb66daeee369f5aa4ac37ec0b400357 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 16:29:42 +0300 Subject: [PATCH 16/33] stable --- pr_agent/settings/configuration.toml | 4 ++-- pr_agent/settings/pr_update_changelog.toml | 13 ++++++++--- pr_agent/tools/pr_update_changelog.py | 26 ++++++++++++++-------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 33b84f79..637d7394 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -2,7 +2,7 @@ model="gpt-4" fallback-models=["gpt-3.5-turbo-16k"] git_provider="github" -publish_output=false +publish_output=true publish_output_progress=true verbosity_level=2 # 0,1,2 use_extra_bad_extensions=false @@ -25,7 +25,7 @@ publish_description_as_comment=false num_code_suggestions=4 [pr_update_changelog] -push_changelog_changes=false +push_changelog_changes=true [github] # The type of deployment to create. Valid values are 'app' or 'user'. diff --git a/pr_agent/settings/pr_update_changelog.toml b/pr_agent/settings/pr_update_changelog.toml index 9fb386ea..802543a8 100644 --- a/pr_agent/settings/pr_update_changelog.toml +++ b/pr_agent/settings/pr_update_changelog.toml @@ -1,9 +1,11 @@ [pr_update_changelog_prompt] system="""You are a language model called CodiumAI-PR-Code-Reviewer. -Your task is to update the CHANGELOG.md file of the project, based on the PR diff. -The update should be short and concise. It should match the existing CHANGELOG.md format. +Your task is to update the CHANGELOG.md file of the project, to reflect the changes in this PR. +The updated content should be short and concise as possible. +It should match the existing CHANGELOG.md format, style and conventions, so it will look like a natural part of the file. +For example, if previous changes were summarized in a single line, you should do the same. -Note that the output should be only the added lines to the CHANGELOG.md file, and nothing else. +Don't repeat previous changes. Generate content that is not already in the CHANGELOG.md file. """ @@ -21,6 +23,11 @@ The PR Diff: {{diff}} ``` +Current date: +``` +{{today}} +``` + The current CHANGELOG.md: ``` {{changelog_file}} diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index 71e5e6e7..72234b3f 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -2,6 +2,7 @@ import copy import json import logging import textwrap +from datetime import date from typing import Tuple from jinja2 import Environment, StrictUndefined @@ -13,6 +14,8 @@ from pr_agent.config_loader import settings from pr_agent.git_providers import get_git_provider, GithubProvider from pr_agent.git_providers.git_provider import get_main_pr_language +CHANGELOG_LINES = 50 + class PRUpdateChangelog: def __init__(self, pr_url: str, cli_mode=False): @@ -21,15 +24,18 @@ class PRUpdateChangelog: self.main_language = get_main_pr_language( self.git_provider.get_languages(), self.git_provider.get_files() ) - max_lines=50 try: - self.changelog_file = self.git_provider.repo_obj.get_contents("CHANGELOG.md", ref=self.git_provider.get_pr_branch()) + self.changelog_file = self.git_provider.repo_obj.get_contents("CHANGELOG.md", + ref=self.git_provider.get_pr_branch()) changelog_file_lines = self.changelog_file.decoded_content.decode().splitlines() - changelog_file_lines = changelog_file_lines[:max_lines] + changelog_file_lines = changelog_file_lines[:CHANGELOG_LINES] self.changelog_file_str = "\n".join(changelog_file_lines) except: raise Exception("No CHANGELOG.md file found in the repository") + today = date.today() + print("Today's date:", today) + self.ai_handler = AiHandler() self.patches_diff = None self.prediction = None @@ -41,6 +47,7 @@ class PRUpdateChangelog: "language": self.main_language, "diff": "", # empty diff for initial calculation "changelog_file": self.changelog_file_str, + "today": today, } self.token_handler = TokenHandler(self.git_provider.pr, self.vars, @@ -56,11 +63,11 @@ class PRUpdateChangelog: await retry_with_fallback_models(self._prepare_prediction) logging.info('Preparing PR changelog updates...') new_file_content, answer = self._prepare_changelog_update() - if settings.config.publish_output or True: + if settings.config.publish_output: self.git_provider.remove_initial_comment() logging.info('publishing changelog updates...') self.git_provider.publish_comment(f"**Changelog updates:**\n\n{answer}") - if settings.pr_update_changelog_prompt.push_changelog_changes: + if settings.pr_update_changelog.push_changelog_changes: logging.info('Pushing PR changelog updates...') self.push_changelog_update(new_file_content) @@ -89,10 +96,11 @@ class PRUpdateChangelog: return response - def _prepare_changelog_update(self) -> Tuple[str,str]: + def _prepare_changelog_update(self) -> Tuple[str, str]: answer = self.prediction.strip().strip("```").strip() - new_file_content = answer.strip().strip("```").strip() + "\n\n" + self.changelog_file.decoded_content.decode() - + new_file_content = answer + "\n\n" + self.changelog_file.decoded_content.decode() + if settings.config.verbosity_level >= 2: + logging.info(f"answer:\n{answer}") return new_file_content, answer def push_changelog_update(self, new_file_content): @@ -100,4 +108,4 @@ class PRUpdateChangelog: message="Update CHANGELOG.md", content=new_file_content, sha=self.changelog_file.sha, - branch=self.git_provider.get_pr_branch()) \ No newline at end of file + branch=self.git_provider.get_pr_branch()) From 1bd47b0d532a7b25d8d951ee37025f7db24628ee Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 17:24:03 +0300 Subject: [PATCH 17/33] enhance pr_reviewer.py code --- pr_agent/tools/pr_reviewer.py | 144 ++++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 34 deletions(-) diff --git a/pr_agent/tools/pr_reviewer.py b/pr_agent/tools/pr_reviewer.py index 0bf952dd..318b3c5e 100644 --- a/pr_agent/tools/pr_reviewer.py +++ b/pr_agent/tools/pr_reviewer.py @@ -2,6 +2,7 @@ import copy import json import logging from collections import OrderedDict +from typing import Tuple, List from jinja2 import Environment, StrictUndefined @@ -16,7 +17,19 @@ from pr_agent.servers.help import actions_help_text, bot_help_text class PRReviewer: - def __init__(self, pr_url: str, cli_mode=False, is_answer: bool = False, args=None): + """ + The PRReviewer class is responsible for reviewing a pull request and generating feedback using an AI model. + """ + def __init__(self, pr_url: str, cli_mode: bool = False, is_answer: bool = False, args: list = None): + """ + 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. + cli_mode (bool, optional): Indicates whether the review is being done in command-line interface mode. Defaults to False. + is_answer (bool, optional): Indicates whether the review is being done in answer mode. Defaults to False. + args (list, optional): List of arguments passed to the PRReviewer class. Defaults to None. + """ self.parse_args(args) self.git_provider = get_git_provider()(pr_url, incremental=self.incremental) @@ -25,13 +38,15 @@ class PRReviewer: ) self.pr_url = pr_url self.is_answer = is_answer + if self.is_answer and not self.git_provider.is_supported("get_issue_comments"): raise Exception(f"Answer mode is not supported for {settings.config.git_provider} for now") - answer_str, question_str = self._get_user_answers() self.ai_handler = AiHandler() self.patches_diff = None self.prediction = None self.cli_mode = cli_mode + + answer_str, question_str = self._get_user_answers() self.vars = { "title": self.git_provider.pr.title, "branch": self.git_provider.get_pr_branch(), @@ -43,16 +58,27 @@ class PRReviewer: "require_security": settings.pr_reviewer.require_security_review, "require_focused": settings.pr_reviewer.require_focused_review, 'num_code_suggestions': settings.pr_reviewer.num_code_suggestions, - # 'question_str': question_str, 'answer_str': answer_str, } - self.token_handler = TokenHandler(self.git_provider.pr, - self.vars, - settings.pr_review_prompt.system, - settings.pr_review_prompt.user) - def parse_args(self, args): + self.token_handler = TokenHandler( + self.git_provider.pr, + self.vars, + settings.pr_review_prompt.system, + settings.pr_review_prompt.user + ) + + 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 + """ is_incremental = False if args and len(args) >= 1: arg = args[0] @@ -60,60 +86,93 @@ class PRReviewer: is_incremental = True self.incremental = IncrementalPR(is_incremental) - async def review(self): + async def review(self) -> None: + """ + Review the pull request and generate feedback. + """ logging.info('Reviewing PR...') + if settings.config.publish_output: self.git_provider.publish_comment("Preparing review...", is_temporary=True) + await retry_with_fallback_models(self._prepare_prediction) + logging.info('Preparing PR review...') pr_comment = self._prepare_pr_review() + if settings.config.publish_output: logging.info('Pushing PR review...') self.git_provider.publish_comment(pr_comment) self.git_provider.remove_initial_comment() + if settings.pr_reviewer.inline_code_comments: logging.info('Pushing inline code comments...') self._publish_inline_code_comments() - return "" - async def _prepare_prediction(self, model: str): + async def _prepare_prediction(self, model: str) -> None: + """ + Prepare the AI prediction for the pull request review. + + Args: + model: A string representing the AI model to be used for the prediction. + + Returns: + None + """ logging.info('Getting PR diff...') self.patches_diff = get_pr_diff(self.git_provider, self.token_handler, model) logging.info('Getting AI prediction...') self.prediction = await self._get_prediction(model) - async def _get_prediction(self, model: str): + 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. + """ variables = copy.deepcopy(self.vars) variables["diff"] = self.patches_diff # update diff + environment = Environment(undefined=StrictUndefined) system_prompt = environment.from_string(settings.pr_review_prompt.system).render(variables) user_prompt = environment.from_string(settings.pr_review_prompt.user).render(variables) + if settings.config.verbosity_level >= 2: logging.info(f"\nSystem prompt:\n{system_prompt}") logging.info(f"\nUser prompt:\n{user_prompt}") - response, finish_reason = await self.ai_handler.chat_completion(model=model, temperature=0.2, - system=system_prompt, user=user_prompt) + + response, finish_reason = await self.ai_handler.chat_completion( + model=model, + temperature=0.2, + system=system_prompt, + user=user_prompt + ) return response def _prepare_pr_review(self) -> str: + """ + Prepare the PR review by processing the AI prediction and generating a markdown-formatted text that summarizes the feedback. + """ review = self.prediction.strip() + try: data = json.loads(review) except json.decoder.JSONDecodeError: data = try_fix_json(review) - # reordering for nicer display - if 'PR Feedback' in data: - if 'Security concerns' in data['PR Feedback']: - val = data['PR Feedback']['Security concerns'] - del data['PR Feedback']['Security concerns'] - data['PR Analysis']['Security concerns'] = val + # Move 'Security concerns' key to 'PR Analysis' section for better display + if 'PR Feedback' in data and 'Security concerns' in data['PR Feedback']: + val = data['PR Feedback']['Security concerns'] + del data['PR Feedback']['Security concerns'] + data['PR Analysis']['Security concerns'] = val - if settings.config.git_provider != 'bitbucket' and \ - settings.pr_reviewer.inline_code_comments and \ - 'Code suggestions' in data['PR Feedback']: - # keeping only code suggestions that can't be submitted as inline comments + # Filter out code suggestions that can be submitted as inline comments + if settings.config.git_provider != 'bitbucket' and settings.pr_reviewer.inline_code_comments and 'Code suggestions' in data['PR Feedback']: data['PR Feedback']['Code suggestions'] = [ d for d in data['PR Feedback']['Code suggestions'] if any(key not in d for key in ('relevant file', 'relevant line in file', 'suggestion content')) @@ -121,8 +180,8 @@ class PRReviewer: if not data['PR Feedback']['Code suggestions']: del data['PR Feedback']['Code suggestions'] + # Add incremental review section if self.incremental.is_incremental: - # Rename title when incremental review - Add to the beginning of the dict last_commit_url = f"{self.git_provider.get_pr_url()}/commits/{self.git_provider.incremental.first_new_commit_sha}" data = OrderedDict(data) data.update({'Incremental PR Review': { @@ -132,6 +191,7 @@ class PRReviewer: markdown_text = convert_to_markdown(data) user = self.git_provider.get_user_id() + # Add help text if not in CLI mode if not self.cli_mode: markdown_text += "\n### How to use\n" if user and '[bot]' not in user: @@ -139,11 +199,16 @@ class PRReviewer: else: markdown_text += actions_help_text + # Log markdown response if verbosity level is high if settings.config.verbosity_level >= 2: logging.info(f"Markdown response:\n{markdown_text}") + return markdown_text - def _publish_inline_code_comments(self): + def _publish_inline_code_comments(self) -> None: + """ + Publishes inline comments on a pull request with code suggestions generated by the AI model. + """ if settings.pr_reviewer.num_code_suggestions == 0: return @@ -153,11 +218,11 @@ class PRReviewer: except json.decoder.JSONDecodeError: data = try_fix_json(review) - comments = [] - for d in data['PR Feedback']['Code suggestions']: - relevant_file = d.get('relevant file', '').strip() - relevant_line_in_file = d.get('relevant line in file', '').strip() - content = d.get('suggestion content', '') + comments: List[str] = [] + for suggestion in data.get('PR Feedback', {}).get('Code suggestions', []): + relevant_file = suggestion.get('relevant file', '').strip() + relevant_line_in_file = suggestion.get('relevant line in file', '').strip() + content = suggestion.get('suggestion content', '') if not relevant_file or not relevant_line_in_file or not content: logging.info("Skipping inline comment with missing file/line/content") continue @@ -172,15 +237,26 @@ class PRReviewer: if comments: self.git_provider.publish_inline_comments(comments) - def _get_user_answers(self): - answer_str = question_str = "" + 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 = "" + if self.is_answer: discussion_messages = self.git_provider.get_issue_comments() - for message in discussion_messages.reversed: + + for message in reversed(discussion_messages): if "Questions to better understand the PR:" in message.body: question_str = message.body elif '/answer' in message.body: answer_str = message.body + if answer_str and question_str: break + return question_str, answer_str From 884317c4f7013471052ab79c131f2430cd72fef2 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:03:22 +0300 Subject: [PATCH 18/33] stable --- pr_agent/settings/configuration.toml | 4 +- pr_agent/settings/pr_update_changelog.toml | 2 +- pr_agent/tools/pr_update_changelog.py | 63 ++++++++++++++++++---- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 637d7394..4a71c04c 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -4,7 +4,7 @@ fallback-models=["gpt-3.5-turbo-16k"] git_provider="github" publish_output=true publish_output_progress=true -verbosity_level=2 # 0,1,2 +verbosity_level=0 # 0,1,2 use_extra_bad_extensions=false [pr_reviewer] @@ -25,7 +25,7 @@ publish_description_as_comment=false num_code_suggestions=4 [pr_update_changelog] -push_changelog_changes=true +push_changelog_changes=false [github] # The type of deployment to create. Valid values are 'app' or 'user'. diff --git a/pr_agent/settings/pr_update_changelog.toml b/pr_agent/settings/pr_update_changelog.toml index 802543a8..053d14c5 100644 --- a/pr_agent/settings/pr_update_changelog.toml +++ b/pr_agent/settings/pr_update_changelog.toml @@ -30,7 +30,7 @@ Current date: The current CHANGELOG.md: ``` -{{changelog_file}} +{{changelog_file_str}} ``` Response: diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index 72234b3f..2f9ee526 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -1,8 +1,7 @@ import copy -import json import logging -import textwrap from datetime import date +from time import sleep from typing import Tuple from jinja2 import Environment, StrictUndefined @@ -31,7 +30,17 @@ class PRUpdateChangelog: changelog_file_lines = changelog_file_lines[:CHANGELOG_LINES] self.changelog_file_str = "\n".join(changelog_file_lines) except: - raise Exception("No CHANGELOG.md file found in the repository") + if settings.pr_update_changelog.push_changelog_changes: + logging.info("No CHANGELOG.md file found in the repository. Creating one...") + changelog_file = self.git_provider.repo_obj.create_file(path="CHANGELOG.md", + message='add CHANGELOG.md', + content="", + branch=self.git_provider.get_pr_branch()) + self.changelog_file = changelog_file['content'] + self.changelog_file_str = "" + if not self.changelog_file_str: + self.changelog_file_str = self._get_default_changelog() + today = date.today() print("Today's date:", today) @@ -46,7 +55,7 @@ class PRUpdateChangelog: "description": self.git_provider.get_pr_description(), "language": self.main_language, "diff": "", # empty diff for initial calculation - "changelog_file": self.changelog_file_str, + "changelog_file_str": self.changelog_file_str, "today": today, } self.token_handler = TokenHandler(self.git_provider.pr, @@ -65,11 +74,13 @@ class PRUpdateChangelog: new_file_content, answer = self._prepare_changelog_update() if settings.config.publish_output: self.git_provider.remove_initial_comment() - logging.info('publishing changelog updates...') - self.git_provider.publish_comment(f"**Changelog updates:**\n\n{answer}") + logging.info('Publishing changelog updates...') if settings.pr_update_changelog.push_changelog_changes: - logging.info('Pushing PR changelog updates...') - self.push_changelog_update(new_file_content) + logging.info('Pushing PR changelog updates to repo...') + self._push_changelog_update(new_file_content, answer) + else: + logging.info('Publishing PR changelog as comment...') + self.git_provider.publish_comment(f"**Changelog updates:**\n\n{answer}") async def _prepare_prediction(self, model: str): logging.info('Getting PR diff...') @@ -98,14 +109,46 @@ class PRUpdateChangelog: def _prepare_changelog_update(self) -> Tuple[str, str]: answer = self.prediction.strip().strip("```").strip() - new_file_content = answer + "\n\n" + self.changelog_file.decoded_content.decode() + existing_content = self.changelog_file.decoded_content.decode() + if existing_content: + new_file_content = answer + "\n\n" + self.changelog_file.decoded_content.decode() + else: + new_file_content = answer if settings.config.verbosity_level >= 2: logging.info(f"answer:\n{answer}") return new_file_content, answer - def push_changelog_update(self, new_file_content): + def _push_changelog_update(self, new_file_content, answer): self.git_provider.repo_obj.update_file(path=self.changelog_file.path, message="Update CHANGELOG.md", content=new_file_content, sha=self.changelog_file.sha, branch=self.git_provider.get_pr_branch()) + d = dict(body="CHANGELOG.md update", + path=self.changelog_file.path, + line=max(2, len(answer.splitlines())), + start_line=1) + + sleep(5) # wait for the file to be updated + last_commit_id = list(self.git_provider.pr.get_commits())[-1] + try: + self.git_provider.pr.create_review(commit=last_commit_id, comments=[d]) + except: + # we can't create a review for some reason, let's just publish a comment + self.git_provider.publish_comment(f"**Changelog updates:**\n\n{answer}") + + + def _get_default_changelog(self): + example_changelog = \ +""" +Example: +## + +### Added +... +### Changed +... +### Fixed +... +""" + return example_changelog From 14eceb6e612977beec24d46b0219e873bec5aaef Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:05:18 +0300 Subject: [PATCH 19/33] PRUpdateChangelog --- pr_agent/agent/pr_agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 7aa61c03..825338e6 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -6,6 +6,7 @@ from pr_agent.tools.pr_description import PRDescription from pr_agent.tools.pr_information_from_user import PRInformationFromUser from pr_agent.tools.pr_questions import PRQuestions from pr_agent.tools.pr_reviewer import PRReviewer +from pr_agent.tools.pr_update_changelog import PRUpdateChangelog class PRAgent: @@ -27,6 +28,8 @@ class PRAgent: await PRCodeSuggestions(pr_url).suggest() elif any(cmd == action for cmd in ["/ask", "/ask_question"]): await PRQuestions(pr_url, args).answer() + elif any(cmd == action for cmd in ["/update_changelog"]): + await PRUpdateChangelog(pr_url, args).update_changelog() else: return False From d8eae7faab6f6898bb5e70e70f4ba7c9f14309bd Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:06:23 +0300 Subject: [PATCH 20/33] Delete CHANGELOG.md --- CHANGELOG.md | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 09d1463d..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,19 +0,0 @@ -## [Unreleased] - 2023-07-26 - -### Added -- New feature to update the changelog based on the contents of a pull request for the Github provider. -- Added 'update_changelog' command to the list of supported commands in the CLI. -- New configuration settings and prompts for the changelog update feature. -- New class `PRUpdateChangelog` in `pr_update_changelog.py` for handling changelog updates. - -### Changed -- Updated configuration settings in `configuration.toml` to include settings for the new feature. - -## [Unreleased] - 2023-07-23 - -### Added -- '/describe' operation now updates also the label of the PR - -### Changed - -### Fixed \ No newline at end of file From 1dbbafc30a5e324ae072e21412fbcbae51b055aa Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:08:06 +0300 Subject: [PATCH 21/33] add CHANGELOG.md --- CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b From 2e7a0a88faabf46347d44046e296f1be9fe2e568 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:08:29 +0300 Subject: [PATCH 22/33] Update CHANGELOG.md --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29b..fcb9c154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +## [Unreleased] + +### Added +- New feature to the PR Agent that allows it to update the changelog based on the contents of a pull request. This feature is currently implemented for the Github provider only. +- New command 'update_changelog' added to the list of supported commands in `pr_agent/cli.py`. +- New configuration file 'pr_update_changelog.toml' added to the list of settings files in `pr_agent/config_loader.py`. +- New class `PRUpdateChangelog` in `pr_agent/tools/pr_update_changelog.py` responsible for updating the changelog based on the PR's contents. +- New prompts for the changelog update feature in `pr_agent/settings/pr_update_changelog.toml`. + +### Changed +- Updated `pr_agent/agent/pr_agent.py` to handle the 'update_changelog' command. +- Updated `pr_agent/cli.py` to handle the 'update_changelog' command and reflect it in the help message. +- Updated `README.md` to include the 'update_changelog' command in the usage section and feature list. +- Updated `pr_agent/settings/configuration.toml` to include settings for the new feature. + +### Fixed +- No bug fixes in this PR. \ No newline at end of file From cfb696dfd5224588a8d0b330eb32e5099a38c963 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:09:18 +0300 Subject: [PATCH 23/33] Delete CHANGELOG.md --- CHANGELOG.md | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index fcb9c154..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,17 +0,0 @@ -## [Unreleased] - -### Added -- New feature to the PR Agent that allows it to update the changelog based on the contents of a pull request. This feature is currently implemented for the Github provider only. -- New command 'update_changelog' added to the list of supported commands in `pr_agent/cli.py`. -- New configuration file 'pr_update_changelog.toml' added to the list of settings files in `pr_agent/config_loader.py`. -- New class `PRUpdateChangelog` in `pr_agent/tools/pr_update_changelog.py` responsible for updating the changelog based on the PR's contents. -- New prompts for the changelog update feature in `pr_agent/settings/pr_update_changelog.toml`. - -### Changed -- Updated `pr_agent/agent/pr_agent.py` to handle the 'update_changelog' command. -- Updated `pr_agent/cli.py` to handle the 'update_changelog' command and reflect it in the help message. -- Updated `README.md` to include the 'update_changelog' command in the usage section and feature list. -- Updated `pr_agent/settings/configuration.toml` to include settings for the new feature. - -### Fixed -- No bug fixes in this PR. \ No newline at end of file From 801923789b5654d81e5ae094df8ccdf8943da92b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:33:21 +0300 Subject: [PATCH 24/33] final --- pr_agent/settings/pr_update_changelog.toml | 15 ++++++--------- pr_agent/tools/pr_update_changelog.py | 17 ++++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/pr_agent/settings/pr_update_changelog.toml b/pr_agent/settings/pr_update_changelog.toml index 053d14c5..85867ec2 100644 --- a/pr_agent/settings/pr_update_changelog.toml +++ b/pr_agent/settings/pr_update_changelog.toml @@ -1,12 +1,9 @@ [pr_update_changelog_prompt] -system="""You are a language model called CodiumAI-PR-Code-Reviewer. -Your task is to update the CHANGELOG.md file of the project, to reflect the changes in this PR. -The updated content should be short and concise as possible. -It should match the existing CHANGELOG.md format, style and conventions, so it will look like a natural part of the file. -For example, if previous changes were summarized in a single line, you should do the same. - -Don't repeat previous changes. Generate content that is not already in the CHANGELOG.md file. - +system="""You are a language model called CodiumAI-PR-Changlog-summarizer. +Your task is to update the CHANGELOG.md file of the project, to shortly summarize important changes introduced in this PR (the '+' lines). +- The output should match the existing CHANGELOG.md format, style and conventions, so it will look like a natural part of the file. For example, if previous changes were summarized in a single line, you should do the same. +- Don't repeat previous changes. Generate only new content, that is not already in the CHANGELOG.md file. +- Be generic and avoid specific details. The output should be minimal, no more than 3-4 short lines. Ignore non-relevant subsections. """ user="""PR Info: @@ -30,7 +27,7 @@ Current date: The current CHANGELOG.md: ``` -{{changelog_file_str}} +{{ changelog_file_str }} ``` Response: diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index 2f9ee526..aa24afb6 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -30,14 +30,15 @@ class PRUpdateChangelog: changelog_file_lines = changelog_file_lines[:CHANGELOG_LINES] self.changelog_file_str = "\n".join(changelog_file_lines) except: - if settings.pr_update_changelog.push_changelog_changes: + self.changelog_file_str = "" + if settings.config.publish_output and settings.pr_update_changelog.push_changelog_changes: logging.info("No CHANGELOG.md file found in the repository. Creating one...") changelog_file = self.git_provider.repo_obj.create_file(path="CHANGELOG.md", message='add CHANGELOG.md', content="", branch=self.git_provider.get_pr_branch()) self.changelog_file = changelog_file['content'] - self.changelog_file_str = "" + if not self.changelog_file_str: self.changelog_file_str = self._get_default_changelog() @@ -84,12 +85,7 @@ class PRUpdateChangelog: async def _prepare_prediction(self, model: str): logging.info('Getting PR diff...') - # we are using extended hunk with line numbers for code suggestions - self.patches_diff = get_pr_diff(self.git_provider, - self.token_handler, - model, - add_line_numbers_to_hunks=True, - disable_extra_lines=True) + self.patches_diff = get_pr_diff(self.git_provider, self.token_handler, model) logging.info('Getting AI prediction...') self.prediction = await self._get_prediction(model) @@ -109,7 +105,10 @@ class PRUpdateChangelog: def _prepare_changelog_update(self) -> Tuple[str, str]: answer = self.prediction.strip().strip("```").strip() - existing_content = self.changelog_file.decoded_content.decode() + if hasattr(self, "changelog_file"): + existing_content = self.changelog_file.decoded_content.decode() + else: + existing_content = "" if existing_content: new_file_content = answer + "\n\n" + self.changelog_file.decoded_content.decode() else: From 413e5f6d771f3c27e388e7887e9ca09ca3965f18 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:36:05 +0300 Subject: [PATCH 25/33] general --- pr_agent/settings/configuration.toml | 2 +- pr_agent/settings/pr_update_changelog.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/settings/configuration.toml b/pr_agent/settings/configuration.toml index 4a71c04c..f951c648 100644 --- a/pr_agent/settings/configuration.toml +++ b/pr_agent/settings/configuration.toml @@ -1,6 +1,6 @@ [config] model="gpt-4" -fallback-models=["gpt-3.5-turbo-16k"] +fallback_models=["gpt-3.5-turbo-16k"] git_provider="github" publish_output=true publish_output_progress=true diff --git a/pr_agent/settings/pr_update_changelog.toml b/pr_agent/settings/pr_update_changelog.toml index 85867ec2..91413010 100644 --- a/pr_agent/settings/pr_update_changelog.toml +++ b/pr_agent/settings/pr_update_changelog.toml @@ -3,7 +3,7 @@ system="""You are a language model called CodiumAI-PR-Changlog-summarizer. Your task is to update the CHANGELOG.md file of the project, to shortly summarize important changes introduced in this PR (the '+' lines). - The output should match the existing CHANGELOG.md format, style and conventions, so it will look like a natural part of the file. For example, if previous changes were summarized in a single line, you should do the same. - Don't repeat previous changes. Generate only new content, that is not already in the CHANGELOG.md file. -- Be generic and avoid specific details. The output should be minimal, no more than 3-4 short lines. Ignore non-relevant subsections. +- Be general, and avoid specific details, files, etc. The output should be minimal, no more than 3-4 short lines. Ignore non-relevant subsections. """ user="""PR Info: From cce2a79a1fdd512a313dc518d9ec4be813138010 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:40:15 +0300 Subject: [PATCH 26/33] add CHANGELOG.md --- CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b From 52576c79b375bbcc58b296e15846f0a9d20cfe9b Mon Sep 17 00:00:00 2001 From: mrT23 Date: Wed, 26 Jul 2023 20:40:28 +0300 Subject: [PATCH 27/33] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29b..0a905f38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +## 2023-07-26 + +### Added +- New feature for updating the CHANGELOG.md based on the contents of a PR. +- Added support for this feature for the Github provider. +- New configuration settings and prompts for the changelog update feature. \ No newline at end of file From eb798dae6f5f3242cf1f404fbb6dce4861df13b0 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:13:21 +0300 Subject: [PATCH 28/33] Why use PR-Agent Why use PR-Agent --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 173b7844..744557d0 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ CodiumAI `PR-Agent` is an open-source tool aiming to help developers review pull - [Usage and tools](#usage-and-tools) - [Configuration](./CONFIGURATION.md) - [How it works](#how-it-works) +- [Why use PR-Agent](#why-use-pr-agent) - [Roadmap](#roadmap) - [Similar projects](#similar-projects) @@ -146,6 +147,19 @@ There are several ways to use PR-Agent: Check out the [PR Compression strategy](./PR_COMPRESSION.md) page for more details on how we convert a code diff to a manageable LLM prompt +## Why use PR-Agent? + +A reasonable question that can be asked is: `"Why use PR-Agent? What make it stand out from existing tools?"` + +Here are some of the reasons why: + +- We emphasize **real-life practical usage**. Each tool (review, improve, ask, ...) has a single GPT-4 call, no more. We feel that this is critical for realistic team usage - obtaining an answer quickly (~30 seconds) and affordably. +- Our [PR Compression strategy](./PR_COMPRESSION.md) is a core ability that enables to effectively tackle both short and long PRs. +- Our JSON prompting strategy enables to have **modular, customizable tools**. For example, the '/review' tool categories can be controlled via the configuration file. Adding additional categories is easy and accessible. +- We support **multiple git providers** (GitHub, Gitlab, Bitbucket), and multiple ways to use the tool (CLI, GitHub Action, Docker, ...). +- We are open-source, and welcome contributions from the community. + + ## Roadmap - [ ] Support open-source models, as a replacement for OpenAI models. (Note - a minimal requirement for each open-source model is to have 8k+ context, and good support for generating JSON as an output) From 231efb33c17bb2239a3562b83520c47cf29f60e4 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:43:29 +0300 Subject: [PATCH 29/33] add CHANGELOG.md --- CHANGELOG.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..e69de29b From 66b94599ec99287cadaac061a85e8eefb16a278d Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:45:33 +0300 Subject: [PATCH 30/33] Update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29b..8fb3cf2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +## 2023-07-27 + +### Added +- New section 'Why use PR-Agent?' in README.md, providing reasons and justifications for using the PR-Agent tool. \ No newline at end of file From 48fcb46d4f3cdf4e898c5359f676bfb56041d686 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:46:14 +0300 Subject: [PATCH 31/33] Delete CHANGELOG.md --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 8fb3cf2e..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,4 +0,0 @@ -## 2023-07-27 - -### Added -- New section 'Why use PR-Agent?' in README.md, providing reasons and justifications for using the PR-Agent tool. \ No newline at end of file From c827cbc0ae6f1e1abcb8ba9f64fb1ad897e01128 Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:47:26 +0300 Subject: [PATCH 32/33] final touches --- pr_agent/agent/pr_agent.py | 4 +- pr_agent/cli.py | 2 +- pr_agent/tools/pr_update_changelog.py | 70 +++++++++++++++++---------- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/pr_agent/agent/pr_agent.py b/pr_agent/agent/pr_agent.py index 825338e6..a30c411b 100644 --- a/pr_agent/agent/pr_agent.py +++ b/pr_agent/agent/pr_agent.py @@ -27,9 +27,9 @@ class PRAgent: elif any(cmd == action for cmd in ["/improve", "/improve_code"]): await PRCodeSuggestions(pr_url).suggest() elif any(cmd == action for cmd in ["/ask", "/ask_question"]): - await PRQuestions(pr_url, args).answer() + await PRQuestions(pr_url, args=args).answer() elif any(cmd == action for cmd in ["/update_changelog"]): - await PRUpdateChangelog(pr_url, args).update_changelog() + await PRUpdateChangelog(pr_url, args=args).update_changelog() else: return False diff --git a/pr_agent/cli.py b/pr_agent/cli.py index 2bae6bd4..f04e51d7 100644 --- a/pr_agent/cli.py +++ b/pr_agent/cli.py @@ -102,7 +102,7 @@ def _handle_review_after_reflect_command(pr_url: str, rest: list): def _handle_update_changelog(pr_url: str, rest: list): print(f"Updating changlog for: {pr_url}") - reviewer = PRUpdateChangelog(pr_url, cli_mode=True) + reviewer = PRUpdateChangelog(pr_url, cli_mode=True, args=rest) asyncio.run(reviewer.update_changelog()) if __name__ == '__main__': diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index aa24afb6..901c5b45 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -17,35 +17,14 @@ CHANGELOG_LINES = 50 class PRUpdateChangelog: - def __init__(self, pr_url: str, cli_mode=False): + def __init__(self, pr_url: str, cli_mode=False, args=None): self.git_provider = get_git_provider()(pr_url) self.main_language = get_main_pr_language( self.git_provider.get_languages(), self.git_provider.get_files() ) - try: - self.changelog_file = self.git_provider.repo_obj.get_contents("CHANGELOG.md", - ref=self.git_provider.get_pr_branch()) - changelog_file_lines = self.changelog_file.decoded_content.decode().splitlines() - changelog_file_lines = changelog_file_lines[:CHANGELOG_LINES] - self.changelog_file_str = "\n".join(changelog_file_lines) - except: - self.changelog_file_str = "" - if settings.config.publish_output and settings.pr_update_changelog.push_changelog_changes: - logging.info("No CHANGELOG.md file found in the repository. Creating one...") - changelog_file = self.git_provider.repo_obj.create_file(path="CHANGELOG.md", - message='add CHANGELOG.md', - content="", - branch=self.git_provider.get_pr_branch()) - self.changelog_file = changelog_file['content'] - - if not self.changelog_file_str: - self.changelog_file_str = self._get_default_changelog() - - - today = date.today() - print("Today's date:", today) - + self.commit_changelog = self._parse_args(args, settings) + self._get_changlog_file() # self.changelog_file_str self.ai_handler = AiHandler() self.patches_diff = None self.prediction = None @@ -57,7 +36,7 @@ class PRUpdateChangelog: "language": self.main_language, "diff": "", # empty diff for initial calculation "changelog_file_str": self.changelog_file_str, - "today": today, + "today": date.today(), } self.token_handler = TokenHandler(self.git_provider.pr, self.vars, @@ -76,7 +55,7 @@ class PRUpdateChangelog: if settings.config.publish_output: self.git_provider.remove_initial_comment() logging.info('Publishing changelog updates...') - if settings.pr_update_changelog.push_changelog_changes: + if self.commit_changelog: logging.info('Pushing PR changelog updates to repo...') self._push_changelog_update(new_file_content, answer) else: @@ -113,8 +92,14 @@ class PRUpdateChangelog: new_file_content = answer + "\n\n" + self.changelog_file.decoded_content.decode() else: new_file_content = answer + + if not self.cli_mode and self.commit_changelog: + answer += "\n\n\n>to commit the new contnet to the CHANGELOG.md file, please type:" \ + "\n>'/update_changelog -commit'\n" + if settings.config.verbosity_level >= 2: logging.info(f"answer:\n{answer}") + return new_file_content, answer def _push_changelog_update(self, new_file_content, answer): @@ -151,3 +136,36 @@ Example: ... """ return example_changelog + + def _parse_args(self, args, setting): + commit_changelog = False + if args and len(args) >= 1: + try: + if args[0] == "-commit": + commit_changelog = True + except: + pass + else: + commit_changelog = setting.pr_update_changelog.push_changelog_changes + + return commit_changelog + + def _get_changlog_file(self): + try: + self.changelog_file = self.git_provider.repo_obj.get_contents("CHANGELOG.md", + ref=self.git_provider.get_pr_branch()) + changelog_file_lines = self.changelog_file.decoded_content.decode().splitlines() + changelog_file_lines = changelog_file_lines[:CHANGELOG_LINES] + self.changelog_file_str = "\n".join(changelog_file_lines) + except: + self.changelog_file_str = "" + if self.commit_changelog: + logging.info("No CHANGELOG.md file found in the repository. Creating one...") + changelog_file = self.git_provider.repo_obj.create_file(path="CHANGELOG.md", + message='add CHANGELOG.md', + content="", + branch=self.git_provider.get_pr_branch()) + self.changelog_file = changelog_file['content'] + + if not self.changelog_file_str: + self.changelog_file_str = self._get_default_changelog() From 808ca48605956236b8766d6389c9bc47ea0d40fe Mon Sep 17 00:00:00 2001 From: mrT23 Date: Thu, 27 Jul 2023 08:48:39 +0300 Subject: [PATCH 33/33] if not self.commit_changelog: --- pr_agent/tools/pr_update_changelog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pr_agent/tools/pr_update_changelog.py b/pr_agent/tools/pr_update_changelog.py index 901c5b45..1b06c381 100644 --- a/pr_agent/tools/pr_update_changelog.py +++ b/pr_agent/tools/pr_update_changelog.py @@ -93,8 +93,8 @@ class PRUpdateChangelog: else: new_file_content = answer - if not self.cli_mode and self.commit_changelog: - answer += "\n\n\n>to commit the new contnet to the CHANGELOG.md file, please type:" \ + if not self.commit_changelog: + answer += "\n\n\n>to commit the new content to the CHANGELOG.md file, please type:" \ "\n>'/update_changelog -commit'\n" if settings.config.verbosity_level >= 2: