Эта статья про одно: Flutter iOS CI/CD (основной якорь: Flutter iOS build on Mac mini) — как стабильно гонять flutter build ipa на Mac mini M4, разделив кэш CocoaPods, self-hosted macOS Runner и Android/тестовые job’ы на Linux. Узкое место Flutter редко в Dart: это зависимость iOS-сборки от Mac — pod install → xcodebuild archive → подпись → загрузка в TestFlight. Вся цепочка завершается только на Mac. У нас типичное разделение: Android на CI Windows/Linux, Flutter iOS CI закреплён за выделенным Mac mini в стойке. Смешанный нативный Xcode — в медленные сборки Xcode в облако; регистрация Runner и TCO — свои macOS Runner.
1) Почему Flutter iOS CI требует отдельный Mac
«Одна кодовая база» переиспользует бизнес-логику, а не плоскость сборки. iOS-артефакты всё равно идут через полный Xcode; Flutter iOS CI на ubuntu-latest невозможен. Хостинг macOS у GitHub дорог по минутам, очередь плавает, без постоянного кэша flutter build ipa снова уходит в 15–25 минут. Flutter iOS CI/CD на постоянно работающем Mac mini даёт: фиксированные версии Flutter/Xcode, трёхуровневый переиспользуемый кэш, 24/7 без сна при закрытой крышке — релизные ритмы Android и iOS реально расходятся.
flutter test, build apk остаются на Linux. Машина Flutter iOS CI — только интеграционная iOS-сборка и подпись.2) Облачная архитектура Flutter iOS CI
Ниже — боевая топология Flutter iOS CI/CD: не каждому разработчику нужен Mac локально; iOS-пакет делает self-hosted Runner.
Облачная архитектура Flutter iOS CI
локально: Dart · Android · git push
ubuntu-latestflutter test · build apk
labels: macos, flutter-ios
fvm flutter build ipapod install · xcodebuild archive
Постоянные слои кэша на Mac mini (ключ ускорения Flutter iOS CI)
~/.pub-cache- CocoaPods cache
- DerivedData
3) Три режима развёртывания Flutter iOS CI
| Режим | Локально / другой CI | Облачный M4 Mac | Кому подходит |
|---|---|---|---|
| A. Только iOS-сборка в облаке | ежедневно flutter run, Android-пакеты | build ipa, загрузка в стор | гибрид (Win/Linux + устройство) |
| B. Flutter полностью удалённо (редко) | только редактор + git | симулятор, flutter doctor зелёный | нет локального Mac, терпима задержка VNC |
| C. Облачный Mac = iOS Runner | PR запускает Linux job | self-hosted Runner с меткой macos | частые релизы, сильный кэш |
У нас A + C: после merge — flutter test на Linux; Flutter iOS CI только по tag / merge main через Runner на Mac mini. B — на короткий период без Mac; в перспективе — Runner.
4) Чеклист окружения Flutter iOS CI (разовая установка на Mac mini)
- Xcode + CLT: вровень с
ios/Podfileи минимумом канала Flutter. Затемsudo xcodebuild -license accept. - Flutter SDK:
fvmили фиксированный~/flutter;.fvmrcв репозитории — одна версия для команды и CI. - CocoaPods:
gem install cocoapodsили Homebrew; крупные репо — коммититьPodfile.lock. - Подпись: Fastlane Match или авто-подпись Xcode. p12 и профили не в Git — шифрованное хранилище или секреты CI.
- Сеть: регион ближе к Git remote и pub.dev/CDN; корпоративный Git — VPN/выделенка; выход — в справочном центре.
При первом запуске на новой машине — flutter doctor -v в wiki: «золотой снимок» для разбора инцидентов.
5) Кэш CocoaPods: ядро ускорения Flutter iOS CI (с Before/After)
Один и тот же средний Flutter-репоз (~40+ плагинов, Release IPA), по 3 прогона в 3 средах, медиана — разница в Flutter iOS CI почти целиком от того, остаётся ли кэш на Mac mini.
| Среда | Первая сборка (холод) | Вторая (попадание в кэш) |
|---|---|---|
| Локальный MacBook Air M2 (8 ГБ, крышка/троттлинг) | 18–25 min | 12–15 min |
GitHub macos-latest (без своего кэша) | 16–22 min | 14–18 min |
| Mac mini M4 (16 ГБ) · Flutter iOS CI + 3 слоя кэша | 15–20 min | 4–8 min |
Вторую сборку с 12+ минут до однозначных — за счёт трёх постоянных слоёв на схеме. Порог, где Flutter iOS build on Mac mini переходит от «собирается» к «годится как CI».
PUB_CACHE/~/.pub-cache: фиксированный путь в env Runner, переиспользование Dart-зависимостей между job’ами.- Кэш CocoaPods +
ios/Pods: неизменныйPodfile.lock→ пропускpod install; иначе инкремент. - DerivedData: фиксированный
~/DerivedData; безclean, пока не major-апгрейд Xcode.
6) Поток команд Flutter iOS CI (SSH или скрипт Runner)
В корне репозитория; версию Flutter замените на зафиксированную в проекте:
cd ~/work/my_flutter_app
export PUB_CACHE=$HOME/.pub-cache
fvm flutter pub get
cd ios && pod install --repo-update && cd ..
fvm flutter build ipa --release \
--export-options-plist=ios/ExportOptions.plist
В GitHub Actions — шаги в release-ios.yml, runs-on: [self-hosted, macos, flutter-ios]. При загрузке в TestFlight исходящий канал Mac mini и маршрут в App Store Connect часто стабильнее домашнего Wi‑Fi.
7) GitHub Actions: разделение workflow Flutter iOS CI/CD
Рекомендуем два workflow (или matrix с if):
test-android.yml: наubuntu-latest—flutter test,build apk.release-ios.yml:runs-on: [self-hosted, macos, flutter],build ipa+ upload только по tag или mergemain.
Если на облачном Mac одновременно VNC и Runner — отдельный системный пользователь для сборок, чтобы ручные правки не конфликтовали с checkout. Параллелизм и диск: TCO Runner. Основная ОС Windows: Xcode под Windows + облачный Mac — у Flutter локально тоже нет Xcode.
8) Частые проблемы: почему Flutter iOS CI «висит» (how/fix)
Соответствует типичным запросам why/how/fix/error — вход в wiki команды.
pod install 10+ минут — это нормально?
Why: Runner каждый раз checkout в temp, кэш загрузок CocoaPods не смонтирован, или в скрипте по умолчанию pod install --repo-update.
Fix: фиксированный путь WORK; сравнить hash Podfile.lock, при совпадении пропуск; --repo-update — в еженедельный maintenance job.
сбой xcodebuild archive / exit code 65
Why: несовпадение версий Flutter/Xcode, битый DerivedData, native plugin не под текущий SDK.
Fix: в начале CI fvm flutter doctor -v с логами; удалить DerivedData и пересобрать; Xcode Release Notes для канала.
signing failed / несовпадение профиля
Why: p12 не в связке ключей, bundle id ≠ профиль, истёк доступ к репозиторию Match.
Fix: отдельная связка на Runner Mac mini; продление Fastlane Match; при необходимости VNC «всегда доверять» — самый частый «не-кодовый» сбой в Flutter iOS CI.
DerivedData забил диск
Why: параллельные job’ы веток, старые Archives, несколько версий Flutter.
Fix: cron для ~/Library/Developer/Xcode/Archives; параллелизм Runner 1–2; диск ≥512 ГБ или регулярное расширение.
разные версии Flutter → ошибка сборки плагина
Why: у разработчика без fvm, у CI другой канал.
Fix: коммит .fvmrc; в CI едино fvm install && fvm flutter pub get; в шаблоне PR — «менялся ли Flutter SDK».
9) Когда нужен выделенный Mac mini под Flutter iOS CI
| Ваша ситуация | Одолжить Mac / купить б/у | Облачный M4 Mac mini |
|---|---|---|
| ≥4 iOS-релиза в месяц | высокие затраты на согласование | рекомендуем фиксированный build + кэш |
| в команде нет Mac, только Flutter | сложно удержать | суточная аренда для реального build ipa |
| Mac mini 24/7 в офисе | своего хоста достаточно | облачный Mac — DR или пик |
| много iOS-плагинов, правка натива | локальный Mac удобен | release в облаке, debug локально |
10) FAQ (Flutter iOS CI/CD)
Чем Flutter iOS CI отличается от нативного iOS CI?
Дополнительный слой flutter pub get и codegen плагинов; версия = .fvmrc; со стороны Xcode — Pods + archive.
Сравнение с Codemagic / хостингом macOS?
Хостинг — для быстрой проверки; если вторую сборку нужно стабильно уложить в 10 минут с сильным кэшем — Flutter iOS build on Mac mini + self-hosted Runner управляемее.
Сколько до первого успешного прогона?
Опытной команде ~1 рабочий день: Xcode/Flutter, Runner, подпись, один build ipa → TestFlight.
Инженерный вывод: минимальная схема Flutter iOS CI (MVF)
Если Flutter-проект выпускает iOS хотя бы раз в неделю, а основные машины — Windows/Linux, минимально жизнеспособно:
- 1× Mac mini M4 16 ГБ (физический или эксклюзивное облако) только под Flutter iOS CI;
- self-hosted macOS Runner, отдельные workflow для Linux
flutter test/build apk; - постоянный кэш:
.pub-cache, CocoaPods, DerivedData; - цель: за 48 часов первый
flutter build ipa→ TestFlight на реальном репозитории; затем — вторая машина для параллели/DR.
Тот же MVF даёт повторные сборки 4–8 минут (таблица в §5). Выделенный узел Mac mini и SSH: тарифы и справочный центр — сверьте с частотой релизов; сначала pipeline, потом масштаб.