GitHub Actions issue-metricsの記録をもとにAIにふりかえりをしてもらう

Cover Image for GitHub Actions issue-metricsの記録をもとにAIにふりかえりをしてもらう
monotalk
monotalk

はじめに

GitHub ActionsでAI呼び出し - Mitsuyuki.Shiiba を見ていて興味を持ったので、何かに活用できないか?考えていたのですが、少し前に個人開発のIssueのメトリクス計測に、github/issue-metrics: Gather metrics on issues/prs/discussions such as time to first response, count of issues opened, closed, etc. を利用し始めていて、「issue-metricsの記録をAIにふりかえりしてもらうのはおもしろいかも?」と考えたので実際に試してみました。

実際の成果物と作成時の手順や考えたことをまとめておきます。

作ったもの

以下のGitHub Actionsを作成しました。といっても作ったのはGitHub Copilotでわたしはエラーが出たり、動きが変だったりするところを修正していっただけです。

  • issue-metrics.yml
name: Monthly issue metrics
on:
  workflow_dispatch:
  schedule:
    - cron: '3 2 1 * *'
  # 毎月1日の午前2時3分に実行されるように設定
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true
  # 同じワークフローの実行が重複しないように設定
  # ただし、異なるブランチやタグでの実行は許可
  # されるように設定
permissions:
  # リポジトリのコンテンツ読み取り権限
  contents: read
  # Issue作成・コメント追加のための書き込み権限
  issues: write
  # GitHub AIモデル呼び出しのための読み取り権限
  models: read
