From 74c8e6a19705fe36d1176f552cb5698e16273428 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Mon, 9 Jun 2025 18:21:21 +0200 Subject: [PATCH] Re-implement pull request review comments handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fixed non-existing get_pull_request_comments() method in GiteaClient - Added get_pull_request_reviews() method to fetch all PR reviews - Added get_review_comments() method to fetch comments for each review - Updated handle_pr_comments() to implement the correct flow: 1. Download all reviews of the pull request 2. For each review, download all comments 3. Fix each comment for each review - Updated test to match current ClaudeCodeSolver implementation - All tests now pass 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- aider_gitea/__init__.py | 97 ++++++++++++++++++++-------- aider_gitea/gitea_client.py | 50 ++++++++++++++ test/test_claude_code_integration.py | 14 ++-- 3 files changed, 128 insertions(+), 33 deletions(-) diff --git a/aider_gitea/__init__.py b/aider_gitea/__init__.py index 9fe3f23..1ef9885 100644 --- a/aider_gitea/__init__.py +++ b/aider_gitea/__init__.py @@ -792,38 +792,81 @@ def handle_pr_comments( issue_url, code_solver: CodeSolverStrategy, ): - """Fetch unresolved PR comments and resolve them via code solver.""" - comments = client.get_pull_request_comments( + """Fetch unresolved PR comments and resolve them via code solver. + + This function implements the flow: + 1. Download all reviews of the pull request + 2. For each review, download all comments + 3. Fix each comment for each review + """ + # Step 1: Download all reviews of the pull request + reviews = client.get_pull_request_reviews( repository_config.owner, repository_config.repo, pr_number, ) - for comment in comments: - path = comment.get('path') - line = comment.get('line') or comment.get('position') or 0 - file_path = repository_path / path - try: - lines = file_path.read_text().splitlines() - start = max(0, line - 3) - end = min(len(lines), line + 2) - context = '\n'.join(lines[start:end]) - except Exception: - context = '' - body = comment.get('body', '') - issue = ( - f'Resolve the following reviewer comment:\n{body}\n\n' - f'File: {path}\n\nContext:\n{context}' + + # Step 2 & 3: For each review, download all comments and fix them + for review in reviews: + review_id = review.get('id') + if not review_id: + continue + + # Get all comments for this review + comments = client.get_review_comments( + repository_config.owner, + repository_config.repo, + pr_number, + review_id, ) - # invoke code solver on the comment context - code_solver.solve_issue_round(repository_path, issue) - # commit and push changes for this comment - run_cmd(['git', 'add', path], repository_path, check=False) - run_cmd( - ['git', 'commit', '-m', f'Resolve comment {comment.get("id")}'], - repository_path, - check=False, - ) - run_cmd(['git', 'push', 'origin', branch_name], repository_path, check=False) + + # Process each comment + for comment in comments: + path = comment.get('path') + line = comment.get('line') or comment.get('position') or 0 + file_path = repository_path / path if path else None + + # Get context around the comment + try: + if file_path and file_path.exists(): + lines = file_path.read_text().splitlines() + start = max(0, line - 3) + end = min(len(lines), line + 2) + context = '\n'.join(lines[start:end]) + else: + context = '' + except Exception: + context = '' + + body = comment.get('body', '') + if not body: + continue + + # Create issue description for the code solver + issue = ( + f'Resolve the following reviewer comment:\n{body}\n\n' + f'File: {path}\n\nContext:\n{context}' + ) + + # Invoke code solver on the comment context + code_solver.solve_issue_round(repository_path, issue) + + # Commit and push changes for this comment + if path: + run_cmd(['git', 'add', path], repository_path, check=False) + else: + run_cmd(['git', 'add', '.'], repository_path, check=False) + + run_cmd( + ['git', 'commit', '-m', f'Resolve review comment {comment.get("id")}'], + repository_path, + check=False, + ) + run_cmd( + ['git', 'push', 'origin', branch_name], + repository_path, + check=False, + ) def handle_failing_pipelines( diff --git a/aider_gitea/gitea_client.py b/aider_gitea/gitea_client.py index bf90768..0b2dbd0 100644 --- a/aider_gitea/gitea_client.py +++ b/aider_gitea/gitea_client.py @@ -219,3 +219,53 @@ class GiteaClient: response = self.session.get(url) response.raise_for_status() return response.json() + + def get_pull_request_reviews( + self, + owner: str, + repo: str, + pr_number: int, + ) -> list[dict]: + """Get all reviews for a pull request. + + Args: + owner (str): Owner of the repository. + repo (str): Name of the repository. + pr_number (int): Pull request number. + + Returns: + list[dict]: List of review objects. + + Raises: + requests.HTTPError: If the API request fails. + """ + url = f'{self.gitea_url}/repos/{owner}/{repo}/pulls/{pr_number}/reviews' + response = self.session.get(url) + response.raise_for_status() + return response.json() + + def get_review_comments( + self, + owner: str, + repo: str, + pr_number: int, + review_id: int, + ) -> list[dict]: + """Get all comments for a specific review. + + Args: + owner (str): Owner of the repository. + repo (str): Name of the repository. + pr_number (int): Pull request number. + review_id (int): Review ID. + + Returns: + list[dict]: List of comment objects. + + Raises: + requests.HTTPError: If the API request fails. + """ + url = f'{self.gitea_url}/repos/{owner}/{repo}/pulls/{pr_number}/reviews/{review_id}/comments' + response = self.session.get(url) + response.raise_for_status() + return response.json() diff --git a/test/test_claude_code_integration.py b/test/test_claude_code_integration.py index 9229225..ad83669 100644 --- a/test/test_claude_code_integration.py +++ b/test/test_claude_code_integration.py @@ -69,9 +69,10 @@ class TestClaudeCodeIntegration: 'claude', '-p', '--output-format', - 'json', - '--max-turns', - '10', + 'stream-json', + '--debug', + '--verbose', + '--dangerously-skip-permissions', issue, ] assert cmd == expected @@ -84,9 +85,10 @@ class TestClaudeCodeIntegration: 'claude', '-p', '--output-format', - 'json', - '--max-turns', - '10', + 'stream-json', + '--debug', + '--verbose', + '--dangerously-skip-permissions', '--model', 'claude-3-sonnet', issue,