SmartHR Advent Calendar 2024 シリーズ2の15日の記事です。前の記事はT2mさんのPlopで簡単にコンポーネントを自動生成する方法でした。
はじめに
12月に入って急激に寒くなったものの、足元のホットカーペットだけでどこまで凌げるかにチャレンジしています。冬でも短パンを履いてく系小学生だったので、寒さにどこまで耐えられるか挑戦したくなる性分のようです。
さて、12月というと一年の総決算として、今年どんな開発していたかふりかえりたくなる人もいるのではないでしょうか?私はなりました。
GitHubの自分がAuthorのPRを見るとしたいところですが、チームで開発をしていると、モブプログラミングをしたり、途中から他の人の作業を引き継いだりで、自分がコミットしているけどPRのAuthorは別の人なんてことも普通にあり、簡単に自分がコミットしたPRを見ることができません。(私が把握してないだけでGitHubにそういう機能があったら教えてください)
ここでタイトル回収、自分がコミットしているPRを抽出するスクリプトを作ってみました。
v1 gh pr list
で取得
GitHub謹製のCLIでPRの一覧を取得し、自分のコミットを抽出しようというアプローチです。先に結論を言ってしまうとこれではうまくいきませんでした。
#!/bin/bash
# ユーザー名とリポジトリを設定
GITHUB_USERNAME="your github username"
REPO="your repository name"
# 抽出する期間の設定
DATE_FROM="2024-07-01" # yyyy-mm-dd形式で抽出開始の日付を指定
DATE_TO=$(date +"%Y-%m-%d") # yyyy-mm-dd形式で抽出開始の日付を指定。デフォルトは今日まで
DATE_RANGE=3 # API制限に引っかかるので指定した日の範囲ごとに取得
# 現在の日付が開始日を超えるまでループ
current_date="$DATE_FROM"
end_date=$(date -v+1d -j -f "%Y-%m-%d" "$DATE_TO" +"%Y-%m-%d")
while [[ "$current_date" < "$end_date" ]]; do
# echo "Processing date: $current_date"
# 指定期間でのコミットを含むPRを検索
gh pr list --limit 40 --state all --repo $REPO --search "updated:$current_date..$current_date" --json number,title,url,updatedAt,commits --jq ".[] | [. | select(.commits[].authors[].login == \"${GITHUB_USERNAME}\") | {number: .number, title: .title, url: .url, updatedAt: .updatedAt}] | unique_by(.number) | select(.[].number > 0) | .[] | {title: .title, url: .url, updatedAt: .updatedAt}"
# 日付をDATE_RANGE日進める
current_date=$(date -v+${DATE_RANGE}d -j -f "%Y-%m-%d" "$current_date" +"%Y-%m-%d")
done
実行すると以下のようなJSONが出力されます。
{
"title": "[PRのタイトル]",
"updatedAt": "2024-12-10T10:22:45Z",
"url": "https://github.com/[リポジトリ名]/pull/[PR番号]"
}
おおうまくいったと思ったのですが、gh pr list
で取得できるコミットは30件までで、それ以上のコミットがあるPRだと取得できず失敗。
それならばと改良したのが次の方法です。
v2 gh pr list
とgh api graphql
の組み合わせ
GitHub APIにはRESTとGraphQLがあり、GraphQLがあれば欲しいデータを取得できそうです。
gh pr list
ではコミットが全部取れなかったので、
- 期間内のPRのリストを取得
- PR番号からGraphQL APIを利用してコミットを取得
という組み合わせでやる方法です。
#!/bin/bash
# ユーザー名とリポジトリを設定
GIT_USERNAME="your github username" # git config user.name を指定 != github usernameなので注意
REPO_OWNER="your repository orgnization name"
REPO_NAME="your repository name"
REPO="${REPO_OWNER}/${REPO_NAME}"
# 抽出する期間の設定
DEFAULT_DATE=$(date +"%Y-%m-%d")
# 何も指定しなければ今日のPRのみ取得する
DATE_FROM=$DEFAULT_DATE # ex: "2024-07-01"
DATE_TO=$DEFAULT_DATE # ex: "2024-10-31"
DATE_RANGE=1 # API制限に引っかかるので指定した日の範囲ごとにPRを取得
# 除外するPRのタイトルを設定
EXCLUDE_PR_TITLE='(\[release\]|Dependency)'
# 現在の日付が開始日を超えるまでループ
current_date="$DATE_FROM"
end_date=$(date -v+1d -j -f "%Y-%m-%d" "$DATE_TO" +"%Y-%m-%d")
while [[ "$current_date" < "$end_date" ]]; do
# echo "Processing date: $current_date"
# 指定期間でのPRを検索
PR_NUMBERS=$(gh pr list --state all --repo $REPO --search "updated:$current_date..$current_date" --json number,title,url,updatedAt --jq ".[].number")
for pr_number in $PR_NUMBERS; do
# echo "Processing PR: $pr_number"
# PRのコミットを取得
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!, $endCursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
title
url
commits(first: 100, after: $endCursor) {
pageInfo{ hasNextPage, endCursor }
nodes {
commit {
oid
committer {
name
email
date
}
}
}
}
}
}
}' -F owner="${REPO_OWNER}" -F repo="${REPO_NAME}" -F pr=$pr_number --paginate --jq "[. | .data.repository.pullRequest | {title: .title, url: .url, committer: .commits.nodes.[].commit.committer.name} | select(.committer == \"${GIT_USERNAME}\") | {title: .title, url: .url}] | unique_by(.url) | select(.[].url > 0) | .[] | .url + \" \" + .title" | uniq | grep -v -E $EXCLUDE_PR_TITLE
done
# 日付をDATE_RANGE日進める
current_date=$(date -v+${DATE_RANGE}d -j -f "%Y-%m-%d" "$current_date" +"%Y-%m-%d")
done
一応これで取得はできました
出力はこんな感じ。
https://github.com/[リポジトリ名]/pull/[PR番号] [PRタイトル]
https://github.com/[リポジトリ名]/pull/[PR番号] [PRタイトル]
https://github.com/[リポジトリ名]/pull/[PR番号] [PRタイトル]
ただ、めちゃくちゃ遅い、遅すぎる
無駄なAPIリクエストが多く、効率が悪すぎてめちゃくちゃ時間がかかります。
v3として、ローカルのgitで自分のコミットからブランチを抽出して効率化をする案を考えているので高速化に挑戦中です。
(以下追記)
v3 ローカルで自分がコミットしたブランチ名を抽出 + gh pr list
の組み合わせ
v2は取得できるようになったものの、無駄なAPIリクエストが多く遅かったのでローカルのコミットからブランチを抽出し、そのブランチの情報を取得する方式に変更しました。
#!/bin/bash
# ユーザー名とリポジトリを設定
COMMITER_NAME="your github username"
REPO_OWNER="your repository organization"
REPO_NAME="your repository name"
REPO="${REPO_OWNER}/${REPO_NAME}"
# 抽出する期間の設定
DEFAULT_DATE=$(date +"%Y-%m-%d")
DATE_FROM="2024-12-01"
DATE_TO=$DEFAULT_DATE
DATE_RANGE=1 # API制限に引っかかるので指定した日の範囲ごとにPRを取得
# 除外するPRのタイトルを設定
EXCLUDE_PR_TITLE='(\[release\]|Dependency)'
# 除外するブランチ名を設定
EXCLUDE_BRANCH='(HEAD|main)'
# 現在の日付が開始日を超えるまでループ
current_date="$DATE_FROM"
end_date=$(date -v+1d -j -f "%Y-%m-%d" "$DATE_TO" +"%Y-%m-%d")
cd [your repository path]
TEMP_FILE=/tmp/$$-get-commit-pr.txt
: > $TEMP_FILE
COMMIT_HASHES=()
PR_NUMBERS=()
while [[ "$current_date" < "$end_date" ]]; do
# echo "Processing date: $current_date"
# 指定期間でのPRを検索
COMMIT_HASHES=$(git --no-pager log -200 --oneline --reverse --committer="$COMMITER_NAME" --after="${current_date} 00:00:00" --before="${current_date} 23:59:59" --pretty=%h)
BRANCHES=()
for hash in $COMMIT_HASHES; do
# echo "Processing commit hash: $hash"
branch=$(git name-rev --name-only $hash | grep remotes | sed -E 's/^remotes\/origin\///' | sed 's/[~^].*//')
# echo "Processing branch: $branch"
if [[ $branch =~ $EXCLUDE_BRANCH ]]; then
continue
fi
echo $branch >> $TEMP_FILE
done
# 日付をDATE_RANGE日進める
current_date=$(date -v+${DATE_RANGE}d -j -f "%Y-%m-%d" "$current_date" +"%Y-%m-%d")
done
COMMIT_BRANCHES=$(cat $TEMP_FILE | sort | uniq)
# echo "COMMIT_BRANCHES: $COMMIT_BRANCHES"
for branch in $COMMIT_BRANCHES; do
# echo "Processing branch: $branch"
gh pr list --state all --head $branch --json number,title,url,updatedAt --jq '.[] | "\(.number) \(.title) \(.url) \(.updatedAt)"'
done
rm $TEMP_FILE
実行結果はこんな感じ
[PR番号] [PRタイトル] [PR URL] [PR更新日時]
[PR番号] [PRタイトル] [PR URL] [PR更新日時]
[PR番号] [PRタイトル] [PR URL] [PR更新日時]
現実的な速度で動かすことができました。
ただ、テストが不足しているので正しく抽出できていなかったらごめんなさい
雑なスクリプトで書いてしまったので、標準入力で条件を受け取るとかリファクタするところはたくさんありそうですが、そこはお許しください
自分がコミットしたPRを取得して、一年のふりかえりにご活用いただけたらうれしいです。