こんにちは。 ANDPADでモバイルアプリの開発を担当しているzigeninです。
ANDPADでは、既存のKotlinで書かれたAndroidアプリに、Flutterモジュールを組み込む取り組みをしています。 最近、Flutterモジュールを自動でデプロイできるようにしました。
Flutterの公式サイトだと、デプロイする方法までは記述されていなかったので、少し悩みました。 どなたかの参考になれば、幸いです。
構成
- Flutterモジュールをaarとしてビルドする
- GCS上にmavenリポジトリを構築する
- リポジトリは特定のアカウントのみアクセスできるようにします*1
- Flutterモジュールのリポジトリにタグをつけた時、aarのビルドとデプロイをする
- GitHub Actionsを使います
Flutterモジュールをaarとしてビルドする理由
公式のAndroidアプリ向けFlutterモジュールのビルド方法
Integrate a Flutter module into your Android project | Flutter
Flutterのビルド方法は2つあります。
- A:aarとしてビルドする
- B:FlutterモジュールのソースコードをAndroidアプリと一緒にビルドする
aarを使う方法では、Androidアプリビルド時にFlutter SDKとFlutterモジュールのソースコードは不要です。 つまり、Flutterの開発をしていないメンバーがFlutterのことを意識しなくて済みます。 また、個人の環境ごとに、Flutter SDKのバージョンが異なる状況を回避できます。
以上より、aarを使う方法を採用しました。
GCSを使う理由
mavenリポジトリの構築方法はいくつかあります。
- GCS, S3などのストレージサービスを使う
- FlutterモジュールのGitリポジトリをmavenリポジトリとして扱う
- git-repoプラグインを使う
- Sonatype Nexus無償版などを使い、自前のmavenリポジトリサーバを用意する
- Sonatype有償版などの用意されているmavenリポジトリサーバを使う
- Google Artifact Registryを使う
2の方法は、ビルド生成物をリポジトリに含めるのがいまいちなのと、 git-repoプラグインがメンテされていないので、不採用にしました。
3の方法は、アプリエンジニアでサーバを管理しないといけなくなるのが面倒なので不採用にしました。
4の方法は、1の方法に比べて料金が高いので不採用にしました。 それほど高機能なものを求めていないというのもあります。
5の方法が一番良さそうですが、Google Artifact Registryがα版なので採用は見送りました*2。 GCSと使い勝手に差がなさそうに思えたというのもあります。
ということで、消去法で1のストレージサービスを使う方法に決めました。 ストレージサービスにはGCSを採用しました。ANDPADでは、Firebaseを利用しているためです。 Firebase と GCP は、アカウントなどの設定を共有しているので、AWS S3を採用する場合よりも設定の手間が減ります。
GitHub Actionsを使う理由
深い理由はないです。 ANDPADでは、AndroidアプリはCircle CI、iOSアプリはBitriseを主に使っています。 それ以外のCIツールを使ってみたかったというのが大きいです。
環境構築手順
前提
- Firebaseをすでに使っており、アプリエンジニアをFirebaseプロジェクトのメンバーに登録している
- GCPの基本操作は把握している
- GCPの課金を有効にしている(GCSのバケットを作るのに必須)
- コマンドラインからGCPを扱うツールも導入済み(gcloud)
- GCPのサービスアカウントを作成済み
- GitHub Actionsが使える環境
1. GCSにバケットを作る(これがMavenリポジトリになる)
- Firebaseプロジェクトに対応するGCPプロジェクトを開く
- GCPのStorageにアクセス
- バケットを作成
- 一意なバケット名を適当に入力(ここでは「flutter-maven-repo」とします)
- リージョンとロケールを設定
- どれを設定しても使えますが「単一リージョン」「asia-northeast1 (東京)」を設定しました
- 可用性は低いもののレイテンシが小さく、価格が少しだけ安いからです($0.020/GB/月)
- デフォルトのストレージクラスに「Standard」を選択
- アクセス制御に「均一」を設定
- 一部のアプリエンジニアのアクセスを禁止するモチベーションがないので
- 詳細設定はデフォルトのまま
- バケットの権限 -> メンバー -> 作成したサービスアカウントに「Storage オブジェクト管理者」のロールを与えて登録
2. mavenでGCSのバケットを扱うためのプラグインを導入する
Flutterモジュールのリポジトリのトップに「.mvn/extensions.xml」を作成します。 記述は以下です。
<extensions> <extension> <groupId>com.gkatzioura.maven.cloud</groupId> <artifactId>google-storage-wagon</artifactId> <version>1.7</version> </extension> </extensions>
このファイルにより、Flutterモジュールをデプロイするときにプラグインを自動で入手してくれます。 このプラグインでは、1.7より新しいバージョンがありますが、それらを使うとGCSの認証に失敗します。
3. PCからFlutterモジュールをデプロイしてみる
CIツールからデプロイする前に、PCからデプロイしてみます。 ここで動作を見ておいた方が、設定の問題を解決しやすいです。
mavenをインストールします(初回のみ)。
brew install maven
gcloudにサービスアカウントを使ってログインします。
export GOOGLE_APPLICATION_CREDENTIALS=<サービスアカウントの認証情報JSONファイルへのパス> # gcpのプロジェクト設定(不要かも) gcloud config set project <your-project-id>
Flutterモジュールをビルド&デプロイします。
# Flutterモジュールをaarとしてビルド flutter build aar --build-number=0.1.0 # ビルドしたモジュールをGCSにデプロイする(sqliteなどの依存モジュールもデプロイしている) find . -name "*.pom" -type f -print0 | xargs -I{} -0 bash -c 'mvn deploy:deploy-file -Durl="gs://flutter-maven-repo/" -DpomFile="$0" -Dfile="${0%.pom}.aar"' '{}' \;
WebのGCSのBrowser上のflutter-maven-repoを見て、aarがアップロードされていればOKです。
4. AndroidアプリからFlutterモジュールを使う
flutter build aarを実行したときに、Androidアプリ側のbuild.gradleにどのような記述をすれば良いのか出力されています。 大体その通りに記述すればOKですが、少しだけGCSを独自の部分もあるので、記述を示します。
AndroidアプリのappモジュールからFlutterモジュールを使うとします。 このとき、app/build.gradleに必要な記述は以下です。
android { buildTypes { // Flutterモジュールのprofileビルドを使う場合。profileビルド不要なら、この記述は不要。 profile { initWith debug // マルチモジュール構成にしているとき、他のモジュールのBuildTypeに'profile'が存在しないとビルドエラー。 // 他のモジュールのBuildTypeに'profile'がない場合は、BuildTypeに'debug'を使うようにする。 // 他のモジュールのBuildTypeに'profile'をいちいち追加しなくても良くなる。 matchingFallbacks = ['debug'] } } } repositories { maven { url 'https://storage.googleapis.com/download.flutter.io' } // Mavenリポジトリ用に作成したGCSのバケットのURLを記述。schemeはgsではなくgcsなので注意。 maven { url 'gcs://flutter-maven-repositories' } } dependencies { debugImplementation 'jp.andpad.andpad_flutter:flutter_debug:0.2.0' profileImplementation 'jp.andpad.andpad_flutter:flutter_profile:0.2.0' releaseImplementation 'jp.andpad.andpad_flutter:flutter_release:0.2.0 }
GCSのバケットへアクセスする時にサービスアカウントが必要になります。 ここでは、デフォルトサービスアカウントを使うように設定します。
以下のコマンドを実行します。
# gcpのプロジェクト設定(不要かも) gcloud config set project <your-project-id> # Webブラウザでログイン画面が表示されるので、エンジニア個人のアカウントでログインします。 gcloud auth application-default login # CIツール上でビルドする場合は、1つ上のコマンドではなく、以下のコマンドで認証情報を設定します。 # https://cloud.google.com/docs/authentication/production?hl=ja#passing_variable # export GOOGLE_APPLICATION_CREDENTIALS=<サービスアカウントの認証情報の.json>
後は、Androidアプリをビルドするだけです*3。
5. Flutterモジュールのリポジトリにタグを付けた時、aarのビルドとデプロイをする
Flutterモジュールのリポジトリに、セマンティックバージョン形式のタグ(例:v1.0.0)をつけたときに、aarをビルドしてデプロイするようにします。
GitHub Actions向けに、Flutterモジュールのリポジトリに「.github/workflows/publish-aar.yml」というファイルを作成します。 記述を以下に示します。デプロイのコマンドは、PC上でデプロイしたときと同じです。
name: publish-aar on: push: tags: - "v[0-9].[0-9]+.[0-9]+" jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: java-version: '12.x' - uses: subosito/flutter-action@v1 with: flutter-version: '1.22.0' - name: build aar run: | version=${GITHUB_REF#refs/tags/} flutter build aar --build-number=${version:1} - uses: GoogleCloudPlatform/github-actions/setup-gcloud@master with: project_id: develop-andpad-40 service_account_key: ${{ secrets.GOOGLE_APPLICATION_CREDENTIALS }} export_default_credentials: true - name: mvn deploy run: | find . -name "*.pom" -type f -print0 | xargs -I{} -0 bash -c 'mvn deploy:deploy-file -Durl="gs://flutter-maven-repositories/" -DpomFile="$0" -Dfile="${0%.pom}.aar"' '{}' \;
後は、GitHubのSecretsの"GOOGLE_APPLICATION_CREDENTIALS"に、サービスアカウントの認証用JSONファイルの中身をコピペしておきます。
以上で、設定は全て完了です。 適当なタグをプッシュすれば、aarがビルドされてGCSにデプロイされます。
最後に
ANDPADでは、一緒に働く仲間を募集中です! engineer.andpad.co.jp
補足
GCSのmavenリポジトリを誰でも読み込めるようにする場合のAndroidアプリのビルドについて
mavenリポジトリの読み込み時に認証不要にした方が、運用が楽です。 Androidアプリをビルドするだけであれば、gcloudの設定は不要になります。
やり方を紹介しておきます。
まず、GCS上のmavenリポジトリのバケットにて、「allUsers」に「Storage閲覧者」の権限を付与します(WebのGCPのConsoleなどで実施)。
次に、app/build.gradleを以下のように記述します。 「4. AndroidアプリからFlutterモジュールを使う」の記述との違いは、mavenリポジトリのURLの記述のみです。
android { buildTypes { // Flutterモジュールのprofileビルドを使う場合。profileビルド不要なら、この記述は不要。 profile { initWith debug matchingFallbacks = ['debug'] } } } repositories { maven { url 'https://storage.googleapis.com/download.flutter.io' } // gcsでなくhttpsであることがポイント。gcsだと一般公開されているバケットでも認証失敗でビルドエラー maven { url 'https://storage.googleapis.com/flutter-maven-repositories' } } dependencies { debugImplementation 'jp.andpad.andpad_flutter:flutter_debug:0.2.0' profileImplementation 'jp.andpad.andpad_flutter:flutter_profile:0.2.0' releaseImplementation 'jp.andpad.andpad_flutter:flutter_release:0.2.0 }
後は、Androidアプリをビルドするだけです。gcloudの設定は不要です。
雑だが楽なデプロイ方法
ここまで、find + mvn deployコマンドでデプロイしましたが、gsutilコマンドでデプロイする方法もあります。
gsutil cp <flutter build aarで生成されるローカルmavenリポジトリのパス> gs://flutter-maven-repositories/
この方法だと、「.mvn/extensions.xml」は不要になりますし、デプロイ時のコマンドが簡単になります。 ただし、mavenリポジトリ内の「maven-metadata.xml」が不正になります。
maven-metadata.xmlには、これまでのモジュールのバージョンのリストが記載されます。 gsutil cpを使うと、最新のバージョンの情報しか記載されません。
といっても、この不正な状態でも、Androidアプリから過去バージョンのモジュールを取得できます。 なので、細かいことが気にならない方は、gsutilを使えば良さそうです。
FlutterモジュールをGCP Artifact Registryにデプロイする方法
https://medium.com/google-cloud/flutter-aar-deploy-to-maven-and-gcp-artifact-registry-d107adebec4d
ひとまず、GCP Artifact Registryの採用は見送りましたが、mvn deployの使い方は、このページが非常に参考になりました。