По официальной схеме Kotlin для iOS CI/CD, управляемый macOS Agent может собрать, подписать и опубликовать iOS-приложение. Симптом: вы пытаетесь отправить все задания Kotlin Multiplatform на один тип узла. Быстрое решение: PR-проверки и обычную сборку симулятора направьте на управляемые агенты, а производственную подпись, приватные зависимости и фиксированную версию Xcode — на выделенный удалённый Mac. При заметных пиках используйте смешанную схему: постоянная производственная база плюс эластичная аренда.
Эта статья для вас, если вы отвечаете за iOS-пайплайн Kotlin Multiplatform, бюджет инфраструктуры и выпуск в TestFlight. Она также пригодится IT- и security-руководителям, которым нужно ограничить доступ к исходному коду, внутренним репозиториям и сертификатам. Если команда сравнивает стоимость управляемых заданий, собственных Mac и риска срыва релиза, ниже есть рабочая модель выбора.
Последнее обновление — 27 августа 2026 года; статусы Xcode и возможности сборки сверены по требованиям Apple к Xcode, документации Kotlin и материалам CI-платформы из ссылок в статье.
SECTION 01Сначала разделите Kotlin Multiplatform iOS CI по рабочим сценариям
Главная ошибка в такой инфраструктуре — считать «сборку iOS» одной операцией. Общая логика Kotlin, модульные тесты и статический анализ могут выполняться на обычном CI-исполнителе. Однако Apple-цель, фреймворк для iOS, симулятор, архив и подпись образуют уже другой класс задач.
В Kotlin Multiplatform цели iosArm64 и iosSimulatorArm64 предназначены для разных результатов. Первая связана с запуском на физических устройствах и производственным артефактом, вторая — с тестированием на симуляторе. Документация Kotlin по сборке Native-бинарников подтверждает, что эти цели нельзя считать взаимозаменяемыми.
| Сценарий | Что нужно доказать | Предпочтительный узел | Почему |
|---|---|---|---|
| Проверка PR | Код собирается, тесты и анализ проходят | Управляемый агент, при возможности не macOS | Нет смысла занимать Mac задачами, не создающими iOS-артефакт |
| Сборка iOS-фреймворка | Apple-цель компилируется с конкретным Xcode | Управляемый macOS Agent или выделенный Mac | Требуется согласованная macOS/Xcode-среда |
| Проверка симулятора | Приложение и фреймворк работают для выбранной архитектуры | Управляемый macOS Agent с подготовленным Simulator | Нужны симулятор, SDK и корректная версия Xcode |
| Архив для релиза | Получен подписываемый производственный пакет | Выделенный удалённый Mac | Важны контроль среды, кэш, журналирование и доступ к секретам |
| Публикация в TestFlight | Артефакт загружен с разрешёнными полномочиями | Отдельный узел выпуска | Сборка успешна только тогда, когда доверенная граница также соблюдена |
| Приватные зависимости | Узел видит внутренние Git- и пакетные сервисы | Выделенный Mac в контролируемой сети | Нельзя расширять доступ к секретам только ради подключения агента |
| Релизный пик | Очередь не блокирует выпуск | Смешанная модель | База остаётся постоянной, временная нагрузка уходит на аренду |
Управляемый агент выигрывает там, где важны быстрое получение чистой среды и освобождение ресурса после задания. Но его нельзя автоматически считать подходящим для любого этапа: возможность запустить job не означает, что вы выполнили требования к хранению сертификата, сетевому периметру или воспроизводимости окружения.
SECTION 02PR, общий Kotlin-код и настоящая iOS-сборка
Первый шаг: разделите конвейер на независимые проверки
Начните не с выбора поставщика Mac, а с графа задач:
- Скачайте исходный код и проверьте конфигурацию проекта.
- Выполните форматирование, статический анализ и тесты общего Kotlin-модуля.
- Соберите Apple-фреймворк для нужной цели.
- Запустите проверку приложения в iOS Simulator.
- Только после успешных проверок создайте архив.
- Подпишите архив и передайте его в этап публикации.
Такой порядок уменьшает очередь на macOS: ошибка в общем модуле обнаруживается до обращения к Xcode. Внутри CI полезно разделить результаты на два статуса. Первый означает «общий код прошёл тесты», второй — «получен и проверен настоящий iOS-артефакт». Подмена второго статуса первым создаёт ложное ощущение готовности к релизу.
Официальный пример обнаружения и сборки Multiplatform-проекта полезен как отправная точка, но корпоративный конвейер должен дополнительно фиксировать версию Xcode, выбранный SDK, целевую архитектуру и способ очистки рабочего каталога.
Второй шаг: установите правило для управляемого агента
Управляемый macOS Agent подходит для PR, если одновременно выполняются следующие условия:
- исходный код и артефакты разрешено передавать в среду CI;
- внутренние зависимости доступны без обхода сетевой политики;
- секреты подписи не попадают в обычное задание;
- нужная версия Xcode доступна и зафиксирована;
- вы принимаете отсутствие постоянного локального кэша между заданиями;
- время ожидания очереди контролируется метрикой, а не ощущением команды.
Преимущество здесь не только в оплате задания. Вы не обслуживаете постоянно включённый узел, не проводите ручную очистку после каждого PR и можете временно увеличить параллельность. Недостаток — меньший контроль над образом, сетевым маршрутом, кэшем и моментом обновления среды.
При использовании GitHub Actions необходимо отдельно проверить правила безопасного применения workflow: официальная документация по защите Actions предупреждает о рисках непроверенных действий, зависимостей и доступов из pull request. Для корпоративного проекта это означает, что права токена, разрешения на запись и доступ к секретам должны различаться для обычной проверки и выпуска.
Третий шаг: не объявляйте CI готовым после одной архитектуры
Сборка только iosSimulatorArm64 показывает, что выбранный сценарий симулятора работает. Она не доказывает готовность архива для физического устройства. Обратное тоже верно: успешная сборка iosArm64 не заменяет проверку пользовательского интерфейса и поведения в симуляторе.
Попросите владельца проекта зафиксировать в репозитории:
- список Apple-целей;
- версию Xcode и требуемую macOS;
- команды сборки фреймворка и приложения;
- порядок запуска симулятора;
- условия, при которых тест считается релизным;
- ожидаемый формат архива;
- правила хранения и удаления артефактов.
Это особенно важно при переходе на Xcode 27. На дату проверки Apple указывает Xcode 27 как beta-версию, поэтому совместимость с ним нельзя выдавать за стабильную производственную гарантию. Для эксперимента выделите отдельный маршрут, а основной релизный узел не обновляйте только потому, что новая версия появилась в списке загрузок Apple.
SECTION 03Симулятор, Xcode и выделенный удалённый Mac
Если вам нужны долгоживущий кэш, предсказуемый Simulator и ручная диагностика падения, выделенный удалённый Mac даёт больше контроля. Вы можете закрепить рабочую версию Xcode, заранее подготовить симуляторы, ограничить пользователей и оставить узел доступным для повторного анализа. Но этот вариант требует эксплуатации: обновлений, очистки, контроля диска, перезапуска и проверки удалённого доступа.
По списку системных требований Xcode от Apple версия Xcode связана с поддерживаемой macOS. Поэтому запись «у нас есть Mac» ничего не гарантирует: нужно доказать совместимость конкретной версии macOS, Xcode, SDK и проекта.
| Критерий | Управляемый macOS Agent | Выделенный удалённый Mac | Смешанная схема |
|---|---|---|---|
| PR с переменной очередью | Сильная сторона | Ресурс может простаивать | PR уходят на эластичную часть |
| Фиксация Xcode | Зависит от доступных образов | Вы контролируете установку | Производственная версия закрепляется на Mac |
| Simulator | Подходит при наличии нужного образа | Можно заранее прогреть окружение | Типовые проверки — агент, диагностика — Mac |
| Подпись и публикация | Возможны, но требуют строгой работы с секретами | Проще выделить доверенный контур | Выпуск остаётся на фиксированной базе |
| Приватная сеть | Ограничение зависит от провайдера | Можно включить в согласованный периметр | Внутренние зависимости — выделенный узел |
| Пик релизов | Быстро добавляет параллельность | Ограничен числом узлов | Аренда закрывает временный избыток |
| Эксплуатация | Меньше задач для вашей команды | Обновления и восстановление на вас | Операционная нагрузка распределяется |
Сценарий из дежурной практики выглядит так: PR проходят общие тесты и сборку фреймворка на управляемом агенте, затем выбранные изменения отправляются на Mac с подготовленным Simulator. Архив и публикация выполняются только на узле, доступном ограниченной группе выпуска. При этом зелёный PR не получает права читать производственный Keychain.
Для удалённого узла заранее проверьте:
- отдельную учётную запись для автоматизации;
- запрет интерактивного доступа к производственному профилю для обычных разработчиков;
- очистку workspace и временных файлов;
- поведение после перезагрузки;
- восстановление SSH или VNC-доступа;
- наличие журналов входа и операций публикации;
- возможность быстро отключить узел при подозрении на компрометацию.
Ссылка на правила для self-hosted runners полезна не как готовая корпоративная политика, а как напоминание: собственный исполнитель имеет доступ к вашей сети и потому требует более строгого контроля доверия, чем одноразовая чистая среда.
SECTION 04Подпись и TestFlight: где проходит граница доверия
Производственная публикация состоит не из одной команды загрузки. В цепочку входят сертификат распространения, профиль подписи, Keychain, API key App Store Connect, разрешения workflow и журнал, позволяющий установить, кто инициировал выпуск.
Разделите поток так:
PR → тесты → iOS-сборка → ручное или защищённое одобрение → архив → подпись → загрузка
Исходный код может проходить через несколько типов исполнителей, но ключ подписи не должен автоматически следовать за ним. Для PR используйте отсутствие производственных секретов как норму. Для выпуска включайте секреты только после проверки ветки, коммита и разрешения ответственного лица.
| Объект | Минимально необходимый доступ | Где контролировать | Что проверять |
|---|---|---|---|
| Общий Kotlin-код | Чтение репозитория и запись отчётов | PR-job | Нет доступа к сертификатам |
| Внутренний пакетный реестр | Только нужные пакеты | Сетевой периметр и токен | Срок действия и журнал запросов |
| Сертификат распространения | Только этап архива | Keychain выделенного узла | Импорт, хранение и удаление |
| App Store Connect API key | Только действия публикации | Секрет CI и роль ключа | Кто изменяет и отзывает ключ |
| Архив | Передача в выпуск | Защищённое хранилище | Срок хранения и доступ на скачивание |
| Журнал публикации | Чтение аудиторам | CI и серверные логи | Время, инициатор, версия и результат |
Apple отдельно описывает загрузку сборок в App Store Connect. Из этого следует практический вывод: успешный upload ещё не равен безопасному выпуску. Вы должны проверять не только наличие файла, но и происхождение артефакта, полномочия ключа, номер версии, журнал одобрения и возможность отозвать доступ.
Управляемый агент можно использовать для подписанного задания, если его сетевой и секретный контур соответствует политике компании. Если же несколько приложений используют разные команды, а сертификаты имеют длительный жизненный цикл, выделенный удалённый Mac обычно проще ограничить и расследовать. Это не «безопасность по умолчанию»: вы всё равно обязаны настроить очистку, аккаунты, аудит и процедуру восстановления.
Для раздельного одобрения релизов можно применить механизм контроля deployment в CI, но название инструмента не заменяет внутреннюю процедуру. Определите, кто может запустить архив, кто подтверждает подпись и кто имеет право отправить сборку внешним тестировщикам.
SECTION 05Приватные зависимости и фиксированная среда
Если проект обращается к внутреннему репозиторию, прокси, реестру артефактов или сервису с ограниченным IP, сначала опишите сетевой поток. Не выдавайте управляемому агенту расширенные полномочия только потому, что иначе PR не собирается. Увеличение доступа к сети и секретам ради удобства сборки часто создаёт более дорогой риск, чем покупка отдельного узла.
Выделенный Mac в контролируемом периметре подходит, когда:
- внутренние зависимости нельзя выносить за пределы сети;
- требуется постоянный адрес выхода;
- Xcode и SDK должны оставаться неизменными до планового окна;
- команде нужна повторная диагностика одного и того же workspace;
- аудит требует связать действие публикации с конкретным узлом.
При этом фиксированный узел не должен превращаться в «вечный сервер без владельца». Назначьте ответственных за обновления, проверку свободного места и восстановление после зависания. Зафиксируйте максимальный срок жизни рабочих файлов и проведите контрольный тест очистки: после завершения задания следующий пользователь не должен видеть исходники, токены или архив предыдущего проекта.
Если вам нужно сравнить варианты доступа и способ подключения команды к реальному Mac, сначала изучите описание удалённого доступа MACNOX. Для финансовой модели используйте страницу тарифов MACNOX, но не подставляйте её цену в общий TCO без учёта трафика, сопровождения, резервного узла и внутренних требований безопасности.
SECTION 06Четвёртый шаг: рассчитайте ёмкость без выдуманных тарифов
Не начинайте с вопроса «сколько Mac купить». Сначала соберите четыре ряда нагрузки:
- ежедневная базовая очередь PR;
- релизные пики и параллельные архивы;
- резерв на отказ или перезагрузку узла;
- отдельные задания для проверки новой версии Xcode.
Далее для каждого ряда определите требуемое число одновременных заданий, допустимое время ожидания и минимально приемлемое время восстановления. Формула может выглядеть так:
Ёмкость фиксированной базы = производственная нагрузка + обязательный резерв
Эластичная ёмкость = пиковая очередь − доступная фиксированная база
TCO = аренда заданий + владение узлами + сопровождение + хранение + стоимость простоев
В формулу владения включите не только покупку Mac. Добавьте замену оборудования, подготовку образа, дисковое пространство, мониторинг, рабочее время инженеров, резервный доступ и влияние неудачного релиза. Для аренды отдельно учитывайте длительность пакета, число параллельных узлов, время ожидания доставки и требования к региону.
Управляемые исполнители, по описанию GitHub-hosted runners, дают стандартизированную среду, но конкретные образы и доступные версии нужно сверять перед закупкой. Не переносите характеристики одной платформы на другую и не записывайте предполагаемое время сборки как доказанный показатель без журнала ваших проектов.
Пятый шаг: проведите пилот по реальным заданиям
Пилот должен проверять не демонстрационный проект, а ваш релизный маршрут:
- Возьмите типичный PR с изменением общего Kotlin-модуля.
- Запустите сборку
iosSimulatorArm64. - Выполните сценарий для
iosArm64. - Проверьте обращение к внутренним зависимостям.
- Создайте тестовый архив без производительного ключа.
- Выполните публикацию в безопасном контуре с отдельным API key.
- Перезапустите узел и проверьте восстановление очереди.
- Убедитесь, что workspace и временные секреты удалены.
Сохраняйте записи очереди, ошибок Xcode, времени восстановления, действий с Keychain и причин ручного вмешательства. Именно эти данные должны решать, добавлять ли удалённые Mac, переносить ли больше PR на управляемых агентов или оставлять текущую архитектуру.
SECTION 07Когда смешанная модель становится корпоративным стандартом
Смешанный вариант подходит, если базовый выпуск нельзя прерывать, но PR-нагрузка резко меняется. Фиксированный удалённый Mac становится доверенным контуром для архива, подписи, приватных зависимостей и диагностики. Управляемые агенты принимают непостоянные проверки и временный рост очереди.
Плюсы:
- производственный Xcode и Keychain не зависят от каждой PR-задачи;
- пиковую параллельность можно добавлять без покупки постоянных узлов;
- ошибки общего кода отсекаются до дорогих Apple-операций;
- можно отдельно тестировать новую версию Xcode;
- отказ одного класса исполнителей не обязательно останавливает весь конвейер.
Минусы:
- появляются две модели окружения, которые нужно синхронизировать;
- кэш и версии зависимостей могут вести себя по-разному;
- требуется ясная маршрутизация заданий;
- аудит становится сложнее, если секреты распределены небрежно;
- итоговый TCO нельзя оценивать только по счёту за минуты CI.
Примите решение о расширении фиксированной базы только после того, как журнал покажет стабильную повторяемую нагрузку. Если же пики редкие, а постоянный узел большую часть времени свободен, аренда MACNOX для отдельного изолированного испытания или временной ёмкости может быть рациональнее покупки ещё одного Mac. Это следует проверить на ваших заданиях, а не принимать как универсальное обещание экономии.
SECTION 08Частые вопросы
Нужен ли Mac для сборки iOS-приложения на Kotlin Multiplatform?
Общие проверки Kotlin-кода, статический анализ и часть тестов можно выполнять вне macOS. Но компиляция Apple-целей, запуск iOS Simulator, создание архива, подпись и загрузка сборки в App Store Connect требуют macOS с подходящей версией Xcode. Поэтому Mac нужен не для каждого задания, а для всех этапов, где появляется настоящий iOS-артефакт.
Что выбрать для Kotlin Multiplatform iOS CI: аренду или собственный узел?
Для стандартных PR-проверок и переменной очереди обычно удобнее управляемый macOS Agent: вы получаете готовую среду и не держите узел без работы. Собственный или выделенный удалённый Mac предпочтительнее для производственной подписи, приватной сети, фиксированной версии Xcode и длительно прогреваемого кэша. При смешанной нагрузке разумно сочетать оба варианта.
Как изолировать ключи подписи при автоматической публикации Kotlin Multiplatform в TestFlight?
Размещайте ключи и сертификаты только на выделенном узле публикации либо в защищённом хранилище CI с минимальными правами и коротким жизненным циклом задания. Разделяйте проверку PR и выпуск, ограничивайте доступ к Keychain, журналируйте операции и не передавайте производственные секреты в обычные сборочные задания. API key App Store Connect также должен иметь только необходимую роль.
Как рассчитать ёмкость macOS-сборки для команды Kotlin Multiplatform?
Сначала разделите ежедневную базовую очередь, релизные пики, резерв на отказ узла и отдельные задания для проверки новой версии Xcode. Для каждого класса определите число параллельных заданий, допустимое время ожидания и долю задач, которую можно вынести на управляемые агенты. Фиксированный Mac покрывает производственную базу, а аренда добавляет ёмкость только при росте очереди.
Текущая схема на одном постоянно работающем Mac часто скрывает три недостатка: ресурс простаивает между релизами, обновление Xcode затрагивает всех пользователей сразу, а отказ узла блокирует и PR, и публикацию. Полностью управляемая среда, в свою очередь, может не дать нужного контроля над приватной сетью, кэшем и производственными ключами. Поэтому для проекта с чувствительным выпуском разумно сначала выделить изолированный удалённый Mac через MACNOX, прогнать на нём реальную KMP-цепочку и сравнить очередь, восстановление и аудит с текущей инфраструктурой. На странице заказа MACNOX можно выбрать следующий шаг после того, как ваша матрица нагрузок подтвердит потребность в таком пилоте.