FlutterもXcode Cloudで楽ちんCI/CD
はじめに
昔リリースしたアプリをアップデートしたいんだけど、iOSの証明書周りどうなってたっけ?ということありますよね。。自分はiOSの証明書周りがあまり自信がなく、毎回調べながらリリースしてるのですが、今回色々調べていたらXcode Cloudなる機能を利用することで、自動署名でビルドしてTestFlightにアップロードできるようなので、備忘録的に記事にしてみます。
今回やりたいこと
FlutterのiOSアプリをXcode Cloudを利用してCI/CDする。
Xcode Cloudとは?
Appleによって提供されているCI/CDサービスです。
Xcode 13.4.1以降とAppleDeveloperProgramのメンバーシップが必要なようです。
料金は25(コンピューティング)h/月までは無料のようです。
Xcode Cloudの設定
序盤の設定はぽちぽち進めてしまい詳しく覚えていないのですが、XcodeでAutomatically manage signingをオンにしてProduct Archiveをしようとしたところ、Xcode Cloudを利用するためにGitHubと連携しませんか?的なダイアログが出てきて、指示に従ってGitHub連携を済ませました。
そして、今回リリースしたいアプリのリポジトリを指定するとXcode Cloudが走ったようです。AppStoreConnectのアプリのページのXcode Cloudのタブからワークフローのログが見れます。
現状はワークフローが失敗しているので、エラーを解消しながらビルドできるところまで持っていきます。
ちなみにWorkflowの編集はXcodeのIntegrate -> Manage Workflows.. -> 対象のアプリで開けます。
Post-clone scriptの用意
ここからはFlutterの公式ドキュメントに従って進めてみます。
まず、Post-clone scriptを用意します。
公式ドキュメントのものをそのままコピペしてきました。
ci_post_clone.sh
#!/bin/sh
# Fail this script if any subcommand fails.
set -e
# The default execution directory of this script is the ci_scripts directory.
cd $CI_PRIMARY_REPOSITORY_PATH # change working directory to the root of your cloned repo.
# Install Flutter using git.
git clone https://github.com/flutter/flutter.git --depth 1 -b stable $HOME/flutter
export PATH="$PATH:$HOME/flutter/bin"
# Install Flutter artifacts for iOS (--ios), or macOS (--macos) platforms.
flutter precache --ios
# Install Flutter dependencies.
flutter pub get
# Install CocoaPods using Homebrew.
HOMEBREW_NO_AUTO_UPDATE=1 # disable homebrew's automatic updates.
brew install cocoapods
# Install CocoaPods dependencies.
cd ios && pod install # run `pod install` in the `ios` directory.
exit 0
中身を見てみると、Flutterのインストール、pub-get、cocoapodsのインストールなどをしているみたいです。fvmで指定しているversionのFlutterをインストールすべきな気がしますが、ここは一旦スルーします。(今気になるという方はこちら)
ci_post_clone.shをコミットしてPushするとWorkflowが走りBuildArchiveまで成功したようです。
TestFlightへのアップロードの設定
しかしまだTestFlightには上がっておらず、TestFlightへのアップロードの設定が必要のようです。
Workflowの設定を変更したいので、Integrate -> Manage Workflows.. -> 対象のアプリ -> Archive - iOSのDeployment PreparationをNoneからTestFlight and AppStoreに変更しました。
この設定はgit管理されていないようなので、おそらくXcode Cloudにすぐ反映されて、Webのコンソールから再実行すれば良さそうな気がしますが、ビルド番号を変えてなかったことを思い出したのでpubspec.yamlからversionをインクリメントして、Commit&Push!
無事にビルドされて、TestFlightに反映されました。
ここで、コンプライアンスがありませんと指摘されてます。(日本語おかしいw)
下記の記事に従ってinfo.plistにApp Uses Non-Exempt Encryptionを追加します。
再度pubspec.yamlからversionをインクリメントして、Commit&Push!
TestFlightに再度反映されて、実際に内部テストまでできました。
Flutterのビルド設定
しかし、アプリをインストールして立ち上げてみるとスプラッシュから動きません、、
- XcodeでArchiveする前にFlutter buildをしてないから?
- dart-defineの設定とかをしてないから?
ちょっとどちらが原因かわかりませんが、--dart-defineを指定したいので、下記をshellscriptの最終行の手前に追加してCommit&Push!
気づいたのですが、pubspec.yamlからversionを上げなくても自動でビルドバージョンがincrementされてました。これはXcode Cloudの仕様?🤔
+ flutter build ios --config-only --dart-define=flavor=prod
TestFlightに再度反映されて、インストールされたアプリはスプラッシュでフリーズせずちゃんと動くようになりました。flavorも反映されており、Firebaseのprod環境に接続していました。
秘匿情報管理
次に秘匿情報の管理についてです。
今回Admobの広告Unit-idを秘匿管理します。
まずAppStoreConnectの該当アプリのページからXcode Cloud -> ワークフローの管理 -> 該当のワークフロー -> 環境 -> 環境変数から編集をクリックしダイアログを開きます。
こんな感じでkeyとvalueを登録できて、secretにチェックを入れると見えなくなります。
この環境変数はshellscript内で${key}でアクセスできるので、
先ほどのflutter build
コマンドを下記のように変更します。
- flutter build ios --config-only --dart-define=flavor=prod
+ flutter build ios --config-only --dart-define=flavor=prod --dart-define=admobIdIos="${admobIdIos}"
TestFlightからインストールすると確かに広告ユニットIdが取得できて広告が表示できました。
これで、アプリをビルドしてTestFlightに挙げるところまで完了しました。
また本番リリースの申請にもビルドファイルを選択することができました。
ここまででやりたいことはほぼ終わったのですが、ここからは細かい設定をしていきます。
Workflowの微調整
パッと思いつくのは
- FlutterのVersionをFVMに合わせたい。
- Workflowが完了したときに通知を飛ばしたい。
- 毎回走らないようにreleaseブランチがpushされた場合のみWorkflowを走らせたい。
FlutterのVersionを指定
今回FVMで指定しているFlutterのVersionを指定してインストールしたいので、ci_post_clone.shを下記のように書き換える。
- # Install Flutter using git.
- git clone https://github.com/flutter/flutter.git --depth 1 -b stable $HOME/flutter
+ # Extract the Flutter version from .fvmrc file
+ FLUTTER_VERSION=$(cat .fvmrc | grep "flutter" | cut -d '"' -f 4)
+
+ # Clone the Flutter repository with the specified version
+ git clone https://github.com/flutter/flutter.git --depth 1 -b $FLUTTER_VERSION $HOME/flutter
+ export PATH="$PATH:$HOME/flutter/bin"
+
+ # Print the Flutter version
+ flutter --version
Workflow完了時の通知
XcodeのIntegrate -> Manage Workflows.. -> 対象のアプリ -> Post-Actionsの+ボタンからNotifyを追加。下の画像のようになるので、今回はSlackを追加することにしました。Slackの欄の+ボタンをクリックするとSlackとの連携のためにWebBrowserが開くので、指示に従って連携したいWorkspaceと連携。Xcodeに戻って通知を流すチャンネルを指定する。
いや、この設定一番最初にやるべきじゃない?w
releaseブランチでWorkflowを走らせる。
XcodeのIntegrate -> Manage Workflows.. -> 対象のアプリから Start Conditionsを開きブランチを設定します。ワイルドカードを利用できるので、release/*
のようにしておくことでrelease/から始まるブランチを指定できます。
また、Custom Conditionsから特定のファイルの変更があった場合のみトリガーさせるという設定もできるようですが、個人開発の範囲で気にするほどのことじゃないかなと思ったので、今回は指定してません。リリースブランチ切っている時点で配布したいことがほとんどだろうし🙆♂️
おわりに
iOSの配布やリリース周りは証明書がかなり厄介なイメージがありましたが、今回Xcode Cloudを利用することで、証明書周りにつまづかずにTestFlightへのアップロードができました。
リリース頻度にもよりますが、軽く使うくらいであれば無料枠で賄えそうなのもオススメです!
2024年は技術発信も頑張ろうと思っているので、記事が参考になった方は記事とGitHubのいいね(スター)とフォローをしていただけると励みになります!
最後まで読んでいただきありがとうございました✨
Discussion