🍎

【Flutter】GitHub Actions で iOS 向けに自動デプロイする

2021/01/14に公開
7

2021/01/017 追記 : Android 版も書きました!

追記

  • 2021/01/22
    コードが間違っていたので修正。
    それとビルドネームが github_actios_sample とタイポしていることに気づいた。これはプロジェクトを作る時にミスってしまったらしい。

はじめに

Flutter build iOS して Xcode で archive してそれをまた App Store Connect にアップロードして...という作業は面倒くさい。GitHub Actions を使えば push などをトリガーにこのやっかいな作業を自動化してくれるというじゃあないか。しかも、パブリックリポジトリなら無料で使わせていただけるという。これはもう、やるしかない。

すでに周辺知識がある人に向けて main.yml をはじめに載せておく。
いろいろ設定はできるが極力シンプルな形にまとめた。

main.yml
name: CI

on:
main branch
  push:
    branches: [ main ]

jobs:
  #
  # Build for iOS
  #
  build_iOS: # jobに名前をつけられる

    name: Build for iOS # GitHubに表示されるジョブの名前
    runs-on: macos-latest # ジョブを実行するマシーンの種類

    steps: #一連のタスク

      # チェックアウト
      - name: Checks-out my repository # step に名前をつけられる なくてもいい
        uses: actions/checkout@v2

      # Flutter のインストール
      - name: Install Flutter
        run: git clone https://github.com/flutter/flutter.git # ターミナルでこれが実行されるイメージ

      # PATH を通す
      - name: Add path
        run: echo "$(pwd)/flutter/bin" >> $GITHUB_PATH # GITHUB_PATH にどんどんぶちこめばいいらしい

      # パッケージのダウンロード
      - name: Download Flutter packages
        run: flutter pub get

      # 証明書の生成
      - name: Import Provisioning Profile
        run: | # 複数行の run を書きたい場合はこうする 以下、Provisioning Profilesを置くべきディレクトリにデコードしている
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          echo -n ${{ secrets.PROVISIONING_PROFILE }} | base64 -d > ~/Library/MobileDevice/Provisioning\ Profiles/distribution.mobileprovision

      # 署名をする
      - name: Import Code-Signing Certificates
        uses: Apple-Actions/import-codesign-certs@v1 # 外部パッケージを使っている
        with:
          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
          p12-password: ${{ secrets.CERTIFICATE_PASSWORD }}

      # ipa ファイルの出力
      - name: Create ipa file
        # GITHUB_RUN_NUMBER をビルドナンバーに指定することで被りがないようにしている。
        run: flutter build ipa --export-options-plist=ExportOptions.plist --build-number ${GITHUB_RUN_NUMBER}

      # AppStoreConnect にアップロードする
      - name: Upload to AppStoreConnect
        run: xcrun altool --upload-app -f "./build/ios/ipa/github_actios_sample.ipa" -u "${{ secrets.APPLE_ID }}" -p "${{ secrets.APPLE_APP_PASS }}"

ここからは、あまり詳しくない人に向けてできるだけ丁寧に説明していきたい。

まずはプロジェクトを作る

テストでやっていくならビルド時間などを節約したい。
というわけで Flutter で新規プロジェクトを作りそれをそのまま GitHub にプッシュする。
今回作業していくのは以下のリポジトリになる。
https://github.com/kenta-wakasa/github_actions_sample

事前に必要なもの

必要なものがいくつかあるのでそれらを準備していこう。

  1. Apple ID と App 用パスワード
  2. Identifiers
  3. Certificates と p12 と パスワード
  4. Provisioning profile
  5. 新規 App ページ

AppleID と App 用パスワード

当然まずはディベロッパー登録をしておかなければいけない。
ここは流石に割愛する。

それをしたら次は App 用パスワードを入手する。
以下のページが参考になる。
https://support.apple.com/ja-jp/HT204397

この App 用パスワードは App Store Connect にアップロードするときに必要になる。

Identifiers

バンドル ID などと呼ばれているもの。
https://developer.apple.com/account/resources/identifiers/list
ここから作成する。いろいろ付け方はあるが、ユニークなものであればとりあえずは大丈夫。

