Help us understand the problem. What is going on with this article?

fastlaneを使って継続的デリバリーを実践する

More than 3 years have passed since last update.

モバイルDevOps Advent Calendar 2015 4日目

寒さもひとしお身にしみるころ、皆様いかがお過ごしでしょうか。

モバイルDevOps Advent Calendarということで、fastlaneを使って継続的デリバリーを実践する的な内容を書きたいと思います。

継続的デリバリーの正しい定義、探したけどコレという感じのが無かったので、この記事では「いつでもリリース可能な状態にして、リリース時のコストを下げておくこと」という定義とします。

iOSアプリの継続的デリバリーに便利なfastlaneのご紹介 - Qiita

内容的には、以前書いた↑ものと被っているのですが、書いた当時からfastlaneがめちゃくちゃ便利になっているので、改めて書き直した次第です。

条件

Require

  • バージョン管理している
    • git(github)前提で話をしています
  • リモートリポジトリは非公開な状態
    • 公開したくない情報の暗号化などには触れてません
  • Rubyの基礎文法くらいは把握してる事

Optional

  • Bundler
  • CIサーバー
    • Jenkins
    • CircleCI
    • TravisCI

Gitを使ってる人は.gitignoreと同じ.gitignoreを作っておいてください。

commitは自分のペースで適宜よろしくお願いします。

筆者の作業環境

  • Git
  • Xcode Version 7.1.1 (7B1005)
  • ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
  • Bundler version 1.10.6

説明の流れ

一旦、ローカルで配信が出来る状態にしてから、自動ビルド・自動配信を出来るようにします。

プロジェクトの作成と必要な設定

schemaを共有する

[Xcode] 作成したSchemeを開発メンバーに共有する | Developers.IO

設定してない人居ない気がするけど一応

fastlaneのinstall

Bundlerを使う場合

$ gem install bundler # bundlerをまだ入れてない場合。必要に応じて、`sudo`つけたりしてください。
$ bundle init
$ vi Gemfile
source "https://rubygems.org"

gem "fastlane" # Gemfileに追記する
$ bundle install --path .bundle

bundle init実行すると、Gemfileが生成されるので、その中にgem "fastlane"と追記します。

その後、Gemfileに書いたgemをbundle install --path .bundleでインストールします。
--path .bundleを書かないとシステム領域にインストールされます。

.bundle/.gitignoreでバージョン管理しないようにしましょう。チームのメンバーにもBundlerを導入してもらってbundle install --path .bundleすればOKです。

【初心者向け】Mac(OSX Lion)でRuby on Railsを動かすための5ステップ « pplog

RubyやBundler関連の環境構築は↑これを見たりしてください。少し古い記事だけどそんなに変わらないです。

Bundlerとは

RubyのCocoapods/Carthageのようなもので、Bundlerというのがあります。

これを使うと、他の作業者と使用するGem(fastlaneやcocoapods)のVersionを合わせる事が出来るのでお薦めです。

また、gemのinstall時にpathを指定すると、システム領域とは別の場所にgemが入るので、「システム領域に入れたfastlaneが定期的にgem cleanupしないと遅くなる」といった問題も起こりにくくなると思います。

Bundlerを使わない場合

$ gem install fastlane # 必要に応じて、`sudo`つけたりしてください。

Bundlerは必須ではありませんが、私がBundler使っているので、以後はBundler使ってる前提でコマンドなど書きます。

Bundler導入してない場合でもbundle execを抜かして実行すれば同じ結果を得られます。

https://github.com/fastlane/fastlane#installation

fastlaneのsetup

$ bundle exec fastlane init

fastlane initを実行すると色々聞かれるので答えていきます

  • Do you have everything commited in version control? If not please do so now! (y/n)
    • バージョン管理してなかったら今直ぐ導入してy
  • App Identifier (com.krausefx.app)
    • 対象のアプリのApp Identifierを入力する
  • Your Apple ID (fastlane@krausefx.com)
    • 自分のApple IDを入力する
  • 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)
    • fastlaneをストア申請に使うかどうか
    • 今回はn
  • Do you want to setup 'snapshot', which will help you to automatically take screenshots of your iOS app in all languages/devices? (y/n)
    • アプリのスクリーンショットを自動で撮るか
    • 今回はn
  • Do you want to use 'sigh', which will maintain and download the provisioning profile for your app? (y/n)
    • provisioning profileのダウンロードにsighを使うかどうか
    • 生成されるコードの状態が変わるだけなので、今回はn

