ionic(cordova) CircleCI2とFastlaneでiOSアプリをデプロイゲートにアップロードする手順まとめ

はじめに

今回やりたかったことは
・ionicで開発したiOSアプリをCircleCI2上でAdHoc版ビルド
・ビルドしたアプリをデプロイゲートへアップロード
・Fastlane Matchは使わず手元にある証明書とプロビジョニングプロファイルで行う
ビルドとアップロードはFastlaneというツールを使う

必要なもの
・証明書
キーチェーンアクセスから書き出す「iPhone Distribution:xxxxxxxxxxx(xxxxxx)」いつものp12ファイル。
・プロビジョニングプロファイル
・デプロイゲートのAPIキーとアカウント名

流れ

CircleCI上で証明書等を予め登録しておく
・証明書とプロビジョニングプロファイルをCircleCI上に暗号化して登録(環境変数を利用)
・デプロイゲートのアカウントとAPIキーもCircleCI上に登録(環境変数を利用)
https://circleci.com/gh/[githubアカウント]/[リポジトリ名]/edit#env-vars
↑ここから登録できる

以下を用意
・.circleci/config.yml
・fastlane/Fastfile

イメージとしては普段macでおこなっている作業をCircleCI上のmacで行うだけ。
・cordova、ionic と依存するnode_modulesのインストール
・予め登録した証明書とプロビジョニングプロファイルを復号化してマシンにインストール
・インストールした証明書等を使ってアプリをビルド
・予め登録したデプロイゲートの情報を使って、デプロイゲートへアップロード

ハマったこと

証明書とプロビジョニングプロファイルを正しく指定してるのにSigning Errorが消えない

エラー

Code Signing Error: Signing for "circleci_demo" requires a development team. Select a development team in the project editor.
Code Signing Error: Code signing is required for product type 'Application' in SDK 'iOS 11.2'

xcodeでいうところの「Automatically manage signing」がONになっている状態だとエラーになってしまっていた。
ONが奨励されてる?のかわからないけど今回はこれをOFFにすることで解決した。
後述するFastlane の disable_automatic_code_signing メソッドを使うとOFFになる。

cordova-iosが生成するxcodeの project.pbxproj ファイルの形式が不完全

エラー

Seems to be a very old project file format - please open your project file in a more recent version of Xcode

TargetAttributes属性がないとFastlaneがこのエラーで終了してしまう。
Cordovaで生成した直後のプロジェクトだとこの属性がないらしい(最新のcordova-iosでは改善してるかも)
で、platforms/ios/circleci_demo.xcodeproj/project.pbxproj もgitで管理してビルドする前にこのファイルを使うことで解決することにした。

アーカイブに成功してエクスポートでエラー

エラー

Exit status: 70
No provisioning profile provided
Make sure to pass a valid provisioning for each required target
Check out the docs on how to fix this: https://github.com/fastlane/fastlane/tree/master/gym#export-options

よくわかってないのだが、xcodebuildでのアーカイブで出力されるplistをエクスポートの際に使用するようなのだが、それが正しくないらしい。スタックオーバーフローとか見てると、xcode上でエクスポートする際に作られるExportOptions.plistを使うと良いらしい。これもGitで管理するようにしてFastfileで指定することで解決した。gymのexport_optionsで指定可能でした。

実装

証明書とプロビジョニングプロファイルをCircleCI2に登録
$ base64 -i xxxxx.p12 | pbcopy
$ base64 -i xxxxx. mobileprovision | pbcopy

こちらのページに細かく書かれている(というかまんま)
http://developabout0309.blogspot.com/2018/05/circleci20fastlanefabric_1.html

今回は以下の変数名で登録した
CERTIFICATES : pm12ファイル
PROVISIONING_PROFILES : プロビジョニングプロファイル
DEPLOYGATE_USER : デプロイゲートのアカウント
DEPLOYGATE_API_KEY : デプロイゲートのAPIキー

