Xcode Cloud + Tophat で iOS PR レビューを効率化
はじめに
iOS アプリの PR レビューでは実際の動作確認が重要です。しかしレビュアーが毎回ブランチを切り替えてビルドするのは負担になります。
ビルド後に生成される .app ファイルをレビュアーに共有できればこの手間を省けます。最も手軽な方法は GitHub の PR コメントへの直接添付ですが、PR コメントには 25MB の上限があります。.app ファイルは依存ライブラリや画像リソースを含むと容易に 25MB を超えてしまうため添付できません。
TestFlight には 1 日あたりのアップロード回数に Rate Limit があるため頻繁な PR レビューには向いていません。
Tophat を使うことで解決できることを知り、Cloudflare R2 と組み合わせてワンクリックでアプリをインストールできる仕組みを作ってみました。
アーキテクチャ
フローは次のようになります。
- GitHub で PR を作成すると Xcode Cloud がトリガーされます
- Xcode Cloud でビルドした .app を .zip に圧縮します
- .zip ファイルを Cloudflare R2 にアップロードします
- GitHub PR コメントに Tophat リンクを自動投稿します
- レビュアーがリンクをクリックすると Tophat が R2 から .zip を取得します
- Tophat が自動でシミュレーターへインストールしてアプリを起動します
実装方法
1. Cloudflare R2 バケット作成
R2 コンソールでバケットを作成して R2.dev サブドメインを有効化します。これにより公開アクセス可能な URL が発行されます。
Xcode Cloud に以下の環境変数を設定します。
R2_BUCKET_NAME=todomemo-pr-builds
R2_ENDPOINT_URL=https://xxxxxxxx.r2.cloudflarestorage.com
R2_PUBLIC_URL=https://pub-xxxxxxxx.r2.dev
AWS_ACCESS_KEY_ID=xxxxx
AWS_SECRET_ACCESS_KEY=xxxxx
GITHUB_TOKEN=ghp_xxxxx
R2_PUBLIC_URL は R2 バケットの設定で「R2.dev サブドメインを許可」を有効化すると発行される公開 URL です。バケット名は含めずに https://pub-xxx.r2.dev の形式で設定します。スクリプト内で /pr-builds/... と結合されます。
2. Xcode Cloud ビルドスクリプト
ci_scripts/ci_post_xcodebuild.sh を作成します。
Tophat は .zip ファイルからのインストールに対応しているため、.app を zip 化してアップロードします。
#!/bin/bash
set -e
# JSON エスケープ関数
function json_escape() {
local string="$1"
string="${string//\\/\\\\}"
string="${string//\"/\\\"}"
string="${string//$'\n'/\\n}"
echo "$string"
}
# エラーハンドリング
function error_exit {
echo "❌ $1" >&2
exit 1
}
# 必須環境変数チェック
for var in R2_BUCKET_NAME R2_ENDPOINT_URL R2_PUBLIC_URL AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY GITHUB_TOKEN; do
[[ -z ${!var} ]] && error_exit "${var} が未設定"
done
# .app を検索(Debug ビルド)
APP_PATH=$(find "$CI_DERIVED_DATA_PATH" -name "*.app" -type d \
-path "*/Build/Products/Debug-*/*.app" \
-print -quit 2>/dev/null)
[[ -z $APP_PATH ]] && error_exit ".app が見つかりません"
# AWS CLI インストール
brew install awscli
# .app を zip 化
APP_NAME=$(basename "$APP_PATH" .app)
ZIP_NAME="${APP_NAME}.app.zip"
ZIP_PATH="${CI_DERIVED_DATA_PATH}/${ZIP_NAME}"
cd "$(dirname "$APP_PATH")"
zip -rq "$ZIP_PATH" "$(basename "$APP_PATH")"
# R2 アップロード
aws s3 cp "$ZIP_PATH" "s3://${R2_BUCKET_NAME}/pr-builds/pr-${CI_PULL_REQUEST_NUMBER}/${ZIP_NAME}" \
--endpoint-url "${R2_ENDPOINT_URL}" \
--region auto || error_exit "R2 アップロード失敗"
# URL 生成
PUBLIC_URL="${R2_PUBLIC_URL}/pr-builds/pr-${CI_PULL_REQUEST_NUMBER}/${ZIP_NAME}"
TOPHAT_URL="http://localhost:29070/install/http?url=${PUBLIC_URL}&platform=ios&destination=simulator"
# PR コメント投稿
COMMENT_BODY=$(cat <<EOF
## 📱 ビルド完了
Xcode Cloud でビルドが完了しました。
### 🚀 ワンクリックインストール(Tophat 必須)
<a href="${TOPHAT_URL}">シミュレーターで起動</a>
> **注意**: このリンクを使用するには Tophat が必要です。
>
> 初回のみ: [Tophat をダウンロード](https://github.com/Shopify/tophat/releases)(macOS 15+ 必要)
EOF
)
ESCAPED_COMMENT=$(json_escape "$COMMENT_BODY")
curl -s -X POST \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${CI_PULL_REQUEST_TARGET_REPO}/issues/${CI_PULL_REQUEST_NUMBER}/comments" \
-d "{\"body\":\"${ESCAPED_COMMENT}\"}" || error_exit "コメント投稿失敗"
3. Tophat セットアップ
Tophat は macOS 15 以降で動作します。Tophat Releases から最新版をダウンロードしてインストールします。
Tophat は iOS だけでなく Android にも対応しています。
使い方は次のようになります。
- Tophat を起動します(
localhost:29070でサーバー起動) - PR コメントのリンクをクリックします
- .zip がダウンロードされシミュレーターへインストールされアプリが起動します
4. GitHub の制約対応
GitHub はセキュリティ上 tophat:// のようなカスタム URL スキームをサニタイズします。
そのため Tophat の HTTP サーバー(localhost:29070)を利用します。
# GitHub で動作する形式
http://localhost:29070/install/http?url=...
まとめ
Xcode Cloud、Cloudflare R2、Tophat の組み合わせで PR レビュー時の .app 配布を自動化できました。
GitHub の 25MB 制限を回避しつつレビュアーはリンクをクリックするだけでアプリを試せるようになりました。PR レビューの速度が大きく向上してとても便利です。