GitHub APIを使って複数のリポジトリの活動を集計する

現在進めているサービスのシステム開発がひと段落したので、振り返りを行うにあたって定量的なデータを集めてみようと思い、その一環として、GitHub上の活動状況データを収集することにしました。

この記事では、GitHub APIを使って複数のリポジトリからユーザーごとのコミット数、プルリクエスト数、レビューコメント数を集計する方法について紹介します。

## GitHub APIを使うとどういった情報を取得できるか

GitHub APIを使うことで、以下の情報を簡単に取得できます。

- **コミット数**: 各ユーザーが行ったコミットの数
- **プルリクエスト数**: 各ユーザーが作成したプルリクエストの数
- **レビューコメント数**: 各ユーザーがプルリクエストに対して行ったレビューコメントの数

これらの情報を集計することで、チームメンバーごとの活動状況を把握し、開発の進捗を評価できます。
なお、イシューなどの情報も取得可能ですが、今回はプロジェクトでイシューを使用していなかったため除外しています。

## コード例

以下のPythonスクリプトでは、複数のリポジトリからデータを取得し、ユーザーごとの活動を集計します。

```python
import requests
from collections import defaultdict

# GitHubのパーソナルアクセストークン
token = 'your_github_token'
headers = {'Authorization': f'token {token}'}

# 対象のリポジトリリスト
repos = ['username/repository1', 'username/repository2', 'username/repository3']

# データを保存するための辞書
user_data = defaultdict(lambda: {'commits': 0, 'pull_requests': 0, 'pr_comments': 0})
total_data = {'commits': 0, 'pull_requests': 0, 'pr_comments': 0}

# ページングを考慮してデータを取得する関数
def fetch_all_pages(url, item_type="items"):
    results = []
    page = 1
    while url:
        print(f"Fetching {item_type} page {page}...")
        response = requests.get(url, headers=headers)
        if response.status_code != 200:
            print(f"Failed to fetch {item_type} page {page}. Status code: {response.status_code}")
            break
        response_data = response.json()
        results.extend(response_data)
        url = response.links.get('next', {}).get('url')
        page += 1
    print(f"Fetched total {len(results)} {item_type}.")
    return results

# リポジトリごとにデータを取得
for repo in repos:
    print(f"\nProcessing repository: {repo}")

    # コミットデータの取得
    commits_url = f'https://api.github.com/repos/{repo}/commits'
    print("Fetching commits...")
    commits = fetch_all_pages(commits_url, "commits")

    for commit in commits:
        author_name = commit['commit']['author']['name']
        user_data[author_name]['commits'] += 1
        total_data['commits'] += 1

    # プルリクエストデータの取得
    prs_url = f'https://api.github.com/repos/{repo}/pulls?state=all'
    print("Fetching pull requests...")
    prs = fetch_all_pages(prs_url, "pull requests")

    for pr in prs:
        author = pr['user']['login']
        user_data[author]['pull_requests'] += 1
        total_data['pull_requests'] += 1

        # プルリクエストのレビューコメントを取得
        review_comments_url = f"https://api.github.com/repos/{repo}/pulls/{pr['number']}/comments"
        print(f"Fetching comments for PR #{pr['number']}...")
        review_comments = fetch_all_pages(review_comments_url, "review comments")

        for comment in review_comments:
            commenter = comment['user']['login']
            user_data[commenter]['pr_comments'] += 1
            total_data['pr_comments'] += 1

# 結果を表示
print("\nUser Data Results:")
for user, data in user_data.items():
    print(f"User: {user}")
    print(f"  Commits: {data['commits']}")
    print(f"  Pull Requests: {data['pull_requests']}")
    print(f"  PR Comments: {data['pr_comments']}")
    print("\n")

print("\nTotal Data Results:")
print(f"Total Commits: {total_data['commits']}")
print(f"Total Pull Requests: {total_data['pull_requests']}")
print(f"Total PR Comments: {total_data['pr_comments']}")
```