jobs:
  monthly_metrics: # より説明的なジョブ名に変更
    name: Monthly issue metrics
    runs-on: ubuntu-latest
    timeout-minutes: 5 # タイムアウト設定を追加
    steps:
      - name: Get dates for last month
        shell: bash
        run: |
          # バッシュスクリプトのエラー処理を強化
          set -euo pipefail

          # 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: Install dependencies
        run: sudo apt-get update && sudo apt-get install -y jq

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

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

      - name: Get issue body
        id: get-issue
        env:
          ISSUE_NUMBER: ${{ steps.create-issue.outputs.issue-number }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # トークンを直接引数として渡す
          gh api repos/${{ github.repository }}/issues/$ISSUE_NUMBER --jq '.body' > issue_body.txt
          echo "ISSUE_CREATED=true" >> $GITHUB_ENV

      - name: Call AI model for KPT analysis
        id: model-call
        if: env.ISSUE_CREATED == 'true'
        env:
          # KPT分析用のプロンプトを環境変数から取得
          PROMPT: '以下のIssueメトリクスについて、Keep(良いところ)、Problem(悪いところ)、Try(今後のアクション)の観点で評価してください:'
        run: |
          # エラー処理を強化
          set -euo pipefail

          # ファイルの存在確認
          if [ ! -f issue_body.txt ]; then
            echo "::error::Required input file issue_body.txt not found"
            exit 1
          fi

          # 入力データの安全な処理
          SAFE_PROMPT=$(echo '${{ env.PROMPT }}' | jq -Rs .)
          SAFE_BODY=$(cat issue_body.txt | jq -Rs .)

          # 安全にJSONリクエストを構築
          cat > request.json << EOF
          {
            "messages": [
              {
                "role": "user",
                "content": $(echo "$SAFE_PROMPT\n\n$(echo $SAFE_BODY | jq -r .)" | jq -Rs .)
              }
            ],
            "model": "openai/gpt-4o"
          }
          EOF

          # API呼び出し
          HTTP_RESPONSE=$(curl -s -w "%{http_code}" -o response_body.txt "https://models.github.ai/inference/chat/completions" \
             -H "Content-Type: application/json" \
             -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
             -d @request.json)
          CURL_EXIT=$?
          HTTP_STATUS=${HTTP_RESPONSE: -3}
          echo "::endgroup::"

          if [ $CURL_EXIT -ne 0 ]; then
            echo "::error::curlコマンドが失敗しました。エラーコード: $CURL_EXIT"
            exit 1
          fi

          if [[ $HTTP_STATUS -lt 200 || $HTTP_STATUS -ge 300 ]]; then
            echo "::error::AIモデルの呼び出しに失敗しました。HTTPステータスコード: $HTTP_STATUS"
            echo "::group::レスポンス本文"
            cat response_body.txt || echo "レスポンス本文を取得できませんでした"
            echo "::endgroup::"
            # エラー時に確実に終了する
            exit 1
          fi

          RAW_RESPONSE=$(cat response_body.txt)
          if [ -z "$RAW_RESPONSE" ]; then
            echo "::error::空のレスポンスが返されました"
            exit 1
          fi

          CONTENT=$(echo "$RAW_RESPONSE" | jq -r '.choices[0].message.content // empty')
          if [ -z "$CONTENT" ]; then
            echo "::error::AIモデルからの応答内容が空または不正な形式です"
            exit 1
          fi

          echo "$CONTENT" > model_response.txt
          echo "AI_SUCCESS=true" >> $GITHUB_ENV

      - name: Add AI analysis comment to issue
        if: env.AI_SUCCESS == 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ISSUE_NUMBER: ${{ steps.create-issue.outputs.issue-number }}
          REPO: ${{ github.repository }}
        run: |
          # エラー処理を強化
          set -euo pipefail

          # ファイルの存在確認
          if [ ! -f model_response.txt ]; then
            echo "::error::AI response file not found"
            exit 1
          fi

          # 安全にファイルの内容を取得
          AI_RESPONSE=$(cat model_response.txt)

          # コメント内容を作成
          cat > ai_comment.md << EOF
          ## AIによるIssueメトリクス分析

          $AI_RESPONSE
          EOF

          # コメントを投稿
          gh issue comment $ISSUE_NUMBER --repo $REPO --body-file ai_comment.md

          if [ $? -ne 0 ]; then
            echo "::error::Failed to post comment to issue"
            exit 1
          fi

      - name: Archive results
        uses: actions/upload-artifact@v4.6.2
        with:
          name: ai-analysis-results
          path: |
            ./model_response.txt
          retention-days: 3 # 必要な最小期間に設定
          if-no-files-found: warn # ファイルが見つからない場合は警告を出す

      - name: Archive debug info (only on failure)
        if: ${{ failure() }}
        uses: actions/upload-artifact@v4.6.2
        with:
          name: debug-info
          path: |
            ./issue_body.txt
            ./prompt.txt
            ./response_body.txt
            ./request.json
          retention-days: 1 # デバッグ情報は短期間のみ保持

以下、GitHub Copilotに処理の内容をまとめてもらいました。

# Issue Metrics GitHub Actionsの概要

このGitHub Actionsは、リポジトリのIssueに関する月次メトリクスレポートを自動生成するワークフローです。主な機能は以下の通りです:

## 実行タイミング
- 毎月1日の午前2時3分に自動実行
- 手動実行も可能

## 主な処理の流れ
1. **日付範囲の計算**: 前月の初日と最終日を特定
2. **メトリクス収集**: `github/issue-metrics`ツールを使用して前月作成されたIssueのメトリクスデータを収集
3. **レポートIssueの作成**: 収集したメトリクスデータをMarkdownレポートとして新しいIssueに変換
4. **AI分析の実行**: OpenAIのGPT-4oモデルを使用してメトリクスデータをKPT(Keep、Problem、Try)フレームワークで分析
5. **分析結果の投稿**: AI分析結果を作成したIssueにコメントとして投稿

## 安全性と信頼性の機能
- 同時実行の制御で重複実行を防止
- エラー処理の強化とタイムアウト設定
- 失敗時のデバッグ情報の自動アーカイブ

このActionにより、プロジェクト管理者はIssue傾向を定期的に分析し、プロジェクト運営の改善点を客観的に把握できます。

また、以下のgistにGitHub Actions実行で作成されたIssueのマークダウンファイルをアップロードしました。AIの出力が気になる方は以下もご確認ください。

作業時の手順

GitHub Actionsの作成は以下の流れで進めました。

  1. ベースをGitHub Copilotで生成。
  2. 動作確認する。
  3. GitHub Actionsのレビュープロンプトを作成し、それを用いてGitHub Copilotでレビューを実施し、指摘を修正していく。

それぞれ具体的に説明します。

1. ベースをGitHub Copilotで生成

作業のIssueには以下の内容を記述しました。
IssueでのGitHub Copilotへの作業指示

その後、github/github-mcp-server: GitHub's official MCP Server を使える状態にしたうえで、以下をGitHub Copilot Agentに依頼しました。

{{GitHub の IssueのURLリンク}}
の本文の内容に従い、GitHub Actionsを修正してください。    

2. 動作確認する。

作成したGitHub Actionsを実際に動かしたところ、以下のエラーが発生しており、少し手間取りました。

**Error:** AIモデルの呼び出しに失敗しました。HTTPステータスコード: 403
レスポンス本文
 {"error":{"code":"no_access","message":"No access to model: openai/gpt-4o","details":"No access to model: openai/gpt-4o"}}

Webで調べてもよくわからなかったのですが、参考元のGitHub Actionsと見比べて、以下の記載がGitHub Actionsのymlファイル上ないのが原因でのエラーということに気がつきました。

  # GitHub AIモデル呼び出しのための読み取り権限
  models: read

3. GitHub Actionのレビュープロンプトを作ってGitHub Copilotでレビュー実施、指摘を直していく。

GitHub CopilotでA11yのレビュー、テストを実施する | Markdown をやった気づきを活かして以下2つのプロンプトを作成しました。

  • GitHub Actionsのレビュー用ベースプロンプト
私はGitHub Actionsのベストプラクティスとセキュリティ強化について学び、業界標準に準拠したワークフローを作成する必要があります。あなたにはGitHub Actionsの専門家として、セキュリティと効率性を考慮したワークフロー設計のコーチになっていただきたいです。回答の際は、以下の信頼できる情報源を参照してください:

* https://exercism.org/docs/building/github/gha-best-practices
* https://developers.cyberagent.co.jp/blog/archives/36423/
* https://docs.github.com/ja/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions
* https://qiita.com/kannkyo/items/dd8b11315b4433fda543
可能であれば、追加学習のためのリンクや参考資料も提供してください。ワークフローの例やコードを提案する際は、以下の点を考慮してください:

* 最小権限の原則に従った適切なパーミッション設定
* Secretsと環境変数の安全な取り扱い
* イミュータブルなバージョン指定(@v2ではなく@v2.1.0など)
* 入力検証とサニタイゼーション
* ワークフロー間の依存関係の明確化

また、GitHub Actionsエコシステムの最新の変更点や非推奨機能についても教えてください。これらの指示を理解していますか?

  • レビュー依頼用のプロンプト
GitHub Actionsのレビューしてください。
レビュー結果は総合得点最大100点で減点方式でスコアリングしてください。

## 出力形式
各チェック項目について以下の形式で評価と改善案を提供してください:

### [チェック項目名]

**評価結果**: [合格/改善が必要]

**問題点**:
```該当コード```

**改善案**:
```修正コード例```

**参照**:
[関連する成功基準へのリンクと説明]

以上の要件に基づいて、対象ファイルの評価を行ってください。

レビュープロンプトは、1つのチャットウィンドウで評価が80点以上になるまでレビュー結果の反映を続けました。

GitHub ActionsをGitHub Copilotで作ってみて感じたことのまとめ

以下が感じたことになります。

1. エージェントの活用に関する考察

  • Issueにエージェントの実行指示を書いておくと、その指示にしたがって動いてくれる。
  • Issueのclose時にエージェントにタスクのふりかえりをしてもらい、Issueに追記するのも良さそう。
  • Issueの取得にgithub/github-mcp-server: GitHub's official MCP Server を利用していたが、Dockerを起動しておかないと、Issue取得失敗してしまう。その後、修正対象のファイルをなんとか探して、改善を始める挙動をしていた。
    • 失敗時には処理を終了するような指示を記載する必要がありそう。
    • Dockerエージェントを自動起動する設定を有効にしておくと良さそう。

2. プロンプト設計とレビュー

  • copilot-instructions.mdの内容を調整し、エージェント向けのベース指示を記載するのは有効かもしれない。
  • 観点別でレビューアーの役割をプロンプトで行うのは有効に思った。
  • レビュープロンプトにスコアリング処理を入れる際は、同じチャットでコンテキストを使い回すようにすると良さそう。
    • ソースコード全体を見てスコアリングしているわけではない様子。チャットのコンテキストを捨てると、スコアが下がる傾向があった。

3. Pull Requestとリリース管理

  • Productionリリース時のPull Requestのタイトルや説明をAI生成するようにしたい。
    • 作業内容を要約となるため、自然な内容に仕上がっていると感じた。

4. GitHub ActionのGitHub Copilotでの生成について

  • GitHub Actionsの生成は可能だが、2025年4月時点の最新のベストプラクティスに対応していない場合がある。   
    • 再度GitHub Copilotにレビュー指示して指摘箇所を修正。
    • この辺りは、copilot-instructions.mdの調整でなんとかできるところなのかもしれない。
    • AIが提案する修正は文脈によっては適切でないことがある。そのため、セキュリティやパフォーマンスに影響する変更については、公式ドキュメントや複数の情報源と照らし合わせて最終的な確認をする。

参考

おわりに

今回は、GitHub ActionsとAIを組み合わせてIssueメトリクスのふりかえりを実装してみました。GitHub Copilotを活用したワークフロー作成では、調整やエラー対応に人間の判断が必要でした。それでも人手のみで実装するより効率的と感じました。
copilot-instructions.md や、プロンプトの調整でもう少し効率的に作業できる可能性があると感じました。また、GitHub ActionsでのAIモデル実行は便利な場面がありそうなので、さらに活用できる場面がないか検討したいと思います。