見出し画像

Firebase App Distribution の新規端末登録・更新配信を自動化する

iOS アプリを開発する中で、社内をはじめ限定的なメンバーだけに絞って開発中のアプリを配信することはよくあるシーンです。
TestFlight に DeployGate に Fabric…いや、Fabric は 5/4 に close してましたね。

note では iOS/Android アプリ開発ともに Firebase App Distribution を利用しています。社員の G Suite ドメインに限定してインストール可能にしたダウンロードリンクを生成して社内でシェアしています。

この運用でいつも問題になるのが新規端末からアクセスされたときの UDID 反映です。

作業の流れとしては

1. 新規端末からダウンロードしようとすると Firebase から管理者へメールが飛ぶ(アクセスしたメンバーのメルアドと端末の UDID 付き)
2. メールにある UDID を Provisioning Profile に登録する
3. ビルドして再度 Firebase App Distribution へ配信する
4. 配信完了後新規メンバーにメールが配信される

となってて、太字にした2と3は手動対応になりがちです。
しかもこれが地味に時間を取られる作業なので面倒。

面倒?じゃぁ自動化しよう

てことでタイトル画像へ。

画像1

2は

Gmail に新規端末登録メールが来たら本文からメルアドとUDIDを抽出する

3は

ビルドマシンで fastlane によるデバイス登録・配信

に置き換えて自動化してみましょう。

出来上がったのがこちら

今回も Zapier で自動化です。

画像2

1. Firebase のメール取得

タイトルが日本語の場合と英語の場合があるので両方検索候補にいれます。
アプリ名が com.myapp.viewer の場合は

(subject: [Firebase] App Distribution: New device registered for com.myapp.viewer) OR (subject: [Firebase] 新しいデバイスが com.myapp.viewer に登録されました )

で新着メールをフックしましょう。

2. device 名と UDID 抽出

Firebase からのメール本文はこんな感じになってます。

画像3

これを正規表現で抜き取りましょう。

テスター[\t\s\n\r]+(.*)[\t\s\n\r]+UDID((.*))[\t\s\n\r]+(.*)[\t\s\n\r]+デバイスを追加

スクショで取りこぼしちゃいましたが「デバイスを追加」というボタンが次の要素になってるので終端要素に入れておきます。

結果、

output[0] # => 登録者のメールアドレス
output[1] # => 端末モデル名
output[2] # => UDID

が抽出できました。

3. スペースを取り除く

抽出した情報を使って、Apple Developer Program に

UDID: output[2]
device name: output[1]-output[0] 
(i.e. iPhone_11_Pro_Max-laprasdrum@note.jp) 

と登録したいとしましょう。

このときデバイス名は iPhone 11 Pro Max とスペースが入ってる場合があります。
後に fastlane で引数として渡すことを考えると無駄なスペースは省いておきたいので、このようにデバイス登録したい場合はスペースを削るなり置換するなりしましょう。

画像4

4. fastlane で証明書更新・配信

常駐起動してる macOS サーバーで fastlane match & gym & firebase_app_distribution するだけです。

指定するサーバーは何でもよいのですが、私は例によって CircleCI API を使って CircleCI の macOS インスタンスで実行してます。

/**
 * inputData
 * circleci_token: {token}
 * api_root: https://circleci.com/api/v2
 * device_name: {3の結果}-{2のoutput[0]}
 * device_udid: {2のoutput[2]}
 */

import json

headers = {
   'Accept': 'application/json',
   'Content-Type': 'application/json'
}
auth = (input_data['circleci_token'], '')
response = requests.post(
   f"{input_data['api_root']}/project/gh/group/repo/pipeline",
   data=json.dumps({
           "branch": "master",
           "parameters": {
               // .circleci/config.yml でこのパラメーターが True じゃないと
               // 起動しない Workflow を用意する
               "run_device_registration_from_ci": True,
               "device_name": input_data['device_name'],
               "device_udid": input_data['device_udid']
           }}),
   headers = headers,
   auth = auth)
response.raise_for_status()
output = response.json()

あとは fastfile に専用の lane を用意して起動すれば OK です。

desc "[CI] add new device UDID. It requires options: name, udid"
lane :add_new_device do |options|
  if (options[:name].nil? or options[:name].empty?) or (options[:udid].nil? or options[:udid].empty?)
    UI.user_error!("missing options! call with name:{device name} udid:{device udid}")
  end

  UI.important("registering #{options[:name]} (#{options[:udid]})")
  register_device(
    name: options[:name],
    udid: options[:udid]
  )
  # 証明書更新
  match(
    app_identifier: ['com.myapp.viewer'], # 登録したい identifier すべて入れる
    type: 'adhoc', # 必要なら development も
    force_for_new_devices: true
  )
  
  # ビルド 
  gym(...)
   
  # 配信
  firebase_app_distribution(...)
end

fastlane から Firebase の CLI を使うときは事前にインストール作業が必要なので公式ドキュメントに従ってセットアップしてください。

地味な手作業は自動化しよう

これで開発に専念できる時間が増えますね 👍

読んでいただきありがとうございますー よろしければシェアもおねがいします🙏