fastlane/Appfilefastlane/Fastfileが作られるのでfastlane/Fastfileを以下のような状態にします。

fastlane/Fastfile
fastlane_version "1.42.0"
default_platform :ios

ENV["GYM_USE_LEGACY_BUILD_API"] = true

platform :ios do
end

全て消しました。

消すところは、消す前によく読んでおきましょう。

ここまでやると以下の様なディレクトリ状態になると思います。

├── ExampleApp
├── ExampleApp.xcodeproj
├── ExampleAppTests
├── Gemfile
├── Gemfile.lock
└── fastlane
    ├── Appfile
    ├── Fastfile
    └── actions

iOSアプリのテスト

fastlaneからテストを実行するためにFastfileに下記の内容を追記しましょう。以後は、laneだけ書くので、同じような感じで追加してください。

fastlane/Fastfile
platform :ios do
  desc "テストを実行する"
  lane :test do
    scan(scheme: "ExampleApp")
  end
end

これだけで、シェルでbundle exec fastlane test実行するとテストされます。最高。

iOSアプリのビルド

fastlane/Fastfile
desc "ipaを作る"
lane :build do
  gym(scheme: "ExampleApp")
end

これだけで、シェルでbundle exec fastlane build実行するとReleaseビルドのipaが生成されます。最高。

ビルド出来ない場合はDistribution証明書を入れたり、プロビジョニングプロファイルを入れたりしましょう。

setupで書いたENV["GYM_USE_LEGACY_BUILD_API"] = trueはおまじないみたいなものだと思ってください。詳しく知りたい人はbundle exec fastlane action gymを見る。

iOSアプリの配信

  • crashlytics
  • deploygate
  • hockey
  • pilot

テスト配信用のactionは上記の4つ存在していて、pilotはTestFlight用です。

4つ全て説明するのも辛いので、fastlane action [name]でactionに必要な設定などが見られるので、あとは頑張ってください。

私がcrashlyticsを使っているので、例としてcrashlyticsを使った場合のlaneを載せておきます。

fastlane/Fastfile
desc "crashlyticsに配信する"
lane :crashlytics do
  gym(scheme: "ExampleApp")
  crashlytics(
    crashlytics_path: "./Pods/Crashlytics/iOS/Crashlytics.framework",
    api_token: "your api_token",
    build_secret: "your build_secret"
  )
end

CIサーバーの設定

Jenkins

Jenkinsの場合は、ローカルの場合と同じように証明書やプロビジョニングプロファイルを入れて、作ったlaneを呼び出すだけでOKだと思います。

CI as a Service

署名関係

CircleCIやTravisCIのようなCIサービスを使う場合は、ビルド毎に証明書の設定をする必要があるので、リポジトリに証明書(p12ファイル)を追加します

次に、CIサービス上で署名できるようにします

fastlane/Fastfile
desc "CircleCIなどにkeychainを作成して証明書を追加する"
lane :keychain do
  ENV["KEYCHAIN_NAME"] = "ios-build.keychain"
  ENV["KEYCHAIN_PASSWORD"] = "password"
  create_keychain(
    default_keychain: true,
    unlock: true,
    timeout: 3600,
    lock_when_sleeps: true
  )
  import_certificate certificate_path: "certs/dist.p12", certificate_password: "password"
end

これでbundle exec fastlane keychainで証明書が登録されるようになりました。これをローカルで実行すると良くないことが起こるので、よく分かってない人は気をつけましょう。

説明しやすさを重視して、パスワードなどは直接Fastfileに書いているけど、適宜CIサービスの環境変数にしたほうが安全です。

他社が絡むプロジェクトだったり、public repositoryの場合はTravis CI for iOS · objc.ioの内容を参考にして証明書自体の暗号化・復号しましょう。

後は、今作ったlaneと、ベータ配信用のlaneを呼べばOKです。

プロビジョニングプロファイルについて

個々人の作業環境によって、プロビジョニングプロファイルの扱いが変わってくると思うので、サラッと触れます。

CIサービス上からプロビジョニングプロファイルをダウンロード出来る権限がある場合

fastlane/Fastfile
desc "crashlyticsに配信する"
lane :crashlytics do
  sigh(adhoc: true) # この行を追加
  gym(scheme: "ExampleApp")
  crashlytics(
    crashlytics_path: "./Pods/Crashlytics/iOS/Crashlytics.framework",
    api_token: "your api_token",
    build_secret: "your build_secret"
  )
end

sighを使いましょう

