OSSで開発メトリクスの計測!GitHub Actionsのissue-metricsを使ってみた

GitHubが公式で出している開発メトリクスを取れるGitHub Actionsを紹介し試してみた内容を書きました!
2024.01.29

はじめに

今回はGitHub上での開発している際に、開発に関するメトリクスを取ることが出来るGitHub Actionsのissue-metricsについて紹介します。こちらのOSSはGitHubが公式のリポジトリで提供しているものになります。

issue-metricsにはデフォルトで、Pull Requestへの最初のコメントやクローズまでの時間計測などを特定の期間指定してレポートすることが出来ます。それ以外にもラベルと組み合わせることでラベルの付与/削除までの期間計測を利用することでオープンからレビューまでやレビューからapproveまでの時間などのメトリクスを取るような応用も出来ます。

本記事では、サンプルや実際の適用されているOSSの紹介、どんな機能が含まれているのか、どんな応用が可能かをご紹介します。

紹介記事

GitHubのリンク

サンプル

先に何ができるのか例がある方がイメージしやすいと思うので、サンプルを紹介します。最低限の機能を有効化した状態で以下のように、PRのオープンからコメントまでの時間(Time to First Response)、PRのクローズまでの時間(Time to Close)、Discussionsが解決するまでの時間(Time to Answer (Discussions Only))を確認する事ができます。

issue-metrics-sample

実際にAWS CDKというOSSでも使用されており、こちらでは月ごとのissue/PRのメトリクスが取られています。

上記のような計測をGitHub Actionsを設定するだけで収集することが出来ます。

どんな機能があるのか

ここからは上記のサンプルなどを元にどんな機能が搭載されているのか記載します。ほぼGitHub上のReadmeの内容に補足を加えています。

Time to First Response

issueやPRに対して、最初にコメントやレビューが付くまでにかかった時間が計測されます。PRやissue単位で計測されるので、個人単位での計測などは現状ありません。また、自分自身のコメントやボットなどのコメントは計測に含まれません。

Time to Close

issue/PRのクローズまでの時間を計測します。

Time to Answer (Discussions Only)

Discussionsの中で作成から回答完了までにかかった時間を計測します。

Time in Label

後述するLABELS_TO_MEASUREオプションで指定した、ラベルの適用から削除までの期間を計測します。計測するラベルは複数設定することが出来ます。

Time in Labelの応用

基本的な計測は、Time to First Response/Time to Closeなどで計測出来ますが、もう少し多種のメトリクスが取りたくなるかと思います。例えば、PRのレビューやレビューからapproveなどにどこまでの時間をかけているのかです。柔軟な計測を行うために、Time in Labelを使用すれば任意のラベルが付与されて外れるまでの時間を計測できるので、ラベルの自動付与や運用によって様々なメトリクスを取れるようになりそうです。

パラメータ

Actionの中のパラメータ設定を変えることで、出力内容を変えたり、特定のユーザを計測から省くなどの操作が可能です。パラメータを以下に記載します。

項目名 必須か 値の例
GH_TOKEN リポジトリをスキャンする際に利用するGitHub Token。スキャン対象の全てのリポジトリへの読み取り権限が必要。 ${{ secrets.GITHUB_TOKEN }} や ${{ steps.generate.outputs.token }} など
SEARCH_QUERY `repo:owner/repo`または`repo:org/repo`で対象リポジトリ、`is:`でissue/PRのどれを抽出するかや期間などを決める。複数リポジトリの場合は、空白を開けて記載する 'repo:owner/repo repo:owner/repo2 is:issue created:${{ env.last_month }} -reason:"not planned"'
LABELS_TO_MEASURE どのラベルを抽出対象とするか記載します。カンマ区切りで複数指定できます。 'waiting-for-review,waiting-for-manager'
HIDE_AUTHOR PRやissueの作成者を出力結果から省きます True OR 任意の値
HIDE_TIME_TO_FIRST_RESPONSE 最初の応答時間を出力から省きます True OR 任意の値
HIDE_TIME_TO_CLOSE クローズまでの時間を出力から省きます True OR 任意の値
HIDE_TIME_TO_ANSWER ディスカッションの回答までにかかった時間を出力から省きます True OR 任意の値
HIDE_LABEL_METRICS ラベルに対する計測時間を出力から省きます True OR 任意の値
IGNORE_USERS メトリクスを計測するときに、指定したユーザを除外します。ユーザはカンマ区切りで指定します。 'github-actions[bot]' や 'user1,user2'

