はじめに
トレタでは、2年ほど前から CI 環境として CircleCI を利用していますが、この度めでたく CircleCI 2.0 が iOS プロジェクトのビルドにも対応したということで、試しに移行してみました。
CircleCI 1.0 と 2.0 の違いをまとめつつ、移行時に調べたことやはまったことをまとめます。
ジョブを定義できるように
CircleCI 1.0 時代のビルドには、3つのジョブ dependencies
test
deployment
があらかじめ用意されており、各ジョブに手順を定義することでビルドの設定を行うというものでした。
machine:
xcode:
version: 8.3.3
dependencies:
override:
- (commands)
test:
override:
- (commands)
deployment:
development:
commands:
- (commands)
CircleCI 2.0 では、あらかじめ用意されたジョブを利用するのではなく、ジョブ自体を自由に定義できるようになりました。また、ジョブ毎に環境を変更することもできます。
設定ファイルは circle.yml
ではなく .circleci/config.yml
に記述します。
version: 2
jobs:
job1:
macos:
xcode: 8.3.3
steps:
- (commands)
job2:
macos:
xcode: 8.3.3
steps:
- (commands)
job3:
macos:
xcode: 8.3.3
steps:
- (commands)
job4:
macos:
xcode: 8.3.3
steps:
- (commands)
job5:
macos:
xcode: 8.3.3
steps:
- (commands)
ジョブを並列実行できるように
CircleCI 1.0 では、3つのジョブ dependencies
test
deployment
が直列に実行されますが、2.0 では自分で定義したジョブを並列で実行することもできるようになりました。
「ジョブCを実行するためにはジョブAとジョブBが完了している必要がある」などジョブ同士の依存関係を定義することで、ジョブが並列に実行されるようになります。
トレタでは
- キャッシュが存在しない場合は RubyGems, CocoaPods をインストールしてキャッシュを作成(
setup
) - ↑が終わったら、3つのテストを並列実行
-
lint
SwiftLintによる構文チェック -
scan
ユニットテスト -
snapshot
UI テスト
-
- ↑が終わったら、2つのアプリのビルドを並列実行
-
deploy_staging
: ステージング環境向けのアプリ作成 -
deploy_prodtest
: 本番環境向けのアプリ作成
-
という風に移行してみました(サンプルで試して見たときのものなのでビルド時間は気にしないでください)。
CircleCI ではこのビルドのパイプラインを workflow と呼んでいます。設定ファイルでは workflows
の下に登場人物となるジョブと、彼らの依存関係を puppet のようなフォーマットで定義していきます。
version: 2
jobs:
setup:
...
lint:
...
scan:
...
snapshot:
...
deploy_staging:
...
deploy_prodtest:
...
workflows:
version: 2
build-and-deploy:
jobs:
- setup
- lint:
requires:
- setup
- scan:
requires:
- setup
- snapshot:
requires:
- setup
- deploy_staging:
requires:
- lint
- scan
- snapshot
- deploy_prodtest:
requires:
- lint
- scan
- snapshot
アプリのテストもビルドもそれなりに時間がかかるので、CircleCI 2.0 に移行して並列実行するだけでも時間をかなり節約できるはずです。
キャッシュを自分でコントロールできるように
1.0 ではサービス側でよしなにキャッシュコントロールしてくれていましたが、2.0 ではユーザー自身で管理する必要があります。
キャッシュの保存 save_cache
は、cache key に任意の文字列を指定して行います。キャッシュを復元 restore_cache
する時は、指定した cache key で呼び出せばOKです。
キャッシュはプロジェクト単位で利用可能なため、各 lock ファイルの checksum や、ブランチごとにキャッシュを作っておくなどしておくと効率がよさそうです。
先ほど紹介した setup
ジョブでは、下記のような設定で RubyGems と CocoaPods のキャッシュを必要に応じて作成するようにしています。
version: 2
jobs:
setup:
steps:
- checkout
- restore_cache:
keys:
- gems-{{ checksum "Gemfile.lock" }}
- gems-
- run:
name: Running bundle install
command: bundle check || bundle install
environment:
BUNDLE_JOBS: 4
BUNDLE_RETRY: 3
- save_cache:
key: gems-{{ checksum "Gemfile.lock" }}
paths:
- vendor/bundle
- restore_cache:
keys:
- pods-{{ checksum "Podfile.lock" }}
- pods-
- run:
name: Running pod install
command: |
curl https://cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh | bash -s cf
bundle exec pod install
- save_cache:
key: pods-{{ checksum "Podfile.lock" }}
paths:
- Pods
リストア時の secondary cache key として gems-
や pods-
を設定しておくと、 cache key にマッチするキャッシュが存在しない場合に最後保存されたキャッシュをリストアしてくれるため、 bundle install
pod install
の時短が期待できます。
保存しておいたキャッシュを別のジョブで利用する場合も、同様に restore_cache
で復元できます。
scan:
macos:
xcode: 8.3.3
steps:
- checkout
- restore_cache:
keys:
- gems-{{ checksum "Gemfile.lock" }}
- gems-
- restore_cache:
keys:
- pods-{{ checksum "Podfile.lock" }}
- pods-
- run:
name: Running fastlane scan
command: bundle exec fastlane scan
Artifacts 保存方法の変更
CircleCI 1.0 では、テストのログやUIテストのスクリーンショットなどのファイルを後から参照するためには、$CIRCLE_ARTIFACTS
ディレクトリに格納しておく必要がありました。
CircleCI 2.0 では、ジョブのステップ定義の中に store_artifacts
を記述し、保存しておきたいファイル/ディレクトリの名前と、保存先のパスを指定します。
snapshot:
steps:
- checkout
- restore_cache:
...
- run:
name: Running fastlane snapshot
command: bundle exec fastlane snapshot
- store_artifacts:
path: fastlane/screenshots
destination: screenshots
ruby バージョンを変える
デフォルトでは ruby 2.0.0 が利用されるため、もっと新しいバージョンの ruby を利用したい場面があると思います。 CircleCI 2.0 では ruby-install
と chruby
がインストールされているようで、それらを利用すれば別のバージョンの ruby が使えそうです。
(1) ジョブの定義に下記のように shell の設定を追加
setup:
macos:
xcode: 8.3.3
shell: /bin/bash --login -eo pipefail
(2) .ruby-version をファイルをプロジェクトルートに追加
ruby-2.4
- See: Custom Ruby Versions
circle.keychain がなくなった
CircleCI 1.0 では circle.keychain
という名前のキーチェーンがありましたが、 CircleCI 2.0 のドキュメントからは消えているので、どうやらなくなっているぽいです。
Fastlane の create_keychain
で keychain を作成してから、証明書のインポートを行う必要があります。
lane :certificates do
create_keychain(
name: ENV["KEYCHAIN_NAME"],
password: ENV["KEYCHAIN_PASSWORD"],
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: true
)
import_certificate(
certificate_path: "certificates/development.p12",
keychain_password: ENV["KEYCHAIN_PASSWORD"]
)
...
end
thanks to @horimislime
さいごに
まだまだ機能を使いきれておらず、最適化されていない感じが満載ですが、なんとなく移行してジョブを並列実行するだけでも、十分なリターンが得られるので試してみる価値は十分にありました。もっとよい方法や tips などあれば教えてもらえると嬉しいです。
そんな CircleCI 2.0 の気になるところとしては、なぜかビルドが失敗することと、Workflow API がまだないこと(Hubot から API 叩きたいので欲しいところ)。
以上、現場よりお伝えしました。Happy CI-ing!