← К техблогу

Flutter iOS CI/CD: IPA на Mac mini M4 и настройка кэша CocoaPods

Flutter iOS CI/CD: flutter build ipa и self-hosted runner на Mac mini M4
Типичное разделение: Dart на Windows/Linux, IPA с runner Mac mini M4.

Эта статья про одно: 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-сборки от Macpod installxcodebuild 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

Разработчик (Windows / Linux / Mac)
локально: Dart · Android · git push
GitHub Actions · ubuntu-latest
flutter test · build apk
Mac mini M4 · self-hosted Runner
labels: macos, flutter-ios
fvm flutter build ipa
pod install · xcodebuild archive
TestFlight / App Store Connect

Постоянные слои кэша на 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 RunnerPR запускает Linux jobself-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)

  1. Xcode + CLT: вровень с ios/Podfile и минимумом канала Flutter. Затем sudo xcodebuild -license accept.
  2. Flutter SDK: fvm или фиксированный ~/flutter; .fvmrc в репозитории — одна версия для команды и CI.
  3. CocoaPods: gem install cocoapods или Homebrew; крупные репо — коммитить Podfile.lock.
  4. Подпись: Fastlane Match или авто-подпись Xcode. p12 и профили не в Git — шифрованное хранилище или секреты CI.
  5. Сеть: регион ближе к 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.

Таблица: полный build ipa Flutter iOS CI (минуты, тот же репоз · замер 2026-05)
СредаПервая сборка (холод)Вторая (попадание в кэш)
Локальный MacBook Air M2 (8 ГБ, крышка/троттлинг)18–25 min12–15 min
GitHub macos-latest (без своего кэша)16–22 min14–18 min
Mac mini M4 (16 ГБ) · Flutter iOS CI + 3 слоя кэша15–20 min4–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.
Цифры: при ≥1 iOS-релизе в неделю после попадания в кэш на Mac mini экономия ~8–12 минут на pipeline — 4 релиза/мес × 3 ожидающих разработчика ≈ целый рабочий день в очереди.

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-latestflutter test, build apk.
  • release-ios.yml: runs-on: [self-hosted, macos, flutter], build ipa + upload только по tag или merge main.

Если на облачном 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. 1× Mac mini M4 16 ГБ (физический или эксклюзивное облако) только под Flutter iOS CI;
  2. self-hosted macOS Runner, отдельные workflow для Linux flutter test / build apk;
  3. постоянный кэш: .pub-cache, CocoaPods, DerivedData;
  4. цель: за 48 часов первый flutter build ipa → TestFlight на реальном репозитории; затем — вторая машина для параллели/DR.

Тот же MVF даёт повторные сборки 4–8 минут (таблица в §5). Выделенный узел Mac mini и SSH: тарифы и справочный центр — сверьте с частотой релизов; сначала pipeline, потом масштаб.