設定方法

ここからはGitHub Actionsの設定方法について解説します。

アクションファイルやリポジトリの準備

まずは使用するリポジトリを選択します。ここで選ぶリポジトリは、集計対象のリポジトリでもメトリクス収集用のリポジトリを新しく作っても問題ないです。理由としては、最終的にGitHubのPersonal Access TokenやGitHub Apps Tokenを使用するので、トークン側に読み取る権限があればどこのリポジトリでも問題ないです。

次にメトリクスを取るActionsの設定を考えます。今回はReadmeにも記載れている以下をベースに作成します。他のサンプルはここにあります。

name: Monthly issue metrics
on:
  workflow_dispatch:
  schedule:
    - cron: '0 0 1 * *'

permissions:
  issues: write
  pull-requests: read

jobs:
  build:
    name: issue metrics
    runs-on: ubuntu-latest
    steps:
    - name: Get dates for last month
      shell: bash
      run: |
        # Calculate the first day of the previous month
        first_day=$(date -d "last month" +%Y-%m-01)

        # Calculate the last day of the previous month
        last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d)

        #Set an environment variable with the date range
        echo "$first_day..$last_day"
        echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"

    - name: Run issue-metrics tool
      uses: github/issue-metrics@v2
      env:
        GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SEARCH_QUERY: 'repo:owner/repo is:issue created:${{ env.last_month }} -reason:"not planned"'

    - name: Create issue
      uses: peter-evans/create-issue-from-file@v4
      with:
        title: Monthly issue metrics report
        token: ${{ secrets.GITHUB_TOKEN }}
        content-filepath: ./issue_metrics.md

内容としては、実行時の前月のissueを抽出し、Time to First Response/Time to Closeなどのデータを抽出して、新規のissueとして書き込みます。書き込みはCreate issueアクションでサンプルとしてissueに書いているので、別の場所にマークダウンを書き出すように変更もできます。またissue-metricsアクション自体もPythonで書かれているので、当たり前ですが不便な部分があればissueやPRを上げることも出来ます。

次にSEARCH_QUERYの部分を修正して、自分のリポジトリやOrgなどが対象になるようにします。今回はrepo:owner/repoの部分をサンプルリポジトリに変換して試します。

認証トークンの設定

次にGitHub Actionsでデータを読み取りためのトークンを設定します。ソース元のリポジトリでは、Personal Access Tokenを使用していますが、GitHub Apps Tokenでも代替できたのでそちらの方法をご紹介します。以下のブログを参考にしています。

参考元ブログの「1. GitHub Appsを作成する」に沿ってGitHub Appを作成し、リポジトリのステータスやissueなどの権限を設定します。GitHub Actionsの実行には以下の権限で足りました、もう少し権限絞れそうなので、後日わかった際は更新します。

項目名
Actions Read and write
Commit statuses Read-only
Contents Read-only
Discussions Read-only
Environments Read-only
Issues Read and write
Metadata Read-only
Pull requests Read-only
Secrets Read-only
Variables Read-only

上記で作成した、GitHub AppのAPP_ID/PRIVATE_KEYをActionsを実行するリポジトリのRepository secretsとして登録します。URLはhttps://github.com/{user or org name}/{repository name}/settings/secrets/actionsになります。

github-repo-secret-example

