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

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

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

# Travisの設定
ここでは.travis.ymlについて書きます。設定画面での設定については以下の素晴らしい記事を参考にしてください。
[Travis CI と GitHub を連携して iOS アプリをテストする](http://qiita.com/star__hoshi/items/c9413b8d20e89bdc5f2f)
[Travis-CI上でiOS アプリをビルドしてdeploygateで配布する](http://qiita.com/tarom/items/9b8a3497907d8d4d1390)
.travis.ymlについても、公式のドキュメントが充実しているので、それ読めばいいって話ですが、書くことなくなっちゃうので自分がやったことを書いていきます。
[Getting started](https://docs.travis-ci.com/user/getting-started/)
[Customizing the Build](https://docs.travis-ci.com/user/customizing-the-build)

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

```yaml:.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
```

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

```yaml
language: objective-c
osx_image: xcode8.3
```
まずは使用する言語とxcodeのバージョンを指定します。
言語はswiftの場合でもobjective-cと指定するようです。
Xcodeのバージョンは、執筆時点ではデフォルトで7.3.1とちょっと古いので、ビルドしたいバージョンを指定します。
[Building an Objective-C or Swift Project](https://docs.travis-ci.com/user/languages/objective-c/)

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

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

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

```yaml
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](https://docs.travis-ci.com/user/environment-variables)

# fastlaneの設定
インストールに関してはこちらで。
[Getting started with fastlane for iOS](https://docs.fastlane.tools/getting-started/ios/setup/)
bundlerを使うのでGemfileをルートディレクトリに配置します。cocoapodsもbundlerでインストール・実行するので、Gemfileに書いておきます。

```Ruby:Gemfile
source "https://rubygems.org"

gem "fastlane"
gem "cocoapods"

```

## Appfile
アカウント情報などをここに記載します。laneごとに使う情報を変えることができます。
[Appfile](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md)
今回はリリースとそれ以外でバンドル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](https://docs.fastlane.tools/actions/)

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

```rb: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
```

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

```rb
fastlane_version "2.50.1"

default_platform :ios

platform :ios do
  ...
end
```

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

```rb
  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](https://docs.fastlane.tools/actions/#cocoapods)をどうぞ。
環境変数に関しては、ファイル内に書くよりCIの方で設定しておいたほうが良いみたいです。

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

scanを使ってビルド・テストを実行し、成功したらslackで通知します。
[scanについて](https://github.com/fastlane/fastlane/tree/master/scan)
注意としては、スキームをsharedにするのと、ワークスペースを使用する場合containerはワークスペースにしないとエラーが出ました。
![スクリーンショット 2017-08-10 16.15.31.png](https://qiita-image-store.s3.amazonaws.com/0/84917/41e71a70-ea45-edf5-8088-132b2a0d9969.png)

```rb
  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](https://docs.fastlane.tools/codesigning/getting-started/)

```rb
  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のオプションについては公式ドキュメントを。。。

```rb
  error do |lane, exception|
    slack(
      message: exception.message,
      success: false
    )
  end
```
各actionで正常終了しなかった時に呼ばれます。ここではslackで何かしらに失敗したことを通知します。

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