17.08.2026 456 материалов

Как автоматизировать сборку мобильных приложений: пайплайн Flutter через GitHub Actions и Fastlane

Разработчик Бимал Хатри описал рабочий пайплайн для Flutter-приложений: на каждый пул-запрос запускаются проверки кода, а при публикации версионного тега автоматически собираются подписанные сборки и улетают в TestFlight и Google Play.

Как автоматизировать сборку мобильных приложений: пайплайн Flutter через GitHub Actions и Fastlane

Хороший CI/CD — это не про то, чтобы убрать человека из релиза, а про то, чтобы убрать рутину. Решение о публикации по-прежнему принимает живой человек — просто ему больше не нужно вручную таскать файлы по папкам и ждать, пока Xcode закончит.

Зачем это вообще нужно

Если вы пользуетесь мобильными приложениями, вы наверняка замечали, что обновления стали приходить чаще и стабильнее, чем пять лет назад. Одна из причин — разработчики массово переходят на автоматизированные пайплайны сборки. Вместо того чтобы вручную собирать приложение для Android, потом переключаться на Mac, собирать для iOS, подписывать, загружать в магазины и молиться, чтобы ничего не сломалось, они настраивают систему, которая делает всё это сама.

Flutter — фреймворк, позволяющий писать одно приложение сразу для двух платформ. Это удобно, но добавляет сложность: из одной кодовой базы нужно получить два подписанных файла (.aab для Google Play и .ipa для App Store), причём каждый — со своей подписью и своими правилами загрузки. Вручную такая процедура легко отнимает полдня.

Разработчик Бимал Хатри описал пайплайн, который он использует на боевых проектах. Идея простая: каждый пул-запрос проверяется автоматически, а полноценная сборка для магазинов запускается только при создании версионного тега.

Два режима: быстрые проверки и медленные сборки

Главное архитектурное решение — разделить два процесса:

Проверки на каждый пул-запрос (CI). Запускаются на Linux, стоят копейки, длятся пару минут. Система проверяет форматирование кода, запускает статический анализатор и прогоняет тесты. Если что-то не так — пул-запрос не пройдёт. Это как пропускной пункт: код не попадёт в основную ветку, пока не будет чистым.

Сборка релиза по тегу (CD). Запускается только когда разработчик ставит тег вида v1.4.0 — то есть фактически объявляет: «это готовая версия». Система собирает подписанные файлы и загружает их в магазины. Для iOS сборка идёт на macOS-машинах, которые стоят примерно в десять раз дороже по минутам на GitHub, поэтому важно не запускать их без нужды.

Разделение на теги полезно ещё и тем, что тег становится записью о релизе. Можно в любой момент посмотреть, какой коммит лежит в основе версии 1.4.0. А если сборки привязаны к обычным коммитам в main — легко получить незапланированный билд в TestFlight в два часа ночи.

Что проверяется автоматически

На каждый пул-запрос система делает три вещи:

  • Форматирование. Команда dart format проверяет, что код оформлен по стандарту. Если нет — сборка падает. Это избавляет от бесконечных споров про отступы и пробелы в ревью.

  • Статический анализ. flutter analyze ищет потенциальные ошибки: неиспользуемые переменные, небезопасные приведения типов, нарушения правил проекта. Флаг --fatal-infos заставляет относиться к предупреждениям серьёзно — если правило настроено, оно должно соблюдаться.

  • Тесты. flutter test прогоняет юнит-тесты и виджет-тесты. Дорогие тесты (интеграционные, с визуальными скриншотами) лучше вынести в отдельный запуск, чтобы не замедлять повседневную работу.

Важная деталь: версия Flutter жёстко зафиксирована. Использовать «последнюю» — плохая идея, потому что обновление Dart SDK может сломать сборку на пул-запросе, который вообще не трогал ничего связанного.

Fastlane: связь с магазинами