参考元では推奨していないですが、今回はサンプルなので手順簡略のためgithub-app-tokenを取得するようにコードを追加/修正します。正式なリポジトリで実行する場合は、自前実装によるGitHub Appsトークンの生成を参考にしてください。

      - name: Generate GitHub Apps token
        id: generate
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.APP_ID }}
          private_key: ${{ secrets.PRIVATE_KEY }}

      - name: Run issue-metrics tool
        uses: github/issue-metrics@v2
        env:
          GH_TOKEN: ${{ steps.generate.outputs.token }}
          SEARCH_QUERY: 'repo:owner/repo is:issue created:${{ env.last_month }} -reason:"not planned"'

実行

後は変更した内容をmainブランチに反映するとアクションを実行できるようになります。GitHub Actionsのコードにworkflow-dispatchが設定されているので、cronでの実行タイミング以外でも任意のタイミングで実行できます。

workflow-dispatch-demo

実行すると最初に表示したサンプルのように最低限のメトリクスを見ることが出来ます。

最終的なソースは以下のようになります。

name: Monthly issue metrics
on:
  workflow_dispatch:
  schedule:
    - cron: '0 0 1 * *'

permissions:
  issues: write
  pull-requests: read

jobs:
  build:
    name: issue metrics
    runs-on: ubuntu-latest
    steps:
      - name: Get dates for last month
        shell: bash
        run: |
          # Calculate the first day of the previous month
          first_day=$(date +%Y-%m-01)

          # Calculate the last day of the previous month
          last_day=$(date -d "$first_day +1 month -1 day" +%Y-%m-%d)

          #Set an environment variable with the date range
          echo "$first_day..$last_day"
          echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"

      - name: Generate GitHub Apps token
        id: generate
        uses: tibdex/github-app-token@v1
        with:
          app_id: ${{ secrets.APP_ID }}
          private_key: ${{ secrets.PRIVATE_KEY }}

      - name: Run issue-metrics tool
        uses: github/issue-metrics@v2
        env:
          GH_TOKEN: ${{ steps.generate.outputs.token }}
          SEARCH_QUERY: 'repo:owner/repo is:issue created:${{ env.last_month }} -reason:"not planned"'

      - name: Create issue
        uses: peter-evans/create-issue-from-file@v4
        with:
          title: Monthly issue metrics report
          token: ${{ secrets.GITHUB_TOKEN }}
          content-filepath: ./issue_metrics.md

応用例

先程の例だと固定のメトリクスしか取れないです。なので、以下のように自動でラベルを付ける機能と組み合わせて、自分が計測したいタイミングで集計できるか試してみます。今回はissueのラベル単位でラベル付与から削除までのメトリクスを取ってみます。Actionの部分にLABELS_TO_MEASUREの一行を加えて修正します。

      - name: Run issue-metrics tool
        uses: github/issue-metrics@v2
        env:
          GH_TOKEN: ${{ steps.generate.outputs.token }}
          SEARCH_QUERY: 'repo:owner/repo is:issue created:${{ env.last_month }} -reason:"not planned"'
          LABELS_TO_MEASURE: 'bug,sample'

bugラベルはデフォルトのもので、sampleは今回の確認用に独自に追加したものです。これで実行すると以下のように、ラベル単位/PR内のラベル付与から削除までの時間が見れます。

issue単位でも以下のように見えます。(ヘッダが見えなくなるぐらい再試行したので、画像は切り貼りしてます)

issueだけでなく、PRにも設定できるので、PRがマージされるまでの時間以外にも、PR作成からレビューやレビューからapproveまでの時間なども測ることが出来ます。

ついでですが、試しに同じラベルをもう一度付与して削除したところ、最初にラベルが付与された時間から最後にラベルが削除された時間で集計されるようです。

所感

開発のメトリクスを何らか取れないか、GitHubの公式機能で試してみました。試した限りだとまだ機能は少ないですが、ラベルをうまく活用できればそれなりのデータは出せそうです。ただ、現状グラフィカルにする部分などは商用製品の方が質はかなり高いという印象でした。

後、BotによるPRが多いと見にくいので、場合によっては隠したりする処理があると便利そうなので、もう少しソースを理解してフィードバックしてみます。本記事が試して見たかった方の参考になれば幸いです。

製造ビジネステクノロジー部の佐藤智樹でした。