-
Notifications
You must be signed in to change notification settings - Fork 135
Expand file tree
/
Copy pathget_pr_review_comments.py
More file actions
executable file
·182 lines (151 loc) · 6.93 KB
/
get_pr_review_comments.py
File metadata and controls
executable file
·182 lines (151 loc) · 6.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python3
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Fetches and formats review comments from a GitHub Pull Request."""
import argparse
import os
import sys
import firebase_github # Assumes firebase_github.py is in the same directory or python path
# Attempt to configure logging for firebase_github if absl is available
try:
from absl import logging as absl_logging
# Set verbosity for absl logging if you want to see logs from firebase_github
# absl_logging.set_verbosity(absl_logging.INFO)
except ImportError:
pass # firebase_github.py uses absl.logging.info, so this won't redirect.
def print_contextual_diff_hunk(diff_hunk, comment_position, context_lines_count):
if not diff_hunk or not diff_hunk.strip():
print("(No diff hunk available or content is empty)")
return
hunk_lines = diff_hunk.split('\n') # Note: Python's split('\n') is generally fine.
# Case 1: User explicitly wants the full hunk
if context_lines_count == 0:
print(diff_hunk)
return
# Case 2: Contextual display is requested (context_lines_count > 0),
# but comment is not on a specific line or position is invalid for contextual display.
if comment_position is None or comment_position < 1 or comment_position > len(hunk_lines):
print("(Comment is not on a specific line in the diff, or position is invalid; full hunk context suppressed by --context-lines setting)")
# As an alternative to the above message, if the hunk is small, one might choose to print it.
# However, sticking to the user's feedback of not wanting full hunks when context is specified:
# print(diff_hunk) # This would be the old behavior for this case.
return
# Case 3: Contextual display is possible and requested
comment_line_index = comment_position - 1 # Convert to 0-indexed
start_index = max(0, comment_line_index - context_lines_count)
end_index = min(len(hunk_lines), comment_line_index + context_lines_count + 1)
# The following line was identified as redundant and is removed:
# start_index = min(start_index, comment_line_index if comment_line_index >=0 else 0)
for i in range(start_index, end_index):
# Robust check, though start/end logic should prevent out-of-bounds
if i >= 0 and i < len(hunk_lines):
prefix = "> " if i == comment_line_index else " "
print(f"{prefix}{hunk_lines[i]}")
def main():
default_owner = firebase_github.OWNER
default_repo = firebase_github.REPO
parser = argparse.ArgumentParser(
description="Fetch review comments from a GitHub PR and format for use with Jules.",
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument(
"--pull_number",
type=int,
required=True,
help="Pull request number."
)
parser.add_argument(
"--owner",
type=str,
default=default_owner,
help=f"Repository owner. Defaults to '{default_owner}'."
)
parser.add_argument(
"--repo",
type=str,
default=default_repo,
help=f"Repository name. Defaults to '{default_repo}'."
)
parser.add_argument(
"--token",
type=str,
default=os.environ.get("GITHUB_TOKEN"),
help="GitHub token. Can also be set via GITHUB_TOKEN env var."
)
parser.add_argument(
"--context-lines",
type=int,
default=10,
help="Number of context lines around the commented line from the diff hunk. Use 0 for the full hunk. Default: 10."
)
parser.add_argument(
"--since",
type=str,
default=None,
help="Only show comments created at or after this ISO 8601 timestamp (e.g., YYYY-MM-DDTHH:MM:SSZ)."
)
args = parser.parse_args()
if not args.token:
sys.stderr.write("Error: GitHub token not provided. Set GITHUB_TOKEN or use --token.\n")
sys.exit(1)
if args.owner != firebase_github.OWNER or args.repo != firebase_github.REPO:
repo_url = f"https://github.com/{args.owner}/{args.repo}"
if not firebase_github.set_repo_url(repo_url):
sys.stderr.write(f"Error: Invalid repo URL: {args.owner}/{args.repo}. Expected https://github.com/owner/repo\n")
sys.exit(1)
print(f"Targeting repository: {firebase_github.OWNER}/{firebase_github.REPO}", file=sys.stderr)
print(f"Fetching comments for PR #{args.pull_number} from {firebase_github.OWNER}/{firebase_github.REPO}...", file=sys.stderr)
if args.since:
print(f"Filtering comments created since: {args.since}", file=sys.stderr)
comments = firebase_github.get_pull_request_review_comments(
args.token,
args.pull_number,
since=args.since # Pass the 'since' argument
)
if not comments:
print(f"No review comments found for PR #{args.pull_number} (or matching filters), or an error occurred.", file=sys.stderr)
return
print("\n--- Review Comments ---")
for comment in comments:
user = comment.get("user", {}).get("login", "Unknown user")
path = comment.get("path", "N/A")
file_line = comment.get("line", "N/A")
hunk_position = comment.get("position") # This is the 1-indexed position in the hunk
body = comment.get("body", "").strip()
diff_hunk = comment.get("diff_hunk") # Can be None or empty
html_url = comment.get("html_url", "N/A")
comment_id = comment.get("id")
in_reply_to_id = comment.get("in_reply_to_id")
created_at = comment.get("created_at")
if not body:
continue
print(f"Comment by: {user} (ID: {comment_id}){f' (In Reply To: {in_reply_to_id})' if in_reply_to_id else ''}")
if created_at:
print(f"Timestamp: {created_at}")
if diff_hunk: # Only show status if it's a diff-related comment
status_text = "[OUTDATED]" if hunk_position is None else "[CURRENT]"
print(f"Status: {status_text}")
print(f"File: {path}")
print(f"Line in File Diff: {file_line}")
print(f"URL: {html_url}")
print("--- Diff Hunk Context ---")
if diff_hunk:
print_contextual_diff_hunk(diff_hunk, hunk_position, args.context_lines)
else:
print("(Comment not associated with a specific diff hunk)")
print("--- Comment ---")
print(body)
print("----------------------------------------\n")
if __name__ == "__main__":
main()