aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Smith <daniel.smith@qt.io>2025-06-05 11:26:15 +0200
committerDaniel Smith <daniel.smith@qt.io>2025-06-30 09:56:09 +0000
commit36141c9a38da51f3ad12d242c94c9d25461378b6 (patch)
tree4d70c2f1b566b3a4176b24e9ec2cf8039623ebb2
parent887f835f864080cc5c16199b1be180abbe2555c1 (diff)
Avoid posting duplicate comments on new patchsets
In order to cut down on noise, the bot will only post on files which have not been commented on previously in the change, even if from an earlier patchset. Fixes: QTQAINFRA-7233 Change-Id: If95ea860398bdca2f62c0e56c1d4ac9f241bace6 Reviewed-by: Daniel Smith <daniel.smith@qt.io>
-rw-r--r--src/main.py59
1 files changed, 59 insertions, 0 deletions
diff --git a/src/main.py b/src/main.py
index 6b27e42..98287db 100644
--- a/src/main.py
+++ b/src/main.py
@@ -341,6 +341,44 @@ def generate_review(comments_per_file):
return review
+async def get_existing_bot_comments(data, session):
+ """Fetch existing comments from Gerrit for the given change to find files already commented on by the bot."""
+ change_number = data['change']['number']
+ url = f"https://codereview.qt-project.org/a/changes/{change_number}/comments"
+ headers = {'Authorization': 'Basic ' + GERRIT_AUTH}
+ commented_files = set()
+
+ log.info("%s: Fetching existing comments", change_number)
+ try:
+ async with session.get(url, headers=headers) as response:
+ if response.status == 200:
+ # Gerrit API response starts with )]}'
+ raw_response_text = await response.text()
+ if raw_response_text.startswith(")]}'\n"):
+ json_text = raw_response_text[5:]
+ else:
+ json_text = raw_response_text
+
+ all_comments = json.loads(json_text)
+ for file_path, file_comments in all_comments.items():
+ for comment_info in file_comments:
+ if 'author' in comment_info and \
+ comment_info['author'].get('username') == GERRIT_USERNAME:
+ commented_files.add(file_path)
+ break # Found a bot comment for this file
+ log.info("%s: Found %d files previously commented on by the bot: %s",
+ change_number, len(commented_files), commented_files if commented_files else "None")
+ else:
+ response_text = await response.text()
+ log.error("%s: Error fetching comments: %s %s", change_number, response.status, response_text)
+ except aiohttp.ClientError as e:
+ log.error('%s: ClientError fetching comments: %s', change_number, str(e))
+ except json.JSONDecodeError as e:
+ log.error('%s: JSONDecodeError parsing comments: %s', change_number, str(e))
+
+ return commented_files
+
+
async def post_review(data, review, retry=0):
"""Post the review to Gerrit."""
change_number = data['change']['number']
@@ -541,6 +579,27 @@ async def handle(request):
await clone_repo(data)
await checkout_patch(data)
comments = await run_security_header_check(data)
+
+ # Filter comments if this is not the first patchset and there are comments
+ # Default patchset number to 1 if not present in data
+ current_patchset_number = data['patchSet'].get('number', 1)
+ if comments and current_patchset_number > 1:
+ log.info("%s: Patchset %s > 1, checking for existing bot comments.",
+ data['change']['number'], current_patchset_number)
+ # Create a new session for this specific task, or pass one if available
+ async with aiohttp.ClientSession() as session:
+ previously_commented_files = await get_existing_bot_comments(data, session)
+
+ if previously_commented_files:
+ original_comment_count = len(comments)
+ comments = {
+ file: comment_info
+ for file, comment_info in comments.items()
+ if file not in previously_commented_files
+ }
+ if len(comments) < original_comment_count:
+ log.info("%s: Filtered comments from %d to %d based on previous bot activity.",
+ data['change']['number'], original_comment_count, len(comments))
except Exception as e:
log.error("Error: %s", str(e))
await post_teams_error_message(data, str(e))