Короткий вывод по срокам и действие на этой неделе
Факт на текущий момент: Apple уже выпустила Xcode 26, а его требования к инструментам и платформе опубликованы в официальных заметках к выпуску. Это означает, что для сборки iOS в Bazel 9 нужен не просто исполнитель команд, а согласованный узел macOS с подходящим Xcode и Apple SDK. Подтверждение доступно в официальных заметках Apple о Xcode 26.
Решение: полный цикл — компиляция Apple-целей, линковка, Simulator, подпись, архивирование и экспорт — следует оставлять на реальном Mac. Linux можно использовать для анализа, генерации, проверок и тестов без Apple-зависимостей. На этой неделе разумно собрать матрицу action, запустить чистую сборку на изолированном удалённом Mac и только после этого решать, достаточно ли одного узла или требуется гибридный CI.
Эта статья предназначена для инженеров сборочной платформы, которые подключают Bazel 9 к iOS-проекту и распределяют задачи между Linux и macOS. Она также пригодится DevOps-командам с Linux-кластером и техническим руководителям, оценивающим расширение Apple-платформы.
Дата последней проверки — 29 августа 2026 года. Сведения сверены с публикацией о модели выпусков Bazel, объявлением Bazel 9, документацией rules и материалами Apple. Конкретные сочетания минорных версий необходимо перепроверять перед каждым обновлением toolchain.
Граница инструментов: Bazel не заменяет Apple toolchain
Bazel отвечает за описание графа сборки, анализ зависимостей, выбор платформы, запуск action и работу с кэшем. Он не поставляет Apple SDK, Simulator, signing tools или содержимое Xcode. Поэтому успешный запуск команды bazel build на Linux ещё не доказывает, что приложение можно архивировать и передать в App Store.
Роли компонентов нужно разделять:
- Bazel 9 планирует и выполняет действия, разрешает платформы и toolchain;
- rules_apple описывает Apple-цели, ресурсы, bundle, архивирование и связанные правила;
- rules_swift связывает Swift-компиляцию с графом Bazel;
- Xcode предоставляет Apple SDK, clang, Swift toolchain,
xcodebuild, Simulator и инструменты подписи; - macOS является операционной средой, в которой эти Apple-компоненты должны согласованно работать.
Поддержка Bazel 9 со стороны правил не превращает Linux в полноценную среду iOS-разработки. Перед миграцией нужно открыть текущие релизы rules_apple и документацию rules_swift, после чего зафиксировать конкретную комбинацию версий. Формулировка «поддерживается Bazel 9» не всегда отвечает на вопрос о каждом минорном выпуске Xcode 26, SDK и используемых правилах.
В MODULE.bazel должны быть явно видны зависимости и версии. Для Bazel 9 это особенно важно: старый подход через WORKSPACE больше нельзя считать единственной точкой входа для зависимостей. Если проект всё ещё полагается на неявную загрузку из старого файла, переход необходимо проверять отдельно, а не считать механической заменой версии Bazel.
Быстрый начальный аудит может выглядеть так:
bazel info release
bazel query @rules_apple//...
xcodebuild -version
xcrun --sdk iphoneos --show-sdk-path
xcrun simctl list devices available
Пример ожидаемого принципа проверки, а не фиксированного вывода:
Bazel release: 9.x
Xcode: 26.x
iPhoneOS SDK path: /Applications/Xcode.app/...
Available Simulators: ...
Число 9.x или 26.x здесь является шаблоном. В рабочем отчёте должны присутствовать реальные значения, дата проверки и ссылка на зафиксированный образ узла.
Метрика совместимости определяет, продолжать ли миграцию
Совместимость нужно оценивать не по тому, завершается ли первая команда без ошибки, а по четырём последовательным результатам:
- Bazel разрешает нужные платформы и toolchain.
- Apple-цели компилируются с ожидаемым SDK.
- Simulator-тесты запускаются без ручного вмешательства.
- Архив подписывается и экспортируется в пригодный для доставки формат.
Если выполнен только первый или второй пункт, перед командой не полноценный iOS CI, а промежуточный этап сборки. Официальная документация Bazel описывает платформы и toolchain resolution, однако конкретные Apple-ограничения всё равно задаются Xcode и SDK.
В проекте стоит зафиксировать минимум следующие поля:
| Объект | Что фиксировать | Почему это влияет на решение |
|---|---|---|
| Bazel | Полный выпуск и способ запуска | Меняется поведение анализа и загрузки зависимостей |
| rules_apple | Версия или commit | Определяет доступные Apple-правила |
| rules_swift | Версия или commit | Влияет на Swift toolchain и переходы |
| Xcode | Полный номер и путь установки | Определяет SDK, Simulator и signing tools |
| SDK | Путь и тип SDK | Ошибка здесь превращает сборку в непереносимую |
| macOS | Версия образа узла | Ограничивает совместимость Xcode |
| MODULE.bazel | Зафиксированные зависимости | Убирает скрытый источник расхождений |
Если нужная комбинация не подтверждена текущими release notes, есть три безопасных решения: оставить существующую связку, обновить правила вместе с Bazel и Xcode в отдельной ветке или временно остановить миграцию. Сообщение из сообщества можно использовать как сигнал для теста, но не как доказательство совместимости.
Разделение action между Linux и macOS
Распределять работу следует по фактическим входам action, а не по названию скрипта. Например, этап с именем build-ios может включать генерацию исходников на Linux, компиляцию на macOS и последующую упаковку. Обратная ситуация также возможна, если универсальный скрипт случайно вызывает Apple-инструмент.
К Linux обычно можно отнести:
- форматирование и статический анализ исходников;
- проверку зависимостей и содержимого
MODULE.bazel; - генерацию файлов, не использующую Apple SDK;
- тесты чистой бизнес-логики;
- проверку конфигурации и публикацию логов;
- подготовку входных данных для Apple-specific action.
На macOS следует направлять:
- компиляцию и линковку iOS-целей;
- действия, использующие
iphoneosилиiphonesimulatorSDK; - запуск Simulator;
- обработку bundle и ресурсов Apple-приложения;
- code signing и работу с provisioning profile;
- создание архива и экспорт IPA либо другого релизного артефакта.
Здесь важно не смешивать три разных понятия:
- удалённый кэш хранит результаты action;
- удалённое выполнение запускает action на управляемом исполнителе;
- удалённый Mac предоставляет саму macOS-среду, Xcode и Apple SDK.
Документация Bazel по правилам удалённого выполнения не означает, что любой удалённый исполнитель может запускать Apple-действия. У него должны быть нужная операционная система, toolchain, SDK, разрешения и безопасный доступ к секретам.
Проверять реальный маршрут следует подробным журналом:
bazel build \
--announce_rc \
--subcommands=pretty_print \
--verbose_failures \
//path/to:ios_app
В отчёте нужно сохранить action mnemonic, платформенные ограничения, команду, executor и причину промаха кэша. Если система CI показывает только зелёный статус, этого недостаточно: успешный результат мог быть восстановлен из кэша и не подтвердить работу текущего Mac-узла.
Воспроизводимость строится вокруг явных зависимостей
Сборка считается воспроизводимой не тогда, когда один разработчик дважды получил зелёный статус, а когда чистый клон на новом исполнителе даёт сопоставимый набор результатов. Для Bazel 9 нужно отдельно проверить:
- загрузку модулей из
MODULE.bazel; - отсутствие зависимости от старого
WORKSPACE; - версии rules и их транзитивных зависимостей;
- путь к Xcode и выбранный SDK;
- переменные
PATH,DEVELOPER_DIRи аналогичные настройки; - наличие локальных скриптов на диске хоста;
- инструменты, которые вызываются, но не объявлены как inputs;
- доступ к сертификатам, связке ключей и provisioning profile.
Частая ошибка — считать локально установленный бинарник частью Bazel-графа. На одном Mac он находится в PATH, на другом отсутствует; результатом становится неочевидный сбой только после замены узла. Аналогично опасны абсолютные пути к рабочему столу, файлам пользователя и каталогам кэша.
Минимальный эксперимент должен включать четыре запуска:
- сборка после чистого клонирования;
- повторная сборка на том же узле;
- сборка после очистки локального кэша;
- сборка на заменяемом Mac-узле с тем же описанием среды.
Сравнивать нужно не только код возврата. В журнале фиксируются версии, action, входные файлы, cache hit или miss, итоговый bundle, тестовые результаты и причина любого отличия. Для релизного контура отдельно сохраняются checksum архива и сведения о подписи.
Кэш и время очереди показывают реальное узкое место
Смешанная архитектура не становится эффективнее автоматически. Она может ускорить поток, если Linux освобождает Mac от независимых подготовительных задач, но может добавить задержку из-за очереди, передачи больших inputs и холодного кэша.
Решение следует принимать по наблюдаемым метрикам:
- доля cache hit отдельно для Linux и macOS;
- время ожидания Mac-исполнителя;
- длительность компиляции, линковки, Simulator и подписи;
- объём переданных входов и выходов;
- число повторных запусков после сбоев;
- время восстановления узла после перезапуска;
- частота блокировки очереди Apple-задачами.
Не следует заранее обещать процент ускорения: он зависит от размера проекта, структуры графа, кэша и нагрузки. В отчёте лучше разделять четыре причины задержки — вычисления, ввод-вывод, маршрутизация и нехватка исполнителей.
Удалённый кэш полезен только при корректной идентичности среды. Если результаты Xcode 26, SDK или signing action смешиваются без контроля, высокий cache hit может скрывать неправильный артефакт. Для подписи кэш и секреты должны рассматриваться раздельно: сертификат не должен попадать в общий архив входов или выходов.
Подпись и Simulator формируют границу поставки
Без подписи можно проверить часть компиляции, но нельзя доказать работоспособность релизной цепочки. В production-проверку следует включить:
- сборку под устройство;
- Simulator-тесты;
- создание архива;
- проверку подписи;
- экспорт артефакта;
- проверку bundle identifier и provisioning profile;
- сохранение диагностических логов;
- повтор после перезапуска узла.
Apple описывает связь сертификатов и provisioning profile в технической заметке о code signing. Из этого следует практическое правило: ключевая связка, профиль и пароль не должны жить в исходном репозитории или в общей файловой системе кэша.
Для безнадзорной сессии нужно заранее решить, каким способом CI получает секреты. Ручной вход в графическую сессию, всплывающее окно Keychain или подтверждение на рабочем столе делают узел непригодным для устойчивого CI. Ключи следует выдавать короткоживущему заданию с минимальным доступом, а после завершения проверять очистку временных файлов.
В рабочей диагностике полезен отдельный блок:
xcodebuild -version
security find-identity -v -p codesigning
xcrun simctl list devices available
bazel test --test_output=errors //path/to:ios_tests
Команда security не должна выводить секреты в общий лог. В систему отчётности передаются только факт наличия подходящей identity, тип подписи и результат проверки.
Три архитектуры и условия выбора
Таблица ниже предназначена для первичного выбора, а не для замены нагрузочного теста.
| Архитектура | Linux-задачи | Apple-задачи | Когда выбирать | Главный риск |
|---|---|---|---|---|
| Один удалённый Mac | Минимальные или отсутствуют | Весь iOS-граф | Небольшая команда, один проект, нужен быстрый замкнутый контур | Очередь и единая точка отказа |
| Linux плюс удалённый Mac | Анализ, генерация, независимые тесты | SDK, Simulator, подпись, архив | Средняя и крупная команда с существующим Linux CI | Ошибочная маршрутизация или рассинхрон среды |
| Только Linux | Неплатформенные проверки | Не выполняются полноценно | Только подготовительный этап или не-Apple-проект | Нет доказательства поставки iOS |
| Несколько Mac-узлов | Подготовка и диспетчеризация | Параллельные Apple-задачи | Очередь и время ожидания подтверждённо ограничивают выпуск | Рост затрат и сложность одинаковых образов |
Небольшой команде обычно выгоднее сначала использовать один изолированный удалённый Mac, пройти полный путь от чистого клона до подписанного архива и собрать журнал. Средней или крупной команде рациональнее оставить Linux для независимых действий, а Apple-часть направить на Mac через платформенные ограничения. Переход к нескольким узлам оправдан только после измерения очереди и повторных запусков.
Подробности подключения и доступные варианты среды можно сверить в описании удалённого Mac от NodeMini. Это не заменяет техническую приёмку, но помогает разделить вопрос доступа к реальному Mac и вопрос корректной конфигурации Bazel.
Приёмка узла до включения в постоянный CI
Перед регистрацией узла в общей очереди полезно оформить протокол с конкретными результатами. В нём не должно быть формулировки «сборка работает» без указания действия, платформы и входной среды.
| Область проверки | Тест | Доказательство прохождения | Решение при сбое |
|---|---|---|---|
| Совместимость | Сборка минимального iOS-проекта | Версии Bazel, rules, Xcode, SDK в логе | Зафиксировать другую комбинацию |
| Маршрутизация | Подробный action log | Исполнитель и platform constraint | Исправить toolchain или правило |
| Чистая сборка | Удалить локальный кэш и повторить | Сопоставимые выходы и ошибки | Найти скрытый input |
| Simulator | Запустить тесты без GUI | Отчёт тестов и код возврата | Проверить runtime и безнадзорный режим |
| Подпись | Архив и экспорт | Архив, identity, profile, checksum | Пересобрать secret flow |
| Восстановление | Перезапуск и потеря соединения | Автоматическое продолжение либо чистый retry | Устранить ручную зависимость |
| Замена узла | Новый Mac с тем же образом | Одинаковые action и toolchain | Запретить локальную настройку |
Дополнительно стоит протестировать смену Xcode 26 на зафиксированную альтернативу только в изолированной очереди. Нельзя менять toolchain внутри общего кэша без отдельного пространства ключей. Иначе старые результаты будут выглядеть пригодными, хотя были созданы другим SDK.
Для удалённого доступа применяется тот же принцип: SSH, VNC и веб-консоль решают задачу управления хостом, но не задачу автоматизации сборки. В CI должен использоваться исполнитель, который стартует без ручного открытия рабочего стола. Инструкции по восстановлению доступа и диагностике среды находятся в справочном центре NodeMini.
Три рабочих таблицы для итогового решения
Первая таблица помогает назначить действия до запуска:
| Тип действия | Базовый узел | Условие переноса | Контрольный журнал |
|---|---|---|---|
| Статический анализ | Linux | Нет Apple SDK и локальных Xcode-инструментов | Команда, версия анализатора |
| Генерация кода | Linux | Все генераторы объявлены в графе | Inputs и outputs |
| Swift-компиляция iOS-цели | macOS | Используется Apple SDK и rules_apple | Toolchain и SDK |
| Simulator-тест | macOS | Требуется runtime Simulator | Device, runtime, тестовый отчёт |
| Архивирование | macOS | Создаётся Apple archive | Путь архива и checksum |
| Подпись и экспорт | macOS | Нужны identity и profile | Identity, profile, export log |
Вторая таблица показывает, какие показатели нужно собирать неделю перед расширением. Фиксированные числовые пороги здесь намеренно не задаются: их следует вывести из собственного SLA и размера очереди, а не выдавать за универсальные значения.
| Показатель | Что записывать | Как трактовать |
|---|---|---|
| Cache hit | По каждому типу action | Низкое значение указывает на нестабильные inputs или новый узел |
| Ожидание Mac | Время от постановки до старта | Рост означает дефицит исполнителей либо неверный приоритет |
| Длительность стадий | Компиляция, тесты, архив, экспорт | Показывает настоящую, а не суммарную задержку |
| Retry rate | Причина каждого повторного запуска | Разделяет ошибки среды и ошибки кода |
| Восстановление | Время и ручные действия после сбоя | Указывает, можно ли считать узел серверным |
Третья таблица связывает результат с архитектурным решением:
| Наблюдение | Рекомендуемый следующий шаг |
|---|---|
| Один проект, полный цикл проходит на одном Mac | Оставить один удалённый Mac и документировать резервное восстановление |
| Linux выполняет независимые этапы, но Apple-очередь растёт | Ввести гибридную маршрутизацию и измерить дополнительный Mac |
| Чистая сборка отличается на новом узле | Не расширять кластер, пока не устранены скрытые зависимости |
| Подпись требует ручного GUI-входа | Остановить production-внедрение и переделать secret flow |
| Кэш даёт результаты только на конкретном хосте | Разделить ключи среды и описать все inputs |
| Сбой после перезапуска не восстанавливается | Добавить автоматический retry либо заменить узел |
FAQ для проектной команды
Полный iOS-граф должен иметь Xcode?
Да, если результатом является поставляемое приложение. Bazel может управлять графом, но Xcode предоставляет SDK и инструменты Apple. Без установленного и выбранного Xcode можно оставить только те этапы, которые не обращаются к Apple toolchain.
Может ли CI на Bazel 9 полностью работать в Linux?
Полностью — только если под «работать» понимать подготовительные действия. Для iOS-доставки Linux не заменяет macOS: компиляция Apple-целей, Simulator, архив, подпись и экспорт требуют отдельной проверки на Mac. Поэтому Linux остаётся полезной частью, но не единственным узлом полного контура.
Как доказать, что action действительно выполняется на macOS?
Нужно включить подробный вывод Bazel, сохранить action mnemonic, выбранную платформу, toolchain и сведения об исполнителе. Название job или shell-скрипта ничего не доказывает. Дополнительным подтверждением служат путь к SDK, версия Xcode и запись фактического executor в журнале CI.
Как подключить удалённый Mac к смешанному кластеру?
Сначала описывается платформа macOS с нужными ограничениями, затем исполнитель регистрируется в системе CI с установленными Bazel, rules и Xcode. После этого минимальный проект запускается с очищенным кэшем, а отдельно проверяются очередь, удалённое выполнение, кэш и восстановление. Доступ по SSH сам по себе кластером не является.
Что означает успешная приёмка Bazel iOS-узла?
Узел принят только после повторяемой чистой сборки, Simulator-тестов, архива, подписи, экспорта и восстановления после перезапуска. Если прошла лишь компиляция без подписи, это подготовительный исполнитель, а не production-узел. Все версии и секретные операции должны быть зафиксированы в протоколе без публикации самих ключей.
Практический вывод для выбора Mac-архитектуры
Linux остаётся выгодным местом для платформенно-независимых задач, но попытка держать там весь Bazel 9 iOS-процесс создаёт три системных недостатка: Apple SDK и Xcode оказываются вне естественной среды, подпись превращается в отдельный ручной обход, а успешная промежуточная сборка создаёт ложное ощущение готовности релиза. Виртуализация или случайно настроенный удалённый исполнитель добавляют риск рассинхронизации toolchain и скрытых зависимостей.
Реальный удалённый Mac закрывает именно этот пробел: он даёт постоянную macOS-среду для Apple action, но не отменяет необходимость описать платформы, секреты, кэш и тесты восстановления. Поэтому разумный путь — сначала прогнать на одной изолированной машине настоящий проект Bazel 9, зафиксировать результаты при чистой сборке, подписи, Simulator и перезапуске, а затем по фактической очереди решить, нужен ли постоянный арендованный узел или смешанный кластер Linux плюс macOS.
Если требуется временная среда для такой проверки, NodeMini можно рассматривать как способ получить отдельный Mac без немедленной покупки физического оборудования. После протокола приёмки команда сможет обоснованно выбрать срок аренды, количество узлов и границы кэша, а не масштабировать CI по предположениям.