モバイル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
を抜かして実行すれば同じ結果を得られます。
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/Appfileと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
だけ書くので、同じような感じで追加してください。
platform :ios do
desc "テストを実行する"
lane :test do
scan(scheme: "ExampleApp")
end
end
これだけで、シェルでbundle exec fastlane test
実行するとテストされます。最高。
iOSアプリのビルド
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を載せておきます。
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サービス上で署名できるようにします
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サービス上からプロビジョニングプロファイルをダウンロード出来る権限がある場合
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_USER
とFASTLANE_PASSWORD
を追加して、プロビジョニングプロファイルをダウンロード出来るアカウントの情報を入れると、あとは最初に指定したapp_identifierとlane :keychain
で登録した証明書に合致するプロビジョニングプロファイルをダウンロード&インストールしてくれます。
権限がない場合
を参考に、プロビジョニングプロファイルをインストールしましょう。
試してませんが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]
などを使って、必要な環境変数を探してみてください
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
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サーバーと連携して、社内や社外にベータ配信するだけで色々なコストが大分減るので、是非やってみてください。
明日は @toshi0383 が iOSアプリ受託開発での署名付け替えの技術 について話してくれるそうです。