Сборка Xcode Cloud слишком медленная не означает, что миграцию нужно начинать сегодня. В отчёте одной сборки разделите время минимум на 4 зоны: ожидание запуска, подготовку окружения и зависимостей, фактическую сборку, тесты и архивирование. Такой порядок опирается на структуру рабочих процессов и отчётов Xcode Cloud, описанную в официальном справочнике Apple по Xcode Cloud. На этой неделе возьмите один и тот же commit, снимите нормальный и аномальный образец, а затем оптимизируйте только устойчиво медленный этап. Если причина — временное окружение, постоянные сервисы или отсутствие контроля над хостом, переносите соответствующую задачу на удалённый Mac, сохраняя Xcode Cloud для стандартной сборки или проверки релиза.
Эта статья предназначена для трёх групп. Первая — разработчики Apple-платформ, у которых время Xcode Cloud постепенно растёт, но источник задержки ещё не установлен. Вторая — CI-инженеры со сложными зависимостями, кэшем и фоновыми сервисами. Третья — руководители разработки, выбирающие между дополнительным вычислительным временем, удалённым Mac и двухконтурным CI.
01Карта задержки
Не сравнивайте только итоговое время двух запусков. Две сборки могут завершиться примерно в один момент, но одна потеряла время на загрузке Swift Package и CocoaPods, а другая — на расширенной матрице UI-тестов. В первом случае нужен аудит подготовки, во втором — изменение рабочих процессов.
Соберите для каждого запуска следующие поля:
- идентификатор commit;
- название Scheme;
- рабочий процесс и причину запуска;
- время ожидания до старта;
- длительность подготовки окружения;
- команды post-clone и их вывод;
- скачивание и проверку зависимостей;
- компиляцию;
- число тестовых наборов и конфигураций;
- архивирование и выгрузку результата;
- статус завершения и ссылку на исходный журнал.
Для наблюдения используйте не один удачный запуск. Возьмите несколько запусков одного и того же commit либо близких изменений, если повторить commit невозможно. Зафиксируйте типичный результат и отдельный аномальный случай. Это не универсальный порог миграции: Apple не публикует единого значения, после которого любой проект следует переносить с Xcode Cloud.
Данные использования нужны для финансового и операционного контекста. В документации Apple по данным использования Xcode Cloud проверяйте расход вычислительного времени и сопоставляйте его с числом рабочих процессов. Рост расхода сам по себе не доказывает, что удалённый Mac дешевле. Он показывает, что следует искать дублирование запусков, широкий тестовый набор или неудачную стратегию триггеров.
02Зависимости и временная среда
Xcode Cloud не следует воспринимать как постоянно включённый сервер с гарантированно сохранённым локальным состоянием. Для каждой сборки важны исходный репозиторий, lock-файлы, доступ к закрытым зависимостям и действия, выполняемые скриптами. Apple отдельно описывает способы сделать зависимости доступными для Xcode Cloud.
Проверьте post-clone-скрипт по строкам:
- Какие инструменты он устанавливает при каждом запуске?
- Можно ли получить их из воспроизводимого источника?
- Указаны ли версии Swift Package, CocoaPods и Carthage?
- Требует ли закрытый репозиторий отдельной авторизации?
- Не скачивает ли скрипт один и тот же архив несколько раз?
- Есть ли понятный код выхода при ошибке?
- Не запускает ли он фоновый процесс, который потом недоступен следующему действию?
Особое внимание уделите скрытым ожиданиям. Команда может не завершаться из-за запроса авторизации, повторной сетевой попытки или ожидания внешнего API. В итоговом журнале это иногда выглядит как «долгая установка», хотя проблема находится не в самом менеджере пакетов.
Нормальная оптимизация здесь — сделать подготовку детерминированной. Lock-файлы должны быть частью репозитория. Секреты нельзя печатать в журнал. Версии инструментов должны быть видны в логе. Команды следует разделить так, чтобы было понятно, где заканчивается настройка среды и начинается сборка.
Перенос на удалённый Mac становится обоснованным, если задача требует локальной службы, подключения к внутренней сети, постоянного процесса или файлов, которые должны переживать следующий запуск. Временный обход через случайные файлы кэша не заменяет управляемую среду.
03Кэш и чистая сборка
Фраза «кэш не работает» слишком неточна для диагностики. В проекте могут одновременно существовать несколько разных состояний:
- Derived Data;
- загруженные и разрешённые зависимости;
- результаты пользовательского скрипта;
- артефакты предыдущей сборки;
- файлы, созданные инструментами проекта;
- данные симулятора и тестов.
Они имеют разный жизненный цикл и разные требования к проверке. Поэтому сначала зафиксируйте, что именно очищает рабочий процесс. Ненужный Clean Build способен сделать каждый запуск похожим на первый, но отключение очистки без контроля воспроизводимости тоже опасно.
Сделайте три отдельных наблюдения:
- чистый запуск после полного удаления временных результатов;
- запуск с доступным кэшем;
- инкрементальный запуск после небольшого изменения исходного кода.
Сравнивайте не только длительность. Запишите, какие команды выполнялись, какие зависимости были скачаны, какие файлы использовались и совпал ли итоговый артефакт. Если ускорился только второй запуск, но третий нестабилен, вы получили не решение, а зависимость от неявного состояния.
В справочнике рабочих процессов Xcode Cloud проверяйте фактические параметры выбранного процесса, а не настройки соседнего workflow. Названия могут быть похожими, а условия запуска, Scheme и тестовый план — разными.
04Предупреждение. Не переносите на удалённый Mac проблему, которую вызывает неверная очистка проекта. Сначала добейтесь воспроизводимого результата на одном commit. Иначе вы получите более доступный хост, но сохраните ошибочную логику CI.
Тестовая матрица и триггеры
Тесты часто создают иллюзию медленной компиляции. Один commit может запускать несколько рабочих процессов: проверку запроса на слияние, тест основной ветки, архивирование и полный регресс. Если каждый процесс повторяет одну и ту же установку зависимостей и часть тестов, задержка растёт даже при стабильной скорости самого Xcode.
Разделите проверки на три уровня:
- быстрый набор для запроса на слияние;
- обязательная регрессия основной ветки;
- полный тестовый план по расписанию или перед выпуском.
Это не означает, что нужно без анализа удалить симуляторы. Сначала найдите повтор. Затем решите, требуется ли конкретная конфигурация для обнаружения ошибки. UI-тесты и архивирование не всегда должны блокировать короткую проверку изменения.
| Задача | Что оставить в Xcode Cloud | Что проверить перед переносом |
|---|---|---|
| Быстрая проверка PR | Компиляция и ограниченный набор тестов | Не дублируется ли тот же запуск другим триггером |
| Регрессия основной ветки | Полный тестовый план | Какие устройства и Scheme действительно дают дополнительное покрытие |
| Архивирование | Отдельный процесс с нужными секретами | Не запускается ли архив на каждый небольшой commit |
| Плановый полный прогон | Периодический workflow | Можно ли отделить его от обратной связи разработчика |
Автоматическая отмена устаревших запусков полезна, когда новые commit делают старую проверку нерелевантной. Но она не должна отменять единственный процесс перед публикацией. Сопоставьте условия старта, ветки и типы изменений с назначением каждого workflow. Описание действий и настроек рабочего процесса Apple используйте как справочник, а не как замену проверке собственных журналов.
Сценарий из практики
Представьте проект, где разработчик считает медленным этапом компиляцию: итоговая строка показывает большой общий интервал. После разметки журнала выясняется, что post-clone устанавливает инструменты, затем выполняются повторные проверки закрытых пакетов, а полный UI-тест запускается вслед за отдельным PR-тестом.
В такой ситуации перенос всей системы на удалённый Mac преждевременен. Сначала нужно убрать лишний триггер, проверить lock-файлы, разделить рабочие процессы и оставить архивирование отдельной задачей. Если после этого остаётся обязательный сервис, который нельзя поднять в временной среде, на удалённый Mac переносится только соответствующий тестовый workflow.
Обратный случай выглядит иначе. Подготовка требует внутреннего API, локальной базы, сохранённого набора тестовых данных и фонового процесса. Каждый запуск тратит время на восстановление этих компонентов, а после сбоя их приходится собирать заново. Здесь оптимизация Xcode Cloud может уменьшить отдельные задержки, но не устранит ограничение архитектуры.
05Сравнение вариантов миграции
Решение принимайте по типу проблемы, а не по раздражению от одного медленного запуска.
| Критерий | Оптимизированный Xcode Cloud | Удалённый Mac | Двухконтурная схема |
|---|---|---|---|
| Окружение | Временное и описанное workflow | Постоянное, с контролем хоста | Стандартная проверка плюс постоянный узел |
| Зависимости | Lock-файлы, скрипты, поддерживаемый кэш | Можно сохранять состояние, но его нужно обслуживать | Простые зависимости в Xcode Cloud, сложные — на Mac |
| Доступ к сервисам | Только допустимые сетевые сценарии | Возможен собственный сетевой маршрут | Закрытые тесты на Mac, публикация в Cloud |
| Параллельность | Зависит от рабочих процессов и условий сервиса | Планируется вами, но ограничена числом узлов | Разные типы задач распределяются отдельно |
| Воспроизводимость | Выше при строгой конфигурации | Требует контроля изменений на хосте | Нужны одинаковые Scheme и тестовые правила |
| Обслуживание | Меньше управления машиной | Обновления, доступы, восстановление | Обслуживаются только специальные задачи |
| Лучший сценарий | Стандартная сборка и релизная проверка | Постоянные сервисы и сложные тесты | Смешанный проект с разными требованиями |
Результат сравнения должен быть задачно-ориентированным. Не переносите весь CI только потому, что один UI-тест зависит от постоянной базы. Сначала отделите этот набор, измерьте его на удалённом Mac и проверьте восстановление после перезагрузки.
06Пошаговая проверка перед решением
Шаг 1. Зафиксируйте одинаковую точку сравнения
Используйте один commit, одну Scheme, один набор тестов и одинаковые параметры подписи. Если сравнивать разные изменения, вы не узнаете, повлияла ли миграция или просто изменился код.
Шаг 2. Разметьте журнал
Запишите начало и конец ожидания, подготовки, установки зависимостей, компиляции, тестов и архивирования. Для действий, которые выполняются скриптом, добавьте явные сообщения с названием этапа. Не выводите токены и приватные ключи.
Шаг 3. Проверьте условия запуска
Составьте список событий, запускающих каждый workflow. Укажите ветку, тип изменения, ручной запуск, расписание и правило отмены. Удалите только те повторы, которые подтверждены логами.
Шаг 4. Проведите чистый и повторный запуск
Сравните поведение без кэша, с кэшем и после небольшого изменения. Убедитесь, что ускорение не связано с остаточным файлом, который не гарантированно существует в следующем окружении.
Шаг 5. Приведите зависимости к воспроизводимому виду
Проверьте lock-файлы, версии инструментов, доступ закрытых репозиториев и результат post-clone. Рекомендации Apple по пользовательским build-скриптам применяйте вместе с журналом: скрипт должен иметь понятные ошибки, ограниченный объём повторов и предсказуемый код выхода.
Шаг 6. Проверьте сетевые ограничения
Если скрипт использует внешние запросы, журналируйте начало, завершение и ошибку каждой критичной операции. В справочнике переменных окружения Xcode Cloud проверьте доступные параметры среды и сетевого прокси. Не рассчитывайте на соединение с внутренним сервисом, пока не подтвердили его из фактического workflow.
Шаг 7. Проведите малый тест миграции
Если проблема связана с постоянной средой, перенесите одну категорию задач, а не всю цепочку. Проверьте установку инструментов, подпись, запуск Scheme, сохранность тестовых данных, журналирование и восстановление после перезапуска. Сравнение должно включать не только скорость, но и трудозатраты сопровождения. Перед началом можно сверить доступные варианты аренды удалённого Mac для разработческой среды, не принимая коммерческое решение до завершения технической проверки.
Шаг 8. Примите решение по стоп-условиям
Продолжайте использовать Xcode Cloud, если после настройки подготовка воспроизводима, тестовая матрица разумна, а стандартная сборка даёт нужную обратную связь. Переходите на удалённый Mac, если постоянное окружение является частью самой задачи. Оставляйте двухконтурную схему, если публикация и стандартные проверки уже удобны в Xcode Cloud, но сложные тесты требуют собственного узла.
07FAQ по диагностике Xcode Cloud
Почему зависимости снова устанавливаются
Временное окружение не следует считать продолжением предыдущей машины. Повторная загрузка может быть ожидаемой частью workflow, следствием post-clone-скрипта или результатом неверной настройки доступа к закрытой зависимости. Сначала определите источник по журналу, затем настройте lock-файлы и проверяемую стратегию кэширования.
Где искать время этапов
Откройте отчёт конкретной сборки, журналы действий и данные использования. Вынесите ожидание запуска в отдельную строку и не смешивайте его с подготовкой. Если отчёт не даёт нужной детализации, добавьте маркеры в собственные скрипты. Итоговая длительность без этапов пригодна только для обнаружения тренда.
Сокращать устройства или разделять workflow
Начинайте с разделения назначений. Быстрый контроль изменения и полный регресс имеют разные требования. Уменьшайте матрицу только после проверки покрытия и повторов. Если один набор тестов нужен перед выпуском, но не для каждого PR, вынесите его в отдельный workflow вместо общего сокращения проверок.
Признаки для перехода на удалённый Mac
Главные признаки — повторная инициализация постоянных сервисов, необходимость внутреннего доступа, сохранение данных между запусками и потребность управлять процессами хоста. Сравните одинаковый commit на малом объёме задач. Если после переноса заметно только изменение интерфейса, но не устранение причины, миграция не оправдана.
08Как оформить итоговую карту задач
После диагностики составьте таблицу не по продуктам, а по операциям. Для каждой задачи укажите:
- источник задержки;
- подтверждающий фрагмент журнала;
- действие по оптимизации;
- условие, при котором оптимизация прекращается;
- среду исполнения;
- владельца обслуживания;
- план восстановления;
- критерий возврата в Xcode Cloud.
Такая карта не позволяет перенести на удалённый Mac неустранённую ошибку сценария. Она также показывает, где постоянная машина действительно нужна, а где достаточно исправить trigger или тестовый план.
Если вы используете удалённый Mac как CI-узел, заранее проверьте способ подключения, права root, запуск агента после перезапуска, хранение секретов и удаление артефактов. Для временной разработки можно рассмотреть варианты аренды Mac mini, но цена не должна быть единственным критерием. Сначала подтвердите, что задача воспроизводится на выбранной конфигурации и что доступ к узлу не создаёт новый ручной этап.
09Вывод и следующий шаг
Когда основная потеря времени находится в лишних триггерах, повторной загрузке зависимостей или чрезмерной тестовой матрице, продолжайте оптимизировать Xcode Cloud. Когда задержка вызвана временным окружением, постоянными сервисами и отсутствием контроля над хостом, переносите конкретную задачу на удалённый Mac. При смешанном проекте оставьте Xcode Cloud для стандартных сборок, а специализированные проверки запускайте на отдельном узле.
Покупка собственного Mac mini даёт физический контроль, но требует единовременных затрат, самостоятельных обновлений, постоянного питания и отдельного восстановления после сбоев. Linux-сервер не заменяет macOS-инструменты, а виртуальная среда может не дать нужной совместимости с Xcode и симуляторами. Если вам нужно сначала проверить гипотезу на коротком цикле, аренда удалённого Mac через VpsMesh позволяет сравнить один класс задач без немедленной покупки оборудования. После проверки логов, воспроизводимости и восстановления вы сможете решить, оставлять ли этот узел для временных задач, переносить ли на него часть CI или сохранять двухконтурную схему.