[iOS] GitHub Actionsでfastlaneのmatchを使わずにAdHoc書き出しをしてからFirebase App Distributionにアップロードする

2020.01.25

こんにちは!きんくまです。

今回は「GitHub Actionsでfastlaneのmatchを使わずにAdHoc書き出しをしてからFirebase App Distributionにアップロードする」です!(長いっ)

作ったGitHub Acitonsのワークフロー

  1. GitHubでプルリクを作ってから、マージされたイベントをトリガーにする
  2. macOSを立ち上げる
  3. fastlaneを使う
  4. テストする。失敗したらSlackに通知
  5. AdHocビルドする。 その際、証明書とプロビジョニングファイルはGitHubのsecretsに登録したものを使用する 登録するファイルはbase64化したもの
  6. Firebase App Distributionにアップロードする
  7. Slackに通知

参考にした神情報のみなさま

先に申し上げますと、さきほど「作った」と書いてしまったのですが、先人のみなさまの情報を元に組み合わせて作成しました。 ありがとうございます!

ファイル構成

2020/05/05追記 いまのプロジェクト見たらCocoaPodsが追加されていたけど、この記事書いたときはないのでPodfile, Podfile.lock, プロジェクト名.xcworkspaceは抜いてあります。

- .github/
    | - workflows/
        | - adhoc.yml
- Cartfile
- Cartfile.resolved
- fastlane/
    | - Fastfile
    | - その他にAppfile, Pluginfile, README.md, report.xmlが入ってる
- Gemfile
- Gemfile.lock
- プロジェクト名/
- プロジェクト名.xcodeproj
- プロジェクト名Tests/
- README.md

Gemfile

fastlaneだけ書いてます。

# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem "fastlane", "2.139.0"

plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)

2020/05/05追記
もしCocoaPods使っているときはこれも入れてください

gem "fastlane", "2.139.0"
// fastlaneの後ろに追加
gem "cocoapods", "1.9.1"

fastalneにFirebase App Distributionのプラグイン追加

fastlane add_plugin firebase_app_distribution

参考

このコマンドを打つと fastlane/Pluginfile が追加されます。これもGitにコミットしてください。

ワークフローファイル

2020/02/11修正 Carthageのif文追加しました。

adhoc.yml

name: AdHoc Distribution

on:
  pull_request:
    branches:
      - master
    types:
      - closed

jobs:
  build:

    runs-on: macos-latest

    steps:
    - uses: actions/checkout@v2

    # Firebaseのため
    - uses: actions/setup-node@v1
      with:
        node-version: '10.x'
      
    - name: Bundle Install
      run: bundle install

    # Xcode 11.2.1 を使う
    - name: Select Xcode version
      run: sudo xcode-select -s '/Applications/Xcode_11.2.1.app'

    - name: Show Xcode version
      run: xcodebuild -version

    - name: Keychain.p12
      run: |
        echo "${{ secrets.P12_BASE64 }}" > ios_distribution.p12.txt
        base64 --decode ios_distribution.p12.txt > ios_distribution.p12

    - name: ProvisioningProfile
      run: |
        echo "${{ secrets.PROVISIONING_BASE64 }}" > adhoc.mobileprovision.txt
        base64 --decode adhoc.mobileprovision.txt > adhoc.mobileprovision

    - name: list files
      run: ls -l

    - name: Cache Carthage
      uses: actions/cache@v1.1.2
      id: cache-carthage
      with:
        path: Carthage
        key: ${{ runner.os }}-carthage-${{ hashFiles('**/Cartfile.resolved') }}
        restore-keys: |
          ${{ runner.os }}-carthage-

    - name: Carthage
      if: steps.cache-carthage.outputs.cache-hit != 'true'
      run: carthage bootstrap --platform iOS --no-use-binaries --cache-builds

    - name: Tests
      env:
        SLACK_URL: ${{ secrets.SLACK_URL }}
      run: |
        bundle exec fastlane tests

    - name: Upload Tests Result
      uses: actions/upload-artifact@v1
      with:
        name: test-results
        path: fastlane/test_output/report.html

    - name: Adhoc
      env:
        KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        SLACK_URL: ${{ secrets.SLACK_URL }}
        CERT_PASSWORD: ${{ secrets.CERT_PASSWORD }}
        FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
      run: |
        npm install -g firebase-tools
        bundle exec fastlane adhoc

2020/05/05追記
もしCocoaPods使っているときは Carthageの前か後ろにこれを追加してください

- name: CocoaPods
  run: bundle exec pod install

あと、Carthage使ってない人はファイルの存在確認するプラグインがあるとのことなので、それを使ってうまいことやってください。

Cacheについて

2020/02/11追記

Carthageのキャッシュ設定を上のyamlでは入れているのですが、この設定だと残念ながらできません。

[warning]No scopes with read permission were found on the request.

