ENECHANGE Developer Blog

ENECHANGE開発者ブログ

octocovでコードカバレッジ計測を始めよう ー 複数リポジトリでも簡単管理!

ENECHANGEのSRE Infraチームの id:tockeysan です。

先日のAIリレーブログではClaude Codeを利用してOSSを読み、弊社に合ったoctocovの使い方を模索した話をしました。
CluadeCodeでOSSを読み解く - 導入時のエラーに立ち向かう - ENECHANGE Developer Blog

この記事はそのoctocovの導入編になります。

前提

前の記事の繰り返しですが、全プロダクト横断的な取り組みをする前は以下のような状態でした。

  • コードカバレッジツールの導入が各チームに依存しており、導入されていないプロダクトが複数存在する
  • 導入されているコードカバレッジツールがバラバラで、横断的な分析ができない
  • 仮に導入を進めたとしてもプロダクト数が非常に多いので各プロダクトがどの程度のカバレッジ率なのかを把握することが難しい

導入するにあたっての課題

弊社ではCIツールとしてGitHub Actionsの他にBuildkiteを利用していますが、octocovはGitHub Actionsでの利用を前提としていました。理想は全てのリポジトリでoctocovを導入することです。Buildkiteを利用しているリポジトリをGitHub Actionsに移行するのは現実的ではなく、Buildkiteでのテスト実行はそのままにoctocovも利用したいということが必要になってきます。

Buildkite + octocov with GitHub Actions

そこでBuildkiteのCIパイプラインに手を加え、最後のステップにGitHub Actions(repository_dispatch)を呼び出す構成をとることにしました。repository_dispatchはHTTPリクエストによって起動するタイプのGitHub Actionsです。

sequenceDiagram
    participant Dev as Developer
    participant GH as GitHub
    participant BK as Buildkite
    participant S3 as S3 Storage
    participant GHA as GitHub Actions
    participant PR as Pull Request

    Dev->>GH: Push to PR branch
    GH->>BK: Trigger Buildkite pipeline

    rect rgb(240, 248, 255)
        Note over BK: Buildkite Pipeline
        BK->>BK: Build
        BK->>BK: Test with Knapsack
        BK->>BK: Generate coverage report
        BK->>S3: Upload .resultset.json
        BK->>GH: curl repository_dispatch
    end

    rect rgb(245, 255, 245)
        Note over GHA: GitHub Actions (octocov)
        GH->>GHA: Trigger octocov workflow
        GHA->>S3: Download coverage data
        GHA->>GHA: Run octocov
        GHA->>GHA: Generate coverage report
        GHA->>PR: Post coverage comment
    end

    PR-->>Dev: Coverage report visible

Buildkiteパイプライン

repository_dispatchはPRには紐づかないため、そのままでは上記のフローにおける Post coverage comment では対象のPRを特定することができません。そのため、Buildkiteパイプライン側からPR等の情報を渡してあげる必要があります。BuildkiteパイプラインにはPRのブランチ名などをはじめ様々な環境変数が用意されているので、これらの変数を渡すこと自体はさほど難しくありません。

一部略...

json_payload="{
  \"event_type\": \"buildkite-build-complete\",
  \"client_payload\": {
    \"build_number\": \"${BUILDKITE_BUILD_NUMBER}\",
    \"state\": \"passed\",
    \"branch\": \"${BUILDKITE_BRANCH}\",
    \"commit\": \"${BUILDKITE_COMMIT}\",
    \"pull_request\": \"${BUILDKITE_PULL_REQUEST}\",
    \"pipeline\": \"${BUILDKITE_PIPELINE_SLUG}\"
  }
}"

curl -X POST \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer ${GITHUB_API_TOKEN}" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/${ 組織名 }/${BUILDKITE_PIPELINE_SLUG}/dispatches \
  -d "$json_payload"

GitHub Actions

続いて、呼び出されるGitHub Actionsのコードです。

name: Coverage Report with Octocov
on:
  repository_dispatch:
    types: [buildkite-build-complete]

permissions:
  id-token: write
  contents: write
  pull-requests: write
  issues: read
  actions: read

jobs:
  coverage:
    runs-on: ubuntu-latest
    if: github.event.client_payload.state == 'passed'
    env:
      PR_BRANCH: ${{ github.event.client_payload.branch }}
      PR_COMMIT: ${{ github.event.client_payload.commit }}

    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ env.PR_BRANCH }}
      
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          aws-region: ap-northeast-1

      - name: Download Coverage Report
        run: |
          aws s3 cp s3://${{ バケット名 }}/coverage/${{ env.PR_COMMIT }}/.resultset.json coverage/.resultset.json
      
      - name: Run Octocov
        uses: k1LoW/octocov-action@v1
        with:
          config: .octocov.yml
          work-dir: ${{ github.workspace }}
        env:
          OCTOCOV_GITHUB_REF: refs/heads/${{ env.PR_BRANCH }}
          OCTOCOV_GITHUB_SHA: ${{ env.PR_COMMIT }}

