From fd4bf9271734fce59ae970fe3f1b7a754c9fcac4 Mon Sep 17 00:00:00 2001 From: Jon Meow <46229924+jonmeow@users.noreply.github.com> Date: Tue, 28 Jul 2020 15:53:09 -0700 Subject: [PATCH] Script to list PR comments (#128) --- .pre-commit-config.yaml | 2 +- src/scripts/pr-comments.py | 128 +++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100755 src/scripts/pr-comments.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d23f6b20a539..73f0fc4a704a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: rev: v1.17.1 hooks: - id: codespell - args: ['-L', 'copyable,circularly'] + args: ['-L', 'circularly,copyable,pullrequest'] exclude: '^src/jekyll/Gemfile.lock$' - repo: local hooks: diff --git a/src/scripts/pr-comments.py b/src/scripts/pr-comments.py new file mode 100755 index 000000000000..6d05ce0e4703 --- /dev/null +++ b/src/scripts/pr-comments.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 + +"""Figure out comments on a GitHub PR.""" + +import argparse +import os +import sys +import textwrap + +# https://pypi.org/project/gql/ +import gql +import gql.transport.requests + +# Use https://developer.github.com/v4/explorer/ to help with edits. +_QUERY = """ +{ + repository(owner: "carbon-language", name: "carbon-lang") { + pullRequest(number: %d) { + reviewThreads(first: 100) { + totalCount + nodes { + comments(first: 100) { + nodes { + body + author { + login + } + url + } + } + isResolved + resolvedBy { + login + } + } + } + author { + login + } + title + } + } +} +""" + + +def parse_args(): + """Parses command-line arguments and flags.""" + parser = argparse.ArgumentParser(description="Lists comments on a PR.") + parser.add_argument( + "pr_num", + metavar="PR#", + type=int, + nargs=1, + help="The pull request to fetch comments from.", + ) + parser.add_argument( + "--github-access-token", + metavar="ACCESS_TOKEN", + help="The access token for use with GitHub. May also be specified in " + "the environment as GITHUB_ACCESS_TOKEN.", + ) + parser.add_argument( + "--include-resolved", + action="store_true", + help="Whether to include resolved review threads. By default, only " + "unresolved threads will be shown.", + ) + return parser.parse_args() + + +def rewrap(content): + indent = " " + lines = [] + for line in content.split("\n"): + for x in textwrap.wrap( + line, width=80, initial_indent=indent, subsequent_indent=indent + ): + lines.append(x) + return "\n".join(lines) + + +def main(): + parsed_args = parse_args() + pr_num = parsed_args.pr_num[0] + access_token = parsed_args.github_access_token + include_resolved = parsed_args.include_resolved + if not access_token: + if "GITHUB_ACCESS_TOKEN" not in os.environ: + sys.exit( + "Missing github access token. This must be provided through " + "either --github-access-token or GITHUB_ACCESS_TOKEN." + ) + access_token = os.environ["GITHUB_ACCESS_TOKEN"] + + 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)) + 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"]) + + +if __name__ == "__main__": + main()