Предыстория
Полтора года назад команда iOS FunCorp переехала на новый сервис для простой организации CI в iOS и Android-проектах.
До этого мы использовали CI на Bamboo, но с ним было много проблем, поэтому мы совсем отказались от него и перешли на BuddyBuild.
Он работал настолько просто, что можно было даже не знать, что такое CI и как заливать приложение в AppStore, а спокойно заниматься кодом, тестами и продуктовой разработкой.
Но времена поменялись, и BuddуBuild уже не тот, поэтому мы начали поиск альтернативы.
В этой статье мы расскажем о новом решении, которое выбрала наша команда, и дадим несколько скриптов для организации CI собственными силами.
Просто — значит хорошо
В BuddyBuild нас привлекла простота. Для начала нужно сделать всего несколько шагов:
- Авторизоваться через GitHub/GitLab/BitBucket.
- Указать репозиторий с проектом.
- Отдать сервису аккаунт и distribution-сертификат.
- Подождать, пока «случится магия» на стороне сервиса.
И сразу после этого можно получать тесты и артефакты по всем веткам, настраивать правила сборки с помощью понятного UI, быстро переключаться между версиями XCode и релизить в AppStore/TestFlight прямо из сервиса.
Когда мы начинали работать с BuddyBuild, его можно было использовать бесплатно, но через несколько месяцев это закончилось. Сейчас стартовый пакет стоит $79 в месяц. Для себя мы выбрали план с тремя конкурентными сборками за $279.
BuddyBuild работал хорошо, но это длилось недолго.
С ростом популярности сервиса, а также с увеличением количества кода в iFunny время сборки увеличилось со стабильных 20 минут на тесты и сборку артефакта до 70 минут.
Мы пробовали найти решение для кеширования средствами сервиса, но ничего достойного в простых настройках не нашлось.
Тем временем сервис выкупила Apple, и мы приняли окончательное решение от него отказаться.
Требования к CI/CD
Исходя из предыдущего опыта работы с CI, у нас было несколько требований к новой системе.
- Она должна быстро разворачивать агентов и среду для сборки с минимальным количеством заходов в UI агентов.
- Обновлять Xcode без захода в UI агента и иметь возможность переключения между несколькими версиями.
- Интегрироваться с системой pull requests.
- Использовать минимум сторонних зависимостей для сборки и выгрузки в AppStore.
- Иметь возможность настраивать и кастомизировать шаги сборки для разных веток.
- Запускать сборку артефакта только по кнопке из UI.
GitLab CI
Вместе с решением отказаться от BuddyBuild появилась идея мигрировать c GitHub на GitLab, а в нём уже есть встроенная CI/CD система, то есть присутствует необходимая нам интеграция с merge requests.
Установка рабочего окружения на агента
Первым делом нужно включить возможность доступа к агенту по SSH с помощью Screen Sharing. Это делается в настройках Sharing:
Теперь, чтобы подключаться к CI-агенту, мы можем использовать терминал и SSH-клиент:
ssh user@local.ip
После успешного подключения по SSH нужно отключить использование пароля для команды sudo. Может показаться, что это небезопасно, но с учётом, что все агенты у нас доступны только внутри локальной сети, для лучшей автоматизации мы отключаем пароль для sudo. Для этого:
sudo visudo
Откроется стандартный редактор Vim, в котором нужно поменять строку:
%admin ALL = (ALL) ALL
на строку
%admin ALL = (ALL) NOPASSWD: ALL
Многие iOS-разработчики не любят копаться в консоли и редко используют Vim, поэтому держите пошаговую инструкцию, как поменять эти строки:
- Нажимаем i, входим в режим insert.
- Стрелками находим нужную строку и меняем её.
- Далее esc.
- Вводим :w для сохранения.
- Вводим :q для выхода из visudo.
Первым делом на каждом агенте нам понадобится менеджер пакетов Homebrew, который можно установить следующей командой:
sudo echo | ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
Здесь используется команда echo, чтобы не появлялся запрос на подтверждение установки.
После этого можно установить необходимый минимум зависимостей из Homebrew. Вот что мы выбрали:
- XCPretty. Он позволяет красиво форматировать вывод стандартной команды для билда iOS-проектов xcodebuild;
sudo gem install xcpretty
- fastlane. Его возможности мы используем по минимуму, так как не хотим, чтобы билд проекта сильно зависел от чего-то стороннего;
sudo gem install fastlane
- xcode-install. Этот инструмент позволит устанавливать Xcode без захода в AppStore и переключаться между несколькими версиями;
sudo gem install xcode-install
- CocoaPods. Этот менеджер зависимостей знаком каждому iOS-разработчику.
sudo gem install cocoapods
Теперь можно установить Xcode, запустив последовательно команды:
export FASTLANE_USER="your@account.todevapple"
export FASTLANE_PASSWORD="yourpasswordtoaccont"
xcversion install 9.2
Можно не выполнять export, но тогда e-mail и пароль от Apple ID будут запрошены в процессе установки.
После этих несложных действий агент готов к тому, чтобы собирать большинство iOS-проектов.
Регистрация агента
Для того чтобы проект на Gitlab CI увидел ваш агент, необходимо установить его и зарегистрировать.
Это также можно выполнить с помощью SSH и командной строки на агенте:
Сначала скачиваем и устанавливаем агента:
curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
Даём права на выполнение:
chmod +x /usr/local/bin/gitlab-runner
Устанавливаем gitlab-runner как сервис и запускаем его:
gitlab-runner install
gitlab-runner start
Теперь нужно зарегистрировать runner на CI, это выполняется командой:
gitlab-runner register -n --url CI_URL --registration-token TOKEN --tag-list fastlane,cocoapods,osx_10-13,xcode_9-2 --executor shell
Это команда, которую стоит пояснить подробно.
CI_URL и TOKEN можно взять в настройках проекта на GitLab:
Settings -> CI/CD -> Runners ->блок Setup a specific Runner manually
--tag-list: указываются теги, которые потребуются нам далее при настройке самого проекта.
Их также можно будет менять, например, в зависимости от того, какие версии Xcode установлены на агенте или какой тип задач мы планируем выполнять на нём.
--executor shell: указываем, что на агенте необходимо выполнять действия в командной строке.
Чтобы не возникло проблем с CocoaPods, также нужно выполнить команды:
echo 'export LC_ALL="en_US.UTF-8"' >> ~/.bash_profile
echo 'export LANG=en_US.UTF-8' >> ~/.bash_profile
Это необходимо, чтобы при установке подов не было ошибок с кодировкой.
После успешной регистрации агента его можно будет увидеть в настройках.
Settings -> CI/CD -> Runners
Настройка проекта
Осталось настроить Xcode-проект для работы с GitLab CI.
Сделать это достаточно просто: нужно положить файл .gitlab-ci.yml с описанием в корень проекта.
После того как данный файл будет добавлен в репозиторий, CI система начнёт выполнять команды, указанные в нём.
Пример нашего yml-файла:
stages:
- test
- archive
before_script:
- git submodule init
- git submodule update --recursive
- pod install --repo-update
archive_project:
stage: archive
script:
- fastlane match appstore
- xcodebuild -workspace iFunny.xcworkspace -scheme iFunny archive -archivePath build/iFunny.xcarchive | xcpretty
only:
- master
- triggers
- web
artifacts:
paths:
- build/iFunny.xcarchive
tags:
- xcode_9-2
- osx_10-13
- cocoapods
- fastlane
test_project:
stage: test
script:
- xcodebuild test -workspace iFunny.xcworkspace -scheme iFunny -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.2' | xcpretty
tags:
- xcode_9-2
- osx_10-13
- cocoapods
- fastlane
yml-формат для GitLab CI достаточно хорошо описан здесь.
Чтобы можно было легко использовать пример файла, опишу основные моменты, которые используем мы:
- before_script — описываем набор инструкций, которые необходимо выполнять перед каждой работой на CI. Для нас это стандартный набор из обновления сабмодулей и установки подов;
- archive_project — название, которое используется, для получения артефакта с xcarchive.
В script указываем все инструкции, которые необходимо выполнить:
fastlane match appstore
Для синхронизации сертификатов у fastlane есть хорошая команда match (подробнее про использование match можно прочитать на сайте fastlane).
Основная команда на запуск сборки архива:
xcodebuild -workspace Project.xcworkspace -scheme ProjectScheme archive -archivePath build/Project.xcarchive | xcpretty
Project.xcworkspace — файл с workspace.
ProjectScheme — схема с основным таргетом.
build/Project.xcarchive — путь, по которому соберётся рабочий артефакт. Далее этот путь используем в artifacts: он указывает CI, откуда нужно забрать архив.
В блоке only указываем, что данную работу нужно выполнять только на ветке мастер или при запуске из веба, то есть при запуске по кнопке Run Pipeline в CI/CD.
tags — это те теги, которые должны быть прописаны на агенте, чтобы он мог запускать эту работу. Сейчас они полностью повторяют то, что мы указывали при регистрации агента.
Далее в файле идёт описание работы test_project.
Из интересного здесь — строка запуска тестов:
xcodebuild test -workspace Project.xcworkspace -scheme ProjectScheme -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.2' | xcpretty
В настройке -destination указываем тот симулятор, который точно доступен на агентах.
Посмотреть список доступных устройств на агенте можно через SSH командой:
instruments -s list
После того как будет сконфигурирован файл .gitlab-ci.yml, можно добавлять его в репозиторий и автоматически будет произведён запуск тестов в ветке, содержащей файл.
Заключение
Чтобы настроить всю связку GitLab и встроенной CI/CD-системы, мы потратили примерно половину рабочего дня, что несколько больше 20 минут, потраченных на настройку BuddyBuild.
Но что мы получили от переезда:
- собственных агентов для сборки проектов и систему работы с ними;
- удалось сократить с 70 до 6 минут время на сборку с тестами или до 30 минут на полную сборку с тестами и архивами;
- возможности для оптимизации времени сборок;
- нам доступна любая кастомизация и интеграция с любым сервисом.
Для нашей команды GitLab CI — это временное решение, сейчас мы готовимся к переезду на Jenkins, в котором добавим ещё больше автоматизации в проект.
Возможно, описанный в статье опыт позволит читателям переехать на GitLab CI менее чем за 4 часа.
А чтобы это легче было сделать, вот пара скриптов с командами, описанными в статье:
- установка зависимостей;
install_dependencies.sh - установка и регистрация агентов.
install_agent.sh
Автор: amyhametov