Flutter で fastlane を使って ipa, apk, aab 作成を行う
目的
CI・CD を行うにあたり、iOS・Android 共に同じツールを使用しておけば何かと設定を省けたりとメリットがあると思います。Android は Gradle である程度できてしまう部分もありますが、ここでは iOS・Android 共に fastlane を使用して Flutter を使用して作成されたプロジェクト配下の iOS と Android からそれぞれ .ipa や .aab, .apk を書き出しまでを行なってみようと思います。
開発環境
- Flutter 2.2.1
- Xcode 12.4
- AndroidStudio 4.1.3
手順
手順途中で rbenv が出てきます。未インストールの場合はインストールしておいてください。
- Flutter プロジェクトを作成します(未作成の場合)
- Flutter Flavorizr で開発環境を(例:Development, Staging, Production)に分けます。(以前書いたFlutter で環境を分ける方法を参考にしてください。)
- Flutter プロジェクト直下で Ruby のバージョンを固定します
rbenv local x.x.x
- Flutter プロジェクト直下で以下を実行し bundler をインストールします
gem install bundler
- Flutter プロジェクト直下で以下を実行し、
Gemfile
を作成します
bundle init
- 作成された
Gemfile
の一番下に以下を追加して fastlane を明記します。
gem 'fastlane'
- 以下コマンドで fastlane のインストール
# ローカル環境(≒Flutterプロジェクト内)で gem をインストールする先を指定するよう設定
bundle config --local path vendor/bundle
# Gemfile に記載された gem をインストール
bundle install
# 以下はオプション
# ./vendor/cache へ gem のロックとキャッシュを行う
bundle package
# rubygems.orgへの接続なしで作成したキャッシュ(./vendor/cache)から gem をインストールする場合
bundle install --local
- ios or android ディレクトリに移動し、以下で fastlane を初期化します。実行すると自動設定するか、手動設定するか選択肢が表示されるので任意で選択して下さい(本記事では手動を選択を前提に進めます)
cd ios
bundle exec fastlane init
cd android
bundle exec fastlane init
- fastlane の初期化を行うとそれぞれのディレクトリ配下に fastlane ディレクトリが作成されます。その中に Appfile と Fastfile が作成されているはずです。Appfile はアプリの構成情報(BundleIDやパッケージ名、AppleID, Playストア更新のための JSON など)を記述するファイルです。Fastfile が実際に fastlane の動作を定義するファイルです。
作成された ios/fastlane/Fastfile
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:ios)
platform :ios do
desc "Description of what the lane does"
lane :custom_lane do
# add actions here: https://docs.fastlane.tools/actions
end
end
作成された android/fastlane/Fastfile
# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
# https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
# https://docs.fastlane.tools/plugins/available-plugins
#
# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane
default_platform(:android)
platform :android do
desc "Runs all the tests"
lane :test do
gradle(task: "test")
end
desc "Submit a new Beta Build to Crashlytics Beta"
lane :beta do
gradle(task: "clean assembleRelease")
crashlytics
# sh "your_script.sh"
# You can also use other beta testing services here
end
desc "Deploy a new version to the Google Play"
lane :deploy do
gradle(task: "clean assembleRelease")
upload_to_play_store
end
end
- Fastfile を編集します
以下の iOS の Fastfile は private 宣言している build レーン で fastlane のアクションである gym
を用いて指定した環境の .ipa 作成を行う例になります。 options
で宣言した build_type
では Debug/Release のどちらかを指定し、scheme
では分けた環境に応じたもの(以下例では development/staging/production のいずれか)を指定するようにしています。export_method
は gym
のパラメータそのものなので環境に応じた適切なものを指定します。
なお、Provisioning Profile の設定は Xcode 上で行うことを想定しているものとなります。 gym の方で export_options
の provisioningProfiles
で bundleID と対応する Provisioning Profile 名を指定することでも上記の設定は行えますが、うまくいかなかったので Xcode 上で設定しました。
ios/fastlane/Fastfile
default_platform(:ios)
platform :ios do
desc "development build"
lane :development do
build(
build_type: "Release",
scheme: "development",
export_method: "development"
)
end
desc "staging build"
lane :staging do
build(
build_type: "Release",
scheme: "staging",
export_method: "adhoc"
)
end
desc "production build"
lane :production do
build(
build_type: "Release",
scheme: "production",
export_method: "app-store"
)
end
desc "build lane"
private_lane :build do |options|
configuration = [
options[:build_type].capitalize,
options[:scheme]
].join("-")
output_name = [
options[:scheme],
options[:build_type].capitalize
].join
gym(
workspace: "Runner.xcworkspace",
configuration: configuration,
scheme: options[:scheme],
clean: true,
export_method: options[:export_method],
output_directory: "output",
output_name: output_name,
)
end
end
Android の Fastfile は Flutter のビルドコマンドを直接実行するようにしました。(経緯は補足を参照してください。)この .apk と .aab の作成ぐらいでは fastlane を使用するメリットがあまりないですが、例えば fastlane アクションの supply
を使用することで作成された .apk や .aab を Google Playストアへアップロードするまでを行うことも可能となります。なお、以下 Fastfile を使用するにあたり署名周りの設定で app/build.gradle を多少なり変更しています。こちらも詳細は補足を参照してください。
android/fastlane/Fastfile
default_platform(:android)
platform :android do
desc "development build apk and aab"
lane :development do
flutter_build(
build_type: "release",
flavor: "development",
target: "lib/main-development.dart"
)
end
desc "staging build apk and aab"
lane :staging do
flutter_build(
build_type: "release",
flavor: "staging",
target: "lib/main-staging.dart"
)
end
desc "production build apk and aab"
lane :production do
flutter_build(
build_type: "release",
flavor: "production",
target: "lib/main-production.dart"
)
end
desc "flutter build lane"
private_lane :flutter_build do |options|
target = options[:target]
build_type = ["--", options[:build_type].downcase].join
flavor = options[:flavor].downcase
gradle(task: "clean")
sh("fvm", "flutter", "build", "apk", build_type, "--flavor", flavor, "--target", target)
sh("fvm", "flutter", "build", "appbundle", build_type, "--flavor", flavor, "--target", target)
end
end
- Flutter でビルドします(iOSのみ)
flutter build ios --release --no-codesign --flavor [flavor名]
Flutter Flavorizr を使用して環境分けをしている場合、iOS では Flutter Flavorizr で作成された各環境の .xcconfig の FLUTTER_TARGET
に main-[flavor名].dart
が設定されているため、build コマンドでの target 指定は不要です(ビルドコマンド自体を Fastfile に追記して自動化しても良いかもしれません)。Android は fastlane で Flutter のビルドコマンドを直接実行しているため、ビルドは不要です。
- fastlane を実行します
cd ios
bundle exec fastlane [lane名]
cd android
bundle exec fastlane [lane名]
- iOS であれば Fastfile で出力先に指定したディレクトリに .ipa が作成されていれば成功です。Android であれば、Flutter プロジェクト配下に build ディレクトリがあり、その中の app/outputs/apk と app/outputs/bundle に .apk と .aab が作成されていれば成功です。
補足
1. fastlane で gradle を実行した時に Flutter の target が正しく設定されない
Flutter Flavorizr を使用して環境分けを行なった Flutter プロジェクト配下の Android プロジェクトをまずは以下コマンドでビルドしました。
flutter build apk --release --flavor [flavor名] --target lib/main-[flavor名].dart
その後、以下 Fastfile(一部)を用いて fastlane で ビルドしました。その結果 target が正しく設定されず、デフォルトの lib/main.dart
が使用された .apk が作成されていました。flutter build
で target
を指定して作成された .apk は正しい lib/main-[flavor名].dart
が使用されていました。また、app/build.gradle の方で Flutter Flavorizr で作成された productFlavors のそれぞれの環境に project.flutter.target = "lib/main-[flavor名].dart"
を追記し、target
の指定をしても全て lin/main-production.dart
が使用されるといった結果でした。
default_platform(:android)
platform :android do
desc "development build apk and aab"
lane :development do
build(
flavor: "development",
build_type: "Release"
)
end
...
desc "build lane"
private_lane :build do |options|
assembleTaskName = [
"assemble",
options[:flavor].capitalize,
options[:build_type].capitalize
].join
bundleTaskName = [
"app:bundle",
options[:flavor].capitalize,
options[:build_type].capitalize
].join
gradle(task: "clean")
gradle(
tasks: [assembleTaskName, bundleTaskName]
)
end
end
2. fastlane から Android の署名を書き換えができない
上記に関連して Android の Fastfile で properties
パラメータの android.injected.signing.store.file
などを使用して署名を書き換えようとすると /android/app と /android のどちらにも .jks を配置しないと .jks が存在しないというエラーが発生し、ビルドできなかったです。(以下に Issue としても挙げられていました。)そのため、Android Developers のビルドファイルから署名情報を削除する を参考に署名設定をするにあたり build.gradle の変更を行いました。
app/build.gradle(一部)
...
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
// ---追記----
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
// ----- BEGIN flavorDimensions (autogenerated by flutter_flavorizr) -----
flavorDimensions "flavor-type"
productFlavors {
development {
...
}
staging {
...
}
production {
...
}
}
// ----- END flavorDimensions (autogenerated by flutter_flavorizr) -----
compileSdkVersion 30
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
...
}
// ---追記---
signingConfigs {
debug {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.release
}
}
// apk ビルドで Execution failed for task ':app:lintVitalRelease' が発生するので追記
// https://github.com/flutter/flutter/issues/30598#issuecomment-622926226
lintOptions {
disable 'InvalidPackage'
checkReleaseBuilds false
}
}
まとめ
fastlane で iOS・Android それぞれの .ipa, .apk, .aab の作成までの手順を紹介しました。これらの作成までであれば効果は薄いかもしれませんが、fastlane をもう少し踏み込んで使用すると各ストアへのアップロードまでも自動化できます。また、公式にもあるように fastlane と既存のツール(GitHub Actions や CircleCI など)と組み合わせて CI・CD のセットアップも可能です。活用して自動化してみてはいかかでしょうか。
参考
Discussion