Почему сборка iOS требует выделенного macOS?
В отличие от Android, где Gradle в Linux-контейнере, цепочка Apple — xcodebuild, подпись кода, notarytool, API App Store Connect — привязана к macOS и физическому Apple Silicon / Intel. Каждая команда с продуктом iOS рано или поздно решает: кто даст этот Mac.
Типичные боли в трёх категориях: очереди публичных Runner (мало бесплатных macOS-минут в GitHub Actions, в пик workflow ждут 20–45 минут), локальная машина занята CI (после push коллеги Xcode тормозит, вентиляторы на полную), хаос с сертификатами (.p12 на разных машинах, срок истёк — никто не заметил). Выделенный облачный Mac решает первые две и даёт единую доверенную среду для третьей.
Железо: Mac mini M4 · CPU 10 ядер · 16 ГБ unified memory · SSD 256 ГБ · выделенный канал 1 Gbps (узел SpinMac Япония).
Система: macOS 15 Sequoia, Xcode 16.4. Проект: среднее SwiftUI-приложение (~120 тыс. строк, 3 Extension target).
CI: GitHub Actions self-hosted runner 2.323.0, Jenkins 2.479 LTS + macOS agent.
Подпись: Apple Distribution + ключ API App Store Connect (Issuer ID + Key ID + .p8).
Три подхода CI: что выбрать?
Перед установкой Xcode определите путь команды. Нет универсально лучшего варианта — разница в терпимости к очередям, числе сборок в месяц и требованиях compliance.
| Подход | Типичная стоимость | Очередь / параллелизм | Подходит командам |
|---|---|---|---|
| macOS Runner от GitHub | Поминутно (от ~$0.08/мин) | Общий пул, заметные очереди в пик | < 500 мин/мес, ожидание допустимо |
| Свой Mac mini в серверной | Железо $599+ разово + электричество и обслуживание | Эксклюзив, но система и сертификаты на вас | Офис, частые сборки долгосрочно |
| Выделенный Mac в облаке (посуточно) | SpinMac от $21.2/день, без контракта | Физическая машина, сразу доступна | Малые команды, релизы по этапам, удалённая работа |
На среднем SwiftUI-проекте полный xcodebuild archive на узле M4 занял около 4 мин 12 с (компиляция и линковка Swift). На macOS-14 Runner GitHub: 18 минут очереди и 5 мин 40 с сборки — почти 24 минуты. Для «push и сразу тест» очередь часто важнее самой сборки.
Инициализация узла: Xcode и материалы подписи
Mac mini SpinMac поставляется с полным macOS и правами администратора. После активации — SSH или VNC в браузере, затем подготовка CI в указанном порядке. Храните сертификаты и профили в фиксированных путях для единообразия в workflow.
-
01
Установка Xcode и CLI
Xcode 16.x из App Store или через
xcode-select, затемsudo xcodebuild -license acceptиxcodebuild -runFirstLaunch. Проверка:xcodebuild -versionпоказывает нужную версию. -
02
Импорт сертификата и Provisioning Profile
Передайте Distribution .p12 и пароль в
~/certs/, импортируйте черезsecurity import; .mobileprovision — в~/Library/MobileDevice/Provisioning Profiles/. В CI — отдельная связка ключей с доверием-T /usr/bin/codesign. -
03
Настройка ключа API App Store Connect
Создайте API Key в Apple Developer, сохраните
AuthKey_XXXXXX.p8в~/private_keys/. Для TestFlight —xcrun altoolилиfastlane pilot upload, без интерактивного входа Apple ID. -
04
Клонирование репозитория и кэш DerivedData
После первого
git cloneсделайте локальный Archive и проверьте цепочку подписи. DerivedData на локальном SSD (256 ГБ хватает для среднего проекта) — инкрементальные сборки быстрее на 30–50%.
В headless (SSH / Runner) 90% сбоев codesign — неразблокированная или неавторизованная связка ключей. В начале скрипта: security unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db и set-key-partition-list для codesign. Пароль не в Git — GitHub Secrets или Jenkins Credentials.
Подключение self-hosted Runner GitHub Actions
После регистрации выделенного Runner workflow направляется через runs-on: self-hosted или label (например macos-m4), минуя общий пул. Шаги проверены на узле SpinMac.
В репозитории GitHub: Settings → Actions → Runners → New self-hosted runner, macOS ARM64, скачайте и распакуйте actions-runner, затем:
./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token RUNNER_TOKEN --labels macos-m4,ios-build --unattended
После регистрации установите как системный сервис: sudo ./svc.sh install → sudo ./svc.sh start. В YAML workflow укажите label:
runs-on: [self-hosted, macos-m4]
Типичный iOS job: checkout → разблокировка связки ключей → xcodebuild archive → xcodebuild -exportArchive → xcrun altool --upload-app или Fastlane upload_to_testflight. На M4 одна lane Fastlane от push до обработки TestFlight (с очередью Apple) в среднем ~11 минут, из них чуть больше 6 — локальная сборка и загрузка.
Self-hosted Runner видит код и ключи подписи: ограничьте права collaborators, регулярно меняйте registration token, не запускайте workflow с секретами на fork PR автоматически (pull_request_target — с осторожностью). На общем узле — отдельные Runner по проектам или изоляция через песочницу OpenClaw.
Настройка macOS Agent Jenkins
Если контроллер Jenkins уже есть (часто на Linux), сборка macOS — через agent. На Mac SpinMac в облаке: JDK 17 и agent.jar Jenkins как LaunchDaemon; контроллер забирает задачи по SSH или JNLP.
Сильные стороны Jenkins — визуальные pipeline и плагины: Xcode, Credentials Binding, AnsiColor, архив артефактов. Типично: stage('Archive') с sh 'xcodebuild ...', stage('Upload') с Fastlane или altool. Зафиксируйте DEVELOPER_DIR как /Applications/Xcode.app/Contents/Developer.
По сравнению с GitHub Actions Jenkins лучше для многоветочных внутренних процессов с ручным approval. Облачный Mac посуточно — «эластичный agent»: узел на неделю релиза, в спад — отключение, без Mac в серверной 365 дней в году.
Archive, подпись и загрузка в TestFlight
Независимо от CI цепочка одна: Archive → .xcarchive, Export → .ipa, загрузка в App Store Connect. В CI стандарт — командная строка без GUI Xcode.
Пример Archive (Release, scheme и путь вывода):
xcodebuild archive -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -archivePath build/MyApp.xcarchive CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER="MyApp AppStore"
Для Export нужен ExportOptions.plist (method app-store), затем:
xcodebuild -exportArchive -archivePath build/MyApp.xcarchive -exportPath build/export -exportOptionsPlist ExportOptions.plist
Для TestFlight лучше API Key — без зависания 2FA в unattended-среде:
xcrun altool --upload-app -f build/export/MyApp.ipa -t ios --apiKey KEY_ID --apiIssuer ISSUER_ID
На среднем проекте 10 ядер M4 ускоряют параллельную компиляцию Swift vs старые Intel CI; 16 ГБ unified memory хватает на полный clean build без swap. При многих Swift Package — кэш -clonedSourcePackagesDirPath для следующих сборок.
Нет серверной в офисе — как стабильно гонять iOS CI?
Дилемма многих инди и малых команд: нужен macOS для сборки, но не хочется офис, кабели и отключения ради одной CI-машины. Публичные VM не подходят (нет полного macOS и цепочки подписи Apple); б/у Mac mini дома — нестабильный uplink, смена IP, случайное выключение.
SpinMac — выделенный физический Mac mini M4: без виртуализации и overselling, 16 ГБ RAM и 1 Gbps на машину, активация за 1–5 минут после оплаты. Пять регионов (Сингапур, Япония, Корея, Гонконг, восток США) — например Токио для приложения на японский рынок, меньше задержка при загрузке в TestFlight.
Тарифы: от $21.2/день, $57.3/неделя, $106.1/месяц, без долгого контракта. Узел на неделю релиза с Runner, в простой — отключение, часто выгоднее года своей машины и электричества. Для параллельных сборок — параллель Thunderbolt 5, кластер 80 Gbps для крупных monorepo или матрицы приложений.
-
01
Выбор узла и активация в SpinMac
Войдите в консоль, выберите регион и срок; после оплаты SSH и VNC выдаются автоматически. Подробнее в центре помощи.
-
02
По разделу 3 настройте Xcode и подпись
Пути связки ключей и API Key — в переменные окружения для GitHub Actions / Jenkins.
-
03
Зарегистрируйте Runner и запустите первый pipeline
Сначала Debug-сборка, затем Release Archive + TestFlight; закрепите Fastlane или нативные скрипты в репозитории.
Стоимость и выбор: одна таблица для принятия решения
| Ваша ситуация | Рекомендуемый путь | Роль облачного Mac |
|---|---|---|
| Индивидуальная разработка, 1–2 релиза в месяц. | Арендуйте SpinMac в день релиза + Архив вручную. | Временная сборочная машина — освобождайте после работы |
| Команда 5–15 человек, несколько операций в день | Всегда включен self-hosted Runner | Помесячная аренда, выделено, без очередей |
| Jenkins есть, нужен агент macOS | Облачный Mac как эластичный Agent | Масштабируйте пики, не покупайте новое оборудование |
| Матрица для нескольких приложений + пакетные сборки в ночное время | TB5 связывание кластеров | Параллельное архивирование нескольких M4 |
Главная сложность iOS CI/CD не в Xcode, а в стабильной macOS-инфраструктуре и воспроизводимой среде подписи. Общие Runner подходят для редких сборок; локальный CI — для зрелых команд с сильным DevOps; выделенный облачный Mac закрывает разрыв между «без очередей» и «без покупки железа» — оплата по дням, предсказуемая производительность физического сервера, близкие глобальные узлы. Перенесите сборку и подпись в облако — пусть MacBook пишет код, а не отдаёт вентиляторы и память ночному CI.
Предоставьте своему проекту iOS машину для сборки macOS без очереди.
SpinMac Mac mini M4 выделенный узел: полная macOS и Xcode, 16 GB унифицированная память, SSH/VNC доступ, GitHub Actions и Jenkins готовый Runner — от $21.2/день.