Добавить объявление

Один пакет для всех тестов: радикальный подход к ускорению CI

Представьте CI-пайплайн, где каждый пул-реквест собирается 30 минут. Разработчики теряют фокус, контекст переключается, а поток работы прерывается. Теперь представьте тот же пайплайн, но работающий за 2.5 минуты. Это не фантастика, а реальная история оптимизации, которая началась с трех Mac mini и закончилась архитектурным прорывом в организации сборок Xcode.

Когда рост становится проблемой:


Все начиналось невинно: добавили тесты для первого пакета, создали конфигурацию. Потом подключили второй пакет - добавили еще одну конфигурацию. К третьему пакету шаблон был очевиден. Через несколько месяцев в проекте оказалось 91 отдельная конфигурация для сборки и тестирования.

Казалось логичным: изменили код в пакете А - запустились только его тесты. Но реальность оказалась сложнее. Основное приложение собиралось 12 минут - это был физический предел скорости. Остальное время уходило на дублирование работы: двадцать разных процессов могли одновременно пересобирать одни и те же зависимости, потому что каждый начинал с нуля.

Миф о портативном кэше:


Первая мысль любого инженера при оптимизации сборок - кэширование. С Xcode эта идея разбивается о суровую реальность.

Оказывается, DerivedData - директория, где Xcode хранит промежуточные артефакты сборки - содержит абсолютные пути, вшитые прямо в файлы. Попробуйте скопировать эту папку в другое место и Xcode просто проигнорирует ее, как будто кэша не существует.

Попытка использовать стандартный механизм кэширования GitHub тоже провалилась: 20 гигабайт данных нужно было скачивать перед каждой сборкой. На обычном интернет-соединении это занимало больше времени, чем полная пересборка проекта.

Но самая глубокая проблема обнаружилась дальше: Xcode принципиально не делится артефактами между разными типами целей. Приложение и Swift Package, даже если они используют один и тот же код, компилируются независимо. Два разных пакета в одном рабочем процессе тоже не могут использовать общие промежуточные файлы. Получался абсурд: изменение в одной библиотеке запускало двадцать параллельных процессов, каждый из которых компилировал одни и те же исходники с нуля.

Архитектурное решение - два мира, два кэша:


Прорыв пришел с парадоксальной идеей: а что если на время CI временно объединить все 89 Swift-пакетов в один гигантский пакет?

Такой подход дает лучшее из двух миров: максимальное повторное использование скомпилированного кода для тестирования и строгий контроль зависимостей для продакшен-сборки.

Система слотов - стабильность через постоянство:


Ключевое открытие оказалось удивительно простым: единственный способ заставить Xcode надежно использовать кэш - никогда не перемещать его. Директория DerivedData должна оставаться на одном и том же месте всегда.

Так родилась концепция «слотов» - фиксированных рабочих каталогов, которые процессы CI не копируют, а используют напрямую.

Интеллектуальный отбор тестов:


Объединение 91 конфигурации в единую систему потребовало умного алгоритма определения того, что действительно нужно проверить. На практике это выглядит так: изменили одну строку в библиотеке - автоматически запустились 12 связанных тестовых наборов и 3 сборки приложений. 61 несвязанный тест был пропущен. В сочетании с предварительно подготовленными кэшами большинство проверок затрагивают менее 20% кодовой базы.

Вывод:


Настоящая ценность этой оптимизации измеряется не в минутах, а в восстановленном потоке работы. Когда обратная связь от системы непрерывной интеграции приходит за 2,5 минуты вместо 30, исчезает внутреннее сопротивление перед созданием пул-реквестов. Разработчики остаются погруженными в задачу, ошибки обнаруживаются и исправляются сразу, циклы итерации становятся короче и эффективнее.

Эта история - прекрасная иллюстрация важного принципа: иногда самые значительные улучшения достигаются не заменой инструментов, а глубоким пониманием их внутренней работы и творческой адаптацией под конкретные нужды.
21.01.2026 14 116