提出ボタンを押してしばらくすると、App Store タブに赤い表示が出ます。ログを開くと、数百行の英語が滝のように流れていました。
Rork のドキュメントには「ログ全体をコピーして Rork に貼り、ビルドを直してもらう」とあります。言われたとおりにやりました。直りました、とはいきませんでした。二度目も、三度目も、末尾に同じ赤い行が出てきます。
最初にお伝えしておきたいのは、ここで貼り直す回数を増やしても、あまり効果がないということです。ドキュメントは「たいてい 1〜2 回で解決する」と書いていますので、3 回目に入ったら、貼る前に自分の目でログのどこを見ているのかを確かめるほうが早いのだと思います。
末尾の赤い行は、結果であって原因ではありません
長いビルドログは、最初のエラーが次のエラーを連れてきます。パッケージが解決できなければ、そのあとのコンパイルも、アーカイブも、アップロードも続けて失敗します。末尾に並ぶ「BUILD FAILED」や「UPLOAD FAILED」は、その連鎖の最後の一行にすぎません。
私は長いログを前にすると、つい下から読んでしまう癖がありました。画面の末尾がいちばん目に入るからです。ところが原因の行は、たいていもっと上、最初に「error」と書かれた場所にあります。
なお、公式の提出エラーのページには、エラーメッセージの文字列そのものは載っていません。載っているのは原因の分類と、直すときの流れです。ですからこの記事のスクリプトも、特定の文言を当てにせず、「error」「FAILED」「ITMS-」のような一般的な目印だけで動くようにしています。
最初のエラーと前後だけを取り出す
やることは単純です。ログを上から読み、最初にエラーらしい行が出た場所と、その前後だけを表示します。集計行の「0 errors」や warning は飛ばします。
#!/usr/bin/env python3
"""提出ログの「最初のエラー」と、その前後だけを取り出す。
使い方: python3 log_first_error.py build.log
"""
import re
import sys
# 「エラーの始まり」を示す語。0 errors のような集計行は除く
START = re.compile(r"(error:|\bERROR\b|\bFAILED\b|ITMS-\d+|Could not resolve|Execution failed)")
SKIP = re.compile(r"(\b0 errors?\b|no errors?|warning:)", re.I)
# Rork のドキュメントが挙げている4つの原因に対応する目印
BUCKETS = {
"権限の説明文が足りない": re.compile(r"(UsageDescription|purpose string|ITMS-90683)", re.I),
"パッケージの解決に失敗": re.compile(r"(Package\.resolved|dependenc(y|ies).*resolve|resolve.*package|version solving)", re.I),
"Capability が未承認": re.compile(r"(capabilit|entitlement|provisioning profile)", re.I),
"ID・バージョンの不一致": re.compile(r"(bundle identifier|CFBundle(Short)?Version|build number|already been used)", re.I),
}
def main(path: str) -> int:
lines = open(path, encoding="utf-8", errors="replace").read().splitlines()
first = next((i for i, ln in enumerate(lines)
if START.search(ln) and not SKIP.search(ln)), None)
if first is None:
print("エラーらしい行が見つかりません。ログが途中で切れていないか確認してください。")
return 1
lo, hi = max(0, first - 5), min(len(lines), first + 11)
print(f"最初のエラー: {first + 1} 行目 / 全 {len(lines)} 行")
print("-" * 40)
for n in range(lo, hi):
mark = ">>" if n == first else " "
print(f"{mark} {n + 1:>5}: {lines[n]}")
print("-" * 40)
window = "\n".join(lines[lo:hi])
hits = [name for name, rx in BUCKETS.items() if rx.search(window)]
print("目印に当たった原因: " + (" / ".join(hits) if hits else "なし(上の行を直接読む)"))
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv[1]) if len(sys.argv) == 2 else print(__doc__) or 2)使い方は、App Store タブのログをテキストファイルに保存し、python3 log_first_error.py build.log と打つだけです。Python が入っていない方は、ログをエディタで開き、「error」で検索して最初に当たった行を読むだけでも同じ効果があります。
なぜ前に 5 行、後ろに 10 行なのか。エラーの直前には「何をしようとしていたか」が、直後には「何が足りなかったか」が書かれることが多いからです。この幅は私の好みですので、ログの密度に合わせて変えていただいて構いません。
模擬ログで動かした結果
実際の Rork のログではなく、私が自分で書いた模擬ログ(約 300 行)で動作を確かめました。Info.plist の権限説明が足りない場合を想定したものです。
最初のエラー: 244 行目 / 全 299 行
----------------------------------------
242: [0242] Archive succeeded
243: [0243] Uploading to App Store Connect...
>> 244: [0244] ERROR: Validation failed (409)
245: [0245] ITMS-90683: Missing purpose string in Info.plist. ...
246: [0246] NSCameraUsageDescription is not set.
247: [0247] ** UPLOAD FAILED **
----------------------------------------
目印に当たった原因: 権限の説明文が足りない末尾の「UPLOAD FAILED」だけを見ていたら、アーカイブが通っていることにも気づけません。最初のエラーから読むと、ビルドは成功していて、アップロードの検証で止まっていると分かります。それだけで、AI に頼む内容が「ビルドを直して」から「Info.plist にカメラの利用目的を足して」に変わります。
パッケージの競合を想定した別の模擬ログでは、101 行目の「Dependencies could not be resolved」が最初のエラーとして出ました。エラーらしい行がひとつもない短いログでは、「途中で切れていないか確認してください」と返します。この最後の挙動は、ログのコピーが途中で終わっている場合に気づくための保険です。
4 つの原因と、見る場所の対応表
Rork のドキュメントがビルドエラーの代表的な原因として挙げているのは、次の 4 つです。ログの最初のエラーがどれに近いかで、確認する場所が決まります。
| 原因 | ログに出やすい目印 | 最初に見る場所 |
|---|---|---|
| 権限を説明するキーが Info.plist にない | purpose string、UsageDescription、ITMS-90683 | アプリが使う機能(カメラ、写真、位置情報など)と Info.plist のキーの対応 |
| Swift パッケージの解決失敗、またはバージョンの競合 | resolve、dependencies、version | 競合している 2 つのパッケージ名と、それぞれが求めるバージョン |
| Apple Developer アカウントで Capability が有効でない | capability、entitlement、provisioning profile | Apple Developer の Identifiers で、該当アプリの Capability の欄 |
| Bundle ID やバージョンが App Store Connect と合わない | bundle identifier、CFBundleVersion、build number | App Store Connect に登録した ID と、アプリ側の設定の見比べ |
このほかに、公式ページは「同じバージョンで審査中のビルドがすでにある」場合も挙げています。Apple は 1 つのバージョンにつき審査中のビルドを 1 つしか認めないため、そのビルドを差し替えるか、バージョン番号を上げてから再提出する流れです。ログにエラーが出ていなくても、アプリが App Store Connect に現れない原因になります。
権限の説明文の書き方そのものは、Apple の自動解析は、purpose string が書いてあるかではなく何に使うかを読んでいますで詳しく書きました。提出が止まる層の見分け方は、Rork Max の2クリック提出が止まったとき、どの層で詰まったかを自分で見分けるも併せてご覧ください。
AI に渡し直すときの形
原因の見当がついたら、Rork に貼る内容を整えます。ここで気をつけたいのは、ログ全体を貼るという公式の勧めは崩さないことです。部分的なログは根本原因を隠すことがある、と公式も書いています。ですから私は「全体のログ」の前に、短い前置きを一つ足す形にしています。
App Store への提出が失敗しました。ログ全体を下に貼ります。
最初のエラーは 244 行目の ITMS-90683 です(purpose string の不足)。
直してほしいのは、この最初のエラーに関係する部分だけです。
ほかの警告は、いまは触らないでください。
〈ここにログ全体〉前置きに入れるのは、最初のエラーの行番号、見当をつけた原因、直してほしい範囲の三つです。AI は、ログのどこから読み始めればよいかが分かると、末尾の連鎖したエラーに引きずられにくくなります。
このスクリプトで分からないこと
正直に書いておきます。目印は、よく見かける語を拾っているだけです。原因が 4 つのどれにも当たらなければ、「目印に当たった原因」は「なし」と出ます。そのときは、表示された前後の行を、自分の目で一行ずつ読むしかありません。
また、模擬ログで確かめたのはスクリプトの動作であって、Rork の実際の提出ログでの的中率ではありません。私自身、Rork 経由の提出で、ここに書いたすべての原因に当たったわけではないのです。
貼り直す前に、最初のエラーを一行だけ声に出して読んでから貼ります。 この線引きだけは、疲れて早く終わらせたい夜ほど守るようにしています。
まずは次に提出が止まったとき、ログをファイルに保存して、最初の「error」を探すところから始めていただければと思います。