LoginSignup
6
3

More than 5 years have passed since last update.

TravisCIとfastlaneでiOSアプリのCI【途中】

Last updated at Posted at 2017-08-10

今時CI回してないとかやばくない?ってとある方に言われました:dizzy_face:
今回はTravis上でのビルドとテストの実行、fastlaneを使ってデプロイするところまでできたので、わかったことをつらつら書いていきます:pencil:
とある理由でTravisが途中で使えなくなってしまって、中途半端なところで終わってしまっていますが、Travisが復活したら続きを書きます:sweat:
わからないながら調べ調べやっているので、間違い等あるやもしれませんが、そのときは指摘・アドバイス等いただけたらと思います:bow:

なぜTravisとfastlane?

CIに関しては、自前でサーバを管理したくなかった、iOS/Android/Webがビルドできる、安いプランでも制限が少ないという理由でTravisにしました。
他のサービスとの比較は以下の記事に書きましたので、良かったら読んで下さい。
iOSアプリでCIを始めようとサービスを調べた
fastlaneに関しては、CIに依存しすぎないために使うことにしました。今後CIサービスが変わったり、CIサービスを使うのをやめた場合でも、fastlane単体でビルド・テスト・デプロイができるからです。

Travisの設定

ここでは.travis.ymlについて書きます。設定画面での設定については以下の素晴らしい記事を参考にしてください。
Travis CI と GitHub を連携して iOS アプリをテストする
Travis-CI上でiOS アプリをビルドしてdeploygateで配布する
.travis.ymlについても、公式のドキュメントが充実しているので、それ読めばいいって話ですが、書くことなくなっちゃうので自分がやったことを書いていきます。
Getting started
Customizing the Build

今回は以下のようなファイルを用意しました。これをプロジェクトのルートディレクトリに配置します。

.travis.yml
language: objective-c
osx_image: xcode8.3

notifications:
    slack: 【slackのAPIキー】

install:
  - bundle install
  - bundle update fastlane

script:
  - bundle exec fastlane test

after_success:
  - test $TRAVIS_BRANCH == develop && bundle exec fastlane adhoc
  - test $TRAVIS_BRANCH == master && bundle exec fastlane release

それぞれ要素を見ていきます。

language: objective-c
osx_image: xcode8.3

まずは使用する言語とxcodeのバージョンを指定します。
言語はswiftの場合でもobjective-cと指定するようです。
Xcodeのバージョンは、執筆時点ではデフォルトで7.3.1とちょっと古いので、ビルドしたいバージョンを指定します。
Building an Objective-C or Swift Project

notifications:
    slack: 【slackのAPIトークン】

今回は通知にslackを使用するので、slackの設定をしておきます。これでビルド結果がslackに流れるようになります。
通知はslackの他にも、メール、IRC、Webhookなどを設定できますので、お好みのものを使ってください。
Configuring Build Notifications
リポジトリがプライベートなので、まあいっかってことでAPIトークンを直書きしちゃっていますが、暗号化しておいた方がいいみたいです。そこら辺も上記のページにちゃんと書いてあるので、参考にしてください。

install:
  - bundle install
  - bundle update fastlane

fastlaneをbundlerで実行したいのでそのための準備をします。
Configuring Build Notifications#Dependency-Management
上記ページに書いてありますが、TravisはルートディレクトリにPodfileがある場合、自動でpod installを実行してくれます。Gemfileがある場合は自動でbundle exec pod installを実行してくれます。
ですが、今回はfastlane内でそれをやらせたいので、Travisにやってほしくありません。そんなときは、上記のようにinstall:を定義しておけば良いみたいです。

script:
  - bundle exec fastlane test

fastlaneのtestというlaneを実行します。中身については後述しますが、ビルドとテストをしています。

after_success:
  - test $TRAVIS_BRANCH == develop && bundle exec fastlane adhoc
  - test $TRAVIS_BRANCH == master && bundle exec fastlane release

:exclamation::exclamation::exclamation:動作未確認:exclamation::exclamation::exclamation:
Travisが途中で使えなくなってしまったため、実行できてないです。こうすれば上手く行くんじゃないかなーというのを書いています。
:exclamation::exclamation::exclamation::exclamation::exclamation::exclamation::exclamation::exclamation::exclamation::exclamation:
script:が成功したら、さらにlaneを実行します。デプロイをするのですが、ブランチによってデプロイ先を変えます。
TRAVIS_BRANCHはTravisが用意してくれる環境変数で、ブランチの名前が入っています。
Environment Variables

fastlaneの設定

インストールに関してはこちらで。
Getting started with fastlane for iOS
bundlerを使うのでGemfileをルートディレクトリに配置します。cocoapodsもbundlerでインストール・実行するので、Gemfileに書いておきます。

Gemfile
source "https://rubygems.org"

gem "fastlane"
gem "cocoapods"

Appfile

アカウント情報などをここに記載します。laneごとに使う情報を変えることができます。
Appfile
今回はリリースとそれ以外でバンドルIDとアカウントを、アドホックと開発でチームを変えたかったので以下のように書きました。

Appfile
app_identifier "【開発用バンドルID】"
apple_id "【開発用アカウントID】"
team_id "【開発用チームID】"

for_platform :ios do

  for_lane :release do
    app_identifier "【リリース用バンドルID】"
    apple_id "【リリース用アカウントID】"
    team_id "【リリース用チームID】"
  end

  for_lane :adhoc do
    team_id "【アドホック用チームID】"
  end
end

Fastfile

fastlaneに実行させる内容を記載します。laneという単位でタスクを定義するとこができ、関数のようにlaneから別のlaneを呼び出すこともできます。
ビルドする、テストする、デプロイするといった処理は予めactionとして用意されており、オプションをいくらか加えるだけで簡単に実行できるようになっています。
fastlane actions

