Четверг, 29 мая 2026, 15:03. В iOS-команде из двенадцати человек в Slack #ios-ci появляется ссылка на run с текстом: «Локально в Xcode прошло за двенадцать минут — PR всё ещё в Queued.» Полчаса тишины — такие сообщения привычны. Через 48 минут та же строка: «Queued 47m, run 12m. Сегодня merge не ждите.» Коллега из release-трека выходит из переговорки, смотрит в телефон: «ios-release Archive всё ещё в очереди — PR-линия снова забита.»
Скриншоты — материалы клиента при разборе инцидента, с маскировкой; цифры не менялись. Статистику можно воспроизвести по документации GitHub о времени выполнения job и queue_wait_seconds.
Два приложения в одном monorepo, CI целиком на macOS runner'ах GitHub. В 2025 терпимо; к весне 2026 цикл обратной связи растянулся до потери терпения. GitHub Actions не «сломан», но для iOS-команд всё больше похоже на переполненный маршрутный автобус в час пик.
Тот четверг: открыть страницу Job — жёлтая полоса в четыре раза длиннее зелёной
Прислали скрин страницы Job run #18472910356 — не пересказ, а таймлайн с цифрами. Жёлтый (Queued): 47 мин 12 с, зелёный (Run): 12 мин 4 с, внизу UI: queue_wait_seconds: 2832, run duration: 724. Формула проста: wait time >> run time.
ios-pr.yml · macos-latest · 2026-05-29 15:04 CSTНа встрече обсуждали параллель -jobs и ключ кэша DerivedData — но никто не выгрузил queued и run по последним десяти macOS job'ам. На release-линии хуже: ios-release.yml совмещал xcodebuild archive, notarization, загрузку в TestFlight и unit-тесты PR в одной линии на том же macos-latest. Archive — 25–40 минут, занимает редкие слоты concurrency и тащит в очередь быстрые PR-job'ы.
Если нужно сначала разобраться с Queued, начните с нашего runbook диагностики очереди macOS runner. Перелом наступил, когда команда признала: проблема в ёмкости — и убедила зал цифрами, а не ощущениями.
Доказательства: таблица из десяти failed job'ов, переписанная вручную
Перед stand-up 30 мая кто-то вручную переписал десять failed macOS job'ов из списка Actions (см. рис. 3). Без BI — таблица с двумя ключевыми столбцами: queued_s и run_s. Медиана queued 2650 с, run 724 с; отношение до 4,5×. Несколько секунд тишины: больше никто не винил Xcode 17.4.
| Часто принимают за | Сигнал на рис. 3 | Вероятнее причина |
|---|---|---|
| Сбой GitHub | 10/10 queued_s > run_s | Лимит concurrency macOS, тяжёлые job'ы держат слоты |
| Рост проекта | run_s стабильно 690–811s | Компиляция не главный фактор — ожидание |
| Хаотичные push'и | Несколько run'ов на один PR (см. рис. 2) | Цикл Agent / bot |
| Истёкший сертификат | Ошибки подписи разбросаны по разным run'ам | Холодный keychain, параллельная конкуренция |
рис. 2: один PR запустили шесть раз
Очередь — только первая половина. В марте подключили coding Agent: CI красный → читать логи → auto-commit → снова Actions. Страница Checks PR #847 наглядна — шесть workflow run'ов, четыре от bot, два от людей. Формально CI; по факту retry loop. Подробнее: Что происходит, когда CI превращается в цикл.
ios-pr.yml (4× bot · 2× человек) · 2026-05-29Позже в команде пошутили: «CI не умер — мы сделали из него вечный двигатель.» Плохо не автоматизация, а то, что каждый bot-fix забирает общую очередь без выделенного пула runner'ов.
Перелом: Cloud Mac и split пайплайнов в ночь на 31 мая
После таблицы разложили логи по типам job'ов: ~70 % PR-проверок и интеграционных сборок, ~20 % unit-тестов на симуляторе, ~10 % archive и upload — почти как у другой команды с 500 iOS CI в день. Предлагали восемь Mac mini или Xcode Cloud. В итоге два шага:
31 мая: аренда Cloud Mac на Apple Silicon, регистрация как self-hosted runner (labels self-hosted, macOS, cloud-mac). Контрольный эксперимент простой: один commit, по 50 сборок на hosted macos-latest и на Cloud Mac — дисперсия queued с «то 40 минут, то 5» сжалась до «обычно единицы секунд».
Split пайплайнов той же ночью. Archive, export и upload из ios-pr.yml в ios-release.yml; PR-линия только build и unit-тесты. Плюс warm-up cron: каждую ночь xcodebuild build на Cloud Mac, DerivedData на диске — надёжнее, чем каждый раз холодный старт из actions/cache.
рис. 6 · Медиана wall time той же команды (PR build, по 50 run'ов за две недели)
Источник: логи контрольного эксперимента клиента (2026-05-31 — 06-08), сверяется с рис. 1 и рис. 4.
9 июня: рис. 4 — наконец страница Job «как надо»
Через две недели после split, утро понедельника: в Slack скрин run #18510488201 — queued 41 с, run 9 мин 17 с, runner self-hosted, cloud-mac. Без лайков, только реакция — для iOS-команд это высшая похвала.
self-hosted, cloud-mac · 2026-06-09 09:11 CSTОграждения для Agent: четыре правила в Wiki
Split снял блокировку Archive на PR; bot loop потребовал отдельных мер. Bot — отдельная ветка и пул runner'ов с низким приоритетом, merge после human review. Четыре жёстких правила в Wiki:
- PR workflow: без archive / без загрузки в store
- macOS job'ы с
concurrency, чтобы одна ветка не копила run'ы - Self-hosted runner'ы с labels — не смешивать с экспериментальными workflow
- Auto-push Agent только в отдельную ветку, без прямого push в protected branches
queue_wait_seconds и run duration (как на рис. 3); посчитайте workflow run'ы на PR (как на рис. 2); проверьте, не остался ли archive в PR workflow. Если медиана queued > run — сначала ёмкость runner'ов, не флаги компиляции.
Новый подход: не отказываться от GitHub, а сменить ёмкость
Опыт команды сводится к четырём вещам, которые iOS-команды чаще всего внедряют в 2026: пул Cloud Mac self-hosted runner'ов, split PR / Release, warm-up и disk cache, ограждения для Agent. Заменяет ли Xcode Cloud? Хорош для чистого Apple-стека; с GitHub PR checks и смешанной Android-линией большинство оставляет Actions и переносит только archive на выделенный macOS.
| Если команда похожа на… | Типичная рекомендация |
|---|---|
| 1–3 человека, релиз раз в неделю | Hosted macOS + сильный cache; archive Release вручную |
| 5–15 человек, merge каждый день | 1–2 Cloud Mac self-hosted + split PR/Release |
| 15+ человек, несколько app'ов | Пул runner'ов с labels по app + warm-up cron |
| Много auto-fix от Agent | Отдельный bot pool + без archive в PR workflow |
В документации Apple xcodebuild подчёркивается согласованность scheme и destination; если CI каждый раз стартует с холодной среды, «локально проходит» теряет смысл. Как сформулировал клиент: лучше ротация сертификатов и major-upgrade Xcode, чем каждый день смотреть на жёлтую полосу 47 минут.
С первого Cloud Mac self-hosted runner
Если страница Job как на рис. 1 — жёлтое длиннее зелёного — следующая разумная инвестиция обычно выделенный Cloud Mac на Apple Silicon: зарегистрированный как GitHub self-hosted runner, PR-сборка перестаёт быть «лотереей очереди» и становится предсказуемыми девятью минутами с рис. 4.
Две недели контрольного эксперимента: одна ветка, по 50 сборок на hosted macOS и Cloud Mac self-hosted — таблица как на рис. 3. Цифры покажут, тормозит Xcode или ёмкость iOS-разработку.
Начните с предсказуемого Cloud Mac и верните archive на release-линию. Тарифы VPSSpark Cloud Mac и регистрация первого iOS self-hosted runner.