ふくふくHukuhuku Inc.
EP.02Eng Dash 13分公開: 2026-05-10

GitHub API で PR・レビュー・コミット活動を集計する

GitHub の Search API + REST API + GraphQL API を組み合わせて、PR数・レビュー時間・マージまでのリードタイム・レビュー応答時間を取得する実装。リポジトリ別・人別・週次の集計まで。

#GitHub api#PR#レビュー#生産性
CO📔 Google Colab で開く(上から順にセルを実行)
シェア

は開発活動の最重要データソース。Search API + API + GraphQL を組合せると PR 数・レビュー時間・コミット頻度・マージまでのリードタイム を時系列で取れます。

認証と PAT

PAT (Personal Access Token) を環境変数に
Bash
# Settings → Developer settings → Personal access tokens (classic) で作成# 必要なスコープ: repo (private含む), read:org
export GITHUB_TOKEN="ghp_xxxxxxxxxx"

PR を一括取得 (REST API)

ある期間の PR を全件取得
Python
import os, requestsfrom datetime import datetime, timezone, timedelta
TOKEN = os.environ["GITHUB_TOKEN"]HEADERS = {"Authorization": f"Bearer {TOKEN}", "X-GitHub-Api-Version": "2022-11-28"}
def fetch_prs(org: str, repo: str, since: datetime) -> list[dict]:    """指定リポの PR を since 以降で全件取得"""    prs = []    page = 1    while True:        r = requests.get(            f"https://api.github.com/repos/{org}/{repo}/pulls",            headers=HEADERS,            params={"state": "all", "per_page": 100, "page": page,                    "sort": "updated", "direction": "desc"},        )        r.raise_for_status()        data = r.json()        if not data:            break        # since より古いものが出てきたら終了        if datetime.fromisoformat(data[-1]["updated_at"].replace("Z","+00:00")) < since:            prs.extend([p for p in data                        if datetime.fromisoformat(p["updated_at"].replace("Z","+00:00")) >= since])            break        prs.extend(data)        page += 1    return prs
# 過去 30 日の PRsince = datetime.now(timezone.utc) - timedelta(days=30)prs = fetch_prs("hukuhuku-inc", "hukuhuku-co-jp", since)print(f"PR 件数: {len(prs)}")

GraphQL でレビュー時間を取得

GraphQL: 1 PR のレビュー履歴を取得
Python
query = """query($owner: String!, $repo: String!, $number: Int!) {  repository(owner: $owner, name: $repo) {    pullRequest(number: $number) {      title createdAt mergedAt      author { login }      reviews(first: 50) {        nodes {          author { login }          state           # APPROVED / CHANGES_REQUESTED / COMMENTED          submittedAt        }      }    }  }}"""
def get_pr_with_reviews(owner: str, repo: str, number: int):    r = requests.post(        "https://api.github.com/graphql",        headers=HEADERS,        json={"query": query, "variables": {"owner": owner, "repo": repo, "number": number}},    )    return r.json()["data"]["repository"]["pullRequest"]

主要メトリクスの計算

PR レベルのメトリクス算出
Python
import pandas as pd
def pr_metrics(pr: dict) -> dict:    """1 PR のメトリクスを算出"""    created = datetime.fromisoformat(pr["createdAt"].replace("Z", "+00:00"))    merged = pr.get("mergedAt") and datetime.fromisoformat(pr["mergedAt"].replace("Z","+00:00"))    reviews = pr.get("reviews", {}).get("nodes", [])    first_review = min(        (datetime.fromisoformat(r["submittedAt"].replace("Z","+00:00")) for r in reviews),        default=None,    )    return {        "title": pr["title"],        "author": pr["author"]["login"],        "lead_time_hours": (merged - created).total_seconds() / 3600 if merged else None,        "first_review_hours": (first_review - created).total_seconds() / 3600 if first_review else None,        "review_count": len(reviews),        "merged": merged is not None,    }
# 100 PR 分の集計df = pd.DataFrame([pr_metrics(p) for p in prs_with_reviews])print(df.describe())print("\n人別の平均リードタイム:")print(df.groupby("author")["lead_time_hours"].mean().sort_values())

レート制限と運用

GitHub API レート制限

REST: 5,000 req/h / トークンGraphQL: 5,000 ポイント/h。組織規模が大きい場合は GitHub App で 15,000/h に引き上げ。ETag を使った If-None-Match キャッシュ + 必要な範囲だけ差分取得 で効率化。

ダッシュボードに載せる指標

  • 週次 PR 作成数 / 人
  • マージまでのリードタイム (中央値・p95)
  • 初回レビュー応答時間 (低いほど良い、目安 24h 以内)
  • レビューする側の負荷: 1 人あたり週レビュー数
  • マージなし PR 比率 (オープンのまま放置されている割合)
  • 変更行数の分布 (大きすぎる PR が多くないか)

次の話

EP.03 では / Anthropic API の usage を集計し、誰がどれだけ を使ったかを可視化します。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp