まえがき
この記事は Ionic Advent Calendar / mediba Advent Calendar 2017 の4日目の記事です。
佐藤よしあきと申します
株式会社medibaにて、ionic使ってiOS/Androidアプリを作ってます
今回は、そのアプリ開発で使用している ionic において、CI(継続的インテグレーション)をやってみた結果を、記録しておきます
使うもの
- GitHub
- Travis CI
- Fastlane
これら各々の使い方解説とかは省略します、多分ググれば山ほど出てくるので
ionic/cordovaならではのつらみ
cordovaはマルチプラットフォームなフレームワークです
一つのコードツリーから、複数のプラットフォーム向けのアプリを生成できます
が、いきなりバイナリをぽんとビルドできるわけではありません
大概出来るのですが、 Apple のもの (iOSとMacOS向けのもの) については Apple のビルド環境を使わないとバイナリを作れないため、Cordovaから [Appleのビルド環境向けのコードを生成] し、それをAppleのビルド環境でビルドしないといけません
※ 狭量ですね
仕方がないので、Travis上でもその流れを再現します
ionic/cordova自体はWindowsとかMacOSとか向けにもビルドできるのですが、今回はAndroidとiOSの2つを対象にします
travis の領域
travisにやってもらう事は、下記になります
- ビルドするための環境セットアップ
- fastlane の呼び出し
iOS と Android 両方ビルドする
というわけでAndroidとiOSの両方をビルドすることになるのですが、
- iOSのビルドはMacOS環境でないと 出来ない
- AndroidのビルドはLinux環境でないとめんどくさい
という、また困った問題があります
仕方がないので、一回のTravisジョブで、2つの環境でビルドが走るようにしましょう
これには Travis の仕組みのうち Matrix を利用して対応します
matrix を利用して Android と iOS のビルドを並行で走らせる
下記のように書くことで、 osx と linux のイメージを用いたTravis処理が同時に走ってくれます
os セクションの下にそれぞれの設定を書くことで、 OS イメージ毎の設定を定義していきます
matrix:
include:
- os: osx
osx_image: xcode9.1
language: objective-c
- os: linux
language: android
android:
components:
- tools
- platform-tools
- build-tools-26.0.2
- android-26
licenses:
- android-sdk-license-.+
addons:
apt:
packages:
- oracle-java8-installer
で、before_install セクションなどで下記のように環境毎のセットアップを行います
TRAVIS_OS_NAME
にはMatrixにおけるOS指定も反映されるので、それを元に処理を分けられます
before_install:
- if [ ${TRAVIS_OS_NAME} = "linux" ]; then
jdk_switcher use oraclejdk8;
fi
- if [ ${TRAVIS_OS_NAME} = "osx" ]; then
gem install cocoapods;
npm install -g ios-sim;
npm install -g ios-deploy;
fi
本来、 ruby とかの複数バージョンごとにビルドを走らせて全部でテストが通ることを確認する、みたいな使い方が想定されていたみたいですが、別にプラットフォーム別ビルドを同時並行で動かしてもいいですよね?
ionic ビルド環境を travis 上にセットアップする
事前にやっておかなきゃいけないことが、わりとあります
-
rvm
とnvm
でそれぞれ ruby と node の任意のバージョンをインストール -
bundle install
で ruby環境の依存モジュールを導入 - Android : linux の場合、使用するJDKを
jdk_switcher
で指定 - iOS : osx の場合、 cocoapods と ios-sim ios-deploy をインストール
- cordova / ionic / typescript をnpmでグローバルインストール
-
npm install
でnodeの依存モジュールを導入
これだけやって、やっと ionic cordova build
のための環境が揃います
.travis.ymlに書くと、こんな感じになります
before_install:
- rvm install 2.3.1
- gem install bundler
- bundle install
- nvm install v6
- npm update npm
- if [ ${TRAVIS_OS_NAME} = "linux" ]; then jdk_switcher use oraclejdk8; fi
- if [ ${TRAVIS_OS_NAME} = "osx" ]; then gem install cocoapods; npm install -g ios-sim;
npm install -g ios-deploy; fi
install:
- npm install -g cordova@7.1.0 ionic@3.18.0 typescript@2.4.2
- npm install
バージョンなどは次節に応じて変更して下さい
ローカルの開発環境では一回やってしまえば(バージョンアップの時とか以外は)気にしないでいい所ですが、CI環境は毎回コレを作り直してもらわないといけないんですね、大変....
fastlane を呼ぶ準備をする
scriptセクションは適当にtestでも通してもらうとして、after_success セクションを用いて fastlane を呼びます
せっかく fastlaneにはビルドの種別などを指定する方法があるので、fastlaneの呼び出し方でビルドの仕方を分岐させることにします
after_success:
- if [ ${TRAVIS_PULL_REQUEST} = "false" ] && [ ${TRAVIS_BRANCH} = "master" ] && [
${TRAVIS_OS_NAME} = "linux" ] && [ ${TRAVIS_TAG} ]; then
bundle exec fastlane android deploy target:prod;
fi
- if [ ${TRAVIS_PULL_REQUEST} = "false" ] && [[ ${TRAVIS_BRANCH} =~ ^[Rr]elease ]] && [ ${TRAVIS_OS_NAME} = "linux" ]; then
bundle exec fastlane android deploy target:dev;
fi
こんなふうに書くと、 PRではなくて、ブランチ名がそれぞれに合致する、OSがLinux、の場合、 指定のオプションでfastlane を実行、という流れに出来ます
※ ここではAndroidのぶんしか書いてませんが、iOSの場合はTravis実行環境はmacosになるので、単に ${TRAVIS_OS_NAME} = "osx"
ってすればいいだけです
fastlane ツールの領域
fastlaneには、 ionic cordova build
の実行と、それによって出力された ./platforms/
以下を用いた実際のビルドを担当してもらいます
ビルドに必要な設定の投入なども、ココで行います
fastlane の基本
fastlaneの起動の仕方は、こんな感じにしています
bundle exec fastlane <<platform>> <<startlane>> [options]
例) bundle exec fastlane ios deploy target:prod
fastfile の中ではこれらのパラメータを下記のように利用します
platform と startlane
bundle exec fastlane ios deploy target:prod
の例の場合、 fastfile にこんなふうに書いておけば、ブロックの中の sh() が実行されます
platform :ios do |options|
lane : deploy do |options|
sh("ionic cordova build")
end
end
自前でlaneを作ることもできるし、下記のように書くと、外部から呼べない android_deploy
という lane を作ったりも出来ます
private_lane :android_deploy do |options|
end
コレを用いて、各OS用のビルドの流れを用意します
options : テスト向けか本番向けかでビルド内容を変更する
fastlaneのコマンドラインでは key:val
という形で任意の文字列をパラメータとして渡せます
例えば bundle exec fastlane ios deploy target:dev
などとすると、 fastfile
内における options[:target]
の内容が dev
になります
コレを使うと、こんな風にビルドに使用するパラメータを切り替えて定義できます
if options[:target] == "prod" then
options[:app_identifier] = "jp.mediba.ysato.test.travistest"
options[:display_name] = "Travis Test App"
else
options[:app_identifier] = "jp.mediba.ysato.test.travistest.dev"
options[:display_name] = "dev Travis Test App"
end
今の所は開発版か本番かくらいしか指定していませんが、他にも便利な使い方があるかもしれませんね
iOS : xcodeでやるような環境設定を自動化する
いわゆるinfo.plistの編集とか、Provisioning Profileの指定とか、ionic cordova build iosの後に xcodeを立ち上げて platforms/ios/xxx.xcworkspace を開いてGUIからぽちぽちやるような設定の類、ありますよね
アレを自動で行わないと、Travis上での自動ビルドは完結しません
※ ./platforms/ios/
以下もリポジトリに入れてしまえばいいんだけど、変更管理が死ぬんですよ....
逆に、これらの設定変更を fastlane で自動化出来ると、例えば platform を初期化しちゃった時とかでも fastlane の当該レーンを通してあげることで、間違いなく全ての設定を反映できるようになります
今となっては、ローカルでも ionic cordova build ios
を呼ぶのではなく、 必ずこれを含んだ bundle exec fastlane ios build
を使ってビルドするようにしています
※ 画面ローテーションの設定とか、忘れるんだ....
ionic cliでの定義 : 署名やProvisioning Profile関連を自動で定義する
ionic cordova build ios
に対して xcodebuilg のオプションを放り込むことで、CI環境上でチームやら署名やらの設定を直接放り込めます
こうすることで、ionic cordova buildが終わった時点で署名関係の設定は既に終了している、という状態を作れるわけです
options[:buildoptions] = ' -- --developmentTeam="' + options[:team_id] + '" --codeSignIdentity="' + options[:code_sign_identity] + '" --provisioningProfile="' + options[:provisioning_profile] + '" --packageType="' + options[:export_method] + '"'
sh ("cd .. && ionic cordova build " + options[:platform] + " --prod --release" + options[:buildoptions])
info.plist関連
予めこんな風に、Projectやplistへのパスを定義しておきます
options[:PROJ_PATH] = "platforms/ios/TravisExample"
options[:PLIST_PATH] = "TravisExample/TravisExample-Info.plist"
targetPlistPath = File.join(options[:PROJ_PATH], '..', options[:PLIST_PATH])
あとは下記のように設定を並べていけばいいです
plist設定に必要なkeyなどは都度調べて下さい、いつどう変えられて使えなくなるか分からないので
アプリの Identifier を更新
update_app_identifier(
xcodeproj: options[:PROJ_PATH],
plist_path: options[:PLIST_PATH],
app_identifier: "jp.mediba.ysato.test.travistest.dev"
)
表示名(アイコンの下に表示されるアレ)を変更
update_info_plist(
xcodeproj: options[:PROJ_PATH],
plist_path: options[:PLIST_PATH],
display_name: "[DEV] Travis Test"
)
それ以外の info.plist 系更新
set_info_plist_value(
path: targetPlistPath,
key: "NSCameraUsageDescription",
value: "フレーム撮影の為、カメラにアクセスします"
)
対象が Arrayの場合、言われてみればそのまんま
set_info_plist_value(
path: targetPlistPath,
key: "UISupportedInterfaceOrientations",
value: ["UIInterfaceOrientationPortrait"]
)
あとは sigh() って gym() れば ipaファイルが出来上がります
Android : 自動で署名したい
ionic cordova build で作成されるapkファイルは未署名の状態なので、このまま配布したりとかPlayストアに登録したりとかは、出来ません
なので署名するのですが、ココをコマンドラインで自動化するには、こんな感じにします
sh("cd .. && jarsigner -verbose -storepass xxxx -sigalg SHA1withRSA -digestalg SHA1 -keystore target.keystore target/apk/path.apk alias_name")
sh("cd .. && " + ENV['ANDROID_HOME'] + "/build-tools/26.0.2/zipalign -f -v 4 target/apk/path.apk output_apk_filename.apk")
fastlaneでは sh() を使ってコマンドライン実行をかけられるので、こんな風に未署名apk に jarsingerとzipalignをかけてあげればOKです
apkファイル名とかパスとかは適宜読み替えてくださいね
これでapkが出来上がります
あとはお好みの配信プラットフォームで
今は fabric.io を使用してテスト配信を行っているので、Android/iOSどちらも fastlane 内で crashlytics() を使用して配布するようにしました
まとめ
かなり乱暴、というか場当たりに対処を並べた感じになりますが、一応Travisでもビルドは行えます
ただ、実用に足りるかというとちょっと疑問符が付く状態です
環境作って、 ionic/cordovaでビルドして、各プラットフォームでバイナリ作って、と手間が多いので、 トータルで30分かかる んですよね、コレ
なので今現在は、Travis上でのビルドは止めています....
この辺、実用の範囲で収めるために皆さんがどうしているか、聞いてみたいところです....
ただ、この環境を作るための試行錯誤の結果、fastlaneを使用してコマンドライン一発で環境設定を含めたビルド→リリースを行える状態に持っていけたので、その点頑張った甲斐はあったかな、と思っています
ionicでバイナリ作る際の労力を少しでも減らすために、何かの参考になれば、幸いです