-
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathresolve_bot_comments.py
More file actions
128 lines (113 loc) · 3.56 KB
/
Copy pathresolve_bot_comments.py
File metadata and controls
128 lines (113 loc) · 3.56 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
#!/usr/bin/env python3
import subprocess
import json
import sys
import os
# Bot authors whose comments we want to resolve automatically
BOT_AUTHORS = [
"sonarqubecloud",
"trunk-io",
"gemini-code-assist",
"sentry",
"github-actions"
]
def run_gh_command(args):
"""Run a gh CLI command and return the output."""
try:
result = subprocess.run(
["gh"] + args,
capture_output=True,
text=True,
check=True
)
return result.stdout.strip()
except subprocess.CalledProcessError as e:
print(f"Error running gh command: {e.stderr}", file=sys.stderr)
return None
def resolve_threads(pr_number):
"""Find and resolve unresolved review threads in a PR."""
print(f"Checking PR #{pr_number} for unresolved bot comments...")
# GraphQL query to get review threads
query = """
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 1) {
nodes {
author {
login
}
}
}
}
}
}
}
}
"""
# Get owner and repo from git remote
remote_url = subprocess.run(["git", "remote", "get-url", "origin"], capture_output=True, text=True).stdout.strip()
# Handle both HTTPS and SSH URLs
if remote_url.startswith("https://"):
parts = remote_url.split("/")
owner = parts[-2]
repo = parts[-1].replace(".git", "")
else: # SSH
parts = remote_url.split(":")[-1].split("/")
owner = parts[0]
repo = parts[1].replace(".git", "")
# Execute query
result_json = run_gh_command([
"api", "graphql",
"-F", f"owner={owner}",
"-F", f"repo={repo}",
"-F", f"number={pr_number}",
"-f", f"query={query}"
])
if not result_json:
return
data = json.loads(result_json)
threads = data.get("data", {}).get("repository", {}).get("pullRequest", {}).get("reviewThreads", {}).get("nodes", [])
for thread in threads:
if thread.get("isResolved"):
continue
comments = thread.get("comments", {}).get("nodes", [])
if not comments:
continue
author = comments[0].get("author", {}).get("login")
if author in BOT_AUTHORS:
print(f"Resolving thread {thread['id']} by {author}...")
# Mutation to resolve thread
mutation = """
mutation($id: ID!) {
resolveReviewThread(input: {threadId: $id}) {
thread {
id
isResolved
}
}
}
"""
run_gh_command([
"api", "graphql",
"-f", f"query={mutation}",
"-F", f"id={thread['id']}"
])
def main():
# If PR number is provided as argument, use it
if len(sys.argv) > 1:
for pr_arg in sys.argv[1:]:
resolve_threads(int(pr_arg))
else:
# Otherwise, get all open PRs
pr_list_json = run_gh_command(["pr", "list", "--state", "open", "--json", "number"])
if pr_list_json:
prs = json.loads(pr_list_json)
for pr in prs:
resolve_threads(pr["number"])
if __name__ == "__main__":
main()