これから始めるfastlane


この記事は、[第2のドワンゴ Advent Calendar 2015](http://qiita.com/advent-calendar/2015/dwango2)の記事です。

# はじめに
ここ最近のiOS界隈のトレンドの１つとして[fastlane](https://github.com/fastlane/fastlane)があります。
fastlaneは様々なアクションを組み合わせて使用することで、テストやipaの作成などを簡潔に記述し実行することができます。

2015年10月25日のニコニコ超パーティにてリリースされた「[ニコナレ](http://niconare.nicovideo.jp/)」などドワンゴでリリースしていくiOSアプリにも今後積極的に使用していく予定です。

今回実際にプロジェクトに取り入れた例を踏まえつつ、これからfastlaneを初めて触る人がこの記事を見ればfastlaneの使い方、actionの定義の仕方などがわかるような記事を目指します。
また、多くの紹介記事ではAppStoreへのリリース自動化やデプロイ等に焦点を当てていますが、今回はテストでの利用やipaの作成など、デプロイフロー以外でもfastlaneを導入するための入門記事としてfastlaneの根本となる機能や作成方法をまとめます。

# インストール
まずは、fastlaneのインストールを行います。

```
gem install fastlane
```
この場合、インストールを行ったfastlaneのバージョン等はそれぞれのPCの環境等に依存することとなります。
個人の開発の場合はそこまで問題はありませんが、チームでの開発やCIの環境を常に同じバージョンで動作させるためには、Bundlerを使用します。

Bundlerをインストールしていない場合、以下のコマンドを使用しインストールしてください

```
gem install bundler
```

プロジェクト直下に`Gemfile`を用意し、以下を記述します。

``` 
source "https://rubygems.org"
gem 'fastlane'
```

そして、fastlaneを`vendor/bundle`配下にインストールします。

```
bundle install --path=vendor/bundle
```
fastlaneを`vendor/bundle`配下にインストールすることで、することで各々の開発環境に依存することなくfastlaneを使用することができます。

***Bundlerを使用して、fastlaneのインストールを行った場合、これからの`fastlane`コマンドの前に`bundle exec`を使用して、bundlerでインストールしたfastlaneを使用することを明記してください***

## ちょっとしたはまり
fastlaneインストール後、コマンドを使用しようとした所以下のエラーに遭遇しました。
https://github.com/fastlane/fastlane/issues/729
```
-bash: fastlane: command not found
```

システム標準のRubyを使用している場合に起きることがあるバグだそうです。
[対応策](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md#update_fastlane)
すでに対応策は明示されており、`~/.bashrc`か`~/.zshrc`に以下を追加した所動作しました。

```
export GEM_HOME=~/.gems
export PATH=$PATH:~/.gems/bin
```

こういったシステム依存の問題を少なくするためにも、Bundlerを使用してのインストールをおすすめします。

# 初期設定
fastlaneのセットアップは以下のコマンドで行います。

```
fastlane init
```

バージョン管理(git等)を使用しているかの確認。
していれば`y`、していなければgitを使ってバージョン管理を行って下さい。

```
Do you have everything commited in version control? If not please do so now!
(y/n)
```

fastlaneを使用するアプリの`Bundle Identifier`を入力してください。
ここで入力する`Bundle Identifier`はfastlaneからiTunesConnect等に接続するときに使用されますので、もしIdentifierを切り替えて使用している場合は本番用のものを入力してください。

```
App Identifier (com.krausefx.app):
```

Developerアカウントのメールアドレスを入力してください。
もし、Developer固有の機能（プロビジョンの更新等）を行わない場合は空でも問題ありません。
また、設定は`Appfile`に書き込まれていますのでいつでも変更可能です。

```
Your Apple ID (fastlane@krausefx.com):
```

AppStoreへのデプロイなどに使用する`deliver`コマンドを使用する場合は`y`を入力してください。
今回の記事ではデプロイを行わないため使用しません。

```
Do you want to setup 'deliver', which is used to upload app screenshots, app
metadata and app updates to the App Store? This requires the app to be in the App
Store already. (y/n)
```

アプリのスクリーンショットを取ってくれる`snapshot`を使用する場合は`y`を入力してください。
`snapshot`コマンドはUIAutomationを使用して実行時にスクリーンショットを自動で撮影してくれます。
`deliver`同様今回の記事では含みません。

```
Do you want to setup 'snapshot', which will help you to automatically take
screenshots of your iOS app in all languages/devices? (y/n)
```

`sigh`コマンドは自動でDeveloperサイトからプロビジョンニングファイルのダウンロードや更新などを行うことができます。
`y`を入力すると自動で`sigh`を使用したサンプルが`Fastfile`に追加されます。

```
Do you want to use 'sigh', which will maintain and download the provisioning
profile for your app? (y/n)
```

プロジェクト内でカスタムスキームを使用している場合は入力してください。
もし、Xcodeデフォルトのままですとなにも入力する必要はありません。

```
Optional: The scheme name of your app (If you don't need one, just hit
Enter): 
```

この時点で`./fastlane`というフォルダが生成され、

* Appfile
* Fastfile

といったファイルが自動生成されています。

基本的にfastlaneを使用して処理を行うにはこの`Fastfile`に様々な処理を記述していきます。

Fastfileの中身を見てみると基本的に４つの処理があることがわかります。

### before_all
ここでは、fastlaneの処理が開始される前に一度だけ実行されます。

```ruby 
before_all do
  # ENV["SLACK_URL"] = "https://hooks.slack.com/services/..."
  cocoapods
  # increment_build_number
  # xctool # run the tests of your app
end
```
`ENV["SLACK_URL"]`にslackの`incoming hook url`を設定しておくことにより、各アクションに設定された自動投稿やslackへの通知が簡単に行うことができます。

`cocoapods`では`pod install`コマンドが実行されます.

このように処理の前に必ず行っておきたい初期化処理などはこの`before_all`で行います。

### lane
fastlaneでは１つの動作の流れを`lane`と呼んでいます。

fastlaneを実行する際は引数として`lane_name`を渡します。

```
fastlane [lane_name]
```

この`lane_name`に対応したlaneが実行されるようになります。

```ruby
desc "Runs all the tests"
lane :test do
  # snapshot
end
```

例えば、上記の`lane :test`を実行するためには以下のコマンドにより行うことができます。

```
fastlane test
```

### after_all
処理の終了時には`after_all`が呼び出されます。

```ruby
after_all do |lane|
  # This block is called, only if the executed lane was successful 
  # slack(
  #   message: "Successfully deployed new App Update."
  # )
end
```

ここでは、実行終了時の通知などをslackに通知したりします。
`|lane|`の中には実行した`lane`の種類が格納されていますので、`test`というlaneだけ終了通知を行いたい場合は以下のような判定を行うことができます。

```ruby
after_all do |lane|
  if lane == :test
    slack(message: "finish lane")
  end
end
```

### error
処理中になんらかの理由でエラーが発生し処理が中断された場合はerrorが呼び出されます。
一度errorのブロックに入ると`after_all`は呼び出されずここで終了するので気をつけて下さい。

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

このようにFastfile自体は４つの基本的には動作のみで処理を記述することができ、各laneを拡張していくという流れになります。

# laneの定義
前述したとおりfastlaneではlaneという流れを使用して処理を行います。
ここでは、cocoapodsのインストールとcathageのインストールのみを行うlaneを定義してみたいと思います。

```ruby
lane :setup_library do 
  cocoapods
  cathage
end
```

このように`lane`の中で予め定められたアクションを記述するだけで、CocoapodsとCarthageを使用したライブラリのセットアップを簡単に行えます。

このlaneを実行させるためには以下のコマンドで実行できます

```
fastlane setup_library
```

# laneの順次実行
定義したlaneはlane内で別のlaneを呼ぶことも可能です。

```ruby
lane :deploy do
  build(release: true)
  # deploy code
end

lane :deploygate do
  build
  # update deploygate
end

lane :build do |options| 
  scheme = (options[:release] ? "Release" : "Staging")
  ipa(scheme: scheme)
end
```

ここでは、`deploy`と`deploygate`のlaneでどちらもipaの出力を行いたい場合は`build`というlaneを別で定義して各lane内で呼び出しています。

laneの呼び出しは引数も取ることができ、`deploy`のlaneでは`release`フラグを引数としてわたしています。

# プロジェクトのビルドとipaの出力
プロジェクトのビルドとipaの出力は別々のアクションとして定義されていますが、[gym](https://github.com/fastlane/gym)というビルドとipaの出力を比較的に簡単に行えるアクションが用意されています。
`gym`を使用することでビルドとipaの出力のコードは以下の様な形となります。

```ruby
gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Debug",
  scheme: "MyApp",
  silent: true,
  clean: true,
  output_name: "my-app.ipa",   
)
```

ここではプロジェクト設定された値を使用してAppStore用のipadを`my-app.ipa`という形でアーカイブしています。

# テストの実行
fastlaneでのテストを実行は`scan`や`xctest`、`xctool`等複数のツールを使用することができます。
この中でも比較的新しく簡単にテストを行うことができる`scan`コマンドをおすすめします。

`scan`は以下の記述で使用することができます。

```ruby
scan(scheme: "ApplicationSchemeName")
```

Slackへの通知を設定した場合は、テストに成功すると以下のような通知を自動で送信されます。
<img width="250" alt="スクリーンショット_2015-12-10_05_27_16.png" src="https://qiita-image-store.s3.amazonaws.com/0/1500/f8e843ad-bcc7-7725-a486-eace03cd5428.png">

失敗した場合は以下のような通知が送信されます。
<img width="250" alt="スクリーンショット_2015-12-10_05_30_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/1500/bb1bea9a-ea07-797b-5051-d7849de43f7d.png">

また、`scan`内部で[xcpretty](https://github.com/supermarin/xcpretty)と呼ばれるログ整形ツールが使用されており、テストの結果はJUnitのレポート形式のhtmlファイルが`./fastlane/test_output/report.html`に出力されています。
出力される場所は別途指定可能です。

<img width="600" alt="スクリーンショット_2015-12-10_05_34_13.png" src="https://qiita-image-store.s3.amazonaws.com/0/1500/97c5a3f1-ccbb-8bd7-e406-b76a4815c963.png">


scanコマンドは様々な引数を取ることが可能であり`scan --help`コマンドで使用できる引数を見ることができます。
ここでは主に使用するであろう代表的なコマンドのみ抜粋します。

```
-w, --workspace 
-p, --project 
-a, --device 
-s, --scheme 
-c, --clean     
-q, --configuration  
--skip_slack 
--slack_only_on_failure  
```

上記のコマンドを組み合わせることにより、`scan`実行時の条件等を変更することができます。
例えば、
・`xcworkspace`を使用しているプロジェクト
・テスト前にクリーンを行う
・slackへの通知はテストに失敗した時のみ行う
ような設定を行いたい場合は

```ruby
scan(
  workspace: "Application.xcworkspace",
  clean: true
  slack_only_on_failure: true
)
```
のような形となります。

## レポートをSlackに通知
[fastlane1.45.0](https://github.com/fastlane/fastlane/releases/tag/1.45.0)より出力されたレポートのパスを取得することができます。
この機能により、簡単にslack等にテスト結果を通知することが可能です。

```ruby
slack(
    message: "Tests results: " + ENV['FL_REPORT_PATH'] 
)
```
# アクションを探す
fastlaneのアクションには様々な種類があり、日々開発者によってそのアクションの数は増大しています。
まず、行いたい動作がある場合は、action一覧がまとまっている
https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md
このページを参照してください。

fastlaneで使用できるアクションは基本的にすべてここにまとまっています。

# アクションに使用できる引数を確認する
## 主要アクション
以下のようなfastlaneが公式に開発を行っている機能に関しては基本的には全て単体のコマンドとしてコマンドライン上で使用することができます。

- fastlane
- deliver
- snapshot
- scan
- gym
- etc.......

このコマンドに関しては引数の実装を確認するためには`scan`の時と同様にコマンドライン上で`--help`で確認するのが一番はやいと思います。

例えば、gymのコマンドは[Action一覧](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Actions.md#gym)で記述されている引数よりも多くの引数を指定することができます。

サンプルだけの引数を使用しているとipaのアーカイブの方法は`AppStore`へのリリースのためのアーカイブとなってしまいます。

```ruby
gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Debug",
  scheme: "MyApp",
  silent: true,
  clean: true,
  output_name: "my-app.ipa",   
)
```

Deploygateなどに配布するための`AdHoc`配信用のアーカイブをするためには

```ruby
gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Debug",
  scheme: "MyApp",
  silent: true,
  clean: true,
  output_name: "my-app.ipa",   
  export_method: "ad-hoc"
)
```
のように`export_method`を`ad-hoc`にする必要があります。

コマンドを実行し、何かエラーに遭遇することがあったらまず`--help`を使用し、使用できる引数がないか確認してみてください。

## その他カスタムアクション
fastlaneのアクションの実装ファイルは
https://github.com/fastlane/fastlane/tree/master/fastlane/lib/fastlane/actions
全てここに定義されています。

アクションの引数を確認する場合は各々のアクション実装ファイルを見ることで確認できます。
ここでは、`Carthage`を例に見ていきます。
https://github.com/fastlane/fastlane/blob/master/fastlane/lib/fastlane/actions/carthage.rb

```ruby
cmd = ["carthage bootstrap"]

cmd << "--use-ssh" if params[:use_ssh]
cmd << "--use-submodules" if params[:use_submodules]
cmd << "--no-use-binaries" if params[:use_binaries] == false
cmd << "--no-build" if params[:no_build] == true
cmd << "--verbose" if params[:verbose] == true
cmd << "--platform #{params[:platform]}" if params[:platform]
```
Carthageの実装ファイルの7-12行目にこのようにパラメータからコマンドを作成している行があります。
`params[:no_build]`この`:no_build`が

```ruby
carthage(
  no_build: true
)
```
fastlane上の引数の`no_build`に対応しています。

そのため、fastlane上でCarthageのビルドをiOSのみにする場合は

```ruby
carthage(
  platform: "iOS"
)
```
のような形にすることでiOSのみのビルドでCarthageを動作させることができます。

このようにfastlaneはアクション毎にある程度定められたフォーマットで動作が定義されているため、比較的簡単に動作を確認したり、引数のチェックを行うことができます。

# カスタムパラメータ
`fastlane`実行にカスタムパラメータを渡すことができます。
現在、自分はPullReuqestBuilderを使用してJenkinsでfastlaneを動作させテストを行わせています。
その際、テストの終了時に対象のプルリクエストのURLをSlackに通知するという処理を行っています。

PullRequestBuilderの説明は省略させてもらいますが、Jenkins上で`${ghprbPullLink}`でプルリクエストのリンクを取得することができます。

fastlane実行時に`key:value`のような形でパラメータを指定することができます。

```
fastlane jenkins prurl:${ghprbPullLink} key:value
```

渡されたたパラメータは`|options|`の中に格納され`options[:key]`で使用することができます。

```ruby
lane :jenkins do |options|
  # テストの実行
  # scan

  # プルリクエストリンクをSlackに通知
  slack(
    message: "Succeed all tests: " + options[:prurl]
  )
end
```

# Jenkins
今回初めにfastlaneを導入しようとしたきっかけはJenkins上でビルドやテストを走らせる際の設定がかなり属人化してしまい、管理者以外がいじりにくいという問題を解消させるために導入しました。
ビルドやテストなどの実行を各pluginやシェル上で行わずfastlaneに全て移行することで、プロジェクトが増えてもfastlaneを設定するだけでよく、JenkinsはあくまでもGithubからのプルリクエストの受け口やfastlaneの実行だけの役割を持つだけでよくなります。
また、開発機からのデプロイフローも自動化することができます。

このように、AppStoreへのリリース以外にもfastlaneは存分に使用できますので、fastlaneをまだ使用したことがない方はぜひこの機会にdeploygateやcrashlyticsへのデプロイ、テスト実行だけでもfastlaneに移行してみてください！

## Jenkins実行時のはまり
- 設定にかなり依存しますが、Jenkins上のコマンドラインから実行する際、LANGの設定でエラーが出る場合はあります。

```
export LANG=en_US.UTF-8
export LANGUAGE=en_US.UTF-8
export LC_ALL=en_US.UTF-8
```

その場合は`fastlane`の実行前に`export`を行ってやってやるか、Jenkinsの環境設定よりLANGを設定してください。

# まとめ
ここまで、基礎の基礎となる紹介をしてあまり実践的な内容に触れることはできませんでしたが、一度fastlaneの基礎を学習することにより様々な拡張を行うことができるかと思います。
時間があれば、ここからもう少し実践的にAppStoreへのデプロイやプロビジョニングプロファイルの取り扱い等の内容を記事にできればと思っています。

iOSアプリ開発を行ううえで便利ツール等は年々増加しています。
適度にツールを導入し、快適なアプリ開発生活を送りましょう٩(๑❛ᴗ❛๑)۶