CircleCI 2.0 + fastlane + Slather でコードカバレッジの変化を Slack に通知する

この記事は RECRUIT MARKETING PARTNERS Advent Calendar 2017 の投稿記事です。

こんにちは、スタディサプリオープンキャンパスの iOS アプリを担当している平井です。

iOS アプリ開発界隈ではテストや CI/CD に関する話題が盛り上がっています。せっかく書いたテストをもっと有効に活用したい方も多いはず。有効活用する方法の1つとして、コードカバレッジの変化を観察することが挙げられます。コードカバレッジが下がったらリジェクト...とまでいかなくても、変化を知ることだけでもメリットがあります。

iOS アプリ開発でも CodecovCoveralls と連携することで簡単に実現できます。しかし、これらのサービスはプライベートリポジトリは有料であるため、なかなか利用できない方も多いのではないようでしょうか。

というわけでこの記事では、これらのサービスを利用せずに CircleCI 2.0 + fastlane + Slather でコードカバレッジの変化を Slack に通知する方法をご紹介します。

本記事では以下の環境を前提に説明します。

  • Xcode 9.2
  • CircleCI 2.0
  • fastlane 2.69.2
  • Slather 2.4.4

適当なテストが書かれた Xcode プロジェクトも準備してください。

Xcode でコードカバレッジを計測できるようにする

Xcode ではコードカバレッジを計測できる機能が用意されています。この機能は Edit scheme... > Test > Gather coverage data にチェックを入れるだけで有効にできます。

Edit scheme... > Test > Gather coverage data にチェック

cmd + U でテストを実行すると Xcode 上でコードカバレッジが確認できます。

Xcodeのコードカバレッジレポートの表示

また、fastlane でテストを実行するときに Scheme を参照するので、Manage Schemes... より 対象の Scheme の Shared にチェックを入れましょう。

Scheme の Shared にチェック

fastlane と Slather をインストールする

コマンドラインでテストを実行するために、fastlaneSlather をインストールします。fastlane は利用されている方も多いと思いますが、Slather はあまり馴染みがないかもしれません。

Slather は Xcode 独自のコードカバレッジレポートから別形式のレポートを生成する gem です。生成できるレポート形式は Cobertura や HTML などがあります。

CircleCI でコードカバレッジレポートを確認するため Slather を利用します。早速、 Gemfile を準備しましょう。

source 'https://rubygems.org'
gem 'fastlane'
gem 'slather'

Gemfile の準備ができたら、以下のコマンドを実行して fastlane と Slather をインストールしておきましょう。

$ bundle install --path=vendor/bundle

続いて Fastfile を用意します。ありがたいことに fastlane には slather を実行するためのアクションがすでに用意されています。

fastlane_version "2.69.2"
default_platform :ios
platform :ios do
  desc "Runs all the tests"
  lane :test do
    scan(
      scheme: "[Your Xcode Scheme]",
      device: "iPhone 8 Plus",
      code_coverage: true,
      clean: true
    )
    slather(
      proj: "[Your Xcode Project].xcodeproj",
      scheme: "[Your Xcode Scheme]"
    )
  end
end

利用しているアクションの仕様は以下で確認できます。

手元で動作を確認してみましょう。

$ bundle exec fastlane ios test

以下のように表示されれば成功です。

[19:44:35]: ---------------------
[19:44:35]: --- Step: slather ---
[19:44:35]: ---------------------
[19:44:36]: $ slather coverage --scheme [Your Xcode Scheme] [Your Xcode Project].xcodeproj
[19:44:37]: ▸ Slathering...
[19:44:38]: ▸ [Your Xcode Scheme]/Xxx.swift: 3 of 3 lines (100.00%)
[19:44:38]: ▸ [Your Xcode Scheme]/Yyy.swift: 3 of 3 lines (100.00%)
[19:44:38]: ▸ Test Coverage: 100.00%
[19:44:38]: ▸ Slathered

CircleCI でテストを実行する

CircleCI でテストを実行できるようにします。以下のように CircleCI の設定ファイルを記述しましょう。

version: 2
jobs:
  test:
    macos:
      xcode: "9.2.0"
    working_directory: ~/project
    shell: /bin/bash --login -eo pipefail
    steps:
      - checkout:
          path: ~/project
      - run:
          name: Set Ruby Version
          command: echo "ruby-2.4" > ~/.ruby-version
      - restore_cache:
          key: v0-gems-{{ checksum "Gemfile.lock" }}
          path: vendor/bundle
      - run:
          name: Bundle install
          command: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle
          environment:
            BUNDLE_JOBS: 4
            BUNDLE_RETRY: 3
      - save_cache:
          key: v0-gems-{{ checksum "Gemfile.lock" }}
          paths: vendor/bundle
      - run:
          name: Run tests
          command: bundle exec fastlane ios test
workflows:
  version: 2
  build:
    jobs:
      - test

