壁紙アプリの新しい一本を、EAS で初めてビルドしようとした夜のことです。ターミナルは「Compressing project files」のあたりで長く止まり、やがて赤い一行が出ました。
Project archive is too big. Maximum allowed size is 2.0 GB.
コードは数百ファイルしかありません。それなのに 2 GB という数字が出てくる理由が、すぐには分かりませんでした。胃のあたりが重くなったのを覚えております。
最初にお伝えしたいのは、この失敗は「コードが大きい」からではなく、「送るつもりのなかったものが一緒に入っている」から起きるという点です。原因は、ほとんどの場合プロジェクトの外れのほうにあります。
上限は二段階あります
Expo のドキュメント(Project upload の節)によると、EAS CLI は手元のプロジェクトを一つのアーカイブに固めてから送ります。サイズの扱いは次のとおりです。
| アーカイブのサイズ | 起きること |
|---|---|
| 150 MiB 超 | EAS CLI が警告を出す。ビルドは続く |
| 2 GiB 超 | アップロードが失敗し、上の「too big」が出る |
警告の段階で気づけば、まだ引き返せます。私は最初の警告を「ネットワークが遅いだけだろう」と読み流しました。ここが分かれ道でした。
なお node_modules は、そもそもアーカイブに入りません。ビルド機が依存を入れ直すためです。ですから「依存が重い」という線は、先に消しておいて大丈夫です。
送る前に、大きい順に並べます
アップロードを何度も試すと、そのたびに待ち時間が消えます。私は手元で、同梱されるものを大きい順に並べるようにしました。
次のスクリプトは、git の照合器に無視ルールを1枚だけ読ませて、実際に入るファイルを数えます。
#!/bin/sh
# 何が重いのかを、送る前に確かめるための手元用スクリプトです
RULES=.gitignore
[ -f .easignore ] && RULES=.easignore # .easignore があれば、.gitignore の代わりに読まれます
echo "適用されるルール: $RULES"
# -o は未追跡、-c は追跡中のファイル。--exclude-from は指定した1枚だけを無視ルールとして読みます
echo "--- 大きい順(KB) ---"
git ls-files -o -c --exclude-from="$RULES" -z | xargs -0 -r du -k | sort -rn | head -10
echo "--- 合計(バイト) ---"
git ls-files -o -c --exclude-from="$RULES" -z | xargs -0 -r du -cb | tail -1
echo "--- ディレクトリごとの件数 ---"
git ls-files -o -c --exclude-from="$RULES" | awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn | head使うのは --exclude-from です。この指定のおかげで、ワークツリーにある別の .gitignore に引きずられず、「いまの設定でアーカイブに入るもの」だけが見えます。
試しに、.gitignore に node_modules/ だけ書いた小さな検証用の箱で動かしました。assets/original/ に 3 MB と 0.5 MB の画像を置くと、先頭に並んだのはその二つでした。つまり、重いものは上から二、三行を見れば分かります。
重さの正体は、たいてい三種類です
実際に私が見つけたのは、次のようなものでした。どれも「git には載せていないつもりだった」ものばかりです。
- 原寸の素材置き場(
assets/original/のような、配信前の高解像度画像や動画) - ビルドの残り物(
android/app/build/、ios/Pods/など、ローカルで一度ビルドしたときの生成物) - 書き出しの途中ファイル(zip、
.psd、録画した動画)
壁紙アプリは、素材そのものが商品です。原寸の画像をプロジェクトの中に置く癖があって、これが一番効きました。アプリに入れるのは縮小後の数十枚なのに、置き場には元の画像が全部あったのです。
「コミットしていないのに上がる」理由
ここで誤解しやすい点があります。既定の動き(コミットを必須にしない設定)では、EAS CLI は浅い git クローンを作ったうえで、手元の作業ディレクトリをそこへ写します。未コミットのファイルも、未追跡のファイルも、無視されていなければ入ります。
つまり「git に add していないから上がらない」とは言えません。原寸の素材を未追跡のまま置いていても、.gitignore か .easignore で外さないかぎり送られます。eas.json の cli.requireCommit を true にすると、直近のコミットを基にしたアーカイブになりますが、それでも .easignore は効きます。
.gitignore に書いたのに入っているとき
「無視しているはずなのに入る」場合は、ルールの当たり方を確かめます。
# どのルールが、そのパスに当たっているか(または当たっていないか)を表示します
git check-ignore -v assets/original/wall_0001.png
echo "exit=$?" # 0 なら無視される、1 なら無視されない終了コードが 1 なら、そのファイルは無視されていません。たとえば .gitignore に assets/original と書いたつもりが、ファイルの置き場所は app/assets/original だった、ということが起こります。先頭に / を付けるかどうかでも当たり方が変わりますので、パスを一文字ずつ見直す価値があります。
.easignore に書くときの線引き
重いものが分かったら、.easignore で外します。ただ、ここには落とし穴があります。.easignore は .gitignore に足されるのではなく、存在した時点で入れ替わります。書き忘れた node_modules などは、そのまま上がってしまいます(詳しくは .easignore を置くと .gitignore は読まれなくなります に書きました)。
ですから、.gitignore の中身を写してから、重いものを足します。
# .gitignore の内容をまず写します
node_modules/
.expo/
dist/
.env
# 今回の原因だった、ビルドに要らないものを足します
assets/original/
android/app/build/
ios/Pods/
*.psd
*.zip
書いたら、さきほどのスクリプトをもう一度走らせて、合計が下がっていることを確かめます。.env のように「送りたくないもの」も、ここで一緒に外しておくと安心です。
ビルドが重いときは、コードを疑う前に、同梱される側を数えます。 この順番だけは、焦っているときほど守るようにしております。
次にすること
まず、いまのプロジェクトで上のスクリプトを一度走らせていただければと思います。合計が 150 MiB を超えていたら、次のビルドの前に .easignore を見直してみる価値があります。何も出なければ、それが分かっただけでも収穫です。