なぜ Mac でセルフホスト CircleCI ランナーを使うのか?
CircleCI はクラウドベースの macOS 実行環境を提供していますが、それには相応のコストと制約が伴います。CircleCI は macOS ジョブをクレジットで課金し、リソースクラス m4pro.medium は 1 分あたり 200 クレジット(約$0.12)、m4pro.large は 400 クレジット(約$0.24)を消費します(2026年10月時点)。頻繁に iOS ビルドを実行するチームでは、これがすぐに月額$300〜$500以上に膨れ上がることがあります。
MyRemoteMac の専用 Mac Mini M4 上のセルフホストランナーなら、分単位の課金を完全になくせます。永続的なストレージ、ウォームキャッシュ、完全な root アクセスを備えた専用の Apple Silicon マシンを、月額$85からの定額で利用できます。以下に詳しい比較を示します。
| 項目 | MyRemoteMac セルフホスト | CircleCI クラウド macOS |
|---|---|---|
| コストモデル | 月額$85 定額(ビルド時間無制限) | m4pro.medium で 200 クレジット/分(約$0.12/分) |
| アーキテクチャ | 1:1 で割り当てられる物理 Apple M4 Mac | M4 Pro 仮想マシン(6 vCPU、28 GB) |
| ビルド速度 | 約 5 分(同じプロジェクト) | 約 14 分(中規模の iOS プロジェクト) |
| キャッシュの永続性 | ディスク上に永続(即座に利用可能) | 使い捨て(ジョブごとに復元が必要) |
| キュー待機時間 | 0 秒(専用ハードウェア) | 30 秒〜5 分(プランにより変動) |
| プライバシー / データ管理 | 専用マシン、完全な管理権限 | 共有インフラ |
| カスタムソフトウェア | 完全な root アクセス、任意のソフトウェア | プリインストールされたイメージのみ |
My Remote Mac の料金は USD・税抜です。
主なメリット: 永続的なセルフホストランナーでは、DerivedData、Swift Package Manager のキャッシュ、CocoaPods がビルド間で保持されます。これだけでも、毎回ゼロから始まる CircleCI の使い捨てクラウド macOS 環境と比べて、ビルド時間を 50〜70% 短縮できます。M4 チップの優れたシングルスレッド性能と組み合わせれば、iOS ビルドは劇的に高速になります。
前提条件
始める前に、次のものが揃っていることを確認してください。
- My Remote Mac の専用 Mac mini M4 ビルドサーバー(月額$85から、USD・税抜)
- 管理者権限を持つ CircleCI の組織(セルフホストランナーは Free を含むすべてのプランで利用でき、2026年10月時点で Free では同時に 5 タスクまで実行できます)
- Mac Mini への SSH アクセス(MyRemoteMac のサブスクリプションに付属します)
- Apple Developer アカウント(コード署名とプロビジョニングプロファイル用)
- YAML、CircleCI の設定、ターミナルコマンドの基本的な知識
ステップ1:Mac Mini に SSH 接続して依存関係をインストールする
まず、Mac Mini M4 に SSH で接続します。MyRemoteMac サーバーをセットアップした際に認証情報を受け取っているはずです。CircleCI ランナーを設定する前に、iOS ビルドに必要な Xcode とツールをインストールする必要があります。
SSH で接続する
# Connect to your Mac mini M4
ssh admin@your-server-ip
# Verify you're on Apple Silicon
uname -m
# Expected output: arm64
# Show the macOS version
sw_vers
Homebrew と Xcode Command Line Tools をインストールする
# Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Add Homebrew to PATH: zsh for your sessions, bash for CircleCI job steps
# (CircleCI runs macOS steps in a bash login shell)
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.bash_profile
eval "$(/opt/homebrew/bin/brew shellenv)"
# Install Xcode Command Line Tools
xcode-select --install
# Accept the license agreement
sudo xcodebuild -license accept
Xcode(フルバージョン)をインストールする
iOS ビルドには Xcode アプリケーションの完全版が必要です。2026年4月28日以降、App Store Connect は Xcode 26 以降でビルドしたものしか受け付けないため、ヘッドレスサーバーで最も速い xcodes を使って最新版をインストールします。
# Install the xcodes CLI tool
brew install xcodes
# Install the latest release of Xcode (asks for your Apple Account)
xcodes install --latest
# Show where it was installed, then make it the default Xcode
xcodes installed
# e.g. 27.0 (27A266a) /Applications/Xcode-27.0.0.app
sudo xcode-select -s /Applications/Xcode-27.0.0.app/Contents/Developer
# Verify installation
xcodebuild -version
iOS シミュレーターとビルドツールをインストールする
# Install the current iOS simulator runtime
xcodebuild -downloadPlatform iOS
# Verify simulator availability
xcrun simctl list runtimes
# Install additional build tools
brew install xcbeautify fastlane swiftlint
ステップ2:CircleCI ランナーエージェントをインストールする
CircleCI のマシンランナー 3 は Mac mini を CircleCI に接続し、ジョブを受け取って macOS ホスト上で直接実行します。macOS では、CircleCI は Homebrew パッケージでのインストールを推奨しています。これはコンテナランナー(Linux でのみ動作)とは異なります。
ジョブを実行するユーザーを決める
マシンランナー 3 は、起動した macOS ユーザーとして各ジョブを実行し、そのユーザーの Homebrew、キーチェーン、Xcode を使います。root ではなく、Homebrew を所有する管理者ユーザーでインストールして起動してください。Homebrew は root での実行を拒否します。
# Run the steps below as the user that will run CI jobs, not with sudo
whoami
# Homebrew must work for this user (prints /opt/homebrew)
brew --prefix
Homebrew でマシンランナー 3 をインストールする
# Add CircleCI's Homebrew tap and install machine runner 3
brew tap circleci-public/circleci
brew install circleci-runner
# Check that macOS accepts the binary notarized by CircleCI
spctl -a -vvv -t install "$(brew --prefix)/bin/circleci-runner"
# Expected: accepted, source=Notarized Developer ID,
# origin=Developer ID Application: Circle Internet Services Inc.
# Remove the quarantine attribute (asks for your macOS password)
sudo xattr -r -d com.apple.quarantine "$(brew --prefix)/bin/circleci-runner"
ランナーエージェントを設定する
Homebrew パッケージが設定ファイルを作成します。CircleCI の Web アプリで取得するランナー認証トークンが必要です(ステップ 3 で説明します)。今のところは、ランナー名を設定し、トークンは仮の値のままにしておきます。
# Open the configuration file created by the Homebrew package
nano "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
# Contents (the token comes in Step 3):
runner:
name: "mac-mini-m4-runner"
working_directory: "/Users/$USER/Library/com.circleci.runner/workdir"
cleanup_working_directory: true
api:
auth_token: "YOUR_RUNNER_TOKEN_HERE"
# The file will hold a secret: make it readable by you only
chmod 600 "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
ヘッドレス Mac 向けに launchd サービスを準備する
Homebrew パッケージは launchd エージェント com.circleci.runner もインストールします。エージェントは通常ユーザーのログイン時に起動します。SSH でしか接続しない Mac では、CircleCI のヘッドレス向け手順に従って /Library/LaunchAgents に移動し、再起動後も読み込まれるようにします。起動はトークンを設定した後、ステップ 3 で行います。
# The launchd agent installed by the Homebrew package
ls ~/Library/LaunchAgents/com.circleci.runner.plist
# Headless Mac reached over SSH only: move it to the per-user agents
# folder so it loads after a reboot (CircleCI's headless setup)
sudo mv ~/Library/LaunchAgents/com.circleci.runner.plist /Library/LaunchAgents/
重要: config.yaml に有効な認証トークンが入るまで、ランナーは接続されません。ステップ 3 でトークンを生成してファイルに追加してから、サービスを開始してください。
ステップ3:CircleCI ダッシュボードでランナーを設定する
次に、CircleCI の Web インターフェースでランナーを登録し、認証トークンを生成する必要があります。これにより、Mac Mini が CircleCI の組織に接続されます。
リソースクラスを作成する
CircleCI では、セルフホストランナーはリソースクラスごとに整理されます。リソースクラスは、.circleci/config.yml を特定のランナーの集合に対応付けるラベルです。
- CircleCI の Web アプリで、サイドバーの Runners を開きます(組織の管理者権限が必要です)
- 「Create Resource Class」をクリックします
- Namespace を組織名(例:
your-org)に設定します - リソースクラス名を分かりやすい名前(例:
mac-runner)に設定します - これにより、リソースクラス識別子
your-org/mac-runnerが作成されます
ランナー認証トークンを生成する
- 「Save and continue」をクリックします。CircleCI がリソースクラスを作成し、その認証トークンを表示します
- トークンはすぐにコピーして安全な場所に保管します(一度しか表示されません)
- 紛失した場合は、CircleCI CLI で新しいトークンを作成できます:
circleci runner token create your-org/mac-runner mac-mini-m4
トークンを追加してランナーを起動する
# SSH back into your Mac mini
ssh admin@your-server-ip
# Replace YOUR_RUNNER_TOKEN_HERE with the resource class token
nano "$HOME/Library/Preferences/com.circleci.runner/config.yaml"
# Start the service. Headless Mac (plist moved to /Library/LaunchAgents):
launchctl bootstrap user/$(id -u) /Library/LaunchAgents/com.circleci.runner.plist
launchctl enable user/$(id -u)/com.circleci.runner
launchctl kickstart -k user/$(id -u)/com.circleci.runner
# Or, from a desktop session (VNC), with the plist left in ~/Library/LaunchAgents:
# launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.circleci.runner.plist
# launchctl enable gui/$(id -u)/com.circleci.runner
# launchctl kickstart -k gui/$(id -u)/com.circleci.runner
# Verify the service, then read the runner log
launchctl print user/$(id -u)/com.circleci.runner
tail -20 "$HOME/Library/Logs/com.circleci.runner/runner.log"
接続されると、ランナーは CircleCI の Web アプリの Runners に表示され、circleci runner instance list your-org/mac-runner でも確認できます。60 秒以内に表示されない場合は、ログファイルでエラーメッセージを確認してください。
CircleCI CLI を使う(代替手段)
CircleCI CLI からランナーを管理することもできます。
# Install the CircleCI CLI
brew install circleci
# Authenticate with CircleCI
circleci setup
# Create the namespace (once per organization)
circleci namespace create your-org --org-id YOUR_ORGANIZATION_ID
# Create the resource class and print its token
circleci runner resource-class create your-org/mac-runner \
"Mac mini M4 runner" --generate-token
# List the runners connected to this resource class
circleci runner instance list your-org/mac-runner
ステップ4:iOS ビルド用の .circleci/config.yml を作成する
リポジトリのルートに .circleci/config.yml ファイルを作成します。標準的な CircleCI 設定との主な違いは、セルフホストランナーを対象とするために machine: true とカスタムの resource_class を使う点です。
version: 2.1
jobs:
build-and-test:
machine: true
resource_class: your-org/mac-runner
environment:
# Xcode for this job, without sudo (path from `xcodes installed`)
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
SCHEME: "MyApp"
# Any device listed by: xcrun simctl list devices available
DESTINATION: "platform=iOS Simulator,name=iPhone 17,OS=latest"
steps:
- checkout
- run:
name: Show the Xcode version
command: xcodebuild -version
# Caches live in $HOME, outside the job's working directory,
# so they survive cleanup_working_directory between jobs
- run:
name: Resolve Swift Package Dependencies
command: |
xcodebuild -resolvePackageDependencies \
-scheme "$SCHEME" \
-clonedSourcePackagesDirPath "$HOME/spm-cache"
- run:
name: Build the app
command: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "generic/platform=iOS Simulator" \
-clonedSourcePackagesDirPath "$HOME/spm-cache" \
-derivedDataPath "$HOME/DerivedData/$SCHEME" \
| xcbeautify
# store_test_results needs JUnit XML: xcbeautify writes test-results/junit.xml
- run:
name: Run unit tests
command: |
xcodebuild test \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-clonedSourcePackagesDirPath "$HOME/spm-cache" \
-derivedDataPath "$HOME/DerivedData/$SCHEME" \
-resultBundlePath TestResults.xcresult \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
- store_artifacts:
path: TestResults.xcresult
destination: test-results
workflows:
ios-pipeline:
jobs:
- build-and-test:
filters:
branches:
only:
- main
- develop
注意: resource_class: your-org/mac-runner の値は、CircleCI ダッシュボードで作成したリソースクラスと完全に一致している必要があります。一致していないと、ジョブは無期限にキューに残ります。
ステップ5:キャッシュと並列化で最適化する
セルフホストランナーの最大の利点の 1 つは、永続的なキャッシュです。ランナーのファイルシステムが永続的であるため、CircleCI のクラウドリソースのようにキャッシュをアップロード・ダウンロードする必要がありません。とはいえ、ビルド速度を最大化するための追加の最適化もあります。
永続的な DerivedData を活用する
ランナーのディスクはビルド間で永続化されるため、DerivedData はすでに自動的にキャッシュされています。一貫したパスを使用してください。
# In your config.yml steps, always use a consistent DerivedData path:
- run:
name: Build with persistent cache
command: |
xcodebuild build \
-scheme "$SCHEME" \
-destination "$DESTINATION" \
-derivedDataPath ~/DerivedData/"$SCHEME" \
-clonedSourcePackagesDirPath ~/spm-cache \
| xcbeautify
# Periodically clean old DerivedData on the runner (cron job):
# 0 3 * * 0 find ~/DerivedData -mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} +
ジョブ間でワークスペースを永続化する
CircleCI のワークスペースを使って、ビルド成果物をテストジョブに渡します。テストジョブは同じリソースクラスの別の Mac で実行されることもあります。
jobs:
build:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Build for testing
command: |
xcodebuild build-for-testing \
-scheme "MyApp" \
-destination "generic/platform=iOS Simulator" \
-derivedDataPath DerivedData \
| xcbeautify
# Pass only the built products, not the whole DerivedData
- persist_to_workspace:
root: .
paths:
- DerivedData/Build/Products
test:
machine: true
resource_class: your-org/mac-runner
steps:
- attach_workspace:
at: .
- run:
name: Run the tests built by the previous job
command: |
xcodebuild test-without-building \
-xctestrun "$(ls DerivedData/Build/Products/*.xctestrun | head -n 1)" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
workflows:
build-test:
jobs:
- build
- test:
requires:
- build
テストの並列実行
- run:
name: Run tests in parallel
command: |
# Xcode clones the simulator and runs test classes on up to 4 clones
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-parallel-testing-enabled YES \
-maximum-parallel-testing-workers 4 \
-derivedDataPath ~/DerivedData/MyApp \
| xcbeautify
CircleCI のテスト分割
大規模なテストスイートでは、CircleCI のテスト分割がテストクラスを並列タスクに分散します。各タスクには専用のランナーが必要なため、parallelism: 3 にはリソースクラス内に 3 台の Mac が必要です。Mac が 1 台だけなら、タスクは順番に実行されます。
jobs:
test:
machine: true
resource_class: your-org/mac-runner
# One task per runner: 3 tasks need 3 Macs in this resource class
parallelism: 3
steps:
- checkout
- run:
name: Split test classes and run this task's share
command: |
# One XCTest class per file, named like the file (LoginTests.swift)
CLASSES=$(circleci tests glob "MyAppTests/**/*Tests.swift" \
| xargs -n1 basename | sed 's/\.swift$//' \
| circleci tests split)
# -only-testing takes Target/Class identifiers, not file paths
ONLY_TESTING=""
for CLASS in $CLASSES; do
ONLY_TESTING="$ONLY_TESTING -only-testing:MyAppTests/$CLASS"
done
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
$ONLY_TESTING \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
ワークフローの例
一般的な iOS の CI/CD シナリオ向けに、完全で本番運用に対応したワークフローの例を紹介します。
iOS のビルド、テスト、デプロイの完全なパイプライン
version: 2.1
jobs:
lint:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Run SwiftLint
command: swiftlint lint --reporter json > swiftlint-results.json || true
- store_artifacts:
path: swiftlint-results.json
build-and-test:
machine: true
resource_class: your-org/mac-runner
environment:
# Xcode for this job, without sudo (path from `xcodes installed`)
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
steps:
- checkout
- run:
name: Resolve dependencies
command: |
xcodebuild -resolvePackageDependencies \
-scheme "MyApp" \
-clonedSourcePackagesDirPath ~/spm-cache
- run:
name: Build and test
command: |
xcodebuild test \
-scheme "MyApp" \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-clonedSourcePackagesDirPath ~/spm-cache \
-derivedDataPath ~/DerivedData/MyApp \
-resultBundlePath TestResults.xcresult \
-parallel-testing-enabled YES \
| xcbeautify --report junit --report-path test-results
- store_test_results:
path: test-results
- store_artifacts:
path: TestResults.xcresult
deploy-testflight:
machine: true
resource_class: your-org/mac-runner
environment:
DEVELOPER_DIR: "/Applications/Xcode-27.0.0.app/Contents/Developer"
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
# MATCH_PASSWORD and the App Store Connect API key variables
# (ASC_KEY_ID, ASC_ISSUER_ID, ASC_KEY_CONTENT) come from a CircleCI
# context or the project's environment variables
- run:
name: Build and upload to TestFlight
command: bundle exec fastlane beta
workflows:
ios-pipeline:
jobs:
- lint
- build-and-test:
requires:
- lint
- deploy-testflight:
requires:
- build-and-test
filters:
branches:
only: main
Fastlane 連携の例
ビルドの自動化に Fastlane を使っている場合、CircleCI セルフホストランナーと連携させる方法は次のとおりです。
# Fastfile (fastlane/Fastfile)
default_platform(:ios)
platform :ios do
desc "Run tests"
lane :test do
scan(
scheme: "MyApp",
device: "iPhone 17",
derived_data_path: File.expand_path("~/DerivedData/MyApp"),
result_bundle: true,
output_directory: "./test_output",
# store_test_results reads JUnit XML
output_types: "junit",
output_files: "junit.xml"
)
end
desc "Build and push to TestFlight"
lane :beta do
# App Store Connect API key: no Apple ID password or 2FA on the runner
app_store_connect_api_key(
key_id: ENV["ASC_KEY_ID"],
issuer_id: ENV["ASC_ISSUER_ID"],
key_content: ENV["ASC_KEY_CONTENT"]
)
match(type: "appstore", readonly: true)
increment_build_number(
build_number: ENV["CIRCLE_BUILD_NUM"]
)
gym(
scheme: "MyApp",
export_method: "app-store",
derived_data_path: File.expand_path("~/DerivedData/MyApp")
)
pilot(skip_waiting_for_build_processing: true)
end
end
# .circleci/config.yml using Fastlane
version: 2.1
jobs:
fastlane-test:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
- run:
name: Run Fastlane tests
command: bundle exec fastlane test
- store_test_results:
path: ./test_output
- store_artifacts:
path: ./test_output
fastlane-deploy:
machine: true
resource_class: your-org/mac-runner
steps:
- checkout
- run:
name: Install dependencies
command: bundle install
- run:
name: Deploy to TestFlight
command: bundle exec fastlane beta
workflows:
ios-workflow:
jobs:
- fastlane-test
- fastlane-deploy:
requires:
- fastlane-test
filters:
branches:
only: main
パフォーマンス比較
CircleCI クラウドの macOS リソースと、MyRemoteMac の専用 Mac Mini M4 との詳細なパフォーマンスおよびコスト比較を示します。
ビルド時間のベンチマーク
| シナリオ | セルフホスト Mac Mini M4 | CircleCI クラウド macOS | 改善率 |
|---|---|---|---|
| クリーンビルド(中規模アプリ) | 5 分 | 14 分 | 2.8 倍高速 |
| インクリメンタルビルド | 1.5 分 | 14 分(キャッシュなし) | 9.3 倍高速 |
| SPM の依存関係の解決 | 5 秒(ディスクにキャッシュ済み) | 3〜5 分(毎回ダウンロード) | 約 60 倍高速 |
| 単体テストスイート(500 テスト) | 2.5 分 | 8 分 | 3.2 倍高速 |
| キュー待機時間 | 0 秒 | 30 秒〜5 分 | 即座 |
月間コスト分析
| チーム規模 | MyRemoteMac の費用 | 月間ビルド数 | CircleCI クラウドの費用 | 月間節約額 |
|---|---|---|---|---|
| 個人開発者 | 月額$85 | 100 ビルド(平均 10 分) | 月額$120 | 月額$35 |
| 小規模チーム(5 人) | 月額$85 | 500 ビルド(平均 10 分) | 月額$600 | 月額$515 |
| 中規模チーム(15 人) | 月額$229(M4 Pro) | 1500 ビルド(平均 10 分) | 月額$1,800 | 月額$1,571 |
| エンタープライズ(50 人以上) | 月額$458(M4 Pro 2 台) | 5000 ビルド以上 | 月額$6,000以上 | 月額$5,542以上 |
My Remote Mac の料金は USD・税抜です。
結論: CircleCI の m4pro.medium クラスの料金(200 クレジット、1 分あたり約$0.12、2026年10月時点)では、10 分のビルド 1 回が約$1.20 になります。そのため、月額$85(USD・税抜)の専用 Mac mini M4 は月約 71 ビルドから安くなります。永続的な Mac のウォームキャッシュにより、ビルドごとの待ち時間も短縮できます。
よくある問題のトラブルシューティング
ランナーが切断される、または「Offline」と表示される
ネットワークの問題、クラッシュ、再起動の後に、ランナーが接続を失うことがあります。gui ドメインで起動した場合は、ユーザーがログインするまで復帰しません。ステップ 2 のヘッドレス設定ならこれを避けられます。サービスとログを確認してください。
# Check the service (use gui/ instead of user/ if you started it
# from a desktop session)
launchctl print user/$(id -u)/com.circleci.runner
# View the runner log
tail -50 "$HOME/Library/Logs/com.circleci.runner/runner.log"
# Restart the service
launchctl kickstart -k user/$(id -u)/com.circleci.runner
# If the token was revoked, create a new one, put it in
# ~/Library/Preferences/com.circleci.runner/config.yaml and restart
circleci runner token create your-org/mac-runner mac-mini-m4
resource_class が見つからない、またはジョブがキューで止まる
ジョブが「No matching runner found」でキューに残る場合、設定内のリソースクラスがダッシュボードと一致していません。
# Verify the exact resource class name in the CircleCI web app:
# Runners (sidebar) lists your resource classes
# The resource_class in .circleci/config.yml must match exactly:
# resource_class: your-org/mac-runner (case-sensitive!)
# Check your runner is online:
circleci runner instance list your-org/mac-runner
# Common mistakes:
# - Wrong namespace (org name vs personal namespace)
# - Typo in resource class name
# - Runner is offline or token is invalid
# - Using 'docker' executor instead of 'machine: true'
Xcode のコード署名エラー
CI マシン上でのコード署名には、慎重なキーチェーン管理が必要です。ランナープロセスがログインキーチェーンにアクセスできない場合があります。
# Option 1: Use Fastlane Match (recommended)
# In your Fastfile:
match(type: "appstore", readonly: true)
# Option 2: Manual keychain management in your job steps
- run:
name: Setup code signing
command: |
# Decode and import the certificate
echo "$BUILD_CERTIFICATE_BASE64" | base64 --decode > /tmp/cert.p12
# Create a temporary keychain
security create-keychain -p "ci" /tmp/ci.keychain
security set-keychain-settings -lut 21600 /tmp/ci.keychain
security unlock-keychain -p "ci" /tmp/ci.keychain
# Import the certificate
security import /tmp/cert.p12 -P "$P12_PASSWORD" \
-A -t cert -f pkcs12 -k /tmp/ci.keychain
# Let codesign use the key without a GUI prompt
security set-key-partition-list -S apple-tool:,apple:,codesign: \
-s -k "ci" /tmp/ci.keychain
security list-keychain -d user -s /tmp/ci.keychain
# Install the provisioning profile (Xcode 16 and later location)
PROFILES=~/Library/Developer/Xcode/UserData/Provisioning\ Profiles
mkdir -p "$PROFILES"
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode \
> "$PROFILES/profile.mobileprovision"
シミュレーターの起動に失敗する、またはタイムアウトする
長時間稼働する CI マシンでは、シミュレーターが不正な状態になることがあります。ビルド間でリセットしてください。
# Add a pre-build step to clean simulators
- run:
name: Reset simulators
command: |
xcrun simctl shutdown all 2>/dev/null || true
xcrun simctl erase all 2>/dev/null || true
# If a specific runtime is missing, reinstall it:
xcodebuild -downloadPlatform iOS
# List available simulators
xcrun simctl list devices available
ディスク容量が不足してくる
Xcode のビルドは大量のデータを生成します。ランナー上で自動クリーンアップを設定してください。
# Check available disk space
df -h /
# Clean DerivedData folders unused for a week
find ~/DerivedData ~/Library/Developer/Xcode/DerivedData \
-mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} +
# Delete simulators the current Xcode no longer supports
xcrun simctl delete unavailable
# List simulator runtimes, then delete the ones you no longer test on
xcrun simctl runtime list
# xcrun simctl runtime delete <runtime-identifier>
# Clean Homebrew cache
brew cleanup --prune=7
# Set up a weekly cron job for automatic cleanup
(crontab -l 2>/dev/null; echo "0 4 * * 0 find ~/DerivedData -mindepth 1 -maxdepth 1 -mtime +7 -exec rm -rf {} + ; /opt/homebrew/bin/brew cleanup --prune=7") | crontab -