Certificates と p12 と パスワード

このページがわかりやすいと思う。
https://i-app-tec.com/ios/apply-application.html

今回は Apple Distribution を選択した。

作成が終わったらこれを .p12 ファイルとして書き出す。
アプリケーション検索で k と入力するとキーチェーンアクセスが見つかるのでこれを開く。

こんな感じで書き出すことができる。
パスワードを要求されるので任意のものを入力して控えておく。

書き出しについてわからない場合はこのページが参考になるかもしれない。
https://mmorley.hatenablog.com/entry/2015/09/07/131745

Provisioning profile

ここから追加できる。
https://developer.apple.com/account/resources/profiles/add

今回は App Store を選択。
ファイル名は distribution.mobileprovision とした。

新規 App ページ

ここから追加できる。
先ほど作成した Identifiers を選択しよう。
https://appstoreconnect.apple.com/apps

準備 OK

長かったけれどこれで下準備は完了した。
次は Xcode での設定。

Xcode でいろいろ設定する

まずは [ プロジェクトの path ]/ios/Runner.xcworkspace を開く。

この画面で先ほど設定した Identifiers や Provisioning profile を指定する。

最低限これだけやっておけば大丈夫だと思う。

Flutter の公式に詳しく載っている。
https://flutter.dev/docs/deployment/ios

ExportOptions.plist をつくる

Xcode で一度アーカイブをしてそれを手元にダウンロードするとその中に ExportOptions.plist というものが入っている。 ipa ファイルを作成するときに必要になるので手に入れておく。

自分の場合はこのようになっていた。
コメントしたところを書き換えればそのまま使えるかもしれない。

ExportOptions.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>destination</key>
	<string>export</string>
	<key>method</key>
	<string>app-store</string>                // <- 出力方式
	<key>provisioningProfiles</key>
	<dict>
		<key>github.actions.sample</key>  // <- バンドルID
		<string>distribution</string>     // <- Provisioning profile
	</dict>
	<key>signingCertificate</key>
	<string>Apple Distribution</string>
	<key>signingStyle</key>
	<string>manual</string>
	<key>stripSwiftSymbols</key>
	<true/>
	<key>teamID</key>
	<string>42XYZEWKER</string>              // <- チームID
	<key>uploadBitcode</key>
	<false/>
	<key>uploadSymbols</key>
	<true/>
</dict>
</plist>

このファイルをプロジェクトのルートに配置した。

VSCode に拡張機能をいれる

GitHub Actions を触りやすくなる拡張機能があるのでインストールする。
https://marketplace.visualstudio.com/items?itemName=cschleiden.vscode-github-actions

Secrets を入力してく

GitHub Actions では Secrets に非公開情報を保存しておくことができる。
今回は次のものを設定する。

  1. APPLE_APP_PASS (App 用 パスワード)
  2. APPLE_ID (Apple ID)
  3. CERTIFICATES_P12 (p12 ファイル)
  4. CERTIFICATE_PASSWORD (p12 ファイルのパスワード)
  5. PROVISIONING_PROFILE (Provisioning profile)

さきほどインストールしておいた拡張機能を使うことで VSCode からでも簡単に設定できる。

Repository Secrets の横の + アイコンをクリックすればどんどん追加できる。
けれどここでひとつ問題がある。
文字列はそのまま入力できるが CERTIFICATES_P12 と PROVISIONING_PROFILE はどうしようか。

これは Base64 に変換することで解決できる。

ターミナルをひらいて

command
% base64 [変換したいファイル名] | tr -d  "\n" | pbcopy

と入力すればクリップボードに保存される。
あとはそれを cmd + v でペーストしてあげればよい。

ちなみに tr -d "\n" は改行コードを削除している。

ようやく Actions のワークフローを書いていく

編集していくファイルは [root]/.github/workflows/main.yml で、ここに命令を書き込んでいけばリモートにある mac 環境でそれを実行してくれる。
このファイルそのものは GitHub の Actions タブなどからも作成できる。

ここまでくればおそらくコピペでも動くはず。

  1. distribution.mobileprovision
  2. ./build/ios/ipa/github_actios_sample.ipa