今回は以下のように書きました。releaseに関しては未実装です。

Fastfile
fastlane_version "2.50.1"

default_platform :ios

platform :ios do

  before_all do
    ENV["SLACK_URL"] = "【URL】"
    cocoapods
    ENV["KEYCHAIN_NAME"] = "【適当な名前】"
    ENV["KEYCHAIN_PASSWORD"] = "【適当なパスワード】"
  end

  desc "Runs all the tests"
  lane :test do
    scan(
      workspace: "【ワークスペース】",
      scheme: "【対象スキーム】"
    )
    slack(
      message: "Test succeeded"
    )
  end

  desc "create ipa and deploy via deploygate"
  lane :adhoc do
    import_adhoc_certificate if is_ci?
    sigh(
      app_identifier: "【アドホック用バンドルID】",
      adhoc: true,
      provisioning_name: "【アドホック用プロビジョニングファイル名】",
    )
    gym(
      workspace: "【ワークスペース】",
      scheme: "【対象スキーム】",
      output_name: "【ipaファイル名】"
    )
    deploygate(
      api_token: "【トークン】",
      user: "【ユーザ名】",
      message: "Fastlane build",
    )
    slack(
      message: "Successfully deployed to deploygate"
    )
  end

  desc "Deploy a new version to the App Store"
  lane :release do
    # TODO
  end

  desc "Import certificate for adhoc"
  lane :import_adhoc_certificate do
    create_keychain(
      name: ENV["KEYCHAIN_NAME"],
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true,
      password: ENV["PASSWORD"]
    )
    import_certificate(
      keychain_name: ENV["KEYCHAIN_NAME"],
      certificate_path: "【開発者証明書のパス】",
      certificate_password: ENV["KEYCHAIN_PASSWORD"]
    )
  end

  error do |lane, exception|
    slack(
      message: exception.message,
      success: false
    )
  end
end

それぞれ要素を見ていきます。

fastlane_version "2.50.1"

default_platform :ios

platform :ios do
  ...
end

これはおまじないだと思っています。

  before_all do
    ENV["SLACK_URL"] = "【URL】"
    cocoapods
    ENV["KEYCHAIN_NAME"] = "【適当な名前】"
    ENV["KEYCHAIN_PASSWORD"] = "【適当なパスワード】"
  end

laneが実行される前に実行されるブロックです。cocoapodsによる依存フレームワークのインストールと、環境変数のセットをしています。
cocoapodsは基本的にpod installを実行しますが、ルートディレクトリにGemfileがある場合、自動でbundle exec pod installに切り替えてくれるみたいです。
その他オプションについては公式ドキュメント#cocoapodsをどうぞ。
環境変数に関しては、ファイル内に書くよりCIの方で設定しておいたほうが良いみたいです。

  desc "Runs all the tests"
  lane :test do
    scan(
      workspace: "【ワークスペース】",
      scheme: "【対象スキーム】"
    )
    slack(
      message: "Test succeeded"
    )
  end

scanを使ってビルド・テストを実行し、成功したらslackで通知します。
scanについて
注意としては、スキームをsharedにするのと、ワークスペースを使用する場合containerはワークスペースにしないとエラーが出ました。
スクリーンショット 2017-08-10 16.15.31.png

  desc "Import certificate for adhoc"
  lane :import_adhoc_certificate do
    create_keychain(
      name: ENV["KEYCHAIN_NAME"],
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true,
      password: ENV["PASSWORD"]
    )
    import_certificate(
      keychain_name: ENV["KEYCHAIN_NAME"],
      certificate_path: "【開発者証明書のパス】",
      certificate_password: ENV["KEYCHAIN_PASSWORD"]
    )
  end

順番が前後しますが、先にこちらを。キーチェーンを作り、開発者証明書を登録します。
今回の場合、開発者証明書はp12で書き出しておいてリポジトリに含めておきます。
certを使えばそこもfastlaneでできる?
証明書を専用のリポジトリで管理するmatchという方法もあるみたいです。詳しいことは調べてないので、今後の課題ということで。
Codesigning concepts

  desc "create ipa and deploy via deploygate"
  lane :adhoc do
    import_adhoc_certificate if is_ci?
    sigh(
      app_identifier: "【アドホック用バンドルID】",
      adhoc: true,
      provisioning_name: "【アドホック用プロビジョニングファイル名】",
    )
    gym(
      workspace: "【ワークスペース】",
      scheme: "【対象スキーム】",
      output_name: "【ipaファイル名】"
    )
    deploygate(
      api_token: "【トークン】",
      user: "【ユーザ名】",
      message: "Fastlane build",
    )
    slack(
      message: "Successfully deployed to deploygate"
    )
  end

CIで実行されているなら開発者証明書を登録し、sighでプロビジョニングファイルを生成、gymでipaを作成、deploygateでデプロイ、成功すればslackで通知します。
is_ciもactionの一つで、手動実行か自動実行か判別してくれるものみたいです。
各actionのオプションについては公式ドキュメントを。。。

  error do |lane, exception|
    slack(
      message: exception.message,
      success: false
    )
  end

各actionで正常終了しなかった時に呼ばれます。ここではslackで何かしらに失敗したことを通知します。

おわりに

Travisとfastlaneをなんとかかんとか使ってみたという記事でした:sweat_smile:
一応自分のやろうとしていることは実現できているのですが、ちゃんと挙動を分かっていないものが多いので、今後きっちり調べて理解しようと思っています:mag:
この記事を書いている途中でTravisを再び使えるようになったので、今度はiTunesConnectへアップロードするところまでやってみて、また記事にまとめます:volcano:

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3