GitHub のリポジトリを見ていると、テストのカバレッジ (全アプリケーションのコードに対するテストコードのカバー率) が表示されたバッジをよく見かける。あれを自分もやってみたいと思い設定をしてみたのだが、思っていた以上に躓くポイントが多かった。そのため、導入方法をここに残しておく。
Codecov も SimpleCov も CircleCI もメジャーなツールではあるのだが、この 3 つを組み合わせた場合の導入方法を紹介している記事がほとんどなかった。この記事が、同じことを実現しようとしている人の役に立てば幸いである。
なお、この記事では、Git のホスティングサービスには GitHub、Ruby のテストツールには RSpec を使用することを想定している。ただ、基本は同じはずなので、異なる部分は適宜、読み替えること。
まずは Codecov にサインアップしてバッジを表示させたいリポジトリを登録する。
Not yet setup をクリックすると、まだ Codecov が設定されていないリポジトリ一覧が表示されるので追加する
https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/settings) にアクセスするActivation セクションから Activate ボタンをクリックして有効化するRepository Upload Token セクションからトークンをクリップボードにコピーして控えておくサインアップした直後はプライベートリポジトリは一覧に表示されない (追加で権限を付与する必要がある) のが少しだけわかりづらいので注意。
次に CircleCI の設定を行う。CircleCI は該当のリポジトリですでに稼働していることを前提とする。
https://app.circleci.com/settings/project/github/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/environment-variables) にアクセスするAdd Environment Variable ボタンをクリックするName に CODECOV_TOKEN と入力するValue に先ほど Codecov のページで控えておいたトークンをコピペするAdd Environment Variable ボタンをクリックして環境変数を登録するOrganization Settings の Security セクション (https://app.circleci.com/settings/organization/github/<USERNAME_OR_ORGNAME>/security) にアクセスするOrb Security Settings セクションから Yes を選択するここからはリポジトリ上のコードを変更していく。
Codecov と SimpleCov の RubyGems をインストールする。RSpec (または minitest などその他のテストツール) はすでにインストール済みであることを前提とする。
Gemfile に以下を記述する。
group :test do
gem 'codecov', require: false
gem 'simplecov', require: false
end
以下のコマンドを実行する。
bundle install
RSpec の設定 (spec/spec_helper.rb) から SimpleCov の読み込み設定と Codecov 用のフォーマットを追記する。SimpleCov を読み込むことで、テスト実行後にカバレッジ情報を coverage ディレクトリに出力してくれる。さらに SimpleCov のフォーマットを Codecov にすることで、Codecov で読み取れる形式の JSON ファイルを書き出してくれる。
RSpec ではないテストツールの場合は適宜変更すること。
require 'simplecov'
SimpleCov.start 'rails'
require 'codecov'
SimpleCov.formatter = SimpleCov::Formatter::Codecov
RSpec.configure do |config|
# 割愛
end
CircleCI の設定ファイル (.circleci/config.yml) に必要な情報を追加する。
executors, commands, workflows などは割愛する。なお、以下はあくまで例なので、必要に応じて変更すること。
version: 2.1
orbs:
codecov: codecov/codecov@1.0.5
executors:
# 割愛
commands:
# 割愛
jobs:
build:
executor: default
steps:
- setup
- checkout
- run:
name: test
command: |
mkdir -p reports
docker create -v /reports --name reports alpine:3.4 /bin/true
docker run --network <NETWORK> --env-file=./.circleci/app.env --volumes-from reports -it <CONTAINER> ./.circleci/docker-test
docker cp reports:/reports reports
- store_test_results:
path: reports
- codecov/upload:
file: ./reports/reports/coverage/codecov-result.json
workflows:
# 割愛
いくつかポイントがあるので説明する。
この Orb のスクリプトはバグが多くて、たとえば 1.0.4 だとシンタックスエラーになってしまう。全部のバージョンを試したわけではないが、他にも 1.1.4, 1.1.5, 1.1.6 などでも同じシンタックスエラーが発生することを確認済み。1.0.5 なら正常に動作することを確認している。
Orb doesn’t work in Alpine images
これは Docker の Alpine イメージ (Bash がない) で発生する問題らしいので、Bash が入っているイメージなら他のバージョンでも正常に動作するのかもしれない。
また、2.x や 3.x だと gpg: not found という別のエラーが発生してしまう。
store_test_results (CircleCI で Docker を利用している場合のみ)Docker コンテナ内のファイルはコマンド終了後に消えてしまう。それを防ぐために、store_test_results を指定する。
path: reports のように記述すると、reports ディレクトリは消えずに残る。ここでは reports というディレクトリ名にしているが、他の名前にすることも可能。その場合は実行するコマンド内の記述も併せて変更すること。
上記の例では ./.circleci/docker-test というスクリプトを実行しているが、このスクリプト内で RSpec (または別のテストツール) を実行する。.circleci/docker-test は以下のように記述する。なお、このファイル名は例なので変更しても良い。
#!/bin/sh
set -eux
RAILS_ENV=test bundle exec rails db:drop db:create >/dev/null 2>&1
RAILS_ENV=test bundle exec rspec
cp -R coverage /reports
SimpleCov をインストールしている場合、RSpec を実行するとプロジェクトディレクトリに coverage というディレクトリが生成され、その中にカバレッジの詳細データが格納される。そしてその coverage ディレクトリを、 store_test_results で指定したパス (今回は /reports) にコピーする必要がある。そうしないと Docker コンテナで生成されたカバレッジの詳細データが消えてしまうからである。
筆者はあまり Docker には詳しくないのだが、以下のように docker run コマンドを複数に分けると、RSpec 実行時に生成された coverage ディレクトリが消えてしまう。
docker run --network <NETWORK> --env-file=./.circleci/app.env --volumes-from reports -it <CONTAINER> "bundle exec rspec"
docker run --network <NETWORK> --env-file=./.circleci/app.env --volumes-from reports -it <CONTAINER> "cp -R coverage /reports"
そのため、RSpec の実行と coverage ディレクトリの退避を単一の docker run コマンドで行うために、別途 .circleci/docker-test というスクリプトを用意してそれを実行している。
そのあとの docker cp reports:/reports reports で、Docker コンテナ側の /reports ディレクトリを、ホスト側の reports ディレクトリに退避している。
CircleCI 上で、RSpec などを Docker を使って実行している場合は、必要な環境変数をどこかのファイルに記述しているはずである。
たとえば、CircleCI 上 (.circleci/config.yml) で docker run --env-file=./.circleci/app.env ... のようなコマンドを実行している場合は .circleci/app.env 内に必要な環境変数を記述しているはず。
その場合は、このファイル内に、CircleCI の設定ページで登録したものと同じ環境変数を登録する必要がある。以下のコードブロックのファイル名は .circleci/app.env としているが、適宜変更すること。
CODECOV_TOKEN=<REPOSITORY_UPLOAD_TOKEN>
codecov/uploadcodecov/upload を記述することにより、生成されたカバレッジの詳細データを Codecov 上に送信することができる。その際に、file オプションを指定することにより、明示的にどのファイル (カバレッジデータ) を Codecov に送信するのかを指定することができる。
Codecov の RubyGems を使用すると、coverage ディレクトリの中に codecov-result.json というのを生成してくれる。この JSON ファイルを Codecov にアップロードすることで Codecov 上でカバレッジの詳細とグラフを確認することができる。なお、Codecov の RubyGems を使わなかった場合 (SimpleCov のみの場合) は index.html が生成されるが、これを Codecov 上にアップロードしてもエラーとなり詳細やグラフなどを確認することができないので注意。
coverage ディレクトリは、今回の例だと ./reports/reports ディレクトリ以下にコピーしてきたので、./reports/reports/coverage/codecov-result.json を指定する。
最後に、README に Codecov のバッジを追加する。
バッジの画像 URL は Codecov のリポジトリ設定ページの Badge タブ (https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/settings/badge) から取得することができる。Markdown セクションにあるコードをコピーして、README の任意の場所にペーストする。
ここまでできたら、変更を GitHub にプッシュする。CircleCI が回るはずなので、すべてのジョブが終了するのを待つ。
.circleci/config.yml の codecov/upload は、CircleCI の画面上では Upload Coverage Results という名前で表示される。RSpec を実行するジョブ (本記事の例では test というジョブ名) と、Codecov にカバレッジ情報をアップロードするジョブ (Upload Coverage Results) が両方ともエラーなく通っていれば OK だ。
ただし、codecov/upload で指定したファイルパスが間違っていると、No such file or directory というエラーが出力されるのだが、これが出ていても CI 自体は正常に通ってしまうので注意。Upload Coverage Results のジョブの実行結果の出力を CircleCI 上で確認して何もエラーが出ていないことまで確認する必要がある。
以下の 2 点を確認する。
https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME> にアクセスして、カバレッジのグラフなどが表示されていることここまでの変更を、別ブランチを切って行っている場合は、おそらくどちらも正常に表示されないはずだ。
unknown と表示されるURL 内にブランチを明示的に指定しない場合は、デフォルトブランチ (何も設定していない場合は GitHub リポジトリ上のデフォルトブランチと同じ) の内容が表示される。しかし、今回の変更ではじめて Codecov を導入する場合で、その変更用に別ブランチを切っている場合は、デフォルトブランチではまだ Codecov を導入していないことになる。そのため、ブランチを指定しない場合は Codecov のページに何も表示されなかったり、バッジが unknown になってしまう。
今回の変更をデフォルトブランチにマージする前に、正しく表示されるのかどうかを確認するためには、一時的にデフォルトブランチを変更するか、URL 内でブランチを指定してアクセスする。
https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/settings) にアクセスするDefault Branch セクションの Branch Name を、今回 Codecov の導入を対応しているブランチに変更するOverview (https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>) にアクセスして、グラフなどが表示されていることを確認するBadge タブ (https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/settings/badge) にある画像 URL にブラウザから直接アクセスし、カバレッジの数値が正しく表示されていることを確認するhttps://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/branch/<BRANCH_NAME> にアクセスして、グラフなどが表示されていることを確認するhttps://codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/branch/<BRANCH_NAME>/graph/badge.svg?token=<TOKEN> にブラウザから直接アクセスし、カバレッジの数値が正しく表示されていることを確認する
<TOKEN> は Codecov のリポジトリ設定ページ (https://app.codecov.io/gh/<USERNAME_OR_ORGNAME>/<REPOSITORY_NAME>/settings) の Repository Graphing Token セクションから確認可能今回は Codecov を利用したが、Codecov を利用せず、GitHub Pages で似たようなことを実現するツールがある。
Codecov ほど視覚的にわかりやすい情報は得られないが、外部サービスに頼りたくない場合や、チームメンバーが 6 人以上いる場合1などはこちらを検討するのも良いだろう。
Codecov は 6 人より多い人数で利用したい場合は有料となる。参考: https://about.codecov.io/pricing/ ↩