3行で
- 会社のCI環境をCircleCIで統一しているため、BitriseやCodemagicではなくCircleCIでやりたかった
- AndroidはDockerイメージで楽に環境構築できるが、iOSは色々対応が必要です
- AndroidとiOSでビルドする手順に違いはあるが、CircleCI2.1の機能でうまく対応できました
config.yml と Fastfile の完成形
今回は、リリースビルドで開発環境向け(Release-Development)のアプリを配布する処理を例にして書きます。Release-Developmentについては、 @mono0926さんのFlutterで環境ごとにビルド設定を切り替える — iOS編
を参考にしてください。
少々長いのでGistにしました。これらのファイルを定義することで、タイトルどおりのCI環境が構築できるはずです。
~/.circleci/config.yml
- https://gist.github.com/masashi-sutou/2d2cf584214f5b8d740d0dcc3cdd27c5
- circleciの設定ファイルです
~/Android/fastlane/Fastfile
- https://gist.github.com/masashi-sutou/c0f3d03ce8587d65c97a318de5161249
- Androidをビルドするために必要な処理が書かれたFastfileです
~/ios/fastlane/Fastfile
- https://gist.github.com/masashi-sutou/ae4bf7c83f29e6bb0523960a65fdc74a
- iOSをビルドするために必要な処理が書かれたFastfileです
- fastlane match で証明書を管理する必要があるので
~/ios/fastlane/Matchfile
も必要です。fastlane matchの説明は公式ドキュメントを参考にしてください。
# 証明書を保存するリポジトリを用意してください
git_url("git@github.com:example/example-ios-cert.git")
git_branch("master")
storage_mode("git")
type("development")
github.com/example-fastlane/util/Fastfile
AndroidとiOSの共通変数や共通メソッドは、別リポジトリのFastfileをインポートして利用しています。実際のFastfileはもう少し分割していますが、今回は例なので一つにまとめています。
別リポジトリのFastfileをインポートすることについては、@star__hoshiさんの Fastfile の import 機能を使い、複数のプロジェクトで共通の Fastfile を使う を参考にしてください。
なお、flutter build コマンドはこのFastfileに定義しており、それぞれのFastfileから引数でパラメータを受け取り、ビルドモードとflavorとtargetを指定しています。iOSに限り、 --no-codesign
にして、後から fastlane match で管理している証明書を差し込めるようにしています。
ローカルとCIの両方の環境で実行できるように is_ci?
で環境を判定しています。CI環境の場合に $FLUTTER_HOME/bin
にしているのは、Androidで利用するDockerイメージのFlutterSDKのPATHが$FLUTTER_HOME になっているためです。
# Flutter App をビルドする
private_lane :build_flutter_in_util do |options|
codesign = options[:platform] == "ios" ? "--no-codesign" : ""
if is_ci?
sh("cd ../../ && $FLUTTER_HOME/bin/flutter build #{options[:platform]} --release --flavor #{options[:flavor]} --target #{options[:target]} #{codesign}")
else
sh("cd ../../ && flutter build #{options[:platform]} --release --flavor #{options[:flavor]} --target #{options[:target]} #{codesign}")
end
end
CircleCI 2.1で Flutterのアプリをビルドする
CircleCI 2.1の機能を有効にするには、CircleCIのプロジェクトの設定画面で Settings -> Advanced Settings の Enable build processing (preview) を On にしてください。
有効にすると、 executors
、 commands
、 parameters
の機能が利用できます。簡単に説明すると、 executors
は環境、 commands
は処理、 parameters
は変数を任意の組み合わせで定義できる機能です。
他にもCircleCI 2.0の機能ですが、 working_directory:
で処理ごとに実行するディレクトリを指定できます。Flutterプロジェクトはプラットフォームごとにスクリプトを実行するディレクトリが違うので、 working_directory:
はとても便利です。
setup_ios_build_setting:
steps:
- run:
name: Run pod setup from cocoapods-specs.circleci.com
command: curl https://cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh | bash -s cf
working_directory: ~/flutterApp/ios
Androidをビルドする場合
Androidのビルドに必要な環境は、 image: cirrusci/flutter:latest
に用意されています。FlutterとAndroidのSDKがインストールされてPATHも通っているので、CircleCI側ではクローンして、 flutter doctor
と flutter analyze
の後にビルドしてDeployGateに配信するだけです。とても楽です。
beta_development_android:
executor:
name: default_android
steps:
- checkout
- setup_flutter
- setup_bundle:
platform: android
- flutter_build:
platform: android
configuration: development
iOSをビルドする場合
iOSのビルドには macOSが必要なので、CircleCIはmacOSの環境を選択してください(有料です)。先ほど、Androidのビルドに利用したDockerイメージは、Linuxなので証明書でビルドが失敗します。したがって、iOSではFlutterのSDKをクローンしてPATHを通す必要があります。
install_flutter:
steps:
- run:
name: Install flutter SDK
command: mkdir -p ~/sdks/flutter && git clone -b stable https://github.com/flutter/flutter.git ~/sdks/flutter
- run:
name: Set flutter SDK PATH in bash
command: echo 'export FLUTTER_HOME=~/sdks/flutter' >> $BASH_ENV && source $BASH_ENV
CircleCI 2.1の新機能で実行環境を別にして処理を共通化
AndroidとiOSの実行環境を別に定義したいので、executors
を使います。 iOSでは、 executor:
に default_ios
を指定したので、Dockerイメージではなく、CircleCIのmacOSをつかってビルドします。
commands:
の機能で、flutter doctor
と flutter analyze
の処理は共通化できますし、 parameters:
の機能で flutter_build:
で必要な引数もプラットフォームごとにわけて渡せます。
beta_development_ios:
executor:
name: default_ios
steps:
- checkout
- install_flutter
- setup_flutter
- setup_bundle:
platform: ios
- setup_ios_build_setting
- flutter_build:
platform: ios
configuration: development
disable_automatic_code_signing で証明書を設定
iOSでは --no-codesign
でビルドしたので、 fastlaneの disable_automatic_code_signing
を呼んで手動で証明書を設定します。このような手動の管理が面倒だと思う方は、 BitriseやCodemagicを検討すると良いです。
options[:app_configs]
が配列なのは、全てのターゲットに一度に証明書を設定可能にするためです。iOSでは複数のターゲットをもつアプリが存在するので、このようにしておくと楽なのですが、今回の例では不要なループ処理です(消すのが面倒でした )。
# Code Signing Style を Manual に変更
private_lane :change_manual_code_signing_style_in_util do |options|
code_sign_identity = ""
profile_name_prefix = ""
if options[:mode] == "development"
code_sign_identity = ENV["CERT_DEVELOPER_ID"]
profile_name_prefix = "match Development"
end
options[:app_configs].each { |app_config|
disable_automatic_code_signing(
targets: app_config[:target],
code_sign_identity: code_sign_identity,
profile_name: "#{profile_name_prefix} #{app_config[:profile_name_app_id]}"
)
}
end
最後に
モバイルアプリ開発でCI環境がない場合、アプリの検証サイクルを走らせるのが一気に面倒になります。できるだけ早くCI環境の構築は終わらせておき、開発に集中したいアプリエンジニアは多いと思います。特に私はそうです
Flutterで開発するとき「CI環境どうしよう」と不安になっていた時期もありましたが、今の所は問題なく、ネイティブアプリの開発フローと変わらずに検証と開発ができています。さらに、本番・ステージング・開発など、環境別にアプリを分けて配信して、同じスマートフォンに複数のアプリをインストールさせることも可能です。
あとは、DartとFlutterのスキルさえ上がれば・・・。Flutter開発がんばろう