Xcode 8 にしたら、いままで使っていた lane でビルドできなくなった。 Xcode 8 から証明書の管理が自動になったため、sigh で取得した証明書が gym で使えずにエラーになっていたのが原因だった。 解決したので、メモを残しておく :memo:

概要

  • Xcode 8 にアップデートすると既存の fastlane ビルドパイプラインが壊れる
  • Xcode 8 から、証明書が自動取得されるようになったため、sigh と競合する
  • Xcode プロジェクトの設定を変更して、証明書の指定を手動にすれば良い

環境

  • Xcode Version 8.1 (8B62)
  • fastlane 1.103.0
  • Unity 5.4.2f2

既存の fastlane ビルドパイプラインが壊れる問題

証明書の手動取得

fastlane の issue によると、Xcode プロジェクトの設定を変更すれば良い。 通常の iOS アプリ開発であれば、.xcodeproj.xcworkspace をリポジトリにコミットするので、この方法で問題ない。

しかし、Unity の場合はビルドの度に Unity が Xcode プロジェクトを書き出す形になる。 つまり、書き出された Xcode プロジェクトの設定をビルドの度に変更する必要がある。これは面倒くさい。

案 1: テンプレートの設定を変更する

Unity が Xcode プロジェクトを書き出すための、テンプレートがある(はず)ので、その設定を変更してしまう。

どうやらテンプレートの .xcodeproj.pbxproj ファイルみたいなものはなくて、 /Applications/Unity/Unity.app/Contents/Tools/nodejs/lib/node_modules/npm/node_modules/node-gyp/gyp/pylib/gyp/generator/xcode.py で生成しているっぽい。

ということで、テンプレートの設定を変更する方針は却下する。

案 2: 書き出されたプロジェクトの設定を変更する

Unity-iPhone.xcodeporj/project.pbxproj を変更して、証明書の指定を手動で行うように設定する。 ここで問題になるのが、UnityEditor.Xcode.PBXProject の API が、TargetAttributes の設定には対応していないことである :angel:

ということで、.pbxproj を自分で書き換えることにする。

書き換える場所

PBXProject section > Project object > attributes > TargetAttributes > Unity-iPhone ターゲット の中に ProvisioningStyle = Manual; を設定すれば良い。

:bulb: Xcode のプロジェクト設定を自動化したい場合、diff を取ると良い(あたりまえ)

  1. .pbxprojcp で退避する
  2. Xcode の GUI でぽちぽち設定する
  3. 更新された .pbxporj と退避しておいたものの diff を取る

書き換える

fastlane で使っていくので、Ruby で書き換える。CI でクリーンビルドすることを想定しているので、冪等性は考慮しないで、雑に書き換えている。

pbxproj_path = File.join(Dir.pwd, xcodeproj_path, 'project.pbxproj')
pbxproj = File.read(pbxproj_path)

target_id = /([0-9-A-Z]+) \/\* Unity-iPhone \*\/,/.match(pbxproj)[1]
target_attributes = "#{target_id} = {\n\tProvisioningStyle = Manual;\n};"

attributes = /\t+attributes = {\n\t+TargetAttributes = {\n((.|\n)*)\n\t+};\n\t+};/.match(pbxproj)

File.write(pbxproj_path, pbxproj.sub!(attributes[1], "#{target_attributes}\n#{attributes[1]}"))

以上を fastlane action にしたものがこちら :point_down:

flunity/manualize.rb at master · thedoritos/flunity

lane から action を使うときは、以下のような感じになる。

manualize(
  project: './path/to/Unity-iPhone.xcodeproj',
  style: 'Manual'
)

gym でビルドするために

ProvisioningStyle = Manual; に設定した場合、gym の引数に xcargs を渡す必要がある。

key value
DEVELOPMENT_TEAM 証明書の Team ID
CODE_SIGN_IDENTITY 'iPhone Distribution'
PROVISIONING_PROFILE_SPECIFIER 証明書の名前

このうち、DEVELOPMENT_TEAM については、 Team ID を AppFile で設定していることが多いと思うので、そこから取得すれば良い。

CredentialsManager::AppfileConfig.try_fetch_value(:team_id)

ちょっと面倒なのは PROVISIONING_PROFILE_SPECIFIER の方で、Xcode 8 からは証明書の指定を、 Bundle ID や証明書の UUID ではなく、証明書の名前で行うことになった。

fastlane のドキュメントを読んでも、証明書の名前の取得方法が分からなかったので、環境変数で渡して ENV で読むことにした。

:pray: 良い方法をご存知でしたら、ご教示くださると大変よろこびます。

まとめ・感想

  • Xcode 8 から、証明書が自動取得されるようになったため、sigh と競合するのを解決した
  • Xcode プロジェクトの設定を変更して、証明書の指定を手動にした
    • Unity を使う場合 .xcodeproj はビルドの度に生成されるので、設定を Git リポジトリにコミットしておくことができない(おそらくできないこともないがしたくない)
    • ビルドの度に毎回手動で設定を変更するのはつらいため、スクリプトで自動化した
    • スクリプトを fastlane action 化して、lane から簡単に使えるようにした

ビルドパイプラインちょくちょく壊れるのまじでつらい。 エコシステム(Unity, Xocde, Android Studio, CocoaPods, Carthage, Gradle など)に複数依存していると、どこかが変更されるたびに壊れるからつらいという構図になっている気がする。