というメッセージが実行時に出力されます。

調べてみると、Cacheが使えるのはpushとpull_requestのみです。 ただしpull_requestにtypesを指定すると使えなくなりました、、、。 上のyamlでは、typesにclosedを指定しています。これはプルリクをマージしたときのイベントです。 pull_requestのデフォルトのtypeは opened, synchronize, reopenedとなっていて、何も指定しないとこのイベントでワークフローが走ります。ですが、今現在はこれ以外を指定するとダメだというだと思います。

なので、types設定を削除すれば、上のyamlでもCacheが使えるようになります! そのうちclosedでも使えるようになれるといいなー

Fastfile

Fastfile

default_platform(:ios)

platform :ios do

    desc "Unit Tests"
    lane :tests do
        scan(
            scheme: "スキーム名",
            device: "iPhone 11",
            clean: true,
            slack_message: "テストが完了しました!",
            slack_url: ENV["SLACK_URL"],
            slack_only_on_failure: true
        )
    end

    desc "Import Certificates and Provisioning Profile"
    private_lane :import_certificates_and_provisioning_profile do
        create_keychain(
            name: "mykeychain",
            password: ENV["KEYCHAIN_PASSWORD"],
            timeout: 1800
        )
        import_certificate(
            certificate_path: "ios_distribution.p12",
            certificate_password: ENV["CERT_PASSWORD"],
            keychain_name: "mykeychain",
            keychain_password: ENV["KEYCHAIN_PASSWORD"]
        )
        install_provisioning_profile(path: "adhoc.mobileprovision")
    end

    desc "Archive AdHoc and send to Firebase"
    lane :adhoc do
        import_certificates_and_provisioning_profile
        build_app(project: "プロジェクト名.xcodeproj",
            scheme: "スキーム名",
            configuration: "Configuration名",
            clean: true,
            export_options: {
            method: "ad-hoc"
        })
        firebase_app_distribution(
            app: "Firebaseプロジェクトから取得したApp ID (AppleのBundle IDではない)",
            groups: "developer",
            release_notes: "ベータ版配信",
            firebase_cli_path: `which firebase`.strip()
        )
        slack(
            message: "ベータ版が配信されました\nhttps://appdistribution.firebase.dev/app_distro/projects",
            slack_url: ENV["SLACK_URL"]
        )
    end

end

2020/05/05追記
もしCocoaPods使っているときは こんな感じに変えてください

//これを
//build_app(project: "プロジェクト名.xcodeproj",

//こうする
build_app(workspace: "プロジェクト名.xcworkspace",

firebase_app_distributionのappのところだけ補足

設定ページのここです。

GitHubのSecretsに登録するもの

Secretsは、GitHubの画面上のSettingsタブ > 画面左のSecretsメニュー から見れます。 ただし、権限がないと見れないかも

キー 何するもの?
P12_BASE64 base64化されたAcHoc用のp12証明書
PROVISIONING_BASE64 base64化されたプロビジョニングプロファイル
SLACK_URL 投稿したいチャンネル情報のWebhook URL
KEYCHAIN_PASSWORD 適当なキーチェーン用パスワード(一時的なものなのでわからなそうなやつであればなんでも)
CERT_PASSWORD キーチェーンから証明書を書き出した際に使用したパスワード
FIREBASE_TOKEN firebaseをCIから利用するときのトークン

ファイルをbase64化する

キーチェーンからp12ファイル形式で書き出したAdHoc用証明書と、プロビジョニングファイルはbase64化します。

コマンド例

openssl base64 -in cert.p12 -out cert_base64.txt

base64化すれば、バイナリファイルがテキストファイルに変換できるのでsecrets内に登録することができます。 それをワークフロー内でbase64デコードしてバイナリファイルに戻してあげて使用します。

FIREBASE_TOKEN

firebase login:ci

を実行すると、こんな感じになります。

Waiting for authentication...

✔ Success! Use this token to login on a CI server:

ここにトークンが書いてあります

2020/05/05追記
たまにトークンが期限切れになってビルドがエラーになります。上のlogin:ciコマンドをもう一度叩いて新しいトークンを発行してから、secretsに再登録してください。

fastalneから書き出されたテスト結果ファイル

ここにあります。 <- 変わりました

2020/05/05追記
いまはこういう感じになってました

まとめ

ここではfastlaneを使いました。ですが、yaml内のrunにコマンドを書けば、xcodebuildコマンドでもいけるみたいです。 あと、acitions/checkoutは@v2が出ていたので、そちらにしました。

あと、チームのみんなと料金表見ていたのですが、macランナーはLinuxランナーに対して10倍のポイント分(と呼べばよいのか?)を消費するみたいなので、少し気をつけた方が良いかもしれないです。

ただ、普段使っているGitHubでCI/CDが使えるというのはとても便利だと思いました! ではでは。