Просмотр исходного кода

Support pagination and improve location info for comments. (#129)

Jon Meow 5 лет назад
Родитель
Сommit
70ee36e8bb
1 измененных файлов с 77 добавлено и 20 удалено
  1. 77 20
      src/scripts/pr-comments.py

+ 77 - 20
src/scripts/pr-comments.py

@@ -16,8 +16,7 @@ _QUERY = """
 {
   repository(owner: "carbon-language", name: "carbon-lang") {
     pullRequest(number: %d) {
-      reviewThreads(first: 100) {
-        totalCount
+      reviewThreads(first: 100%s) {
         nodes {
           comments(first: 100) {
             nodes {
@@ -25,6 +24,8 @@ _QUERY = """
               author {
                 login
               }
+              path
+              originalPosition
               url
             }
           }
@@ -33,6 +34,10 @@ _QUERY = """
             login
           }
         }
+        pageInfo {
+          endCursor
+          hasNextPage
+        }
       }
       author {
         login
@@ -46,6 +51,8 @@ _QUERY = """
 
 def parse_args():
     """Parses command-line arguments and flags."""
+    # TODO: Add flag to filter for review threads including a specific user.
+    # TODO: Add repo flag, to allow for use with carbon-toolchain.
     parser = argparse.ArgumentParser(description="Lists comments on a PR.")
     parser.add_argument(
         "pr_num",
@@ -69,7 +76,22 @@ def parse_args():
     return parser.parse_args()
 
 
+def accumulate_threads(threads_by_path, review_threads, include_resolved):
+    """Adds threads to threads_by_path for later sorting."""
+    for thread in review_threads["nodes"]:
+        if thread["isResolved"] and not include_resolved:
+            continue
+
+        first_comment = thread["comments"]["nodes"][0]
+        path = first_comment["path"]
+        line = first_comment["originalPosition"]
+        if path not in threads_by_path:
+            threads_by_path[path] = []
+        threads_by_path[path].append((line, thread))
+
+
 def rewrap(content):
+    """Rewraps a comment to fit in 80 columns with a 4-space indent."""
     indent = "    "
     lines = []
     for line in content.split("\n"):
@@ -81,6 +103,7 @@ def rewrap(content):
 
 
 def main():
+    # Parse command-line flags.
     parsed_args = parse_args()
     pr_num = parsed_args.pr_num[0]
     access_token = parsed_args.github_access_token
@@ -93,35 +116,69 @@ def main():
             )
         access_token = os.environ["GITHUB_ACCESS_TOKEN"]
 
+    # Prepare the GraphQL client.
     transport = gql.transport.requests.RequestsHTTPTransport(
         url="https://api.github.com/graphql",
         headers={"Authorization": "bearer %s" % access_token},
     )
     client = gql.Client(transport=transport, fetch_schema_from_transport=True)
-    threads_result = client.execute(gql.gql(_QUERY % pr_num))
+
+    # Get the initial set of review threads, and print the PR summary.
+    threads_result = client.execute(gql.gql(_QUERY % (pr_num, "")))
     pull_request = threads_result["repository"]["pullRequest"]
     print(
         "'%s' (%d) by %s"
         % (pull_request["title"], pr_num, pull_request["author"]["login"])
     )
 
-    for thread in pull_request["reviewThreads"]["nodes"]:
-        resolved = thread["isResolved"]
-        if resolved and not include_resolved:
-            continue
-        # TODO: Print relevant file/line. Possibly LOC.
-        print("\nThread (%s)" % ("resolved" if resolved else "unresolved"))
-        # TODO: Try to link to the review thread with an appropriate diff.
-        # Ideally comment-to-present, worst case original-to-comment (to see
-        # comment). Possibly both.
-        print("    %s" % thread["comments"]["nodes"][0]["url"])
-        # TODO: Add a short comment mode that does comment-per-line.
-        # TODO: Timestamps would be nice.
-        for comment in thread["comments"]["nodes"]:
-            print("  %s:" % comment["author"]["login"])
-            print(rewrap(comment["body"]))
-        if resolved:
-            print("  %s:\n    RESOLVED" % thread["resolvedBy"]["login"])
+    # Paginate through the review threads.
+    threads_by_path = {}
+    while True:
+        # Accumulate the review threads.
+        review_threads = pull_request["reviewThreads"]
+        accumulate_threads(threads_by_path, review_threads, include_resolved)
+        if not review_threads["pageInfo"]["hasNextPage"]:
+            break
+        # There are more review threads, so fetch them.
+        threads_result = client.execute(
+            gql.gql(
+                _QUERY
+                % (
+                    pr_num,
+                    ', after: "%s"' % review_threads["pageInfo"]["endCursor"],
+                )
+            )
+        )
+        pull_request = threads_result["repository"]["pullRequest"]
+
+    # Print threads sorted by path and line.
+    for path in sorted(threads_by_path.keys()):
+        # Print a header for each path.
+        print()
+        print("=" * 80)
+        print(path)
+        print("=" * 80)
+
+        for line, thread in sorted(threads_by_path[path], key=lambda x: x[0]):
+            resolved = thread["isResolved"]
+            # Print a header for each thread.
+            # TODO: Add flag to fetch/print diffHunk for more context.
+            print()
+            print(
+                "line %d; %s"
+                % (line, ("resolved" if resolved else "unresolved"))
+            )
+            # TODO: Try to link to the review thread with an appropriate diff.
+            # Ideally comment-to-present, worst case original-to-comment (to see
+            # comment). Possibly both.
+            print("    %s" % thread["comments"]["nodes"][0]["url"])
+            # TODO: Add a short comment mode that does comment-per-line.
+            # TODO: Timestamps would be nice.
+            for comment in thread["comments"]["nodes"]:
+                print("  %s:" % comment["author"]["login"])
+                print(rewrap(comment["body"]))
+            if resolved:
+                print("  %s:\n    RESOLVED" % thread["resolvedBy"]["login"])
 
 
 if __name__ == "__main__":