Lento con forza

大学生気分のIT系エンジニアが色々書いてく何か。ブログ名決めました。

GitHub Actionsを使ってFirebase App Distributionへ配布する

FirebaseとGitHub Actionsがマイブームの id:kouki_dan です。

つい先日Firebase App Distributionがリリースされましたね!

firebase.google.com

FirebaseはFabricの機能を順次Firebaseで扱えるようにしていますが、App Distributionは今までFabric Betaで行なっていたBeta版、テスト版のアプリを社内やテスターの方に配布できるサービスです。 似たようなサービスとしてはDeployGateが有名ですね。

そんなFirebase App DistributionをGitHub Actionsで使ってみようというのが今回の記事になっています。GitHub Actionsのおかげで、外部CIを使うことなくビルドの自動化が行えるようになりました。

iOSアプリのDistribution

iOSアプリを配布するためには、

  • 配布用証明書とプロビジョニングプロファイルを発行する
  • 証明書とプロビジョニングプロファイルを使ってipaをビルドする
  • ビルドしたipaをアプリの情報が書かれたplistと一緒にサーバーにホスティングする

を行う必要があります。

このうち3つ目のサーバーにホスティングする部分はApp Distributionが行なってくれますが、ipaを生成し、生成したipaをApp Distributionにアップロードしなくてはいけません。

GitHub Actionsでの証明書管理

iOSアプリの証明書の管理方法として、直接証明書を使う方法と、fastlane matchを使う方法があります。ここでは各々2つの方法を説明していきます。また、matchを使うかどうかに関わらず、fastlaneを使った方法を説明します。

直接証明書を管理している場合

証明書を直接管理している場合、リポジトリに直接証明書ファイルをコミットする方法や、GitHubのSecretsにbase64エンコードした証明書を入れておく方法が考えられます。ここでは証明書をリポジトリにコミットし、パスワードをSecretsに入れて環境変数から読み込む方法について説明します。 注: パスワードが設定されているとはいえ、Publicリポジトリで証明書のp12ファイルを公開するのは辞めておきましょう。Publicリポジトリでこの方法を使いたい場合は、後述のmatchを使うか、証明書本体をSecretに入れる方法が考えられます。

まず、証明書のp12ファイルとmobileprovisionをリポジトリにコミットします。ここでは certificates/distribution.p12certificates/adhoc.mobileprovision の2つのファイルをコミットしたとして説明をします。

CI環境で証明書を管理するために、まずはsetup_ciというアクションを利用する必要があります。 このコマンドにはprovider引数としてどのCIを使っているのかの情報を与えるのですが、現状GitHub Actions用のproviderは用意されていません。travisで代用するとうまく動くので、こちらを指定しましょう。

setup_ci(
  force: true,
  provider: "travis",
)

あとは、fastlaneに用意されているimport用のアクションを使って証明書をimportします

import_certificate(
  certificate_path: "certificates/distribution.p12",
  certificate_password: ENV["CERTIFICATE_PASSWORD"],
  keychain_name: ENV["MATCH_KEYCHAIN_NAME"] || "" # MATCH_KEYCHAIN_NAME created by setup_ci action
)
install_provisioning_profile(path: "certificates/adhoc.mobileprovision")

CERTIFICATE_PASSWORDはSecret経由で与えられる環境変数で MATCH_KECHAIN_NAMEはsetup_ciアクションで作られる環境変数です。

これらをprivate_laneとしてFastfileに定義しておきましょう。

platform :ios do
#....
  desc "Import Certificates for GitHub Actions"
  private_lane :import_certificates_for_actions do
    setup_ci(
      force: true,
      provider: "travis",
    )

    import_certificate(
      certificate_path: "certificates/distribution.p12",
      certificate_password: ENV["CERTIFICATE_PASSWORD"],
      keychain_name: ENV["MATCH_KEYCHAIN_NAME"] || "" # MATCH_KEYCHAIN_NAME created by setup_ci action
    )
    install_provisioning_profile(path: "certificates/adhoc.mobileprovision")
  end
#....
end

matchで証明書を管理している場合

GitHub Actionsを実行するとGitHubにアクセス可能なトークンを得ることができますが、そのトークンはActionsが発生したリポジトリにのみアクセス権限を持っています。matchで証明書を管理している場合は別の証明書管理用のPrivate Gitリポジトリがあることが多いと思われます。GitHub Actionsで別のPrivateリポジトリにアクセスする方法はいくつかあるのですが、ここでは一番簡単なPersonal access tokenを使った方法を説明します。
また、すでにmatchを使って証明書をgitリポジトリに保存してあることを前提としています。設定されていない方はこちらを参考に設定をお願いします。

まず、こちらの場合もCIで証明書周りを動かすために、setup_ciというアクションを利用する必要があります。 このコマンドにはprovider引数としてどのCIを使っているのかの情報を与えるのですが、現状GitHub Actions用のproviderは用意されていません。travisで代用するとうまく動くので、こちらを指定しましょう。

