Bitriseワークフローの中で
AppStoreConnectからdSYMをダウンロードするには
いくつかの課題があります。
この記事では、それら課題に対しての私なりのアプローチを書きます。
前提として、fastlaneを主に利用します。
fastlaneを利用したdSYMのダウンロード
このActionでdSYMのダウンロードが可能です。
download_dsyms
2ファクタ認証を解決する
Bitriseで2ファクタ認証を解決する方法が2つあります。
- FASTLANE_SESSIONを利用するセッションベース
- API Keyを利用したAppStoreConnect API
AppStoreConnect APIはdSYMダウンロードの機能を提供していません。
よって、fastlaneの download_dsyms
を利用する際はセッションベースの方法で2ファクタ認証を解決するしかありません。
bitcode再コンパイルを待たなければならない
ipaのアップロードと同時にアプリ審査をする場合
fastlaneのAction deliver を利用し、パラメータ submit_for_review
をtrueにします。
こうすることで deliver Actionが bitcodeの再コンパイルを待機し、完了後にアプリ審査へサブミットされます。
deliver の直後にdownload_dsymsを呼び出せばdSYMをダウンロードすることが可能です。
ipaのアップロードと同時にアプリ審査をしない場合
このケースが最も事態が複雑です。
パラメータwait_for_dsym_processing
は絶対ではない
download_dsyms Actionにも再コンパイルを待機する機能があります。
パラメータ wait_for_dsym_processing
がそれにあたります。
下の画像のリストにアップロードしたバージョンが表示されると、このwait_for_dsym_processing
は有効に作用します。
しかし、ipaをアップロードしてからこのリストに表示されるまで若干のラグがあるため
アップロード直後にdownload_dsyms Actionを実行しても、再コンパイルを待機してくれない場合があります。
しかも、この場合はfastlaneは正常終了と判断します。
(bitcodeなしビルドのケースを考慮してのこと。とfastlaneのコードにコメントが書かれています)
そのため、dSYMダウンロードの成否は dSYMファイル (zipファイル)の有無で判定する のが確実です。
この時、download_dsymsのパラメータ build_number
version
は必ず指定する必要があります。
download_dsymsは、build_number
とversion
を指定しなければ、すべてのバージョンのdSYMをダウンロードしてしまうからです。
また、事前にclean_build_artifactsを利用し、Bitriseでビルドした際に出力されるdSYMファイルを削除しておく必要もあるので注意が必要です。
再コンパイル待ちワークフローが長時間bitriseを占有してしまう。
再コンパイル完了まで、数十分かかることもあります。
- Bitriseの契約プランのビルド時間の上限を迎える
- 他のワークフローをブロックしてしまう
可能性があります。
そこで、dSYMファイルのダウンロードに失敗したら (ファイルがなかったら) Bitriseのワークフローを一旦終了し、新たにBitriseにビルドを要求させます。
download_dsyms に
wait_timeout
というパラメータがあります。デフォルトは5分です。
5分経過後、dSYMがダウンロードできなくともfastlane
は正常終了します。
終了後、dSYMファイル(zipファイル)の有無を判定し、ファイルがなければbitriseのRestful APIを利用して、再びdSYMをダウンロードするビルドを要求します。
こうすることで、長時間bitriseを占有する問題を回避できます。
この時、build_number
にBitriseの Set Xcode Project Build Number
を利用していると注意が必要です。
アプリをビルドした時のbuild_number
と、dSYMダウンロード時のbuild_number
が異なってしまうからです。
これは、BitriseAPIをリクエストする際にbuild_number
をパラメータにすることで解決できます。
サンプル
lane :deploy_store do
match(type: "appstore", readonly: true)
gym()
deliver()
clean_build_artifacts() # ⚠️これを入れないとCIビルド時のdSYMをアップロードしてしまう⚠️
upload_dsym()
end
lane :upload_dsym do
version = get_version_number(
xcodeproj: "DeploySample.xcodeproj",
target: "DeploySample"
)
# `Set Xcode Project Build Number`を使わない場合
# build_number = get_build_number(
# xcodeproj: "DeploySample.xcodeproj"
# )
build_number = ENV["DSYM_BUILD_NUMBER"] || ENV["XCODE_BUNDLE_VERSION"]
puts "build_number = #{build_number}"
download_dsyms(
app_identifier: "アプリのBundleID",
wait_for_dsym_processing: true,
wait_timeout: 180, # 再コンパイルを3分待つ
version: version,
build_number: build_number
)
# dSYMファイルの有無を判定
if Dir.glob("#{ENV['PWD']}/*.dSYM.zip").empty?
puts "No dsym files"
trigger_upload_dsym_workflow()
next
end
upload_symbols_to_crashlytics(
gsp_path: "./DeploySample/GoogleService-Info.plist",
binary_path: "./Pods/FirebaseCrashlytics/upload-symbols",
)
end
lane :trigger_upload_dsym_workflow do
uri = URI.parse("https://api.bitrise.io/v0.1/apps/#{ENV["BITRISE_APP_SLUG"]}/builds")
params = {
build_params: {
branch: ENV["BITRISE_GIT_BRANCH"],
commit_hash: ENV["BITRISE_GIT_COMMIT"],
workflow_id: "upload_dsym", # `fastlane upload_dsym` を呼ぶだけのworkflowを事前に用意する。このパラメータはそのworkflowの名前。
environments: [
{
# `build_number`を環境変数に渡す
is_expand: true,
mapped_to: "DSYM_BUILD_NUMBER",
value: ENV["DSYM_BUILD_NUMBER"] || ENV["XCODE_BUNDLE_VERSION"]
}
],
},
hook_info: {
type: "bitrise"
}
}
headers = {
"Content-Type" => "application/json",
"Authorization" => ENV["BITRISE_API_ACCESS_TOKEN"]
}
uri.query = URI.encode_www_form(params)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.request_uri)
request["Content-Type"] = "application/json"
request["Authorization"] = ENV["BITRISE_API_ACCESS_TOKEN"]
request.body = params.to_json
response = http.request(request)
response.value
end