From 8f8b91bf5e9e6b642322a47d2bf21d8d13f10f0e Mon Sep 17 00:00:00 2001 From: "Jon Michael Aanes (aider)" Date: Tue, 15 Apr 2025 00:42:59 +0200 Subject: [PATCH 1/3] refactor: Add ruff pass before aider and validate changes --- aider_gitea/__init__.py | 33 ++++++++++- test/test_solve_issue_in_repository.py | 82 ++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 test/test_solve_issue_in_repository.py diff --git a/aider_gitea/__init__.py b/aider_gitea/__init__.py index 1c300ee..4799100 100644 --- a/aider_gitea/__init__.py +++ b/aider_gitea/__init__.py @@ -300,6 +300,21 @@ def solve_issue_in_repository( run_cmd(['git', 'checkout', args.base_branch], tmpdirname) run_cmd(['git', 'checkout', '-b', branch_name], tmpdirname) + # Run initial ruff pass before aider + run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], tmpdirname, check=False) + run_cmd(['git', 'add', '.'], tmpdirname) + run_cmd(['git', 'commit', '-m', 'Initial ruff pass'], tmpdirname, check=False) + + # Save the commit hash after ruff but before aider + result = subprocess.run( + ['git', 'rev-parse', 'HEAD'], + check=True, + cwd=tmpdirname, + capture_output=True, + text=True, + ) + pre_aider_commit = result.stdout.strip() + # Run aider issue_content = f'# {issue_title}\n{issue_description}' succeeded = run_cmd( @@ -311,10 +326,24 @@ def solve_issue_in_repository( logger.error('Aider invocation failed for issue #%s', issue_number) return False - # Auto-fix standard code quality stuff + # Auto-fix standard code quality stuff after aider run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], tmpdirname, check=False) run_cmd(['git', 'add', '.'], tmpdirname) - run_cmd(['git', 'commit', '-m', 'Ruff'], tmpdirname, check=False) + run_cmd(['git', 'commit', '-m', 'Ruff after aider'], tmpdirname, check=False) + + # Check if aider made any changes beyond the initial ruff pass + result = subprocess.run( + ['git', 'diff', pre_aider_commit, 'HEAD', '--name-only'], + check=True, + cwd=tmpdirname, + capture_output=True, + text=True, + ) + files_changed = result.stdout.strip() + + if not files_changed: + logger.info('Aider did not make any changes beyond the initial ruff pass for issue #%s', issue_number) + return False # Push changes return push_changes( diff --git a/test/test_solve_issue_in_repository.py b/test/test_solve_issue_in_repository.py new file mode 100644 index 0000000..5eb9083 --- /dev/null +++ b/test/test_solve_issue_in_repository.py @@ -0,0 +1,82 @@ +import subprocess +import tempfile +from pathlib import Path +from unittest.mock import MagicMock, patch + +import pytest + +from aider_gitea import solve_issue_in_repository + + +class TestSolveIssueInRepository: + def setup_method(self): + self.args = MagicMock() + self.args.gitea_url = "https://gitea.example.com" + self.args.owner = "test-owner" + self.args.repo = "test-repo" + self.args.base_branch = "main" + + self.gitea_client = MagicMock() + self.tmpdirname = Path("/tmp/test-repo") + self.branch_name = "issue-123-test-branch" + self.issue_title = "Test Issue" + self.issue_description = "This is a test issue" + self.issue_number = "123" + + @patch('aider_gitea.run_cmd') + @patch('aider_gitea.push_changes') + @patch('subprocess.run') + def test_solve_issue_with_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd): + # Setup mocks + mock_run_cmd.return_value = True + mock_push_changes.return_value = True + + # Mock subprocess.run to return different commit hashes and file changes + mock_subprocess_run.side_effect = [ + MagicMock(stdout="abc123\n", returncode=0), # First git rev-parse + MagicMock(stdout="file1.py\nfile2.py\n", returncode=0), # git diff with changes + ] + + # Call the function + result = solve_issue_in_repository( + self.args, + self.tmpdirname, + self.branch_name, + self.issue_title, + self.issue_description, + self.issue_number, + self.gitea_client + ) + + # Verify results + assert result is True + assert mock_run_cmd.call_count >= 8 # Verify all expected commands were run + mock_push_changes.assert_called_once() + + @patch('aider_gitea.run_cmd') + @patch('aider_gitea.push_changes') + @patch('subprocess.run') + def test_solve_issue_without_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd): + # Setup mocks + mock_run_cmd.return_value = True + + # Mock subprocess.run to return same commit hash and no file changes + mock_subprocess_run.side_effect = [ + MagicMock(stdout="abc123\n", returncode=0), # First git rev-parse + MagicMock(stdout="", returncode=0), # git diff with no changes + ] + + # Call the function + result = solve_issue_in_repository( + self.args, + self.tmpdirname, + self.branch_name, + self.issue_title, + self.issue_description, + self.issue_number, + self.gitea_client + ) + + # Verify results + assert result is False + assert mock_push_changes.call_count == 0 # push_changes should not be called -- 2.45.1 From 6c4e17999b8a4a408672692c52064d448d514e8e Mon Sep 17 00:00:00 2001 From: "Jon Michael Aanes (aider)" Date: Tue, 15 Apr 2025 00:43:32 +0200 Subject: [PATCH 2/3] fix: mock secrets in tests to prevent LLM_API_KEY access --- test/test_solve_issue_in_repository.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/test_solve_issue_in_repository.py b/test/test_solve_issue_in_repository.py index 5eb9083..6d8bdf2 100644 --- a/test/test_solve_issue_in_repository.py +++ b/test/test_solve_issue_in_repository.py @@ -23,10 +23,11 @@ class TestSolveIssueInRepository: self.issue_description = "This is a test issue" self.issue_number = "123" + @patch('aider_gitea.secrets.llm_api_key', return_value="fake-api-key") @patch('aider_gitea.run_cmd') @patch('aider_gitea.push_changes') @patch('subprocess.run') - def test_solve_issue_with_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd): + def test_solve_issue_with_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key): # Setup mocks mock_run_cmd.return_value = True mock_push_changes.return_value = True @@ -53,10 +54,11 @@ class TestSolveIssueInRepository: assert mock_run_cmd.call_count >= 8 # Verify all expected commands were run mock_push_changes.assert_called_once() + @patch('aider_gitea.secrets.llm_api_key', return_value="fake-api-key") @patch('aider_gitea.run_cmd') @patch('aider_gitea.push_changes') @patch('subprocess.run') - def test_solve_issue_without_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd): + def test_solve_issue_without_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key): # Setup mocks mock_run_cmd.return_value = True -- 2.45.1 From 8d6764138117226aa588e5bac405ef5f2c8610a9 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 15 Apr 2025 00:46:12 +0200 Subject: [PATCH 3/3] Ruff --- aider_gitea/__init__.py | 9 ++-- aider_gitea/gitea_client.py | 8 +++- test/test_solve_issue_in_repository.py | 64 +++++++++++++------------- 3 files changed, 46 insertions(+), 35 deletions(-) diff --git a/aider_gitea/__init__.py b/aider_gitea/__init__.py index 4799100..388e033 100644 --- a/aider_gitea/__init__.py +++ b/aider_gitea/__init__.py @@ -304,7 +304,7 @@ def solve_issue_in_repository( run_cmd(['bash', '-c', RUFF_FORMAT_AND_AUTO_FIX], tmpdirname, check=False) run_cmd(['git', 'add', '.'], tmpdirname) run_cmd(['git', 'commit', '-m', 'Initial ruff pass'], tmpdirname, check=False) - + # Save the commit hash after ruff but before aider result = subprocess.run( ['git', 'rev-parse', 'HEAD'], @@ -340,9 +340,12 @@ def solve_issue_in_repository( text=True, ) files_changed = result.stdout.strip() - + if not files_changed: - logger.info('Aider did not make any changes beyond the initial ruff pass for issue #%s', issue_number) + logger.info( + 'Aider did not make any changes beyond the initial ruff pass for issue #%s', + issue_number, + ) return False # Push changes diff --git a/aider_gitea/gitea_client.py b/aider_gitea/gitea_client.py index 76028f8..267c3c7 100644 --- a/aider_gitea/gitea_client.py +++ b/aider_gitea/gitea_client.py @@ -158,7 +158,13 @@ class GiteaClient: requests.HTTPError: If the API request fails. """ url = f'{self.gitea_url}/repos/{owner}/{repo}/pulls' - json_data = {'title': title, 'body': body, 'head': head, 'base': base, 'labels': labels} + json_data = { + 'title': title, + 'body': body, + 'head': head, + 'base': base, + 'labels': labels, + } response = self.session.post(url, json=json_data) response.raise_for_status() diff --git a/test/test_solve_issue_in_repository.py b/test/test_solve_issue_in_repository.py index 6d8bdf2..33c90c8 100644 --- a/test/test_solve_issue_in_repository.py +++ b/test/test_solve_issue_in_repository.py @@ -1,43 +1,43 @@ -import subprocess -import tempfile from pathlib import Path from unittest.mock import MagicMock, patch -import pytest - from aider_gitea import solve_issue_in_repository class TestSolveIssueInRepository: def setup_method(self): self.args = MagicMock() - self.args.gitea_url = "https://gitea.example.com" - self.args.owner = "test-owner" - self.args.repo = "test-repo" - self.args.base_branch = "main" - - self.gitea_client = MagicMock() - self.tmpdirname = Path("/tmp/test-repo") - self.branch_name = "issue-123-test-branch" - self.issue_title = "Test Issue" - self.issue_description = "This is a test issue" - self.issue_number = "123" + self.args.gitea_url = 'https://gitea.example.com' + self.args.owner = 'test-owner' + self.args.repo = 'test-repo' + self.args.base_branch = 'main' - @patch('aider_gitea.secrets.llm_api_key', return_value="fake-api-key") + self.gitea_client = MagicMock() + self.tmpdirname = Path('/tmp/test-repo') + self.branch_name = 'issue-123-test-branch' + self.issue_title = 'Test Issue' + self.issue_description = 'This is a test issue' + self.issue_number = '123' + + @patch('aider_gitea.secrets.llm_api_key', return_value='fake-api-key') @patch('aider_gitea.run_cmd') @patch('aider_gitea.push_changes') @patch('subprocess.run') - def test_solve_issue_with_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key): + def test_solve_issue_with_aider_changes( + self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key, + ): # Setup mocks mock_run_cmd.return_value = True mock_push_changes.return_value = True - + # Mock subprocess.run to return different commit hashes and file changes mock_subprocess_run.side_effect = [ - MagicMock(stdout="abc123\n", returncode=0), # First git rev-parse - MagicMock(stdout="file1.py\nfile2.py\n", returncode=0), # git diff with changes + MagicMock(stdout='abc123\n', returncode=0), # First git rev-parse + MagicMock( + stdout='file1.py\nfile2.py\n', returncode=0, + ), # git diff with changes ] - + # Call the function result = solve_issue_in_repository( self.args, @@ -46,28 +46,30 @@ class TestSolveIssueInRepository: self.issue_title, self.issue_description, self.issue_number, - self.gitea_client + self.gitea_client, ) - + # Verify results assert result is True assert mock_run_cmd.call_count >= 8 # Verify all expected commands were run mock_push_changes.assert_called_once() - @patch('aider_gitea.secrets.llm_api_key', return_value="fake-api-key") + @patch('aider_gitea.secrets.llm_api_key', return_value='fake-api-key') @patch('aider_gitea.run_cmd') @patch('aider_gitea.push_changes') @patch('subprocess.run') - def test_solve_issue_without_aider_changes(self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key): + def test_solve_issue_without_aider_changes( + self, mock_subprocess_run, mock_push_changes, mock_run_cmd, mock_llm_api_key, + ): # Setup mocks mock_run_cmd.return_value = True - + # Mock subprocess.run to return same commit hash and no file changes mock_subprocess_run.side_effect = [ - MagicMock(stdout="abc123\n", returncode=0), # First git rev-parse - MagicMock(stdout="", returncode=0), # git diff with no changes + MagicMock(stdout='abc123\n', returncode=0), # First git rev-parse + MagicMock(stdout='', returncode=0), # git diff with no changes ] - + # Call the function result = solve_issue_in_repository( self.args, @@ -76,9 +78,9 @@ class TestSolveIssueInRepository: self.issue_title, self.issue_description, self.issue_number, - self.gitea_client + self.gitea_client, ) - + # Verify results assert result is False assert mock_push_changes.call_count == 0 # push_changes should not be called -- 2.45.1