За неделю до релиза Phone 18 в переговорке никто не обсуждал фокусные расстояния камеры — все смотрели на ленту Actions, усыпанную красными точками. Xcode 27, SDK iOS 27, хостед-лейбл GitHub xcode-27 плюс несколько только что подвешенных self-hosted runner’ов на Apple Silicon: четыре линии в одну неделю. Съедет любая — и скриншоты App Store, и внешняя TestFlight-бета встанут вместе. Мы пересобрали весь iOS CI/CD с «зеленеет» на «держит релизную неделю» и записали здесь реальные грабли.
Временной якорь жёсткий: 10 сентября 2026 GitHub в Changelog пишет, что образ runner’а Xcode 27 теперь крутится на macOS 27 — лейблы те же xcode-27 / xcode-27-xlarge, и есть только arm64. Job’ы, которые накануне «случайно зеленели» на базе macOS 26, после смены образа могут лечь пластом из‑за версии ОС, имени симулятора или дискового запаса. Окно GA Golden Gate и срез моделей — во внутренней статье macOS 27 Golden Gate: дата релиза и поддерживаемые модели; здесь только сторона CI: куда наступаешь, как зачищаешь.
Зачем перед релизом прогонять всю цепочку — а не просто поправить matrix
Мышечная память многих команд: сменить xcode-version с 26 на 27, в destination поставить iPhone 18. Перед окном Phone 18 так почти всегда взрывается. Три слоя. Первый: SDK и образ runner’а не выходят синхронно — локально уже Xcode 27 GM, хостед-образ ещё в public preview или только что уехал с базы macOS 26 на 27. Второй: таблица устройств симулятора меняется. В runtime iOS 27 захардкоженного имени может не быть; name=iPhone 18 сразу даёт Unable to find a device matching the provided destination. Третий: cache key для DerivedData, SwiftPM и CocoaPods, если всё ещё привязан к путям «до большого скачка Xcode», попадание в старые артефакты — это вчерашний мусор в сегодняшнем компиляторе.
Мы разводим приёмку: «собирается» и «держит релизную неделю». Первое — зелёный PR-job. Второе — concurrency, диск, сертификаты и холодный старт Simulator не топчут друг друга на пике. Хостед — сначала документация GitHub-hosted runners; self-hosted — по лейблам разнести быстрый PR-фидбек и тяжёлые Archive — та же логика, что в GitHub Actions тормозит iOS/Xcode CI: новый подход, только триггер теперь Phone 18.
Грабля 1: всё ещё macos-latest — или xcode-27 значит «подойдёт любой Mac»
macos-latest осенью 2026 всё ещё годится для «как-то бежит», но не годится как единственная поверхность приёмки в неделю Phone 18. Это политика дефолтного образа GitHub, а не «уже есть SDK iOS 27 + нужный Simulator». Для Xcode 27 явно пишите runs-on: xcode-27 (тяжёлые job’ы — xcode-27-xlarge), а не надейтесь, что maxim-lobanov/setup-xcode спасёт на кривой базе — без runtime даже правильный Xcode.app не вытянет simctl.
Тоньше: Changelog подчёркивает только arm64. Если в оргe остались Intel self-hosted узлы или runs-on: [self-hosted, macOS] читается как «любой Mac берёт Xcode 27 job», matrix превращается в «иногда зелёный, иногда красный» — зелёный попал на Apple Silicon, красный на Intel, где toolchain не встанет. Лейблы — реальное готовое множество, например [self-hosted, macOS, ARM64, xcode27], а неапгрейженные узлы с лейбла снять.
xcode-27, кто compat-линию macos-26 — в runbook и раз в неделю сверять на странице Runners в Actions.
Грабля 2: destination жёстко на iPhone 18 — в образе ещё нет этого «телефона»
Маркетинговый Phone 18 и имя устройства в Simulator — разные таблицы. В actions/runner-images runtime iOS 27.0 сейчас перечисляет серию iPhone 17, iPhone Air, несколько iPad — без гарантии, что в день презентации появится симулятор с именем iPhone 18. YAML вида -destination 'platform=iOS Simulator,name=iPhone 18,OS=27.0' — самая частая самострельная рана перед релизом.
Воспроизводимо: в начале job’а xcrun simctl list devices available -j, разобрать JSON, отсортировать «runtime iOS 27 + имя начинается с iPhone», взять одну машину, UDID в $GITHUB_OUTPUT, дальше xcodebuild test только с id=…. Мультидевайс-matrix: пересечение доступного списка ∩ ваш whitelist; нет устройства — skip и алерт, а не красная вся Release. UI-тесты: первый холодный старт новой ОС часто съедает лишние минуты — timeout всё ещё как при Xcode 26 на 20 минут, и редкий флейк становится системным.
| Запись | Риск релизной недели | Более стабильная замена |
|---|---|---|
name=iPhone 18,OS=27.0 | Имени нет → fail | Динамический выбор по UDID |
OS=27.0 в жёсткий пин | Образ может быть 27.0.1 | Префикс runtime или последний доступный |
Только macos-latest | SDK/симулятор неопределённы | Явная приёмка на xcode-27 |
| Intel + Apple Silicon вперемешку | Один job, спорадические arch-fail | Разнести пулы, линия 27 только ARM64 |
Грабля 3: cache key не инвалидировали с Xcode 27 — зелёный это «старый мир»
На major-апгрейде самая убедительная иллюзия «починили» — из кэша. SwiftPM SourcePackages, CocoaPods Pods, свои tarball DerivedData: если в key только ветка или hash lockfile без $(xcodebuild -version) / версии образа, Actions радостно restore’ит артефакты эпохи Xcode 26. Симптомы: нет символов на линковке, падает Swift driver, тесты собираются, но падает проверка module cache.
Мы жёстко кладём в key три сегмента: короткий номер Xcode, версия runner image (хостед — из env или доки), плюс Package.resolved / Podfile.lock. В день апгрейда меняем префикс — лучше +8–12 минут холодного старта, чем в релизную неделю разбирать, почему тот же commit локально зелёный, а в CI нет. Self-hosted Apple Silicon: общий DerivedData между job’ами без каталогов по версии Xcode смешивает preview и GM.
Грабля 4: Apple Silicon runner’ы есть — диск и очередь возвращают вас на старт
Xcode 27 + runtime iOS 27 + несколько Simulator — убийцы диска. Хостед-образ xcode-27 уже тяжёлый; self-hosted Mac mini с Xcode 26, двумя бетами и DerivedData трёх приложений часто взрывается ближе к концу Archive: No space left on device. Перед релизной неделей: одна «текущая приёмка» Xcode, whitelist runtime’ов, DerivedData чистить по job’у, ночной cron с df -h и xcrun simctl delete unavailable.
Очередь не исчезает от Silicon. Перед презентацией matrix раздувается с «один SDK» до «compat 26 + приёмка 27» — хостед concurrency и self-hosted слоты забиваются одновременно. Быстрый PR-фидбек и TestFlight Archive обязаны жить на разных лейблах: минуты для первого, очередь ок для второго, но без блокировки первого. Кто берёт слот на пике — это дизайн лейблов, а не M-серия на постере.
Поведение toolchain — по документации Apple Xcode; какие Simulator в образе — по Readme runner-images за эту неделю, не по скриншоту блога прошлого года.
Runbook релизной недели: как мы реально переключались
День 1: приёмочный job прод-PR явно на xcode-27, оставить compat-сентинел macos-26 (или предыдущее поколение) — 27 красный блокирует merge, 26 красный только алерт. День 2: все test destination на динамический UDID, маркетинговые имена из YAML убрать. День 3: инвалидировать префикс кэша, форсировать холодную сборку, зафиксировать wall time как baseline. День 4: self-hosted пул разнести: ci-pr и ci-release, без Intel-узлов на лейбле 27.
Дежурство ежедневно: Changelog / runner-images на новый образ, диск runner’ов, отношение queued/run — жёлтый длиннее зелёного? Цифры хуже — сначала режем параллелизм matrix, потом машины: больше машин без разделения лейблов только ускоряет неправильные job’ы.
xcode-27 уже стоит в очереди дольше компиляции, или нужно закрепить конкретный GM-образ под нотаризацию и аплоад — суточная аренда Apple Silicon облачного Mac как self-hosted часто дешевле лотереи в общей очереди. Выделенные узлы — для Release и цепочки сертификатов; массовый PR-конкуренс можно оставить на хостед preview-образах как первый шлюз.
FAQ
Нужно ли сразу переносить все job’ы на xcode-27?
Нет. Релизная приёмка и store-сборки — на 27; compat старых ОС и хотфиксы могут пока остаться на предыдущем образе. Главное — ясные лейблы и branch protection, без «любой зелёный = merge».
Локально Xcode 27 зелёный, CI всё ещё красный — почему?
Сначала три проверки: runner правда arm64 xcode-27, имя симулятора существует, кэш не попал в старый DerivedData. Локально полный runtime, в хостед-образе — не обязательно.
Можно ли смешивать self-hosted и GitHub-hosted в одном workflow?
Можно, но разными job’ами и лейблами. Не давайте одному массиву runs-on матчить и хостед, и любой self-hosted — иначе scheduling невоспроизводим.
Может ли тест на реальном Phone 18 заменить Simulator CI?
Нет. Устройство — камера, сотовая сеть, ощущение перфоманса; CI — compile, юнит-тесты и регрессия на каждый PR. Релизная неделя нужна обеим сторонам — устройство не как единственный merge-gate.
Окно Phone 18 · Дежурство Xcode 27
Нужен Apple Silicon без лотереи общей очереди?
Закрепите Release и нотаризацию на выделенном облачном Mac; PR оставьте на хостед xcode-27. Суточная проверка образа и цепочки сертификатов спокойнее, чем гонка за concurrency в день презентации.