1. はじめに
コードレビューをするようになって数ヶ月。まともにメインで使ってる言語のコードレビューをした時に、想像以上に色々拾えず、「これはまずい」となった。
そこで、レビュー観点を整理するために、過去PRでのレビュー指摘が財産となるはずなので、振り返ろうと思ったが、
GithubのWebページ上で一々PRを一つずつを開いていって、コメントを一つずつ見ていく、欲しくない情報も目に入る、それだと手間も時間もかかって面倒だと思いツールを作って収集&ファイル出力しようと思い、そのためのコードを書いた。
せっかく作ったため、その過程でGithub APIのレスポンスデータで押さえた点と、「やろうと思えばこんなことできます」という参考として作成物を簡単に紹介する(何番煎じか分からないが)。
2. Github API
Github アクセストークンが必要ですが、発行方法はググれば出てくるので割愛。Github APIについても同様に割愛。
APIで取得できるPR及びPRのレビューコメントのレスポンスデータが少々分かりにくい部分あるため、
レスポンスデータの一部項目と、それがGUI上のどの部分に対応するかを重点的に記す。
2.1. Pull Request 取得
リクエスト
GET /repos/{owner}/{repository}/pulls
以下で全PRのデータ取得可能。
curl -s -k -H "Accept: application/vnd.github.v3+json" -u :<Github access token> https://api.github.com/repos/<owner>/<repository>/pulls?state=all
レスポンス
レスポンス形式は以下参照。だが
Github Docs - Pull Request
レスポンスデータのどの項目が何のデータか特に説明がないため、(個人的に欲しかった)一部項目だけに絞って紹介する。
# | 項目 | 説明 |
---|---|---|
① | html_url | PRのwebページのURL |
② | review_comments_url | PR上の全レビューコメントを取得するREST APIのURL (*1) |
③ | comments_url | PR上の全コメントを取得するREST APIのURL (*1) |
④ | number | PR番号 |
⑤ | title | PRのタイトル |
⑥ | user.login | PRの作成者 |
⑦ | body | PRの説明 |
(*1) ②、③で取れる情報は以下の通り
以降、②をレビューコメント、③をコメントと記す。
2.2. PRのレビューコメント取得
レスポンス
レスポンス形式は以下参照。
Github Docs - PR review comments
こちらも同様に、レスポンスデータのうち、個人的に欲しかった項目だけに絞って紹介する
# | 項目 | 説明 |
---|---|---|
① | pull_request_review_id | ※一度に複数コメントした場合、このidは重複する |
② | id | ユニークキー |
③ | diff_hunk | レビューコメントされたコードの周辺差分() |
④ | path | レビューコメントされたファイルのパス |
⑤ | user.login | レビューコメントした人 |
⑥ | body | レビューコメント内容 |
⑦ | _links.html.href | GitHubのWebページ(当該レビューコメント)へのリンクURL |
※ body に入っているデータは、レビューコメントのスレッドの先頭の内容のみのようです。
3. PR上のレビューコメントを収集/出力するために作成した物
pythonで実装
3.1. 出力
csvファイル(念の為の一覧出力) と mdファイル(各PRのレビューコメント1件を1ファイル)に出力
上記のPRから取得した結果の出力は以下の通り(レビューコメントが5つあるため、mdファイルも5つ出力)
mdファイルの中身は以下の通り
やろうと思えば、このように出力も可能というご紹介。
mdファイルへの出力は、jinja2 を利用。
簡単に紹介すると、以下のようなテンプレートファイルを用意して
# PR Review Comment Info
- PR title
{{title}}
- PR create user
{{create_user}}
: (略)
テンプレートに当てはめるためのデータを用意し、
{
'title': 'pr title',
'create_user': 'user name'
: (略)
}
以下のようにするだけで、テンプレートに基づいて出力内容作成、ファイル出力できる
from jinja2 import Environment, FileSystemLoader
TEMPLATES_DIR_PATH = './templates/'
PR_REVIEW_COMMENT_TEMPLATE_FILE_NAME = 'pr_review_comment.j2'
env = Environment(loader=FileSystemLoader(searchpath=TEMPLATES_DIR_PATH, encoding='utf8'))
template = env.get_template(PR_REVIEW_COMMENT_TEMPLATE_FILE_NAME)
md_file_data = template.render(json_data)
with open(OUTPUT_DIR_PATH + md_file_path , mode='w', encoding='CP932', errors='ignore') as f:
f.write(md_file_data)
※jinja2の詳細はググれば分かるため割愛。
3.2. API実行周り
Github APIには、ページネーション機能があり、1回のAPI実行で最大100件までしか取得できない。
それ以上のデータがある場合は、ページ番号をカウントアップしてAPIを再実行する必要がある(PRもPR上のレビューコメントもその他色々も)。
先にページ数を取得する方法がパッと見、見当たらなかったため、申し訳程度のリトライ機構を用意して以下の通りに実装。
※今回取得したいのは、レビューコメントなので、コメントは現状取得しない
※レビューコメント以外も取得したくなったら、用意したリトライ機構に乗っけて取れば良い算段で実装
# Callback-only function used by retry_execute_github_api()
def collect_and_write_pull_requests(page_count: int, non_arg) -> int:
api_url = build_get_pull_requests_api_url(page_count)
response_json_pr_array = execute_github_api(api_url)
pr_data_list = PullRequestDataList(response_json_pr_array)
pr_data_list.write_csv(PULL_REQUEST_LIST_FILE_NAME)
# collect review comment in PR
for pr_data in pr_data_list.values:
retry_execute_github_api(collect_and_write_pr_review_comments, pr_data)
return len(response_json_pr_array)
# Callback-only function used by retry_execute_github_api()
def collect_and_write_pr_review_comments(page_count: int, pr_data) -> int:
api_url = build_get_pr_review_comments_api_url(pr_data.review_comments_api_url, page_count)
response_json_review_comments_array = execute_github_api(api_url)
pr_review_comment_list = PullRequestReviewCommentList(response_json_review_comments_array, pr_data)
pr_name = pr_data.build_pr_name()
pr_review_comment_list.write_csv(pr_name)
pr_review_comment_list.write_md(pr_name)
return len(response_json_review_comments_array)
def retry_execute_github_api(api_execute_func: Callable[[int, any], int], *args):
is_retry = True
page_count = 1
while is_retry:
count = api_execute_func(page_count, args[0] if len(args) != 0 else None)
page_count += 1
is_retry = False if count < 100 else True
retry_execute_github_api(collect_and_write_pull_requests)
上記は、一部抜粋のため、ソース全文は以下リンク先をご参照ください。
3.3. ソースコード
※MIT License にしているため、コードはパクって頂いて問題ありません。可読性/変更容易性は高くしているつもりのため、欲しい方はご活用ください。
4. おわりに
現状は、レビューコメントのスレッドのうち先頭の内容しか取ってない。
先頭以外は回答/議論が主になると思っているため、先頭だけで十分ではないかと推測している。
足りないと思えば追加実装すればよいというスタンスで、追加実装しやすいように実装もした。
これで少しでも捗ることを期待。
以上。