setup_ci(
  force: true,
  provider: "travis",
)

証明書をインストールするコマンドは以下のようなものになります。

sync_code_signing(
    git_url: "https://github.com/kouki-dan/certificates.git",
    git_basic_authorization: ENV["PERSONAL_ACCESS_TOKEN_GITHUB"],
    type: "adhoc",
    readonly: true
)

git_urlは自分のリポジトリに置き換えてください。僕は https://github.com/kouki-dan/certificates というリポジトリで自分の証明書を管理しているので、このようになります。

ここで大事なのは、SSHではなくHTTPSのURLを指定していることです。GitHubではリポジトリをcloneする時に、Personal Access Tokenを利用する方法が用意されています。この方法を使うことで、CIからでも簡単に複雑なSSHの設定をすることなく、証明書が入っているmatchのリポジトリをcloneすることができます。 また、ここでアクセストークンを指定するのに使っている git_basic_authorization はfastlane 2.132.0 でリリースされた機能です。最新のfastlaneが使われていることを確認してください。これ以下のバージョンをお使いの場合はアップデートが必要になります。

git_basic_authorization に指定するのは username:personal-access-token という形式の文字列をBase64エンコードしたものです。これは後ほど設定します。

これらを踏まえてmatchを利用した時のFastfileはこのようになります。

platform :ios do
#....
  desc "Import Certificates for GitHub Actions"
  private_lane :import_certificates_for_actions do
    setup_ci(
      force: true,
      provider: "travis",
    )

   sync_code_signing(
     git_url: "https://github.com/kouki-dan/certificates.git",
     git_basic_authorization: ENV["PERSONAL_ACCESS_TOKEN_GITHUB"],
     type: "adhoc",
     readonly: true
  )
  end
#....
end

ここまでで一番複雑な証明書の設定が完了しています。

FastlaneでApp Distributionに配布しよう!

CI上で証明書を管理することができるようになったので、いよいよビルドと配布用のlane app_distributionを作って、Actionsを使ってApp Distributionにアップロードしていきましょう!

platform :ios do
#....
  desc "Push a new beta build to Firebase App Distribution"
  lane :app_distribution do
    # ここに証明書を読み込み、ビルドし、App Distributionにアップロードするアクションを記述する
  end
#....
end

証明書を読み込む

先ほど証明書を読み込むprivate_laneを作成しました。このlaneを呼び出すことでActions上でも証明書の設定を完了させましょう。

import_certificates_for_actions if is_ci
sync_code_signing(
  git_url: "git@github.com:kouki-dan/certificates.git",
  type: "adhoc",
  readonly: true
) unless is_ci

CI環境で実行された時に証明書を読み込みます。is_ciはfastlane標準のアクションですが、GitHub Actionsでis_ciがtrueと判定されるためには、fastlaneのバージョンが2.131.0以上である必要があります。アップデートされていることをご確認ください。 また、CI環境以外でも使えるように、CI以外でもmatchを呼び出すようにしています

ビルドを行う

素直にgymを使いビルドを行います。schemeはご自身のschemeを指定してください。

build_app(
  scheme: "ActionsAndAppDistributionTest",
  export_options: {
    method: "ad-hoc"
  }
)

fastlaneでApp Distributionにアップロードする

いよいよ生成されたipaをApp Distributionにアップロードします。App Distributionをfastlaneから使うには、プラグインを追加する必要があります。

firebase.google.com

ここの説明に沿って、プラグインをインストールしていきましょう。このコマンドでプラグインを追加できます

fastlane add_plugin firebase_app_distribution

プラグインを追加することで、firebase_app_distribution アクションが扱えるようになります。以下のように指定しましょう。

firebase_app_distribution(
  app: "1:595091170546:ios:29fc33f9349780038c9c6b",
  groups: "all",
  release_notes: "Lots of amazing new features to test out!",
  firebase_cli_path: `which firebase`.strip()
)

app, groups, release_notesについては各自のものを指定する必要があります。 appに指定する値は、Firebaseにアプリを追加した時に生成されるアプリIDです。Firebaseコンソールの設定→マイアプリから確認できます。

f:id:kouki_dan:20190927003308p:plain

groupsにはFirebaseのApp Distributionに指定したグループ名を指定しましょう。僕はallというグループを作りました。testersというキーを指定することで、テスターを一人ずつ追加することも可能です。複数の値を指定するときはカンマ(,)区切りで指定します。 f:id:kouki_dan:20190927003403p:plain

最後に、このコマンドを実行するためにはfirebase-toolsのバージョン7.4.0が必要です。古いバージョンを使っている場合はアップデートを行いましょう。

npm install -g firebase-tools

これらを組み合わせ、Fastfileは以下のようになるはずです。