CIサービスの環境変数にFASTLANE_USERFASTLANE_PASSWORDを追加して、プロビジョニングプロファイルをダウンロード出来るアカウントの情報を入れると、あとは最初に指定したapp_identifierとlane :keychainで登録した証明書に合致するプロビジョニングプロファイルをダウンロード&インストールしてくれます。

権限がない場合

https://github.com/fastlane/fastlane_core/blob/e44ac3ce3e1d16e5094453dccbe16c8e709393ad/lib/fastlane_core/provisioning_profile.rb#L41-L66

を参考に、プロビジョニングプロファイルをインストールしましょう。

試してませんがFastlaneCore::ProvisioningProfile.install(profile_path)でインストール出来るかもしれません。

配信する

$ bundle exec fastlane keychain
$ bundle exec fastlane crashlytics

上記2つがCIサービス上で実行されるようにすれば終了です。

おまけ

この記事で書いた説明だけじゃ、実際に運用するのに必要な要件を満たせないと思うので、所属先のプロジェクトで実際に運用しているFastfileとcircle.ymlをおまけとして載せておきます。アプリ名とかapp_identifier以外はそのままです。

他社が開発しているものなので、この記事のディレクトリ構造とは違ってます。あと、fastlaneのVersionが少し古い。あと、テストが書かれていないので諸事情により、lane :testではビルド通るかの確認をしています。

APIサーバーと繋ぐアプリなので、edge・stagingサーバーそれぞれに繋ぐアプリを配信しています。サーバーの切り替えなどはUser-Defined Settingを使っています。

iOS - XCode設定Tips 1: 独自のプロジェクト設定やマクロを活用する - Qiita

それぞれのbuild configで共通でfastlaneに設定する値などは、ほとんど環境変数で設定しているので、これをこのまま使いたい場合は、fastlane action [action_name]などを使って、必要な環境変数を探してみてください :ghost:

fastlane/Fastfile
fastlane_version "1.39.0"

default_platform :ios

platform :ios do
  before_all do
  end

  desc "Runs all the tests"
  lane :test do
    build(
      env: :edge,
      configuration: "Debug",
    )
  end

  lane :keychain do
    create_keychain(
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true
    )
    import_certificate certificate_path: "certs/dist.p12", certificate_password: ENV['DIST_CERT_PASSWORD']
  end

  desc "edgeサーバーに繋ぐアプリ"
  lane :edge do |options|
    increment_build_number unless options[:build_only]

    build(
      env: :edge,
      configuration: "Debug",
    )
    crashlytics(notifications: false) unless options[:build_only]
  end

  desc "stagingサーバーに繋ぐアプリ"
  lane :staging do |options|
    increment_build_number unless options[:build_only]

    build(
      env: :staging,
      configuration: "Staging",
    )
    crashlytics unless options[:build_only]
  end

  private_lane :build do |options|
    app_identifier = "jp.app_name.#{options[:env].to_s}"
    display_name = "#{options[:env].to_s}_app_name"

    update_app_identifier(app_identifier: app_identifier)
    update_info_plist(display_name: display_name)
    sigh(adhoc: true)
    update_project_provisioning(build_configuration: options[:configuration])
    gym(
      configuration: options[:configuration],
      use_legacy_build_api: true,
    )
  end

  after_all do |lane|
  end

  error do |lane, exception|
  end
end
circle.yml
machine:
  environment:
    FL_BUILD_NUMBER_BUILD_NUMBER: $CIRCLE_BUILD_NUM
  xcode:
    version: "7.0"

dependencies:
  post:
    - openssl aes-256-cbc -k "$DIST_P12_ENC_KEY" -in certs/dist.p12.enc -d -a -out certs/dist.p12

test:
  override:
    - bundle exec fastlane keychain
    - bundle exec fastlane edge build_only:true

deployment:
  edge:
    branch: master
    commands:
      - bundle exec fastlane edge
  staging:
    branch: deployment/staging
    commands:
      - bundle exec fastlane staging

おわり

という感じで割りと駆け足になってしまいましたが、モバイルDevOps Advent Calendar 2015 4日目でした。

冒頭で紹介した記事も参考になればと思います。

CIサーバーと連携して、社内や社外にベータ配信するだけで色々なコストが大分減るので、是非やってみてください。

明日は @toshi0383iOSアプリ受託開発での署名付け替えの技術 について話してくれるそうです。

gin0606
iOSとかAndroidとかRuby on RailsとかReactのアプリ開発したりしてました
http://gin0606.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした