CI для мобильной разработки или как мы используем fastlane
Привет. Я Эрнест Пивнев, девопс-инженер в команде мобильной разработки Контура. Мы относительно новое направление в компании и наша задача –– предоставлять услуги по разработке мобильных приложений другим командам и продуктам.
Среди нас есть инженеры, которые создают продуктовые приложения, и те, кто занимается инфраструктурой разработки:
— создает инструменты для ускорения разработки приложений
— поддерживает дизайн-систему и системы CI/CD и автотестов
— развивает переиспользуемые между приложениями модули
— исследует новые мобильные технологии
У нашей команды на текущий момент в репозитории 40+ разных проектов, что накладывает свои технические требования к CI/CD. Основные требования следующие:
Мультиплатформенность
Быстрое подключение в проект
Быстрый процесс обновления
Все виды тестирования
Единое место хранения секретов
Но давайте для начала посмотрим, как это было устроено до и с чем приходилось мириться.
Как было и почему это проблема
Особенности
Этот раздел статьи мог бы называться «Минусы». Но часто случается так, что то, что для одного контекста минус, то для другого плюс. Поэтому мы оперировали термином «особенность». Итак, пройдемся по некоторым особенностям, с которыми я столкнулся, когда только пришёл в команду.
Разный CI для Android и для iOS
Первой моей задачей стало создание автотегирования. Я написал код, прошёл ревью. Приношу тимлиду, на что он мне сказал, что сделано только для Android — теперь надо для iOS.
Разный gitflow на Android и на iOS
Ну кажется, что проблем не много, код то уже написанный, скопировал-вставил и заработает. Давайте посмотрим, как выглядят pipeline для android и для ios.
Pipeline Android:
Pipeline iOS:
Как вы поняли, оказалось, что для двух платформ не просто разные процессы, а разный гитфлоу. И то решение, которые я создал, пришлось переписать с другими вводными и под другие условия.
Submodules
Итак, я наконец выкатываю решение. Сообщаю разработчикам, что можно пользоваться, но у них ничего не происходит. Дело в том, CI подключался в проект через сабмодули, которые не умеют «налету» подхватывать обновления.
То есть чтобы появился новый CI в проекте, нужно удалить сабмодули, собрать их заново, и это не всегда помогает с первого раза. В общем, боль, унижение, принятие. Идём дальше.
Разный процесс работы с секретами
У команды iOS секреты хранились в Vault и вытаскивались на этапе fastlane. А команда Android хранили секреты в CI/CD переменных. По сути, оба эти подхода имеют место быть, но когда речь заходит о мультиплатформенности, то очевидно, что срастить их будет достаточно сложно.
Более того, у команды iOS была боль, что секреты нужно получать до начала сборки. Когда как у команды Android проблема заключалась в том, что доступ к CI/CD переменным не всем хочется давать. И если нужно что-то поправить, то все ходят через одного человека.
Итого:
— Приходится поддерживать обе версии CI
— Submodules не подтягивают обновления
— Дублирование кода
— Сложная реализация мультиплатформы
Плюсы
Основа CI — fastlane
Инженеры обеих команд уже использовали в работе fastlane. Это платформа с открытым исходным кодом, которая упрощает процесс сборки и выкладки мобильных Android и iOS приложений. В ней множество готовых функций и плагинов, а также можно писать собственные функции и плагины, используя синтаксис языка ruby.
Как работает fastlane? Внутрь репозитория помещается Fastfile –– файл с кодом для fastlane. В этом файле описываются lane. Это определенные дорожки, которые описывают последовательность команд, которые необходимо выполнить для того, чтобы приложение оказалось на продовой площадке. В качестве команд могут использоваться как готовые плагины, так и самописные функции. Из CI в таком случае вызывается конкретный Lane, который, например, собирает приложение, или выкладывает его на продуктовую площадку…
Удобная тестовая площадка
Уже был создан классный тестовый стенд с множеством разных телефонов с различными версиями ОС и через центр приложений от Microsoft можно было подтягивать все тестовые версии. Соответственно, этот вопрос был закрыт.
Хорошо настроенное тестирование
В этот процесс тоже лезть не пришлось, потому что уже писались тесты под разный формат, активно внедрялись и продолжают внедряться UI-тесты.
Как стало
Перевели CI для обеих платформ на один шаблон
Чтобы это сделать, пришлось убедить разработчиков перейти на единый гитфлоу, объединить процессы, чтобы деплой был одинаковый, и унифицировать сам CI для обеих платформ.
После того, как мы это сделали, например, функция запуска тестов выглядит вот так для любой платформы:
script:
— bundle exec fastlane test
С точки зрения CI это одна и та же команда, но в зависимости от fastfile внутри проекта вызывает разные lane. Выглядит красиво и вызывать удобно.
Ушли от submodules
Конфиги для тестов, которые подтягивались через сабмодули, инженеры научились подтягивать через плагин gradle. А сам CI мы стали подтягивать через функцию импорта, которая есть у fastlane. Она позволяет импортировать любой файл в проект в рамках гита.
Написали скрипт для вытягивания секретов в before_script
Скрипт доставал секреты из Vault на этапе before_script в CI. Команда Android перенесла секреты в Vault. Команда iOS получила возможность вытаскивать секреты до запуска fastlane, что позже облегчило возможность реализации мультиплатформенного решения.
Перешли на модульную структуру fastlane
На этом хочется остановиться и разобрать подробнее.
Модульная структура fastlane
Казалось бы, причем тут модули? Ведь у нас есть файл .gitlab-ci для Android, есть файл .gitlab-ci для iOS, которые наследуются от общего шаблона. К Android подключается свой Fastfile, к iOS — свой. Вроде бы выработана идеальная схема.
Теперь начинаем писать CI для мультиплатформы, и нам потребуется написать новый Fastfile или с нуля, или переработать то, что уже есть. Получается, что наша новая система не упрощает нам работу при добавлении новой платформы. То есть мы будем сталкиваться с той же проблемой, от которой уходили при переходе на шаблон, только уже на этапе fastlane.
В поисках решения этой проблемы обнаружили в документации, что fastlane может легко собираться из модулей.
То есть текущий fastfile делим на модули: модуль тестирования для Android, модуль тестирования для iOS и тд. И уже из этих модулей, как из кубиков, мы заново формируем fastfile.
Получается следующая схема работы шаблона CI и модулей:
Сверху — шаблон CI, в котором наследуется gitlab всей фабрики. Снизу — модули fastlane, из которых собираются Fastfile.
Мы нарисовали удобную для нас систему модулей:
Из этих модулей собирается fastfile для Android и iOS, и здесь же формируется fastfile для КММ –– мультиплатформы типологий.
После этих внедрений Fastfile iOS стал выглядеть так (до этого он выглядел на 400+ строк кода):
Теперь сам fastlane предоставляется для команд разработки как сервис. Я вношу изменения в модули, а они могут просто собирать из необходимых модулей CI, и у них всё работает.
Почему всё это хорошо
Больше не надо поддерживать разные CI
Изменения в проекты подтягиваются автоматически
Появился мультиплатформенный CI
Появилась возможность быстро создавать новые pipeline
Спустя короткое время добавился packages iOS, который я собрал достаточно быстро, и React Native, который был собран без моего участия. И актуальная структура модулей выглядит так:
Выводы или история успеха
Изначально мобильной разработке понадобился девопс, потому что сложилась классическая ситуация. Было 2 сеньора: один на anrdoid, второй на iOS. И у каждого сеньора был этот огромный fastfile, который только они и понимали. А это критично, ведь люди имеют свойство выходить из проектов.
По итогу после внедрения всех этих изменений так и случилось: оба покинули проект. Но катастрофы не произошло, ведь, как я писал выше, CI и Fastlane сейчас предоставляется по сути как сервис. При желании инженеры могут из готовых модулей собирать себе готовый CI, и в модульную структуру вносить изменения гораздо проще.
На данный момент я уже полгода как тоже вышел из проекта и команда живёт без девопса. Меня звали что-то починить только пару раз. В остальном разработчики самостоятельно поддерживают и развивают модульный CI.