-
-
Notifications
You must be signed in to change notification settings - Fork 8
Hide review comments and resolve or delete review conversations #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
cpp_linter/cli.py
Outdated
| #: See :std:option:`--delete-comments`. | ||
| delete_comments: bool = True | ||
| #: See :std:option:`--reuse-comments`. | ||
| reuse_comments: bool = True |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adds 2 new user options:
- delete_comments: Old out of date review comment conversations/suggestions will be deleted if set to true of marked as resolved if set to False
- reuse_comments: On subsequent runs if an issue still exists and an existing review comment conversation exists then if set to true then the existing conversation will be used. If False a new conversation will be made and the old one resolved/deleted depending on
delete_comments
|
You'll also need to update the PR reviews test with mocked responses to the graphQL API. cpp-linter/tests/reviews/test_pr_review.py Line 119 in baee3c2
You'll find more about that at requests-mock lib's docs
|
Added the mock responses (I think I did it correctly) |
With the html marker used in our other generated comments. This is to avoid ambiguity and help on-board new features in #131.
With the html marker used in our other generated comments. This is to avoid ambiguity and help on-board new features in #131.
|
Warning Rate limit exceeded@maoliver-amd has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 24 minutes and 2 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughThe changes introduced in this pull request enhance the functionality of the Changes
Possibly related PRs
Suggested labels
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Co-authored-by: Brendan <[email protected]>
Co-authored-by: Brendan <[email protected]>
Yes. If there are unresolved suggestions in old reviews, then dismissing them can prevent those unresolved suggestions getting addressed.
Good question. Let me propose an example as a goal:
and after another commit where 1 concern is addressed but 2 new ones pop up
I don't think we should be updating old reviews' summary comment when posting a new review. That would seem superfluous. |
|
Today, I'm going to tackle using pagination when getting the review threads with graphql. I'll be using the upstream branch ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (2)
cpp_linter/clang_tools/patcher.py (1)
132-143: Simplify string concatenation for readabilityConsider using a single f-string to construct the summary message instead of concatenating multiple strings. This enhances readability and maintains consistency.
Apply this diff to improve the code:
- summary += ( - f"Only {posted_tool_advice[tool_name]} out of " - + f"{self.tool_total[tool_name]} {tool_name}" - + " new concerns fit within this pull request's diff." - ) + summary += f"Only {posted_tool_advice[tool_name]} out of {self.tool_total[tool_name]} {tool_name} new concerns fit within this pull request's diff."cpp_linter/rest_api/github_api.py (1)
507-574: Improve logging for clarity in_close_review_commentWhen deleting a comment, the logging message references
thread_id, which may not correspond to the operation being performed. Update the logging to reference the correct ID based on the operation.Apply this diff to enhance the logging:
response = self.api_request( url=f"{self.api_url}/graphql", method="POST", data=json.dumps({"query": mutation, "variables": variables}), strict=False, ) - logger.debug( - "%s review comment thread %s (thread_id: %s)", - operation.title(), - "failed" if response.status_code != 200 else "succeeded", - thread_id, - ) + logger.debug( + "%s review comment %s (%s: %s)", + operation.title(), + "failed" if response.status_code != 200 else "succeeded", + "comment_id" if delete else "thread_id", + comment_id if delete else thread_id, + )This ensures that the log message accurately reflects the operation and references the correct identifier.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cpp_linter/clang_tools/patcher.py(4 hunks)cpp_linter/rest_api/github_api.py(7 hunks)
🔇 Additional comments (4)
cpp_linter/clang_tools/patcher.py (1)
75-80: Initialization of self.tool_reused is appropriate
The new attribute self.tool_reused is correctly initialized as a dictionary to track reused concerns from previous reviews.
cpp_linter/rest_api/github_api.py (3)
40-69: GraphQL queries are well-defined with proper variable usage
The GraphQL query QUERY_REVIEW_COMMENTS is correctly defined using variables, enhancing security and preventing injection risks.
336-337: Method signature updates align with new functionality
The addition of delete_review_comments and reuse_review_comments parameters to the post_review method appropriately extends functionality.
735-765: Ensure ignored_reviews contains valid review node IDs
In the _hide_stale_reviews method, confirm that the ignored_reviews list contains valid node_id strings to prevent unintended hiding of active reviews.
Run the following script to validate the ignored_reviews:
This script checks that each node_id in ignored_reviews exists in the list of current reviews.
|
OK I have added code to not dismiss old reviews if they are reused and to correct the suggestion count in new reviews |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (4)
cpp_linter/rest_api/github_api.py (2)
474-475: Update method docstring to include new parametersThe method has new parameters but the docstring hasn't been updated to reflect these changes. Consider adding parameter descriptions for
delete_review_commentsandreuse_review_comments.def post_review( self, files: List[FileObj], tidy_review: bool, format_review: bool, no_lgtm: bool, passive_reviews: bool, clang_versions: ClangVersions, delete_review_comments: bool = True, reuse_review_comments: bool = True, + ): + """Post a review for the pull request. + + :param files: List of files to review + :param tidy_review: Flag to enable clang-tidy review + :param format_review: Flag to enable clang-format review + :param no_lgtm: Flag to control LGTM comments + :param passive_reviews: Flag for passive review mode + :param clang_versions: Clang tool versions + :param delete_review_comments: Flag to delete rather than resolve outdated comments + :param reuse_review_comments: Flag to reuse existing comments if they match + """ ):
713-778: Consider breaking down the complex review comment checking logicThe
_check_reused_commentsmethod is quite long and handles multiple responsibilities. Consider breaking it down into smaller, more focused methods for better maintainability.Suggested structure:
def _check_reused_comments(self, delete_review_comments: bool, reuse_review_comments: bool, review_comments: ReviewComments) -> List[str]: """Main orchestrator method for handling review comments.""" ignored_reviews = [] found_threads = self._get_existing_review_comments( no_dismissed=reuse_review_comments and not delete_review_comments ) if not found_threads: return ignored_reviews if reuse_review_comments: return self._handle_reusable_comments(found_threads, review_comments) else: self._close_all_comments(found_threads, delete_review_comments) return ignored_reviews def _handle_reusable_comments(self, found_threads: List[Dict], review_comments: ReviewComments) -> List[str]: """Handle reusable comments logic.""" ignored_reviews = [] existing_review_comments = [] for thread in found_threads: self._process_thread_comments(thread, review_comments, ignored_reviews, existing_review_comments) review_comments.remove_reused_suggestions(existing_review_comments=existing_review_comments) return ignored_reviews def _process_thread_comments(self, thread: Dict, review_comments: ReviewComments, ignored_reviews: List[str], existing_review_comments: List): """Process individual thread comments.""" for comment in thread["comments"]["nodes"]: if self._is_comment_reusable(comment, thread): self._try_reuse_comment(comment, thread, review_comments, ignored_reviews, existing_review_comments) else: self._close_review_comment(thread["id"], comment["id"], delete_review_comments)cpp_linter/clang_tools/patcher.py (2)
132-134: Consider improving readability of the conditionThe condition could be more readable by breaking it down into smaller parts or using intermediate variables.
Consider this refactor:
- if len(comments) and ( - posted_tool_advice[tool_name] != self.tool_total[tool_name] - or self.tool_reused[tool_name] != 0 - ): + has_comments = len(comments) > 0 + has_unposted_concerns = posted_tool_advice[tool_name] != self.tool_total[tool_name] + has_reused_concerns = self.tool_reused[tool_name] != 0 + if has_comments and (has_unposted_concerns or has_reused_concerns):
154-178: Consider adding input validation for existing review commentsThe
remove_reused_suggestionsmethod processes existing review comments without explicit validation. Consider adding validation to ensure the safety of processing user-provided content.Consider:
- Validating the structure and content of existing review comments
- Adding length limits for comments
- Sanitizing or escaping special characters in comments
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cpp_linter/clang_tools/patcher.py(4 hunks)cpp_linter/rest_api/github_api.py(7 hunks)
🧰 Additional context used
📓 Learnings (1)
cpp_linter/rest_api/github_api.py (1)
Learnt from: maoliver-amd
PR: cpp-linter/cpp-linter#131
File: cpp_linter/rest_api/github_api.py:0-0
Timestamp: 2024-12-09T09:50:38.136Z
Learning: In `cpp_linter/rest_api/github_api.py`, when using the GitHub GraphQL API, the maximum allowable pagination limit for `first` and `last` parameters is 100.
🔇 Additional comments (3)
cpp_linter/rest_api/github_api.py (1)
40-92: Well-structured GraphQL queries with proper pagination limits!
The implementation follows GraphQL best practices with proper variable usage and pagination limits. The maximum limit of 100 is correctly used for both review threads and comments, which aligns with GitHub's API limitations.
cpp_linter/clang_tools/patcher.py (2)
75-80: LGTM! Well-structured addition of reused concerns tracking
The new tool_reused dictionary follows the established pattern and is properly documented.
168-177: Correct the condition when updating 'clang-format' counts
The condition for updating the clang-format counts incorrectly checks clang_tidy_comments. It should check clang_format_comments to ensure accurate tracking.
|
Awesome! I'm just now falling asleep, so I'll take a look tomorrow. I also implemented paginated graphql on the upstream branch, but I haven't finished updating the tests to validate it. Updating the test may be harder than I thought. This PR depends on the state of the PR being reviewed (first review, second review, etc). It might be easier to rewrite several simpler tests instead of 1 heavily parametrized test. |
2bndy5
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maoliver-amd I'm curious. What is your preferred programming language?
I ask because if you enable pre-commit for this repo (in your local clone) or just run mypy, you'd see that there are typing errors. The reasons these errors go unnoticed here is because
- The CI workflow that runs
pre-commitis not triggered by PR sync events (yet -- I will be changing that) - The tests do not actually invoke code with typing errors or the dynamic runtime types happen to work in the tests.
Co-authored-by: Brendan <[email protected]>
Very much a C/C++ programmer, hence the interest in clang CI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
cpp_linter/rest_api/github_api.py (1)
748-754: Simplify line number handling logicThe current implementation for handling line numbers is a bit complex. Consider using the null coalescing pattern for better readability.
Apply this diff:
- line_start = ( - comment.get("startLine", None) - or comment.get("originalStartLine", None) - or -1 - ) - line_end = comment.get("line", None) or comment["originalLine"] + line_start = next((val for val in [ + comment.get("startLine"), + comment.get("originalStartLine"), + -1 + ] if val is not None), -1) + line_end = comment.get("line") or comment["originalLine"]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
cpp_linter/clang_tools/patcher.py(4 hunks)cpp_linter/rest_api/github_api.py(7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- cpp_linter/clang_tools/patcher.py
🧰 Additional context used
📓 Learnings (1)
cpp_linter/rest_api/github_api.py (1)
Learnt from: maoliver-amd
PR: cpp-linter/cpp-linter#131
File: cpp_linter/rest_api/github_api.py:0-0
Timestamp: 2024-12-09T09:50:38.136Z
Learning: In `cpp_linter/rest_api/github_api.py`, when using the GitHub GraphQL API, the maximum allowable pagination limit for `first` and `last` parameters is 100.
🔇 Additional comments (4)
cpp_linter/rest_api/github_api.py (4)
40-92: Well-structured GraphQL operations!
The GraphQL queries and mutations are well-defined with proper parameterization and pagination limits. The code follows GraphQL best practices and maintains good readability.
474-475: Clean implementation of review comment management!
The new parameters and logic for handling existing comments are well-implemented. The code maintains a clear flow and proper handling of review states.
Also applies to: 507-516
Line range hint 573-611: Good error handling and documentation!
The updated _dismiss_stale_reviews method has proper error handling and clear documentation. The addition of the ignored_reviews parameter is well-implemented.
624-629: 🛠️ Refactor suggestion
Consider using parameterized GraphQL queries
The GraphQL query construction should use variables to prevent potential injection risks.
Apply this diff:
- data=json.dumps({"query": query, "variables": variables}),
+ data=json.dumps({
+ "query": query,
+ "variables": variables,
+ "operationName": "GetReviewComments"
+ }),Likely invalid or redundant comment.
Ah yes. I should've guessed, but its better not to assume things about people. At least you understand the importance of static typing, even though python is a dynamically typed language. FWIW, we'll be migrating to cpp-linter-rs, a rust port of this python project. Hopefully in the future, C/C++ aficionados will feel more at home tinkering with rust. I wouldn't have learned rust as easily as I did if I didn't understand the woes of C/C++ pointers beforehand. I've also started porting this feature into cpp-linter-rs to maintain feature parity. We're pretty close to making the switch in cpp-linter-action v3. |
|
I'm beginning to think the new CLI option The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
cpp_linter/cli.py (1)
368-369: Fix grammatical error in help text.The word "effects" should be "affects" as it's being used as a verb.
Apply this change:
-This only effects review comments made when either +This only affects review comments made when either
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
cpp_linter/rest_api/github_api.py (1)
725-781: Consider breaking down the complex logic into smaller functionsWhile the implementation is correct, the method's complexity could be reduced by extracting some logic into helper functions:
- Comment matching logic (lines 750-761)
- Line number handling (lines 744-749)
Example refactor:
def _check_reused_comments( self, delete_review_comments: bool, review_comments: ReviewComments, ) -> List[str]: + def _get_line_numbers(comment: Dict[str, Any]) -> Tuple[int, int]: + line_start = ( + comment.get("startLine", None) + or comment.get("originalStartLine", None) + or -1 + ) + line_end = comment.get("line", None) or comment["originalLine"] + return line_start, line_end + + def _is_matching_comment( + suggestion: Any, + comment: Dict[str, Any], + thread: Dict[str, Any], + line_start: int, + line_end: int, + existing_review_comments: List[Any] + ) -> bool: + return ( + suggestion.file_name == comment["path"] + and suggestion.line_start == line_start + and suggestion.line_end == line_end + and f"{COMMENT_MARKER}{suggestion.comment}" == comment["body"] + and suggestion not in existing_review_comments + and thread["isResolved"] is False + and thread["isCollapsed"] is False + and comment["pullRequestReview"]["isMinimized"] is False + ) ignored_reviews = [] found_threads = self._get_existing_review_comments( no_dismissed=not delete_review_comments ) if found_threads: existing_review_comments = [] for thread in found_threads: for comment in thread["comments"]["nodes"]: found = False if "originalLine" not in comment: raise ValueError( "GraphQL response malformed: 'originalLine' missing in comment" ) - line_start = ( - comment.get("startLine", None) - or comment.get("originalStartLine", None) - or -1 - ) - line_end = comment.get("line", None) or comment["originalLine"] + line_start, line_end = _get_line_numbers(comment) for suggestion in review_comments.suggestions: - if ( - suggestion.file_name == comment["path"] - and suggestion.line_start == line_start - and suggestion.line_end == line_end - and f"{COMMENT_MARKER}{suggestion.comment}" - == comment["body"] - and suggestion not in existing_review_comments - and thread["isResolved"] is False - and thread["isCollapsed"] is False - and comment["pullRequestReview"]["isMinimized"] is False - ): + if _is_matching_comment( + suggestion, + comment, + thread, + line_start, + line_end, + existing_review_comments + ): found = True logger.info( "Using existing review comment: path='%s', line_start='%s', line_end='%s'",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
cpp_linter/rest_api/github_api.py(7 hunks)
🧰 Additional context used
📓 Learnings (1)
cpp_linter/rest_api/github_api.py (1)
Learnt from: maoliver-amd
PR: cpp-linter/cpp-linter#131
File: cpp_linter/rest_api/github_api.py:0-0
Timestamp: 2024-12-09T09:50:38.136Z
Learning: In `cpp_linter/rest_api/github_api.py`, when using the GitHub GraphQL API, the maximum allowable pagination limit for `first` and `last` parameters is 100.
🔇 Additional comments (5)
cpp_linter/rest_api/github_api.py (5)
40-92: Well-structured GraphQL queries with proper parameterization
The implementation follows best practices with:
- Proper parameterization using GraphQL variables
- Correct pagination limits (100 is GitHub's maximum)
- Clear separation of concerns between different operations (query vs mutations)
473-473: Well-documented method signature updates
The parameter additions are properly typed and documented:
delete_review_comments: bool = Truein post_reviewignored_reviews: Optional[List[str]] = Nonein _dismiss_stale_reviews
Also applies to: 571-579
610-656: Robust implementation with proper error handling
The method effectively retrieves review comments with:
- Proper GraphQL error handling using try-except
- Clear documentation of parameters
- Efficient filtering of comments
658-690: Clean implementation with proper logging
The method effectively handles both resolving and deleting comments with:
- Clear parameter documentation
- Proper error handling
- Consistent logging format
692-724: Effective implementation for hiding stale reviews
The method properly handles:
- Pagination of review comments
- GraphQL mutation for minimizing comments
- Proper logging of operations
Going forwardThe current state of this PR looks good except for the tests. I can take care of the tests, but I will do so from the
@maoliver-amd I cannot merge this into main without rewriting the PR reviews test. Therefore, I'll close this PR and open a new one using the Thank you for putting us on the right path here! 😍 |
This extends the existing cang-tidy/format review capability by adding additional functionality:
Uses graphql queries to provide additional functionality beyond what the github api suppots
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Tests