Flutter умеет собирать файлы, но не умеет загружать их в App Store Connect и Google Play Console. Для этого используется Fastlane — инструмент, который знает API обоих магазинов и умеет с ними разговаривать.

Для Android загрузка происходит через сервисный аккаунт Google Cloud — специальный JSON-файл с правами на управление приложением в Play Console. Fastlane берёт подписанный .aab и отправляет его во внутренний трек (internal track) в статусе «черновик». Это значит, что файл загружен, но ещё не опубликован — кто-то из команды должен вручную нажать кнопку «опубликовать». Такая страховка намеренная: автоматизировать загрузку, но решение о публикации оставить за человеком.

Для iOS используется API-ключ App Store Connect — пара из идентификатора ключа и файла .p8. Это надёжнее, чем вход через Apple ID: ключи не требуют двухфакторной аутентификации и не ломаются, когда Apple решает, что сессия выглядит подозрительно. Fastlane загружает .ipa в TestFlight и сразу возвращается, не дожидаясь, пока Apple обработает сборку. Обработка может занять десятки минут, и ждать за это на платном сервере — пустая трата денег.

Самая частая ошибка: работа с подписями

Здесь же кроется главный источник боли при настройке CI/CD. Сборку нельзя подписать без ключей — файла keystore для Android и сертификата для iOS. Но нельзя и положить их в репозиторий, потому что это секреты.

Решение — шифрованные секреты GitHub Actions. Бинарные файлы (keystore, ключ .p8) кодируются в base64 и хранятся в настройках репозитория в зашифрованном виде. На сервере сборки они раскодируются обратно, но никогда не записываются в лог и не попадают на диск в открытом виде.

Для iOS есть дополнительный уровень: Fastlane Match. Это инструмент, который хранит сертификаты и профили подписи зашифрованными в отдельном приватном Git-репозитории. На сервере сборки Match распаковывает их во временный keychain, использует для подписи, а после завершения удаляет. Преимущество — сертификаты не привязаны к конкретной машине, и вся команда работает с одним и тем же набором подписей.

Два правила, которые стоит запомнить:

  • Никогда не выводить секреты в лог. GitHub умеет маскировать известные значения, но base64-преобразование или обрезка строки могут обойти фильтр.
  • Давать минимально необходимые права. Сервисный аккаунт Google — только на управление релизами одного приложения. API-ключ Apple — с ограниченной ролью. Если токен утечёт, ущерб будет меньше.

Как это выглядит в сборе

Полный пайплайн — это два файла workflow для GitHub Actions и два Fastfile (для Android и iOS). Когда разработчик открывает пул-запрос, за пару минут проходят все проверки. Когда он ставит тег v1.4.0 и пушит его — запускаются две параллельные задачи:

  • На Linux собирается Android-сборка, подписывается keystore'ом и улетает в Google Play.
  • На macOS устанавливаются сертификаты через Match, собирается iOS-сборка, подписывается и улетает в TestFlight.

Ещё одна полезная привычка — зафиксировать версию Fastlane через Gemfile и запускать её через bundle exec. Это гарантирует, что обновление Fastlane не сломает пайплайн посреди ночи. Мелочь, но она экономит часы отладки.

Почему это важно не только для разработчиков

Конечно, большинство читателей не будут настраивать CI/CD пайплайны. Но понимание этого процесса объясняет, почему обновления приложений стали приходить чаще, почему в бета-версии попадают раньше, и почему иногда в App Store появляется сборка, которая потом быстро исчезает — это автоматика загрузила её во внутренний трек, а кто-то из команды передумал публиковать.

Автоматизация сборки — это не «робот заменил программиста». Это инструмент, который отнимает у разработчика рутину и оставляет ему время на то, что действительно требует решения: когда выпускать, что включать в релиз, как реагировать на баги. И в этом смысле пайплайн из YAML-файла делает приложения лучше не только для тех, кто их пишет, но и для тех, кто ими пользуется.