GitHub上でのPullRequestについて
GitHub上のPullRequestベースでドキュメントレビューやコードレビューを行うのが好きなんですが、何事にもメリット・デメリットがあります。
メリット
- このファイルのここ!って指定してコメントできる
- 特定コメントに対してリプライしたり、Resolveでクローズもできる
- レビューをしてOKになるまでマージ不可!というブランチ保護ができる
デメリット
- レビュー結果の残り方が証跡として確認しづらい
- レビュー結果を元にしたデータ分析ができない
要は、レビューというもの自体を管理するときにちょっと物足りないって感じです。
品質管理・改善をしよう!となったときに、レビュー内容を監視・管理したい、というのはあるあるかな、と思います。
やったこと
GitHubActionsで、
プルリクエストおよびそのレビューコメントをCSV出力するPythonを走らせて、
出力したCSVを同じリポジトリにコミットしておく。
CSV出力しておけば、つぶしが効きます。
Excel化して渡したりとか、DBに流し込んだりとか。
GitHubActionsはすべてGitHubの中で完結するので、
今回のようなケースにはピッタリです!似たようなCIツールに比べて安いし。
やり方
1. GitHub Actionsを書く
今回は、テスト的な意味合いもあったので、
気軽に試せるように手動トリガーにしてみました。
name: run-CSVCreate-python
on: [ workflow_dispatch ]
jobs:
build:
runs-on: ubuntu-latest
env:
token: ${{secrets.GITHUB_TOKEN}}
repo: ${{github.repository}}
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Run python
run: python createCSV.py
- name: CSV commit
run: |
git config --global user.email ${{secrets.EMAIL}}
git config --global user.name ${{secrets.NAME}}
git add .
git commit -m "add CSV"
git push origin main
2.Pythonを書く
import json
import requests
import os
import pandas as pd
import requests.packages.urllib3
requests.packages.urllib3.disable_warnings()
def main():
os.environ['no_proxy'] = 'localhost'
token = os.environ['token']
repo = os.environ['repo']
urldomain = 'https://api.github.com/repos/' + repo
pr_df = pd.DataFrame(columns=["プルリクエストID","タイトル","内容","プルリクエストした人","更新日時","URL"]) #PRのカラム
rv_df = pd.DataFrame(columns=["プルリクエストID","プルリクエストレビューID","レビューコメント","レビューした人","更新日時","URL"])#PRに対するレビューのカラム
cm_df = pd.DataFrame(columns=["プルリクエストID","コメントID","対象","コメント","コメントした人","更新日時","URL","プルリクエストレビューID","親コメントID"])#PRに対するコメントのカラム
pr_url = urldomain + '/pulls?access_token='+ token +'&state=all' # 全てのPRを取得する
pr_response = requests.get(pr_url, verify=False)
json_dict = json.loads(pr_response.text)
for i,pr in enumerate(json_dict):
cnt=str(i+1)
pr_se = pd.Series([cnt,pr['title'],pr['body'],pr['user']['login'],pr['updated_at'],pr['html_url']], index=pr_df.columns)
pr_df = pr_df.append(pr_se,ignore_index=True)
rv_url = urldomain +'/pulls/' + cnt + '/reviews?access_token=' + token # PRのFileChanged->Reviewchangesで残したコメント
rv_response = requests.get(rv_url, verify=False)
rv_dict = json.loads(rv_response.text)
for review in rv_dict:
rv_se = pd.Series([cnt,review['id'],review['body'],review['user']['login'],review['submitted_at'],review['html_url']], index=rv_df.columns)
rv_df = rv_df.append( rv_se, ignore_index=True)
issuecm_url = urldomain +'/issues/comments?access_token=' + token # PRConversationで残したコメント
issuecm_response = requests.get(issuecm_url, verify=False)
issuecm_dict = json.loads(issuecm_response.text)
for issuecm in issuecm_dict:
issuecm_se = pd.Series([cnt,issuecm['id'],"",issuecm['body'],issuecm['user']['login'],issuecm['updated_at'],issuecm['html_url'],"",""], index=cm_df.columns)
cm_df = cm_df.append( issuecm_se, ignore_index=True)
cm_url = urldomain + '/pulls/comments?access_token=' + token # PRのFileChangedでAddsinglecomment/Startareviewで残したコメント
cm_response = requests.get(cm_url, verify=False)
cm_dict = json.loads(cm_response.text)
for cm in cm_dict:
cm_parentid = cm.get('in_reply_to_id',None)
cm_prid = cm.get('pull_request_review_id',None)
cm_se = pd.Series([cnt,cm['id'],cm['path'],cm['body'],cm['user']['login'],cm['updated_at'],cm['html_url'],cm_prid,cm_parentid], index=cm_df.columns)
cm_df = cm_df.append( cm_se, ignore_index=True)
print(pr_df)
print(rv_df)
print(cm_df)
pr_df.to_csv("output_pr.csv", index=False)
rv_df.to_csv("output_rv.csv", index=False)
cm_df.to_csv("output_cm.csv", index=False)
if __name__ == '__main__':
main()
3.GitHub Actionsを動かす
4.CSVが3つ出力されて、メインブランチにコミットされてる!
- プルリクエストの一覧CSV
- プルリクエスト全体へのコメントの一覧CSV
- プルリクエストの差分に対するコメントの一覧CSV
1~3を実行すると、上記CSVが出力されます。やった~
動かしたリポジトリのすべてのプルリクエスト、コメントを取得してそれぞれ一覧にするようにしています。
CSVの分け方については、今回扱っているデータのリレーションが下記のようになるので、この後DBに流し込むとしたときに使いやすいデータモデルになるように正規化しました。
①プルリクエスト
②プルリクエスト全体へのコメント
③プルリクエストの差分に対するコメント
④プルリクエストの差分に対するコメントへのコメント
※④にさらにコメントしても、全て③にぶら下がるという扱われ方になるようです。
ハマったポイント
APIの使い分け
PullRequest周りで付けるコメントを全て取得するためには3種類のAPIを叩く必要があった。
見え方としてはぜんぶPRに対するコメントとして捉えることもできるのだが、②はissueのAPIになるようで、②だけは一覧で一気に取ってくるAPIが存在しないようだ。
GitHubAPI使うのが初めてでよく分かっていなかった…。
①PRのFileChanged->Reviewchangesで残したコメント
https://docs.github.com/ja/rest/reference/pulls#list-reviews-for-a-pull-request
②PRのConversationで残したコメント
https://docs.github.com/ja/rest/reference/issues#get-an-issue-comment
③PRのFileChangedでAdd single comment/Start a reviewで残したコメント
https://docs.github.com/ja/rest/reference/pulls#list-review-comments-in-a-repository
今後
CSVがあれば処理の最初に読み込んで、差分更新とかやってあげた方がいいかな?かえって処理時間延びちゃうか?
活用面
AWS RDSでDB立てて、SuperSetとかで可視化するとかしてあげると、
DB触れる人もそうでない人にもやさしくなりそうです。
DBに流し込むのもGitHub Actionsでやってあげると管理もラクそう。
SuperSetについて分かりやすかったブログ
https://blog.engineer.adways.net/entry/advent_calendar_2017/10