## コードの説明

### 1. アクセストークンの設定

まずはGitHubのパーソナルアクセストークンを用意します。これを使ってAPIリクエストを認証します。トークンはGitHubの設定画面から取得可能です。

```python
token = 'your_github_token'
headers = {'Authorization': f'token {token}'}
```

### 2. リポジトリリストの設定

`repos` リストに対象のリポジトリを追加します。ここに記載された全てのリポジトリからデータを取得します。

```python
repos = ['username/repository1', 'username/repository2', 'username/repository3']
```

### 3. データ取得のための関数

`fetch_all_pages` 関数は、GitHub APIのページングを考慮してデータを取得します。これは、APIレスポンスが複数ページに渡る場合に全ページを取得するために使用します。

```python
def fetch_all_pages(url, item_type="items"):
    results = []
    page = 1
    while url:
        print(f"Fetching {item_type} page {page}...")
        response = requests.get(url, headers=headers)
        if response.status_code != 200:
            print(f"Failed to fetch {item_type} page {page}. Status code: {response.status_code}")
            break
        response_data = response.json()
        results.extend(response_data)
        url = response.links.get('next', {}).get('url')
        page += 1
    print(f"Fetched total {len(results)} {item_type}.")
    return results
```

### 4. コミット、プルリクエスト、レビューコメントの集計

各リポジトリに対してコミット、プルリクエスト、レビューコメントのデータを取得し、ユーザーごとに集計します。また、全体の統計も同時に集計します。各コミット、プルリクエスト、レビューコメントごとに対応するユーザーのデータを更新し、全体のデータにも加算します。

```python
for repo in repos:
    print(f"\nProcessing repository: {repo}")

    # コミットデータの取得
    commits_url = f'https://api.github.com/repos/{repo}/commits'
    print("Fetching commits...")
    commits = fetch_all_pages(commits_url, "commits")

    for commit in commits:
        author_name = commit['commit']['author']['name']
        user_data[author_name]['commits'] += 1
        total_data['commits'] += 1

    # プルリクエストデータの取得
    prs_url = f'https://api.github.com/repos/{repo}/pulls?state=all'
    print("Fetching pull requests...")
    prs = fetch_all_pages(prs_url, "pull requests")

    for pr in prs:
        author = pr['user']['login']
        user_data[author]['pull_requests'] += 1
        total_data['pull_requests'] += 1

        # プルリクエストのレビューコメントを取得
        review_comments_url = f"https://api.github.com/repos/{repo}/pulls/{pr['number']}/comments"
        print(f"Fetching comments for PR #{pr['number']}...")
        review_comments = fetch_all_pages(review_comments_url, "review comments")

        for comment in review_comments:
            commenter = comment['user']['login']
            user_data[commenter]['pr_comments'] += 1
            total_data['pr_comments'] += 1
```

### 5. 結果の表示

最後に、ユーザーごとの集計結果と全体の統計を表示します。これにより、各ユーザーの貢献度とプロジェクト全体の進捗状況が一目で分かります。

```python
print("\nUser Data Results:")
for user, data in user_data.items():
    print(f"User: {user}")
    print(f"  Commits: {data['commits']}")
    print(f"  Pull Requests: {data['pull_requests']}")
    print(f"  PR Comments

: {data['pr_comments']}")
    print("\n")

print("\nTotal Data Results:")
print(f"Total Commits: {total_data['commits']}")
print(f"Total Pull Requests: {total_data['pull_requests']}")
print(f"Total PR Comments: {total_data['pr_comments']}")
```

## まとめ

このスクリプトを使うことで、複数のリポジトリにまたがる活動を簡単に集計し、開発チームのアクティビティを把握することができます。
これにより、開発の進捗状況をより正確に評価できるようになり、プロジェクト管理やシステム開発のさらなる改善点やサポートが必要な部分を見つけやすくなります。

この記事が、プロジェクト管理をより効果的に行うための一助となれば幸いです。