Cборка проекта требует от разработчика кучи утомительных операций. Когда менеджеру нужно отчитаться перед заказчиком о ходе работы, разработчик тратит время на выкачивание репозитория, подтягивание библиотек, установку сертификатов, сборку, проверку, выкладывание сборки на сервер и прочие промежуточные этапы. Но на Земле становится всё меньше рутинных действий, которые не подверглись бы автоматизации. Прогрессивная часть девелоперской среды практикует методику непрерывной интеграции (CI), и iOS-отдел компании Лайв Тайпинг решил к ней примкнуть, развернув сервер для сборок на платформе Jenkins. С тех пор началась совсем другая жизнь.
Что мы получили в итоге:
Теперь мы экономим кучу времени и сил, забыв, что такое делать сборку и кому-то её отправлять. Данное руководство — плод желания поделиться этим ощущением и опытом. Но, как и любой другой из множества гайдов, наш тоже не претендует на истину в последней инстанции.
Для начала ознакомьтесь со следующими оговорками и поймите, сопоставим ли наш гайд с вашими задачами:
Руководство получилось довольно громоздким, и мы решили разбить его на две части. Эта часть посвящена базовой установке и настройке Jenkins.
Что необходимо:
Пользователя можно создать как через консоль, так и используя GUI. Вряд ли во втором варианте возникнут сложности, поэтому мы лучше рассмотрим первый (источник):
Наш пользователь готов, и теперь нам нужно зайти за него. Мы можем произвести вход в систему через GUI, либо зайти, используя консоль:
Внимание: все дальнейшие действия мы совершаем под пользователем Jenkins.
Для установки Jenkins воспользуемся системой управления пакетами Homebrew. В дальнейшем это также упростит процесс установки и обновления дополнительных пакетов, которые мы будем использовать для получения метрик кода.
Чтобы наш сервер автоматически запускался при старте системы, нам необходимо настроить запуск соответствующей задачи для launchd. У нас есть выбор: сделать это через LaunchAgents или LaunchDaemon. Мы воспользуемся именно LaunchAgents, т.к. это упростит* дальнейшую работу с Jenkins. Достаточно посмотреть на таблицу ниже, чтобы понять это:
*Главная проблема запуска через Daemon заключается в том, что нельзя выполнить тестирование, не запустив iOS Simulator (подробнее про разницу использования демона и агента можно прочитать здесь)
Однако из-за выбора LaunchAgents нам нужно решить проблему отсутствия залогиненного пользователя при запуске системы. Для этого необходимо настроить autologin. Мне известен только один способ это сделать: через GUI (Системные настройки -> Пользователи и группы -> Параметры входа -> Автоматический вход). Если кто-то знает, как это сделать через shell — пожалуйста, отпишитесь в комментариях!
Для настройки запуска через LaunchAgents выполним следующие шаги:
Пример содержимого файла plist:
Здесь стоит обратить внимание на поле httpListenAddress со значением 0.0.0.0 и поле httpPort со значением 8080 — так сервер будет «прослушивать» любые ip-адреса по указанному порту.
Напомню: для закрытия и сохранения файла в редакторе vim вводим :wq
После установки Jenkins доступен по умолчанию по адресу 127.0.0.1 (localhost).
Для доступа из внешней сети можно пробросить порты на маршрутизаторе: 8080 для веб-интерфейса Jenkins и 22 для доступа по ssh.
Заходим на сервер Jenkins -> Настроить Jenkins -> Управление плагинами. Во вкладке «Доступные» находим и устанавливаем следующие плагины:
Через веб-интерфейс Jenkins`а заходим в «Настройки», а затем в раздел «Конфигурирование системы». На что здесь стоит обратить внимание:
4.1. Настройки Xcode Builder. Если вы устанавливали Xcode в стандартную директорию, то настройки менять не нужно. В противном же случае необходимо задать путь для указанных компонентов. Учитывая, что мы запустили сервер с использованием LaunchAgents, то Xcode будет иметь доступ к login.keychain. Но если вы хотите подстраховаться, то можете в этом разделе добавить keychain, как указано на скриншоте.
Теперь мы можем загрузить необходимые сертификаты через GUI или через shell в login.keychain, а Xcode будет автоматически подтягивать нужные ему во время сборки.
4.2. Для настройки доступа по ssh делаем так:
4.3. Для настройки безопасности воспользуемся плагином Role-based Authorization Strategy. Перейдём в настройки через веб-интерфейс Jenkins, а далее в раздел Manage and Assign Roles. Здесь мы сможем создать различные роли, которые назначаются для пользователей и групп, и определить для них права на те или иные операции. Здесь каждый всё делает на своё усмотрение. Но, если вы настроили внешний доступ к серверу, настоятельно рекомендую убрать все права для пользователя «гость».
5.1. На главной странице веб-интерфейса Jenkins выбираем «Создать Item». Далее выбираем пункт «Создать задачу со свободной конфигурацией» и вводим название проекта;
5.2. на странице настройки job’а первое, что мы сделаем — это перейдём во вкладку «Управление исходным кодом» и настроим загрузку проекта с GitLab. Здесь нам нужно ввести адрес репозитория нашего проекта, указать, с помощью каких полномочий (credentials) сервер получит доступ к GitLab и какую ветку проекта необходимо собрать:
5.3. Далее переходим в раздел «Сборка». Если вы используете систему управления зависимостями CocoaPods и не добавляете Pod файлы в git, нужно добавить шаг сборки «Выполнить команду shell», в которой запустить установку подов:
Тут можно обернуть установку условием, чтобы не устанавливать поды каждый раз. Пример (если файл обновился за последние 60 секунд, то выполнить...):
5.4. Добавляем шаг сборки: Xcode. Если вы используете систему управления зависимостями CocoaPods, то вам не следует указывать значение в поле Target, а в разделе Advanced Xcode build options указать название исполняемой схемы и файла .xcworkspace. Пример минимальной конфигурации показан на скриншотах ниже:
Стоит отметить, что в настройках вашего проекта исполняемая схема должна быть отмечена как shared (контейнер может быть как workspace, так и project):
Благодаря плагину Keychains and Provisioning Profiles Management можно значительно упростить работу с установкой provisioning profile.
Добавление provisioning profile:
Чтобы указать конкретный Provisioning Profile, если это необходимо, то в настройках job’a в разделе «Cреда сборки» нужно установить флаг Mobile Provisioning Profiles и выбрать один из загруженных профайлов:
Затем в настройках Xcode нужно установить custom xcodebuild arguments, как на скриншоте:
Для загрузки сертификата в связку ключей login.keychain можно воспользоваться GUI (просто кликнуть по сертификату), но такая возможность есть не всегда. Поэтому мы рассмотрим чуть более сложный вариант — добавление через ssh:
Подробнее о команде security можно прочитать здесь.
Более подробную информации о том, как добавить конкретный сертификат в job, можно найти в документации соответствующего плагина.
Теперь мы готовы нажать на кнопку Built Now на главной странице веб-интерфейса Jenkins или на странице самого проекта. Жмём и переходим в раздел Console Output начавшейся сборки. Здесь в логах можно найти много полезной информации, включая ошибки.
Если вы всё сделали правильно, то по окончанию сборки в конце лога будет указано: Finished: SUCCESS, а на главной странице Jenkins рядом с названием сборки будет гореть синий индикатор УСПЕХА.
Вот и всё, начальный этап пройден! Скоро появится продолжение статьи, где будет описано, как получить основные метрики кода, заархивировать проект в .ipa и настроить взаимодействие со slack.
Пишите свои вопросы и замечания в комментариях!
Если будет много вопросов касательно какого-либо аспекта, то мы дополним статью!
Что мы получили в итоге:
- Сервер начинает сборку:
- по веб-хуку в случае push’а в master-ветку;
- по команде в чате slack с указанием нужной ветки и доп. параметров.
- Выполняет Unit и UI-тесты.
- Получает следующие метрики:
- покрытие кода тестами;
- количество строк кода;
- дублирование кода;
- цикломатическая сложность кода.
- Архивирует проект в ipa, далее отправляет его на сервер сборок (собственной разработки) и отправляет в slack ссылку на сборку.
Теперь мы экономим кучу времени и сил, забыв, что такое делать сборку и кому-то её отправлять. Данное руководство — плод желания поделиться этим ощущением и опытом. Но, как и любой другой из множества гайдов, наш тоже не претендует на истину в последней инстанции.
Для начала ознакомьтесь со следующими оговорками и поймите, сопоставим ли наш гайд с вашими задачами:
- сервер разворачивался под нужды iOS-разработки;
- установка большинства вспомогательных программ через homebrew, включая и сам Jenkins (быстрые обновления и удобство использования);
- использование xcodebuilder для всех задач сборки-тестирования (отказались от использования xctool из-за невозможности запуска UI-тестов);
- в качестве репозитория у нас используется GitLab;
- для хранения сборок мы используем сервер собственной разработки. Для каждой сборки генерируется уникальный URL, и далее достаточно открыть ссылку с мобильного устройства в браузере и нажать «Установить» — благодаря аккаунту Enterprise любой может установить приложение на телефон. Из-за специфичности действий, связанных с отправкой файлов на наш сервер, этот этап в статье не описывается;
- все наши проекты используют систему управления зависимостями СocoaPods.
Руководство получилось довольно громоздким, и мы решили разбить его на две части. Эта часть посвящена базовой установке и настройке Jenkins.
Что необходимо:
- Mac c установленным OS X и Xcode (В нашем случае MacBook Pro 2011 года с OS X 10.11.4);
- несколько часов свободного времени.
1. Создание пользователя Jenkins и его настройка
Пользователя можно создать как через консоль, так и используя GUI. Вряд ли во втором варианте возникнут сложности, поэтому мы лучше рассмотрим первый (источник):
#Создание группы ‘Applications’
dseditgroup -o create -n . -u username -p -r ‘Applications’ applications
#Получение идентификатора для группы
sudo dscl . -read /Groups/applications
#Получение списка идентификаторов для пользователей (будет нужен уникальный идентификатор для пользователя)
sudo dscl . -list /Users UniqueID
#Создание пользователя (значения идентификаторов должны быть уникальными)
sudo dscl . -create /Users/jenkins
sudo dscl . -create /Users/jenkins PrimaryGroupID 777
sudo dscl . -create /Users/jenkins UniqueID 1777
sudo dscl . -create /Users/jenkins UserShell /bin/bash
sudo ddcl . -create /Users/jenkins RealName "Jenkins"
sudo dscl . -create /Users/jenkins NFSHomeDirectory /Users/jenkins
sudo dscl . -passwd /Users/jenkins
#Создание домашней директории и установка прав на неё
sudo mkdir /Users/jenkins
sudo chown -R jenkins /Users/jenkins
Наш пользователь готов, и теперь нам нужно зайти за него. Мы можем произвести вход в систему через GUI, либо зайти, используя консоль:
sudo -u jenkins -i
Внимание: все дальнейшие действия мы совершаем под пользователем Jenkins.
2. Установка необходимых программ
Для установки Jenkins воспользуемся системой управления пакетами Homebrew. В дальнейшем это также упростит процесс установки и обновления дополнительных пакетов, которые мы будем использовать для получения метрик кода.
- Установка Homebrew
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- Установка jenkins:
brew install jenkins
- Установка системы управления зависимостями cocoapods:
sudo gem install -n /usr/local/bin cocoapods
Чтобы наш сервер автоматически запускался при старте системы, нам необходимо настроить запуск соответствующей задачи для launchd. У нас есть выбор: сделать это через LaunchAgents или LaunchDaemon. Мы воспользуемся именно LaunchAgents, т.к. это упростит* дальнейшую работу с Jenkins. Достаточно посмотреть на таблицу ниже, чтобы понять это:
Daemon | Agent | |
---|---|---|
Launch Time | System start | User login |
User Type | Non-login | Login |
Home Folder | No | Yes |
Login Keychain | No | Yes |
iOS Simulator | No | Yes |
Provisioning Profiles | No | Yes |
Однако из-за выбора LaunchAgents нам нужно решить проблему отсутствия залогиненного пользователя при запуске системы. Для этого необходимо настроить autologin. Мне известен только один способ это сделать: через GUI (Системные настройки -> Пользователи и группы -> Параметры входа -> Автоматический вход). Если кто-то знает, как это сделать через shell — пожалуйста, отпишитесь в комментариях!
Для настройки запуска через LaunchAgents выполним следующие шаги:
- выгрузим демона (был создан автоматически при установки Jenkins):
sudo launchctl unload /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist
- удалим демона:
sudo rm /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist
- создадим агента:
cd /Users/jenkins/Library/LaunchAgents tap homebrew.mxcl.jenkins.plist
- настроим агента, используя vim-редактор:
vim homebrew.mxcl.jenkins.plist
Пример содержимого файла plist:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>homebrew.mxcl.jenkins</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/java</string>
<string>-Dmail.smtp.starttls.enable=true</string>
<string>-jar</string>
<string>/usr/local/opt/jenkins/libexec/jenkins.war</string>
<string>—httpListenAddress=0.0.0.0</string>
<string>--httpPort=8080</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>UserName</key>
<string>jenkins</string>
</dict>
</plist>
Здесь стоит обратить внимание на поле httpListenAddress со значением 0.0.0.0 и поле httpPort со значением 8080 — так сервер будет «прослушивать» любые ip-адреса по указанному порту.
Напомню: для закрытия и сохранения файла в редакторе vim вводим :wq
После установки Jenkins доступен по умолчанию по адресу 127.0.0.1 (localhost).
Для доступа из внешней сети можно пробросить порты на маршрутизаторе: 8080 для веб-интерфейса Jenkins и 22 для доступа по ssh.
3. Установка плагинов для Jenkins
Заходим на сервер Jenkins -> Настроить Jenkins -> Управление плагинами. Во вкладке «Доступные» находим и устанавливаем следующие плагины:
- Role-based Authorization Strategy — обеспечение безопасности. Даёт возможность создания групп пользователей с распределением прав;
- GitLab Plugin и Gitlab Hook Plugin— плагины для работы с gitlab;
- Xcode integration — интеграция с Xcode;
- Keychains and Provisioning Profiles Management — облегчает работу с provisioning profile.
4. Базовая настройка Jenkins
Через веб-интерфейс Jenkins`а заходим в «Настройки», а затем в раздел «Конфигурирование системы». На что здесь стоит обратить внимание:
4.1. Настройки Xcode Builder. Если вы устанавливали Xcode в стандартную директорию, то настройки менять не нужно. В противном же случае необходимо задать путь для указанных компонентов. Учитывая, что мы запустили сервер с использованием LaunchAgents, то Xcode будет иметь доступ к login.keychain. Но если вы хотите подстраховаться, то можете в этом разделе добавить keychain, как указано на скриншоте.
Теперь мы можем загрузить необходимые сертификаты через GUI или через shell в login.keychain, а Xcode будет автоматически подтягивать нужные ему во время сборки.
4.2. Для настройки доступа по ssh делаем так:
- генерируем shh-ключ, если его нет (инструкцию для примера можно найти здесь);
- добавляем ключ в разделе CVS;
- для доступа к GitLab перейдём в раздел Credentials на главной странице веб-интерфейса Jenkins. Здесь необходимо добавить закрытый ключ для нашего пользователя (Jenkins в данном случае пользователь, зарегистрированный в GitLab). Вы можете вставить его непосредственно, как показано на примере ниже, или же указать путь к нему:
- в настройках самого GitLab необходимо указать соответствующий открытый ключ:
4.3. Для настройки безопасности воспользуемся плагином Role-based Authorization Strategy. Перейдём в настройки через веб-интерфейс Jenkins, а далее в раздел Manage and Assign Roles. Здесь мы сможем создать различные роли, которые назначаются для пользователей и групп, и определить для них права на те или иные операции. Здесь каждый всё делает на своё усмотрение. Но, если вы настроили внешний доступ к серверу, настоятельно рекомендую убрать все права для пользователя «гость».
5. Создание job’а и настройка сборки проекта
5.1. На главной странице веб-интерфейса Jenkins выбираем «Создать Item». Далее выбираем пункт «Создать задачу со свободной конфигурацией» и вводим название проекта;
5.2. на странице настройки job’а первое, что мы сделаем — это перейдём во вкладку «Управление исходным кодом» и настроим загрузку проекта с GitLab. Здесь нам нужно ввести адрес репозитория нашего проекта, указать, с помощью каких полномочий (credentials) сервер получит доступ к GitLab и какую ветку проекта необходимо собрать:
5.3. Далее переходим в раздел «Сборка». Если вы используете систему управления зависимостями CocoaPods и не добавляете Pod файлы в git, нужно добавить шаг сборки «Выполнить команду shell», в которой запустить установку подов:
#!/bin/bash -l
export LANG=UTF-8
pod install
Тут можно обернуть установку условием, чтобы не устанавливать поды каждый раз. Пример (если файл обновился за последние 60 секунд, то выполнить...):
if [ $(( $(date +"%s") - $(stat -f %m Podfile) )) -le 60 ]; then
pod install
fi
5.4. Добавляем шаг сборки: Xcode. Если вы используете систему управления зависимостями CocoaPods, то вам не следует указывать значение в поле Target, а в разделе Advanced Xcode build options указать название исполняемой схемы и файла .xcworkspace. Пример минимальной конфигурации показан на скриншотах ниже:
Стоит отметить, что в настройках вашего проекта исполняемая схема должна быть отмечена как shared (контейнер может быть как workspace, так и project):
6. Работа с сертификатами и provisioning profile
Благодаря плагину Keychains and Provisioning Profiles Management можно значительно упростить работу с установкой provisioning profile.
Добавление provisioning profile:
- переходим в настройки Jenkins;
- в списке выбираем Keychains and Provisioning Profiles Management;
- нажимаем кнопку «Выберите файл», находим свой provisioning profile и нажимаем Upload.
Чтобы указать конкретный Provisioning Profile, если это необходимо, то в настройках job’a в разделе «Cреда сборки» нужно установить флаг Mobile Provisioning Profiles и выбрать один из загруженных профайлов:
Затем в настройках Xcode нужно установить custom xcodebuild arguments, как на скриншоте:
Для загрузки сертификата в связку ключей login.keychain можно воспользоваться GUI (просто кликнуть по сертификату), но такая возможность есть не всегда. Поэтому мы рассмотрим чуть более сложный вариант — добавление через ssh:
- скачиваем нужный сертификат на свой Mac и устанавливаем в локальный keychain access двойным кликом;
- заходим в keychain access, находим нужный сертификат и экспортируем ключ в формат .p12;
- передаём сертификат на сервер:
certificate — название сертификата;scp certificate.crt jenkins@server:/Users/jenkins
jenkins — имя пользователя;
server — адрес сервера;
:/Users/jenkins — путь сохраняемого файла;
(Можно использовать параметр для указания нужного порта в формате: scp -P 20 certificate.crt jenkins@server:/Users/jenkins)
- передаём ключ на сервер:
scp privatekey.p12 jenkins@server:/Users/jenkins
- подключаемся к серверу по ssh:
ssh jenkins@server
- Открываем доступ к связке ключей:
security unlock-keychain -p password /Users/jenkins/Library/Keychains/login.keychain
- Устанавливаем сертификат:
security add-certificates ./certificate.crt
- копируем ключ:
security import privatekey.p12 -k /Users/jenkins/Library/Keychains/login.keychain -P password -A
Подробнее о команде security можно прочитать здесь.
Более подробную информации о том, как добавить конкретный сертификат в job, можно найти в документации соответствующего плагина.
7. Перед запуском
Теперь мы готовы нажать на кнопку Built Now на главной странице веб-интерфейса Jenkins или на странице самого проекта. Жмём и переходим в раздел Console Output начавшейся сборки. Здесь в логах можно найти много полезной информации, включая ошибки.
Если вы всё сделали правильно, то по окончанию сборки в конце лога будет указано: Finished: SUCCESS, а на главной странице Jenkins рядом с названием сборки будет гореть синий индикатор УСПЕХА.
Вот и всё, начальный этап пройден! Скоро появится продолжение статьи, где будет описано, как получить основные метрики кода, заархивировать проект в .ipa и настроить взаимодействие со slack.
Пишите свои вопросы и замечания в комментариях!
Если будет много вопросов касательно какого-либо аспекта, то мы дополним статью!
Поделиться с друзьями
Комментарии (6)
pryaniq
01.06.2016 13:18Интеграция со Slack очень удобна, допустим когда едешь в метро, или нет под рукой рабочего компьютера, но надо срочно сделать сборку, чтобы предоставить сборку заказчику. Одна строчка сообщения с параметрами, отправленная с мобильного устройства боту в Slack для активации процесса сборки приложения из нужной git ветки, исправит эту ситуацию и позволит сэкономить кучу времени.
dittohead
А чем xcode server не устроил?
MaiorPro
Если говорить только об автоматизации сборки, то Xcode Server хорошо подходит. Но когда нужна гибкая конфигурация с различными сценариями, то здесь лучше подойдет такой CI, как Jenkins. Например, интеграция со Slack в Jenkins настраивается легко и быстро и при этом значительно упрощает работу с сервером (запуск сборки, получение ссылки на сборку, получение отчетов и ошибок), а сделать то же самое в связке с Xcode Server будет значительно сложнее.
LionAlex
У Xcode Server есть проблема – большие проекты в него просто не пролазят. Он пытается склонировать их из гита и с завидной регулярностью выдает «Не шмогла я, не шмогла».
Плюс он прямой как палка, никаких тебе интеграций, кастомного флоу и прочего.