Предыстория


Полтора года назад команда iOS FunCorp переехала на новый сервис для простой организации CI в iOS и Android-проектах.

До этого мы использовали CI на Bamboo, но с ним было много проблем, поэтому мы совсем отказались от него и перешли на BuddyBuild.

Он работал настолько просто, что можно было даже не знать, что такое CI и как заливать приложение в AppStore, а спокойно заниматься кодом, тестами и продуктовой разработкой.
Но времена поменялись, и BuddуBuild уже не тот, поэтому мы начали поиск альтернативы.
В этой статье мы расскажем о новом решении, которое выбрала наша команда, и дадим несколько скриптов для организации CI собственными силами.

Просто — значит хорошо


В BuddyBuild нас привлекла простота. Для начала нужно сделать всего несколько шагов:

  1. Авторизоваться через GitHub/GitLab/BitBucket.
  2. Указать репозиторий с проектом.
  3. Отдать сервису аккаунт и distribution-сертификат.
  4. Подождать, пока «случится магия» на стороне сервиса.

И сразу после этого можно получать тесты и артефакты по всем веткам, настраивать правила сборки с помощью понятного UI, быстро переключаться между версиями XCode и релизить в AppStore/TestFlight прямо из сервиса.

Когда мы начинали работать с BuddyBuild, его можно было использовать бесплатно, но через несколько месяцев это закончилось. Сейчас стартовый пакет стоит $79 в месяц. Для себя мы выбрали план с тремя конкурентными сборками за $279.

BuddyBuild работал хорошо, но это длилось недолго.

С ростом популярности сервиса, а также с увеличением количества кода в iFunny время сборки увеличилось со стабильных 20 минут на тесты и сборку артефакта до 70 минут.

Мы пробовали найти решение для кеширования средствами сервиса, но ничего достойного в простых настройках не нашлось.

Тем временем сервис выкупила Apple, и мы приняли окончательное решение от него отказаться.

Требования к CI/CD


Исходя из предыдущего опыта работы с CI, у нас было несколько требований к новой системе.

  1. Она должна быстро разворачивать агентов и среду для сборки с минимальным количеством заходов в UI агентов.
  2. Обновлять Xcode без захода в UI агента и иметь возможность переключения между несколькими версиями.
  3. Интегрироваться с системой pull requests.
  4. Использовать минимум сторонних зависимостей для сборки и выгрузки в AppStore.
  5. Иметь возможность настраивать и кастомизировать шаги сборки для разных веток.
  6. Запускать сборку артефакта только по кнопке из 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, поэтому держите пошаговую инструкцию, как поменять эти строки:

  1. Нажимаем i, входим в режим insert.
  2. Стрелками находим нужную строку и меняем её.
  3. Далее esc.
  4. Вводим :w для сохранения.
  5. Вводим :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 часа.

А чтобы это легче было сделать, вот пара скриптов с командами, описанными в статье:

Комментарии (6)


  1. MeGaPk
    20.04.2018 14:45

    Когда бадди билд офигел и запел что не гитхаб репозитории нельзя юзать без покупки бизнес акка за 400 баксов, вроде бы. Я тут же начал искать альтернативу. И нашёл, это bitrise.io.

    Можно настраивать не только иос и андроид, а свои скрипты писать. По сути конструктор.


  1. ALexhha
    20.04.2018 18:34

    С ростом популярности сервиса, а также с увеличением количества кода в iFunny время сборки увеличилось со стабильных 20 минут на тесты и сборку артефакта до 70 минут.
    я может что то упускаю, но какое отношение CI имеет к скорости сборки артефактов и запуска тестов на удаленных агентах? Если удаленный агент перегружен, выполняет много билдов в данный момент, то как поможет смена CI как такового?


    1. amyhametov Автор
      20.04.2018 19:06

      На BuddyBuild мощности одного агента распределяются между несколькими проектами.
      После переезда сборка артефактов происходит на наших мощностях. В стороннем сервисе мы этим не управляли.


  1. walkline
    20.04.2018 21:13
    +1

    Для нашей команды GitLab CI — это временное решение, сейчас мы готовимся к переезду на Jenkins, в котором добавим ещё больше автоматизации в проект.

    Вас в чем-то ограничивает GitLab CI?
    Какие дополнительные возможности открывает Jenkins для автоматизации?


    1. amyhametov Автор
      20.04.2018 21:21
      -1

      Вот почему будет Jenkins:

      • Большое сообщество
      • Есть несколько интересных нам плагинов с интеграцией
      • Команде DevOps проще поддерживать решение на нём

      GitLab CI можно порекомендовать небольшим командам, у которых пока нет инфраструктуры CI совсем.


    1. ALexhha
      21.04.2018 21:52

      Какие дополнительные возможности открывает Jenkins для автоматизации?
      из личного опыта так сказать (последние пару лет работаю исключительно с Jenkins, в котором можно сделать все, на что только хватит фантазии). В GitLab CI с этим было очень печально, какие то базовые вещи без проблем, но шаг влево или вправо и приплыли. Да и сам gitlab тяжелый очень, помню на машине с 2 Гб он еле ворочался.

      Так же очень не нравилось, что с каждым выпуском они почти полностью меняли UI, честно говоря утомляло каждый раз привыкать к новому расположению. Обновления самого гитлаба были частенько кривыми (я использовал omnibus пакет) и запросто могли разломать систему

      Может кто подсказать, кто активно использует gitlab:

      1. Есть ли там возможность гибкой настройки авторизации по проектам? Например, создать группы, которые смогут только запускать просматривать джобы, только запускать

      2. Как обстоят дела с multi-configuration проектами? Т.е. когда для запуска используется сложная матрица?