blob: 16a7422eaeae611a19bbd5b5c75f7c834d5d4f66 [file] [log] [blame] [edit]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import json
import os
import re
import subprocess
import sys
def get_change_id():
"""Extracts the Change-Id from the HEAD commit message."""
try:
# Get the commit message of HEAD
result = subprocess.run(
["git", "log", "-n", "1"],
capture_output=True,
text=True,
check=True,
)
# Look for Change-Id: I...
match = re.search(r"Change-Id: (I[a-f0-9]+)", result.stdout)
if match:
return match.group(1)
return None
except subprocess.CalledProcessError:
print("Error: Failed to run git log.")
sys.exit(1)
def get_change_details(change_id):
"""Queries Gerrit for the change details using the Change-Id."""
base_url = "https://fuchsia-review.googlesource.com"
query_url = f"{base_url}/changes/?q=change:{change_id}&o=DETAILED_ACCOUNTS"
try:
# Use simple curl. If you need authentication (for internal changes),
# ensure your .netrc is configured or use gob-curl if available.
cmd = ["curl", "-L", "-s", query_url]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
content = result.stdout
# Strip XSSI protection prefix
if content.startswith(")]}'"):
content = content[4:]
data = json.loads(content)
if not data:
return None, None
# Return the first match.
# Data is a list of changes.
change = data[0]
return base_url, change
except Exception as e:
print(f"Error fetching change details: {e}")
sys.exit(1)
def get_comments(base_url, change_number):
"""Fetches comments for the specific change number."""
url = f"{base_url}/changes/{change_number}/comments"
try:
cmd = ["curl", "-L", "-s", url]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
content = result.stdout
if content.startswith(")]}'"):
content = content[4:]
return json.loads(content)
except Exception as e:
print(f"Error fetching comments: {e}")
sys.exit(1)
def print_comments(comments_data):
"""Prints unresolved comments with context."""
has_unresolved = False
for file_path, comments in comments_data.items():
by_id = {c["id"]: c for c in comments if "id" in c}
threads = {}
for c in comments:
curr = c
while curr.get("in_reply_to") in by_id:
curr = by_id[curr["in_reply_to"]]
root_id = curr.get("id")
if not root_id:
root_id = id(c)
if root_id not in threads:
threads[root_id] = []
threads[root_id].append(c)
unresolved_comments = []
for thread_comments in threads.values():
thread_comments.sort(key=lambda x: x.get("updated", ""))
# A thread is unresolved if its *latest* comment is unresolved
if thread_comments[-1].get("unresolved", False):
unresolved_comments.extend(thread_comments)
if not unresolved_comments:
continue
# sort comments by line
unresolved_comments.sort(key=lambda x: x.get("line", 0))
for comment in unresolved_comments:
has_unresolved = True
line = comment.get("line", "FILE")
msg = comment.get("message", "").strip()
author = comment.get("author", {}).get("name", "Unknown")
print(f"\n{'='*60}")
print(f"File: {file_path}:{line}")
print(f"Author: {author}")
print(f"Comment: {msg}")
# Try to print the code line
if isinstance(line, int) and os.path.exists(file_path):
try:
with open(
file_path, "r", encoding="utf-8", errors="replace"
) as f:
# Read file, get line (1-indexed)
lines = f.readlines()
if 0 <= line - 1 < len(lines):
code_line = lines[line - 1].strip()
print(f"Code: {code_line}")
except Exception:
pass
if not has_unresolved:
print("\nNo unresolved comments found.")
else:
print(f"\n{'='*60}")
def main():
print("Finding Change-Id from HEAD...")
change_id_str = get_change_id()
if not change_id_str:
print("Error: Could not find Change-Id in HEAD commit message.")
sys.exit(1)
print(f"Found Change-Id: {change_id_str}")
print("Fetching change details...")
base_url, change_info = get_change_details(change_id_str)
if not change_info:
print(f"Error: Change {change_id_str} not found on Gerrit.")
sys.exit(1)
change_number = change_info.get("_number")
print(f"Found Change {change_number} ({base_url}/c/{change_number})")
print("Fetching comments...")
comments_data = get_comments(base_url, change_number)
print_comments(comments_data)
if __name__ == "__main__":
main()