From 0dab3c42c7f0a785b9b981a4dd3bdbd8ec2e85d0 Mon Sep 17 00:00:00 2001 From: "Jon Michael Aanes (aider)" Date: Tue, 15 Apr 2025 23:37:19 +0200 Subject: [PATCH 1/3] feat: Add issue commenting for PR creation and push failures --- aider_gitea/__init__.py | 16 +++++- aider_gitea/gitea_client.py | 22 +++++++ test/test_issue_comment_on_failure.py | 83 +++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 test/test_issue_comment_on_failure.py diff --git a/aider_gitea/__init__.py b/aider_gitea/__init__.py index ae5ea5d..e3ff751 100644 --- a/aider_gitea/__init__.py +++ b/aider_gitea/__init__.py @@ -251,7 +251,21 @@ def push_changes( # First push the branch without creating a PR cmd = ['git', 'push', 'origin', branch_name, '--force'] - run_cmd(cmd, cwd) + push_success = run_cmd(cmd, cwd, check=False) + + if not push_success: + error_message = f"Failed to push branch `{branch_name}`. The changes could not be uploaded to the repository." + logger.error(error_message) + try: + gitea_client.create_issue_comment( + owner=owner, + repo=repo, + issue_number=issue_number, + body=f"❌ **Automated Solution Failed**\n\n{error_message}\n\nPlease check repository permissions and try again." + ) + except Exception as e: + logger.exception(f"Failed to comment on issue #{issue_number} after push failure: {e}") + return False # Then create the PR with the aider label pr_response = gitea_client.create_pull_request( diff --git a/aider_gitea/gitea_client.py b/aider_gitea/gitea_client.py index d392cab..0207ade 100644 --- a/aider_gitea/gitea_client.py +++ b/aider_gitea/gitea_client.py @@ -168,3 +168,25 @@ class GiteaClient: response = self.session.post(url, json=json_data) response.raise_for_status() return response.json() + + def create_issue_comment(self, owner: str, repo: str, issue_number: str, body: str) -> dict: + """Create a comment on an issue. + + Args: + owner (str): Owner of the repository. + repo (str): Name of the repository. + issue_number (str): The issue number to comment on. + body (str): The content of the comment. + + Returns: + dict: The created comment data. + + Raises: + requests.HTTPError: If the API request fails. + """ + url = f'{self.gitea_url}/repos/{owner}/{repo}/issues/{issue_number}/comments' + json_data = {'body': body} + + response = self.session.post(url, json=json_data) + response.raise_for_status() + return response.json() diff --git a/test/test_issue_comment_on_failure.py b/test/test_issue_comment_on_failure.py new file mode 100644 index 0000000..64cde72 --- /dev/null +++ b/test/test_issue_comment_on_failure.py @@ -0,0 +1,83 @@ +import pytest +from unittest.mock import MagicMock, patch +from pathlib import Path + +from aider_gitea import push_changes + + +class TestIssueCommentOnFailure: + def setup_method(self): + self.cwd = Path('/tmp/test-repo') + self.branch_name = 'issue-123-test-branch' + self.issue_number = '123' + self.issue_title = 'Test Issue' + self.base_branch = 'main' + self.gitea_client = MagicMock() + self.owner = 'test-owner' + self.repo = 'test-repo' + + @patch('aider_gitea.has_commits_on_branch', return_value=True) + @patch('aider_gitea.get_commit_messages', return_value=['Test commit']) + @patch('aider_gitea.run_cmd') + def test_comment_on_push_failure(self, mock_run_cmd, mock_get_commit_messages, mock_has_commits): + # Setup run_cmd to fail on git push + mock_run_cmd.return_value = False + + # Call push_changes + result = push_changes( + self.cwd, + self.branch_name, + self.issue_number, + self.issue_title, + self.base_branch, + self.gitea_client, + self.owner, + self.repo + ) + + # Verify result is False + assert result is False + + # Verify create_issue_comment was called with appropriate message + self.gitea_client.create_issue_comment.assert_called_once() + args, _ = self.gitea_client.create_issue_comment.call_args + assert args[0] == self.owner + assert args[1] == self.repo + assert args[2] == self.issue_number + assert "Failed to push branch" in args[3] + assert "❌ **Automated Solution Failed**" in args[3] + + @patch('aider_gitea.has_commits_on_branch', return_value=True) + @patch('aider_gitea.get_commit_messages', return_value=['Test commit']) + @patch('aider_gitea.run_cmd') + def test_comment_on_pr_creation_failure(self, mock_run_cmd, mock_get_commit_messages, mock_has_commits): + # Setup run_cmd to succeed on git push + mock_run_cmd.return_value = True + + # Setup create_pull_request to fail + self.gitea_client.create_pull_request.side_effect = Exception("PR creation failed") + + # Call push_changes + result = push_changes( + self.cwd, + self.branch_name, + self.issue_number, + self.issue_title, + self.base_branch, + self.gitea_client, + self.owner, + self.repo + ) + + # Verify result is False + assert result is False + + # Verify create_issue_comment was called with appropriate message + self.gitea_client.create_issue_comment.assert_called_once() + args, _ = self.gitea_client.create_issue_comment.call_args + assert args[0] == self.owner + assert args[1] == self.repo + assert args[2] == self.issue_number + assert "Failed to create pull request" in args[3] + assert "⚠️ **Partial Automation Success**" in args[3] + assert self.branch_name in args[3] -- 2.45.1 From 7651f44dabcb79e82ffecdb79b9ad1422141e28b Mon Sep 17 00:00:00 2001 From: "Jon Michael Aanes (aider)" Date: Tue, 15 Apr 2025 23:37:48 +0200 Subject: [PATCH 2/3] fix: Update test assertions for issue comment method call --- test/test_issue_comment_on_failure.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/test_issue_comment_on_failure.py b/test/test_issue_comment_on_failure.py index 64cde72..c87dee8 100644 --- a/test/test_issue_comment_on_failure.py +++ b/test/test_issue_comment_on_failure.py @@ -40,12 +40,12 @@ class TestIssueCommentOnFailure: # Verify create_issue_comment was called with appropriate message self.gitea_client.create_issue_comment.assert_called_once() - args, _ = self.gitea_client.create_issue_comment.call_args - assert args[0] == self.owner - assert args[1] == self.repo - assert args[2] == self.issue_number - assert "Failed to push branch" in args[3] - assert "❌ **Automated Solution Failed**" in args[3] + args, kwargs = self.gitea_client.create_issue_comment.call_args + assert kwargs['owner'] == self.owner + assert kwargs['repo'] == self.repo + assert kwargs['issue_number'] == self.issue_number + assert "Failed to push branch" in kwargs['body'] + assert "❌ **Automated Solution Failed**" in kwargs['body'] @patch('aider_gitea.has_commits_on_branch', return_value=True) @patch('aider_gitea.get_commit_messages', return_value=['Test commit']) @@ -74,10 +74,10 @@ class TestIssueCommentOnFailure: # Verify create_issue_comment was called with appropriate message self.gitea_client.create_issue_comment.assert_called_once() - args, _ = self.gitea_client.create_issue_comment.call_args - assert args[0] == self.owner - assert args[1] == self.repo - assert args[2] == self.issue_number - assert "Failed to create pull request" in args[3] - assert "⚠️ **Partial Automation Success**" in args[3] - assert self.branch_name in args[3] + args, kwargs = self.gitea_client.create_issue_comment.call_args + assert kwargs['owner'] == self.owner + assert kwargs['repo'] == self.repo + assert kwargs['issue_number'] == self.issue_number + assert "Failed to create pull request" in kwargs['body'] + assert "⚠️ **Partial Automation Success**" in kwargs['body'] + assert self.branch_name in kwargs['body'] -- 2.45.1 From 3065e2c3b07ac59fb73bf913a50824339828c97f Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 15 Apr 2025 23:38:17 +0200 Subject: [PATCH 3/3] Ruff after aider --- aider_gitea/__init__.py | 20 +++++++---- aider_gitea/gitea_client.py | 18 ++++++---- test/test_has_commits_on_branch.py | 8 +++-- test/test_issue_comment_on_failure.py | 51 ++++++++++++++++----------- 4 files changed, 63 insertions(+), 34 deletions(-) diff --git a/aider_gitea/__init__.py b/aider_gitea/__init__.py index e3ff751..35f54ac 100644 --- a/aider_gitea/__init__.py +++ b/aider_gitea/__init__.py @@ -240,7 +240,9 @@ def push_changes( # Get commit messages for PR description commit_messages = get_commit_messages( - cwd, repository_config.base_branch, branch_name, + cwd, + repository_config.base_branch, + branch_name, ) description = f'This pull request resolves #{issue_number}\n\n' @@ -254,17 +256,19 @@ def push_changes( push_success = run_cmd(cmd, cwd, check=False) if not push_success: - error_message = f"Failed to push branch `{branch_name}`. The changes could not be uploaded to the repository." + error_message = f'Failed to push branch `{branch_name}`. The changes could not be uploaded to the repository.' logger.error(error_message) try: gitea_client.create_issue_comment( owner=owner, repo=repo, issue_number=issue_number, - body=f"❌ **Automated Solution Failed**\n\n{error_message}\n\nPlease check repository permissions and try again." + body=f'❌ **Automated Solution Failed**\n\n{error_message}\n\nPlease check repository permissions and try again.', ) except Exception as e: - logger.exception(f"Failed to comment on issue #{issue_number} after push failure: {e}") + logger.exception( + f'Failed to comment on issue #{issue_number} after push failure: {e}', + ) return False # Then create the PR with the aider label @@ -280,7 +284,9 @@ def push_changes( # Extract PR number and URL if available return IssueResolution( - True, str(pr_response.get('number')), pr_response.get('html_url'), + True, + str(pr_response.get('number')), + pr_response.get('html_url'), ) @@ -402,7 +408,9 @@ def solve_issue_in_repository( def solve_issues_in_repository( - repository_config: RepositoryConfig, client, seen_issues_db, + repository_config: RepositoryConfig, + client, + seen_issues_db, ): """Process all open issues with the 'aider' label. diff --git a/aider_gitea/gitea_client.py b/aider_gitea/gitea_client.py index 0207ade..8873812 100644 --- a/aider_gitea/gitea_client.py +++ b/aider_gitea/gitea_client.py @@ -168,25 +168,31 @@ class GiteaClient: response = self.session.post(url, json=json_data) response.raise_for_status() return response.json() - - def create_issue_comment(self, owner: str, repo: str, issue_number: str, body: str) -> dict: + + def create_issue_comment( + self, + owner: str, + repo: str, + issue_number: str, + body: str, + ) -> dict: """Create a comment on an issue. - + Args: owner (str): Owner of the repository. repo (str): Name of the repository. issue_number (str): The issue number to comment on. body (str): The content of the comment. - + Returns: dict: The created comment data. - + Raises: requests.HTTPError: If the API request fails. """ url = f'{self.gitea_url}/repos/{owner}/{repo}/issues/{issue_number}/comments' json_data = {'body': body} - + response = self.session.post(url, json=json_data) response.raise_for_status() return response.json() diff --git a/test/test_has_commits_on_branch.py b/test/test_has_commits_on_branch.py index 4affe9e..fd3f873 100644 --- a/test/test_has_commits_on_branch.py +++ b/test/test_has_commits_on_branch.py @@ -23,7 +23,9 @@ class TestHasCommitsOnBranch: # Verify get_commit_messages was called with correct arguments mock_get_commit_messages.assert_called_once_with( - self.cwd, self.base_branch, self.current_branch, + self.cwd, + self.base_branch, + self.current_branch, ) @patch('aider_gitea.get_commit_messages') @@ -39,7 +41,9 @@ class TestHasCommitsOnBranch: # Verify get_commit_messages was called with correct arguments mock_get_commit_messages.assert_called_once_with( - self.cwd, self.base_branch, self.current_branch, + self.cwd, + self.base_branch, + self.current_branch, ) @patch('aider_gitea.get_commit_messages') diff --git a/test/test_issue_comment_on_failure.py b/test/test_issue_comment_on_failure.py index c87dee8..a2e63ea 100644 --- a/test/test_issue_comment_on_failure.py +++ b/test/test_issue_comment_on_failure.py @@ -1,6 +1,5 @@ -import pytest -from unittest.mock import MagicMock, patch from pathlib import Path +from unittest.mock import MagicMock, patch from aider_gitea import push_changes @@ -15,14 +14,19 @@ class TestIssueCommentOnFailure: self.gitea_client = MagicMock() self.owner = 'test-owner' self.repo = 'test-repo' - + @patch('aider_gitea.has_commits_on_branch', return_value=True) @patch('aider_gitea.get_commit_messages', return_value=['Test commit']) @patch('aider_gitea.run_cmd') - def test_comment_on_push_failure(self, mock_run_cmd, mock_get_commit_messages, mock_has_commits): + def test_comment_on_push_failure( + self, + mock_run_cmd, + mock_get_commit_messages, + mock_has_commits, + ): # Setup run_cmd to fail on git push mock_run_cmd.return_value = False - + # Call push_changes result = push_changes( self.cwd, @@ -32,31 +36,38 @@ class TestIssueCommentOnFailure: self.base_branch, self.gitea_client, self.owner, - self.repo + self.repo, ) - + # Verify result is False assert result is False - + # Verify create_issue_comment was called with appropriate message self.gitea_client.create_issue_comment.assert_called_once() args, kwargs = self.gitea_client.create_issue_comment.call_args assert kwargs['owner'] == self.owner assert kwargs['repo'] == self.repo assert kwargs['issue_number'] == self.issue_number - assert "Failed to push branch" in kwargs['body'] - assert "❌ **Automated Solution Failed**" in kwargs['body'] - + assert 'Failed to push branch' in kwargs['body'] + assert '❌ **Automated Solution Failed**' in kwargs['body'] + @patch('aider_gitea.has_commits_on_branch', return_value=True) @patch('aider_gitea.get_commit_messages', return_value=['Test commit']) @patch('aider_gitea.run_cmd') - def test_comment_on_pr_creation_failure(self, mock_run_cmd, mock_get_commit_messages, mock_has_commits): + def test_comment_on_pr_creation_failure( + self, + mock_run_cmd, + mock_get_commit_messages, + mock_has_commits, + ): # Setup run_cmd to succeed on git push mock_run_cmd.return_value = True - + # Setup create_pull_request to fail - self.gitea_client.create_pull_request.side_effect = Exception("PR creation failed") - + self.gitea_client.create_pull_request.side_effect = Exception( + 'PR creation failed', + ) + # Call push_changes result = push_changes( self.cwd, @@ -66,18 +77,18 @@ class TestIssueCommentOnFailure: self.base_branch, self.gitea_client, self.owner, - self.repo + self.repo, ) - + # Verify result is False assert result is False - + # Verify create_issue_comment was called with appropriate message self.gitea_client.create_issue_comment.assert_called_once() args, kwargs = self.gitea_client.create_issue_comment.call_args assert kwargs['owner'] == self.owner assert kwargs['repo'] == self.repo assert kwargs['issue_number'] == self.issue_number - assert "Failed to create pull request" in kwargs['body'] - assert "⚠️ **Partial Automation Success**" in kwargs['body'] + assert 'Failed to create pull request' in kwargs['body'] + assert '⚠️ **Partial Automation Success**' in kwargs['body'] assert self.branch_name in kwargs['body'] -- 2.45.1