platform :ios do
  desc "Push a new beta build to Firebase App Distribution"
  lane :app_distribution do
    import_certificates_for_actions if is_ci
    sync_code_signing(
      git_url: "git@github.com:kouki-dan/certificates.git",
      type: "adhoc",
      readonly: true
    ) unless is_ci
    build_app(
      scheme: "ActionsAndAppDistributionTest",
      export_options: {
        method: "ad-hoc"
      }
    )
    firebase_app_distribution(
      app: "1:595091170546:ios:29fc33f9349780038c9c6b",
      groups: "all",
      release_notes: "Lots of amazing new features to test out!",
      firebase_cli_path: `which firebase`.strip()
    )
  end
#....
end

ここまでで準備は完了です。手元で firebase login されていることを確認後、fastlaneのコマンドを発行するとApp Distributionにアップロードされることが確認できると思います。

fastlane app_distribution # or `bundle exec fastlane app_distribution`

f:id:kouki_dan:20190927013002p:plain

あとはGitHub Actionsでこのコマンドを動かせば、手元でビルドする必要がなくなり便利です。最後にGitHub Actionsの設定をしていきましょう。

GitHub Actionsでfastlane(とFirebase App Distribution)を動かす

ここまででビルドし配布を行う環境をfastlaneで作ってきました。これをGitHub Actionsで実行しましょう。

まず、.github/workflows/main.yml に以下のように記述します。

name: Deploy to App Distribution

on: [push]

jobs:
  build:

    runs-on: macOS-10.14
    timeout-minutes: 20

    steps:
    - uses: actions/checkout@v1
    - uses: actions/setup-node@v1
      with:
        node-version: '10.x'
    - name: Deploy to Firebase App Distribution
      env:
        CERTIFICATE_PASSWORD: ${{ secrets.CERTIFICATE_PASSWORD }} # In case of using manual certificates import
        PERSONAL_ACCESS_TOKEN_GITHUB: ${{ secrets.PERSONAL_ACCESS_TOKEN_GITHUB }} # In case of using match
        MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} # In case of using match
        FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
      run: |
        npm install -g firebase-tools

        sudo xcode-select -s /Applications/Xcode_11.app
        bundle install
        bundle exec fastlane app_distribution

Firebase Toolsの最新版の動作環境としてnode 8以上が必要なので、actions/setup-node を利用し準備しています。

envにfastlaneで利用する環境変数をSecretから読み込むように指定します。Secretの指定は後ほど記述します。

runには実際のコマンドを記述しています。 - Firebase Toolsのインストール - Xcode 11を使用するようにする - Xcode 11以外も利用することができます。こちらから確認できます。 - fastlaneを実行する

fastlaneで処理を記述しているので、Actionsに書く内容ははこれだけです。

あとは利用する環境変数をSecretに追加していきましょう。
https://github.com/:username/:reponame/settings/secrets から追加できます。

Name Value 備考
CERTIFICATE_PASSWORD Keychain Accessでp12の証明書を書き出すときに指定したパスワード 証明書を直接読み込む場合のみ必要
PERSONAL_ACCESS_TOKEN_GITHUB 設定から取得したGitHubのPersonal Access Token match利用時のみ必要
MATCH_PASSWORD matchを設定時に指定したパスワード match利用時のみ必要
FIREBASE_TOKEN firebase login:ci で得ることができるトークン文字列

これらを設定し、リポジトリにpushすると、Actionsタブでビルドが始まり、成功するとFirebase App Distributionに配布されます。

f:id:kouki_dan:20190930230323p:plain

任意のタイミングでGitHub Actionsを動かす

この設定を行うと、毎push時に配布されることになります。Actionの on に指定できるイベントはいくつも用意されています。

help.github.com

on の部分にscheduleを指定することで毎日ビルドを行なったり、pull_requestを指定し、プルリクごとに配布するなどが考えられます。

また、このようなユースケースでは、ビルドしたい任意のタイミングで、Botやボタンのクリックによって配布ビルドを回すことができると便利な場合があります。 そういった場合のために、外部イベントであるrepository_dispatchというのも用意されています。これはエンドポイントに向けてPOSTするとGitHub内でイベントが発生します。

毎回POSTするのが面倒!という方のために、ボタンをクリックするだけでブラウザ上でユーザーのOAuthトークンを使ってrepository_dispatchをPOSTしてくれるGitHub Appsを作りました!

kouki.hatenadiary.com

まとめ

GitHub ActionsでiOSアプリのad-hocビルドを行い、それをApp Distributionに配布することを行いました。今回はApp Distributionを使いましたが、Fabric BetaやDeployGateでもビルド方法は同じなので、似たような感じで実現できます!

また、Action Buttonはとても便利なのでぜひ改善PRをお待ちしております・・!

ここまでの説明はコードにしてこのリポジトリに置いてあります。参考にしてください。

github.com