以下がポイントです。

1.カバレッジレポートの受け渡し

octocovはカバレッジレポートそのものを生成するものではなく、simplcovで生成したレポートを渡すことで機能します。simplcovレポートはBuildkiteパイプライン上で生成されるため、GitHub Actions側に明示的に受け渡す処理が必要です。そこでsimplecovレポートを一度S3にアップロードしておき、GitHub Actions側でダウンロードするということをしています。S3のsimplecovレポートはコミットハッシュをprefixに含めることで、リポジトリ側から簡単に特定できるようにしています。

実装例ではRails+simplecovを利用していますが、他言語でもサポートされているフォーマットのレポートが生成できればoctocovを利用できます。

2.環境変数のOverride

この機能があったからこそ、この構成が実現できました。GitHub Actionsにはデフォルトで用意されている環境変数があり、 octocovステップの実行時にOCTOCOV_というプレフィックスを付けた環境変数を設定することでその変数を上書きすることができます。OCTOCOV_GITHUB_SHAOCTOCOV_GITHUB_REFを指定して上書きすることで、無事PRに対してoctocovのコメントがされました。

なお、withでconfigwork-dirの指定も必要でした。指定しなかった場合.octocov.yml and octocov.yml are not foundとなってしまい正常に動きませんでした。repository_dispatchを使用しているため起こった事象かもしれません。

.octocov.yml

あとはoctocov自体の設定のみです。ここはほとんどREADMEにあるサンプル通りです。

coverage:
  paths:
    - coverage/.resultset.json
codeToTestRatio:
  code:
    - "app/**/*.rb"
  test:
    - "spec/**/*spec.rb"
comment:
  if: is_pull_request
summary:
  if: true
diff:
  datastores:
    - artifact://${GITHUB_REPOSITORY}
report:
  if: is_default_branch
  datastores:
    - artifact://${GITHUB_REPOSITORY}

制約

特殊な使い方をしているため制約もあります。repository_dispatchはPRによって直接トリガーされるactionではないため以下のようなことが出てきました。

  • テスト実行はBuildkiteで行われるためテスト実行時間をoctocovに渡せずバッジ生成ができない
  • PRのステータスチェック欄に表示されない
    • そのためoctocovのカバレッジ率をマージ条件にする設定が(おそらく)動かない

この制約はやむを得ないものとして割り切っています。

複数リポジトリのカバレッジを管理できるCentralモード

octocovにはCentralモードという機能があり、複数リポジトリのoctocovレポートを集約用のリポジトリに集めて一覧化することができます。弊社はプロダクト(リポジトリ)が非常に多いため、この一覧機能が大変助かっています。バッジも生成することができ各リポジトリのREADMEにペタリと貼れるのでこちらも便利です。

Centralリポジトリ用の.octocov.ymlとGitHub Actionsワークフローのyamlはサンプルコードをほとんどそのまま利用していますが、留意点としてはPrivateリポジトリを収集する時は対象リポジトリの参照権限があるアクセストークンを設定する必要があります。

name: Collect

on:
  workflow_dispatch:
  schedule:
    - cron: '0 0,3,6,9 * * *' # JST 9時,12時,15時,18時
  push:
    branches:
      - main

jobs:
  collect:
    name: Collect metrics using central mode of octocov
    runs-on: ubuntu-latest
    env:
      GH_TOKEN: ${{ secrets.OCTOCOV_GITHUB_TOKEN }}
      DEBUG: true
    steps:
      - name: Check out source code
        uses: actions/checkout@v4

      - name: Run octocov
        uses: k1LoW/octocov-action@v1

Centralモードは導入設定がシンプルながら、簡単にカバレッジ管理をすることができます。

まとめ

octocovでコードカバレッジを計測し、Centralモードで管理した取り組みについて紹介しました。なかでもBuildkiteとのCI併用は少々苦労しました。試行錯誤の結果、統一的なツールを選定したことで、アプリチームのカバレッジ率への関心を高め、継続的な運用の下地を作ることができました。今後はこのCentralモードによってできたリストを元に運用の議論を進めることができそうです。

GitHub Actions以外のCIでテストを実行している環境でoctocovを導入したい方の参考になれば幸いです。