お久しぶりです、3/31, 4/1は仕事ですが、4/2はバルト9へ行く田村(@Utmrer)です。
今回は弊社でのfastlaneによるアプリ配布についてお話します。
アプリ配布の通知メールの回数を減らしたい
開発段階のアプリを社内やテスターに配布する処理を何かしらのScriptで書いてCIで実行し、自動化している人は多いかと思います。
弊社も一時期配布を自動化していましたが、2つの問題がありました。
- RC, AdHoc, Development等、複数の環境のビルドが必要である
- PushしてCIが回るだびに通知メールがくると煩わしい
という感じで毎回複数環境のBuildを実行するのは時間が掛かり過ぎる、メールを送ってアップデートを促したいが「頻繁にメールが来ることによって、メールを無視することが普通になる」とアップデートしてもらえない、と問題を抱えていたので都度手動で配布してましたが、「fastlaneが便利らしいぞ」という話を聞いたので、Travisで自動化することにしました。
証明書をリポジトリに含める
Travis上でアプリをビルドするために証明書を書き出します。
キーチェーンからiPhone Distribution: Your Name (Team ID)
、そして今回はDevelopmentのBuildも配布するのでiPhone Developer: Your Name (Team ID)
のp12
を書き出します。
WWDRCAはTravisなら最初から入ってます。
各環境のlane等をFastfile
に定義する
最終目的なFastfile
はこんな感じになります。
fastlane_version "1.61.0"
default_platform :ios
platform :ios do
before_all do
ENV['KEYCHAIN_NAME'] = "TempKeychain.keychain"
end
desc "Build for AdHoc"
lane :build_adhoc do
sigh(
app_identifier: "com.example.app.adhoc",
adhoc: true,
skip_certificate_verification: true,
)
ENV["PROFILE_UDID"] = lane_context[SharedValues::SIGH_UDID]
gym(
clean: true,
workspace: "App.xcworkspace",
scheme: "Adhoc",
use_legacy_build_api: true
)
deploy_crashlytics
end
desc "Build for Debug"
lane :build_debug do
sigh(
app_identifier: "com.example.app.dev",
development: true,
provisioning_name: "Debug",
skip_certificate_verification: true
)
ENV["PROFILE_UDID"] = lane_context[SharedValues::SIGH_UDID]
gym(
clean: true,
workspace: "App.xcworkspace",
scheme: "Debug",
use_legacy_build_api: true
)
deploy_crashlytics
end
desc "Build for Release"
lane :build_release do
sigh(
app_identifier: "com.example.app",
adhoc: false,
provisioning_name: "Release",
)
ENV["PROFILE_UDID"] = lane_context[SharedValues::SIGH_UDID]
gym(
clean: true,
workspace: "App.xcworkspace",
scheme: "Release",
use_legacy_build_api: true
)
deliver(
force: true,
skip_screenshots: true,
skip_metadata: true
)
end
desc "Import Certificates"
lane :import_certificates do
return unless Helper.is_ci?
create_keychain(
name: ENV["KEYCHAIN_NAME"],
default_keychain: true,
unlock: true,
timeout: 3600,
lock_when_sleeps: true,
password: ENV["PASSWORD"]
)
import_certificate(
certificate_path: "./certificates/distribution.p12",
certificate_password: ENV['PASSWORD'],
keychain_name: ENV["KEYCHAIN_NAME"]
)
import_certificate(
certificate_path: "./certificates/development.p12",
certificate_password: ENV['PASSWORD'],
keychain_name: ENV["KEYCHAIN_NAME"]
)
end
after_all do |lane|
end
error do |lane, exception|
end
def deploy_crashlytics
crashlytics(
api_token: "YOUR_API_TOKEN",
build_secret: "YOUR_BUILD_SECRET",
groups: 'tester_group',
notifications: true
)
end
end
build_adhoc
/build_debug
/build_release
がそれぞれの環境別のBuildを作成し、Crashlyticsへアップロードするlaneです。
ENV['PASSWORD']
は「ちょっとパスワードとかcommitに含めるの嫌だなぁ〜」って時に、コマンドで暗号化して.travis.yml
で定義した環境変数です。
fastlaneでiOS Dev CenterやiTune Connectに接続するために必要なFASTLANE_USER
やFASTLANE_PASSWORD
と合わせてこのようなコマンドで暗号化します。
travis encrypt PASSWORD=CERT_PASSWORD FASTLANE_USER=APPLE_EMAIL FASTLANE_PASSWORD=APPLE_PASSWORD
詳しくはこちらをご覧ください。
Encryption keys - Travis CI
ちょっとハマったところはprovisioning_name
を指定するところです。ここでprovisioning_name
を指定しないと、新しいProvisioning Profileを次々に生成し、iOS Dev Centerがかなりカオスになりました。
Travisでいい感じのタイミングでBuildする
さて、それぞれの環境のlaneが出来たので、それぞれをいい感じのタイミングで実行しましょう。
弊社のiOSプロジェクトはgit-flowで開発しており、新しい機能はfeature
branchを作り、実装が終わるとdevelop
branchへmerge、リリースのタイミングになるとそこからrelease
branchを作って全体的な機能の確認をし、問題が無ければmater
へmergeされるというflowをとっています。
- 新しい機能の実装が一旦完了した段階でみんなに開発環境アプリをインストールしてもらいたい
- 新しいリリースを申請する段階でみんなにAdHoc環境アプリをインストールしてもらいたい
という2つのタイミングがアプリを配布したいタイミングなので「feature
branchをdevelop
branchへmergeしたタイミングで開発環境アプリ」「master
branchへrelease
branchをmergeしたタイミングでAdHoc環境アプリ」を配布することにしました。あとmaster
へmergeされたタイミングでiTunesConnectにもアップロードしちゃいましょう。
その仕様を満たす.travis.yml
はこんな感じになりました。
language: objective-c
osx_image: xcode7.2
env:
global:
- LANG=en_US.UTF-8
- LC_ALL=en_US.UTF-8
- secure: COMMAND_DE_ANGO_KA_SHITA_PASSWORD_TOKA
script:
after_success:
- "[[ $TRAVIS_PULL_REQUEST == 'false' ]] && [[ $TRAVIS_BRANCH == 'master' ]] && fastlane import_certificates && fastlane build_release && fastlane build_adhoc"
- "[[ $TRAVIS_PULL_REQUEST == 'false' ]] && [[ $TRAVIS_BRANCH == 'develop' ]] && fastlane import_certificates && fastlane build_debug"
本当はDeploymentを使ってDeployしたかったのですが、Deploymentではうまくfastlaneが動かなかったのでafter_success
で実行しています。
$TRAVIS_PULL_REQUEST
の判定でPushした時だけ実行するようにし、$TRAVIS_BRANCH
の判定で特定のbranch
でlaneを実行するようにしています。
ここの処理はもうちょっと綺麗に出来そうですね、Circle CIならもうちょっと綺麗だった気がします。
これでdevelop
へmergeした時に開発環境アプリが、master
へmergeした時にAdHoc環境アプリがテスターに届くようになりました!
おわりに
この機能の実装に50 commit以上かかってしまって、完成した時の達成感がかなり大きかったです!(証明書辛い…)
fastlaneは社内でテスター募集をする時にboarding
を使ったりしてるのですが、もうちょっと色々触ってみたいですね。(dg add-devices
的なのをSlackからやったり)
それと今回はCrashlyticsから新しいアプリがメールで行きますが、出来ればSlackに何かしら通知したいですね。弊社の人はメールクライアントよりSlackにいる時間が長いのでSlackに通知した方がアップデートしてくれそうです。
もっと良い配布方法や.travis.yml
の書き方をご存じの方は是非教えて下さい!ではでは👋