GithubのPRコメント数を集計→Slack通知で賞賛の文化を作る

## はじめに

こちらは[Wano Group Advent Calendar 2024](https://qiita.com/advent-calendar/2024/wano-group) の23日目の記事です。

## きっかけ

TuneCore Japanのバックエンドエンジニア [@tutti618](https://qiita.com/tutti618)です。

先日、チーム内で「普段行っているレビューやSlackへの反応数を目にみえるようして
賞賛できる文化があると良いね」という話になり、アドカレの題材にしてみました。

検討したのは下記3件

- テスト環境でのバグ報告数
何を持ってバグ報告とするのか、定義が難しいため今回は対象外

- Slackのスタンプ反応数
Slack APIを使わずとも、
アナリティクス機能で閲覧可能のため今回は対象外
`Slack> アナリティクス > メンバー > 列を編集する > 
[追加されたリアクション数]をチェック > [追加されたリアクション数]でソート`
![Slackアナリティクス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/318932d9-bc85-6395-ecf9-ed28ed7a6647.png)

- GithubのPRコメント数
→レビューを積極的に行っていただいている方を賞賛できる🎉ということで
今回はこちらを作ります！

## まずはローカルで

### Github Tokenの生成
:::note warn
注意
後述しますがこのscopeだとリポジトリに対して書き込みもできてしまうため
よりscopeを細分化して設定できるFine-grained personal access tokensを使う方がセキュアです。
:::
![Githubトークン5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/e3c1cb9d-e80b-8a52-fb92-755342324d23.png)

scopeは下記にしました。
![githubトークン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/b4df567c-9f7d-447c-c60b-df37fc599636.png)

### Slack WebHook URL発行のご依頼
権限のあるマネージャーに、Slack通知したいチャンネルに対して
Slack WebHook URLを発行していただきます。

### スクリプトの作成
Github APIへのリクエストは[@octokit/core](https://github.com/octokit/core.js)を使いますので
必要に応じて`npm install @octokit/core`

```javascript:calc.mjs
import { Octokit } from "@octokit/core";
import axios from "axios";
import dayjs from "dayjs";

// GitHub Personal Access Token
const TOKEN = "GitHubToken";
const WEBHOOK_URL =
  "Slack WebHook URL";

// リポジトリ情報
const OWNER = "owner"; // リポジトリのオーナー
const REPO = "reponame"; // リポジトリ名

const octokit = new Octokit({ auth: TOKEN });

async function postToSlack(formattedSummary, since) {
  const data = {
    text: "Weekly PR Comments Summary",
    attachments: [
      {
        fallback: "Weekly Summary",
        color: "#36a64f",
        pretext: `*Weekly PR Comments Summary*\n\n${formattedSummary}`,
        author_name: "Since",
        title: since,
        text: "からの集計",
      },
    ],
  };

  axios
    .post(WEBHOOK_URL, data)
    .then((response) => {
      console.log("Sended successfully");
    })
    .catch((error) => {
      console.error(error);
    });
}

async function getPRComments(owner, repo, since) {
  let page = 1;
  const perPage = 100;
  let comments = [];

  while (true) {
    const response = await octokit.request(
      "GET /repos/{owner}/{repo}/pulls/comments",
      {
        owner,
        repo,
        since,
        per_page: perPage,
        page,
      }
    );

    comments = comments.concat(response.data);

    if (response.data.length < perPage) {
      break;
    }
    page++;
  }

  return comments;
}

async function main() {
  // 過去1週間の日付を計算
  const since = dayjs().subtract(1, "week").toISOString();

  console.log(`Fetching PR comments since: ${since}`);

  // PR コメント取得
  const comments = await getPRComments(OWNER, REPO, since);

  // ユーザーごとの集計
  const userCommentCount = comments.reduce((acc, comment) => {
    const user = comment.user.login;
    acc[user] = (acc[user] || 0) + 1;
    return acc;
  }, {});

  // 結果を文字列型に変換
  const formattedSummary = Object.entries(userCommentCount)
    .map(([user, count]) => `- *${user}*: ${count} comments`)
    .join("\n");

  await postToSlack(formattedSummary, since);
}

main().catch((error) => {
  console.error("Error:", error.message);
});

```

### 実行
`node calc.mjs`

集計値が対象のSlackチャンネルに送信されました🎉
![Slack送信.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/8b741d4c-7d6c-0014-a4a2-f00d9b56b7f1.png)



## 定期実行するための方針
上記では自分のローカルから、手動でしか実行できないため
毎週定期的に実行するための方針を3点検討しました。

1. Slackアプリ上で[Bolt for JavaScript](https://tools.slack.dev/bolt-js/ja-jp/getting-started)を使用
→Github TokenをSlack側に持つ必要があり、セキュリティ的にNG
2. AWS Lambda + Amazon SNS + AWS Chatbot
→ちょっと面倒
3. Github Actions
→マネージャーよりこちらをお薦めいただきました！

### Github SecretにGithub TokenとSlack WebHook URLを登録
Github Actionsに直接Token情報を記載しないようにするため
権限のあるマネージャーに、対象のGithub リポジトリのsettingから設定いただきます。

### Github Actionsの作成
時刻はUTCのため9時間早く設定します。（下記の例は毎週水曜10:00）
```.github/workflows/pr-comments-summary.yml
name: Weekly PR Comments Summary

on:
  schedule:
    - cron: '0 1 * * 3'
  workflow_dispatch:

jobs:
  pr-comments-summary:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    - name: Set up Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    - name: Install dependencies
      run: yarn install

    - name: Fetch PR comments and send to Slack
      env:
        SLACK_WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }}
        GITHUB_TOKEN: ${{ secrets.TEST_GITHUB_TOKEN }}
      run: |
        #!/usr/bin/env node
        以下、処理内容を記載
```

### ですが、時間切れ、、、😔
対象リポジトリではyarnを使用しているのですが
こちらの依存関係解消ができず、今回は間に合いませんでした。。。

### 他にも改善点

- Github Tokenが書き込みもできてしまうscope
→Fine-grained personal access tokensを使う
→こちらのtoken + @octokit/coreではGithub APIが404で呼べなかったため
さらなる調査が必要💻
![Githubトークン4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/c5304d7d-0b4f-a247-e351-44512c0530dd.png)

![githubトークン3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635225/1e6f5119-afc4-3d06-8e6d-bbb598b38843.png)

- 自分、他の方のレビュー全然できてないのにコメント数多い…😎？
→自分が作成したPRに対するコメントは計上しないようにしたい

## 最後に
今後は定期実行の問題を解決し、毎週のチームMTGなどで
コメント数をふりかえりできるようにしていきたいと思います！

アイディアをいただいた [@gibom](https://qiita.com/gibom)さん、[@masafumi330](https://qiita.com/masafumi330)さん
技術相談にのっていただいた [@lulin](https://qiita.com/lulin)さん、[@shogoroy](https://qiita.com/shogoroy)さん
お忙しい中、アドバイスいただきありがとうございました🙇‍♀


## PR
現在、Wanoグループ / TuneCore Japan では人材を募集しています。
興味のある方は下記をご参照ください:information_desk_person:

[Wano | Wano Group JOBS](https://group.wano.co.jp/jobs/)
[TuneCore Japan | TuneCore Japan JOBS](https://www.tunecore.co.jp/jobs)