0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actionsを使ってAIによる生産性を簡易的に計測する

0
Posted at

こんにちは!
ポーラ・オルビスホールディングスのITプロダクト開発チームでスクラムマスターをしている川田です。

Claude CodeやGitHub Copilotなどを利用していると、AIによってどれだけ生産性があがったか報告が求められたり、組織のKPIになっていたりするケースがあると思います。
そこまでは求められていなくても、チームとして状況を見える化しておくことは重要ですよね🤩

今回は、AIによって作られたPRの数をカウントすることで行う簡易的な生産性の計測を、GitHub Actionsを使って実現してみようと思います。

前提

この記事においては、AIによって作られたPRの定義を以下と定めます。

  • コミットがGitHub CopilotなどのAIエージェントによって作成された内容のみで構成されている
  • Devinが作成したもの

コミット内容がAIエージェントによって作成されているかについては、

  1. コミットメッセージに [ai] という文字列が含まれている
  2. コミットメッセージに Apply suggestion from @Copilot が含まれている

のどちらかを満たしているかどうかで判定しています。

1の条件については、例えばClaude Codeなら settings.json にある attribution設定commit に設定しておくと入力漏れが防げるため便利です。
また2の条件については、PR上でGitHub CopilotからのCommit suggestionを受け入れたときのコミットメッセージとなるので、こちらも対象としています。

GitHub Actionsでの判定とラベル付け

PRが変更されたタイミングで、前提に記載した条件にあてはまるか判定するGitHub Actionsを動かすようにします。
条件にあてはまった場合は、そのPRに ai-only ラベルを付与することで後からカウントできるようにします。
なお、ラベルは対象のリポジトリで事前に作成しておいてください。

実際のコードは以下の通りです。

name: Label AI-only PRs

on:
  pull_request:
    types: [opened, reopened, edited, synchronize, ready_for_review, closed]

permissions:
  contents: read
  pull-requests: write

jobs:
  check-and-label:
    runs-on: ubuntu-latest
    steps:
      - name: Check pull request for AI-only
        id: check
        uses: actions/github-script@v7
        with:
          script: |
            const pr = context.payload.pull_request;
            if (!pr) {
              core.setOutput('ai_only', 'false');
              return;
            }

            // --- PRの作成者だけでAI利用と判断するユーザー名のリスト ---
            const aiIntegrationBots = [
              'devin-ai-integration[bot]',
              'devin-ai-integration', 
              'app/devin-ai-integration'
            ];

            if (aiIntegrationBots.includes(pr.user.login)) {
              core.setOutput('ai_only', 'true');
              return;
            }

            const per_page = 100;
            let page = 1;
            let commits = [];
            while (true) {
              const { data } = await github.rest.pulls.listCommits({
                owner: context.repo.owner,
                repo: context.repo.repo,
                pull_number: pr.number,
                per_page,
                page
              });
              commits = commits.concat(data);
              if (data.length < per_page) break;
              page++;
            }

            // --- ここで「PR内部の merge commit(親が2つ以上)」を除外する ---
            const commitsForCheck = commits.filter(c => {
              const parentCount = Array.isArray(c.parents) ? c.parents.length : (c.commit?.parents?.length ?? 0);
              return parentCount <= 1; // parentが2つ以上 = merge commit → 除外
            });

            if (commitsForCheck.length === 0) {
              core.setOutput('ai_only', 'false');
              return;
            }

            const isAiOnly = commitsForCheck.every(c => {
              const msg = (c.commit?.message || '').trim().toLowerCase();
              return msg.includes('[ai]') || msg.includes('apply suggestion from @copilot');
            });

            core.setOutput('ai_only', String(isAiOnly));

      - name: Add label if ai-only
        if: steps.check.outputs.ai_only == 'true'
        uses: actions-ecosystem/action-add-labels@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          labels: ai-only

      - name: Remove label if not ai-only
        if: steps.check.outputs.ai_only != 'true'
        uses: actions-ecosystem/action-remove-labels@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          labels: ai-only

実装のポイントは以下の通りです。

  • PRの作成者の名前が aiIntegrationBots に含まれるものと一致するかチェックしています
    • Devinはおそらく中央の1つだけあればよいと思います(他は念のため・・・)
    • 将来的に新しいAIエージェントも対象とする場合は、このリストに1行追加するだけでOKです
  • GitHub APIの List Commits を利用して、各コミットメッセージが条件を満たすかチェックしています
  • ステップの最後に、前段で判定した条件をもとにラベルの付け外しを行っています

カウント方法

あとは ai-only ラベルが付いたPRの数をカウントするだけです。
カウント方法はいくつかあるので、お好みの方法で実施してください。

GitHub API でカウントする

Search API を利用してカウントし、jq を利用して結果から total_count のみを抜き出します。
repo を複数指定することで、複数のリポジトリの合算を算出することも可能です。

curl -s -L \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer TOKEN" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "https://api.github.com/search/issues?q=repo:OWNER/REPO+is:pr+label:ai-only" | jq '.total_count'

ghコマンドを利用した方法でも同様に実行可能です。

gh api \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer TOKEN" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  search/issues?q=repo:OWNER/REPO+is:pr+label:ai-only | jq '.total_count'

GitHub上でカウントする

APIと同じ検索方法で、ブラウザからGitHubにアクセスして確認することも可能です。
リポジトリの複数指定も可能です。

https://github.com/pulls?q=repo:OWNER/REPO+is:pr+label:ai-only

上記にアクセスすると、PRのリストの上部に合計が表示されています。

image.png

おまけ:NewRelicで可視化してみる

とりあえず困ったらNewRelicに送っちゃおう😎ということで、ラベルの付与状況をNewRelicに送ることで、ダッシュボードを作って可視化してみました。

PRがcloseしたタイミングでPRのメトリクスを計測するため、NewRelicのカスタムイベントを送る既存のGitHub Actionsがあったので、そのイベントに以下のように処理した内容を追加しました。

// 対象のPRにai-onlyラベルが付いている場合はai-only, そうでない場合はhuman-madeをセット
const isAIOnly = context.payload.pull_request.labels.some(label => label.name === "ai-only") ? "ai-only" : "human-made";

この処理結果を ai_only という項目名称、PullRequest というイベントタイプでNewRelicに送信した場合、以下のクエリで集計が可能です。

SELECT count(*)
FROM PullRequest
FACET ai_only

集計した結果は pie chartで表示するといい感じです👍

image.png

さいごに

今回はPRの数という観点で生産性の簡易的な計測を行ってみました。
GitHubのコミット情報と紐づけられたので、将来的には変更行数など、もう一歩踏み込んだ分析も実施したいと思います!💪

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?