この二つのファイル名はそれぞれ自分が名付けたものに変更してほしい。

これらもハードコードする必要はなく、すでにあるリポジトリの情報から引っ張ってくることも可能なので、次の学習のステップとして試してみるといいかもしれない。

できるだけシンプルに書いたのであまり解説するところもない。
わからないコマンドはそのコマンド名で調べると出てくると思う。

そんなわけで、ふたつに絞って補足の説明をする。

flutter build ipa

これは mono さんの記事を参考にさせていただたいた。
https://medium.com/flutter-jp/ipa-e176de0276c6
ipa ファイルを生成するのは面倒だったが、このコマンドをたたくだけで実現できるようにった。

xcrun altool --upload-app

アップロードはなんとこれだけでできてしまう。
この記事を参考にさせていただいた。
https://qiita.com/messhi/items/cb8c6f2b4b6540995189

おまけ

アップロードに成功するとテストフライトなどでそのままダウンロードしてこれる。
しかし、輸出コンプライアンスがありません などと言われて App に暗号化が使用されていないことを回答しなければ TestFlight などに落としてくることができない。

info.plist に

info.plist
  <key>ITSAppUsesNonExemptEncryption</key>
  <false/>

を追加すれば OK らしい。
これで、何の設定もすることなくアプリを落としてくることができる。

この記事を参考にさせていただいた。
https://tommy10344.hatenablog.com/entry/2020/04/29/025809

おしまいに

わからないこと、間違っていることなどあれば気軽にコメントいただけるとうれしいです。
次は Android 向けにも書いていこうとお思います。

参考記事

https://www.mum-meblog.com/entry/research-detail/flutter-ios-ci-cd

Discussion

yujitakaseyujitakase

はじめまして。貴重な情報をまとめていただき、ありがとうございます。
参考にして同じものを取り組んでみたのですが、Create ipa fileで下記エラーが出てしまいます。

error: No signing certificate "iOS Distribution" found: No "iOS Distribution" signing certificate matching team ID "XXXXXXXXX" with a private key was found. (in target 'Runner' from project 'Runner')

Secretsの設定、Xcodeのバージョンなどいろいろ見直してみたのですが、解消されません。
何か思い当たることがありましたら、教えていただけると幸いです。
よろしくお願いいたします。

こんぶこんぶ

コメントありがとうございます!
考えられるのは Certificates の登録と書き出し、Secretsへの登録がうまくいっていない。もしくは、Provisioning Profileで指定したCertificatesがSecretsで登録したものとことなっているなどの原因です。

登録したCertificatesのチームIDが現在対象としているプロジェクトで使っているチームIDと一致しているのかもチェックが必要ですね。(複数のプロジェクトに参加していると自分のチームIDと他組織のチームIDがごっちゃになりがち)

そのあたりを見直してみるとよいとおもいます!

yujitakaseyujitakase

返信ありがとうございます。
ご指摘の点、確認してみます。

yujitakaseyujitakase

Certificatesを再度書き出して登録し直したところ、無事に通りました。
アドバイスありがとうございます。

Certificatesの再書き出しの際、ご記載のキーチェーンアクセスではなく、
Xcode > Preferences > Accounts > Manage Certificates
を利用しました。
最初に試したキーチェーンアクセスと同じものを参照しているような気がするため、
要因は正しく理解できていませんが、まずは動いて良かったです。

ヤクランヤクラン

とても参考になりました、ありがとうございます!

以下の箇所ですが、
タイプ指定-t (--type) が必須になっているので反映しておいて頂けると助かります。

  • Before
run: xcrun altool --upload-app -f "./build/ios/ipa/github_actios_sample.ipa" -u "${{ secrets.APPLE_ID }}" -p "${{ secrets.APPLE_APP_PASS }}"
  • After
run: xcrun altool --upload-app -t ios -f "./build/ios/ipa/github_actios_sample.ipa" -u "${{ secrets.APPLE_ID }}" -p "${{ secrets.APPLE_APP_PASS }}"
こんぶこんぶ

ありがとうございます!
この記事みんなに読まれている割には、今はもっといい方法があるはずなので、書き直さないと...