設定ファイルについては Configuration Reference - CircleCI を参照してください。

このファイルを含めて push すると、CircleCI 2.0 上で bundle exec fastlane ios test が実行されます。以下のように表示されれば成功です。

CircleCI 成功!

コードカバレッジの変化をSlackで通知する

ここまでで CircleCI のジョブごとにテストレポートを確認できるようになりました。CircleCI ではジョブごとにテストレポートなどの生成物を保存できます。このことを利用して、前回のジョブとのコードカバレッジの差分を抽出し、その変化を Slack で通知するようにしたいと思います。

まず、Slack の通知先を設定するために Slack の Webhook URL を取得します。CircleCI App の Add COnfiguration をクリックします。

Slack の Webhook URL を取得

通知先のチャネルを選び Add CircleCI Integration をクリックします。

通知先のチャネルを選

Webhook URL が表示されるのでコピーしておきましょう。

Slack Webhook URL をコピーする

次に CircleCI API 用のトークンを取得します。このトークンは前回のコードカバレッジレポートを取得するために必要です。Project Settings > API Permissions に移動して Create Token をクリックします。

CircleCI API 用のトークンを取得する

Build Artifacts を選択し、適当なラベルをつけて Add Token をクリックします。

Build Artifacts を選択し、適当なラベルをつけて Add Token をクリック

生成されたトークンをコピーしておきましょう。

生成されたトークンをコピー

続いて CircleCI の環境変数を追加します。環境変数の追加は Project Settings > Environment Variables で行います。追加する環境変数は以下のとおりです。

Name Value
SLACK_ENDPOINT 生成した Slack の Webhook URL
CIRCLE_TOKEN 生成した CircleCI API 用のトークン
COVERAGE_FILE cobertura.xml

環境変数追加後は以下のように表示されます。

環境変数追加後のEnvironment Variables

最後にテスト実行完了後に前回のコードカバレッジレポートと比較する処理を追加します。ここで、 @u-minor さんの CircleCIで計測したカバレッジ変化をSlackに晒す - Qiita で紹介されている神スクリプトを利用します。 Fastfile と .circleci/config.yml を以下のように変更しましょう。

fastlane_version "2.69.2"
default_platform :ios
platform :ios do
  desc "Runs all the tests"
  lane :test do
    scan(
      scheme: "[Your Xcode Scheme]",
      device: "iPhone 8 Plus",
      code_coverage: true,
      clean: true
    )
    slather(
      proj: "[Your Xcode Project].xcodeproj",
      scheme: "[Your Xcode Scheme]",
      source_directory: "./[Your Xcode Project Dir]",
      output_directory: "./fastlane/test_output/slather",
      cobertura_xml: true
    )
  end
  desc "Notify Slack about changes in code coverage"
  lane :coverage do
    commit = last_git_commit
    circleci2_coverage_slack = "https://gist.githubusercontent.com/u-minor/20c7876b9bc4b875d9c73b59f2a4365f/raw/68be9dd0db31ff98ceb91cc580875905e895890e/circleci2-coverage-slack"
    sh("curl -s -L #{circleci2_coverage_slack} | COMMIT_AUTHOR=\"#{commit[:author]}\" COMMIT_LOG=\"#{commit[:message]}\" bash -s")
  end
end
version: 2
jobs:
  test:
    macos:
      xcode: "9.2.0"
    working_directory: ~/project
    shell: /bin/bash --login -eo pipefail
    steps:
      - checkout:
          path: ~/project
      - run:
          name: Set Ruby Version
          command: echo "ruby-2.4" > ~/.ruby-version
      - restore_cache:
          key: v0-gems-{{ checksum "Gemfile.lock" }}
          path: vendor/bundle
      - run:
          name: Bundle install
          command: bundle check --path=vendor/bundle || bundle install --path=vendor/bundle
          environment:
            BUNDLE_JOBS: 4
            BUNDLE_RETRY: 3
      - save_cache:
          key: v0-gems-{{ checksum "Gemfile.lock" }}
          paths: vendor/bundle
      - run:
          name: Run tests
          command: bundle exec fastlane ios test
      - store_artifacts:
          path: fastlane/test_output
      - run:
          name: Slack
          command: bundle exec fastlane ios coverage
workflows:
  version: 2
  build:
    jobs:
      - test

この変更を push すると、CircleCI の Artifacts に cobertura.xml が生成されます。

CircleCI の Artifacts に cobertura.xml が生成される

このジョブを再度実行してみましょう。以下のように通知が届けば成功です。

Slack から通知がきた

まとめ

CircleCIで計測したカバレッジ変化をSlackに晒す - Qiita で紹介されている神スクリプトのおかげで、 CodecovCoveralls を利用しなくてもコードカバレッジの変化を知ることができるようになりました。

@u-minor さん、本当にありがとうございます!

iOS アプリ開発でも是非お試しください!