.circleci/config.yml
version: 2
jobs:
  build:
    macos:
      xcode: "9.2.0"
    shell: /bin/bash --login
    working_directory: ~/workspace
    steps:
      - checkout
      - run:
          name: install ionic
          command: |
            # Ionicインストール
            npm install -g cordova@6.5.0
            npm install -g ionic@2.2.3                                                                                                                                                                             
            npm install                                                                                                                                                                                            
                                                                                                                                                                                                                   
            # 最新のcordova-iosでプロジェクト作成                                                                                                                                                                  
            ionic platform rm ios                                                                                                                                                                                  
            ionic platform add ios@4.5.4                                                                                                                                                                           
            ionic prepare                                                                                                                                                                                          
      - run:
          name: build and upload deploygate
          command: |
            # stagingブランチの場合、デプロイゲートへアップロード
            if [ "${CIRCLE_BRANCH}" == "staging" ]; then                                                                                                                                                           
              # 証明書をデコード
              base64 -D -o circleci_demo.p12 <<< $CERTIFICATES                                                                                                                                                     
                                                                                                                                                                                                                   
              # プロビジョニングプロファイルのインストール                                                                                                                                                         
              mkdir -pv ~/Library/MobileDevice/Provisioning\ Profiles/                                                                                                                                             
              base64 -D -o ~/Library/MobileDevice/Provisioning\ Profiles/CircleCI_Adhoc.mobileprovision <<< $PROVISIONING_PROFILES                                                                                 
                                                                                                                                                                                                                   
              # xcodeprojファイルをチェックアウト                                                                                                                                                                  
              git checkout platforms/ios/circleci_demo.xcodeproj/project.pbxproj                                                                                                                                   
                                                                                                                                                                                                                   
              # AdHocビルド                                                                                                                                                                                        
              fastlane ios build                                                                                                                                                                                   
                                                                                                                                                                                                                   
              # デプロイゲートへアップロード                                                                                                                                                                       
              fastlane ios upload_deploygate                                                                                                                                                                       
                                                                                                                                                                                                                   
            # masterブランチの場合、AppStoreへアップロード                                                                                                                                                         
            elif [ "${CIRCLE_BRANCH}" == "master" ]; then                                                                                                                                                          
            fi                                                                                                                                                                                                     
fastlane/Fastfile
platform :ios do
  desc "Build for iOS"
  lane :build do
    if is_ci?
      setup_circle_ci
      import_certificate(
        keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
        keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
        certificate_path: 'circleci_demo.p12',
        certificate_password: ''
      )
    end
    update_app_identifier(
      xcodeproj: "platforms/ios/circleci_demo.xcodeproj",
      plist_path: "circleci_demo/circleci_demo-Info.plist",
      app_identifier: 'ciecleci.test'
    )
    disable_automatic_code_signing(
      path: "platforms/ios/circleci_demo.xcodeproj"
    )
    gym(
      scheme: 'circleci_demo',
      workspace: 'platforms/ios/circleci_demo.xcworkspace',
      configuration: 'Debug',
      export_method: "ad-hoc",
      clean: true,
      verbose: true,
      output_directory: "build",
      output_name: "circleci_demo",
      xcargs: "OTHER_SWIFT_FLAGS='$(inherited) -DSTGING' PROVISIONING_PROFILE_SPECIFIER='CircleCI Adhoc' CODE_SIGN_IDENTITY='iPhone Distribution'",
      export_xcargs: "-allowProvisioningUpdates",
      export_options: "fastlane/ExportOptions.plist"
    )
  end
  desc "Upload To Deploygate"
  lane :upload_deploygate do
    deploygate(
      api_token: ENV["DEPLOYGATE_API_KEY"],
      user: ENV["DEPLOYGATE_USER"],
      ipa: "build/circleci_demo.ipa"
    )
    sh "rm -rf ../build"
  end
end

こんなもんかな。以上です。