Как за день поднять CI для iOS-разработчиков

Прогрессивная часть девелоперской среды практикует методику непрерывной интеграции (CI), и iOS-отдел компании Лайв Тайпинг решил к ней примкнуть, развернув сервер для сборок на платформе Jenkins. С тех пор началась совсем другая жизнь. 

Что мы получили в итоге: 

  1. Сервер начинает сборку:
    1. по веб-хуку в случае push’а в master-ветку;
    2. по команде в чате slack с указанием нужной ветки и доп. параметров.
  2. Выполняет Unit и UI-тесты.
  3. Получает следующие метрики:
    1. покрытие кода тестами;
    2. количество строк кода;
    3. дублирование кода;
    4. цикломатическая сложность кода.
  4. Архивирует проект в ipa, далее отправляет его на сервер сборок (собственной разработки) и отправляет в slack ссылку на сборку.

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

Для начала ознакомьтесь со следующими оговорками и поймите, сопоставим ли наш гайд с вашими задачами:

  1. сервер разворачивался под нужды iOS-разработки;
  2. установка большинства вспомогательных программ через homebrew, включая и сам Jenkins (быстрые обновления и удобство использования);
  3. использование xcodebuilder для всех задач сборки-тестирования (отказались от использования xctool из-за невозможности запуска UI-тестов);
  4. в качестве репозитория у нас используется GitLab;
  5. для хранения сборок мы используем сервер собственной разработки. Для каждой сборки генерируется уникальный URL, и далее достаточно открыть ссылку с мобильного устройства в браузере и нажать «Установить» — благодаря аккаунту Enterprise любой может установить приложение на телефон. Из-за специфичности действий, связанных с отправкой файлов на наш сервер, этот этап в статье не описывается;
  6. все наши проекты используют систему управления зависимостями СocoaPods.

Руководство получилось довольно громоздким, и мы решили разбить его на две части. Эта часть посвящена базовой установке и настройке Jenkins. 

Что необходимо:

  1. Mac c установленным OS X и Xcode (В нашем случае MacBook Pro 2011 года с OS X 10.11.4);
  2. несколько часов свободного времени.

Создание пользователя 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.

Установка необходимых программ

Для установки Jenkins воспользуемся системой управления пакетами Homebrew. В дальнейшем это также упростит процесс установки и обновления дополнительных пакетов, которые мы будем использовать для получения метрик кода. 

1. Установка Homebrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2. Установка 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

*Главная проблема запуска через Daemon заключается в том, что нельзя выполнить тестирование, не запустив iOS Simulator (подробнее про разницу использования демона и агента можно прочитать здесь)

Однако из-за выбора LaunchAgents нам нужно решить проблему отсутствия залогиненного пользователя при запуске системы. Для этого необходимо настроить autologin. Мне известен только один способ это сделать: через GUI (Системные настройки -> Пользователи и группы -> Параметры входа -> Автоматический вход). Если кто-то знает, как это сделать через shell — пожалуйста, отпишитесь в комментариях!

Для настройки запуска через LaunchAgents выполним следующие шаги:

1. выгрузим демона (был создан автоматически при установки Jenkins):

sudo launchctl unload /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist

2. удалим демона:

sudo rm /Library/LaunchDaemons/homebrew.mxcl.jenkins.plist

3. создадим агента:

cd /Users/jenkins/Library/LaunchAgents
tap homebrew.mxcl.jenkins.plist

4. настроим агента, используя 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.

Установка плагинов для Jenkins

Заходим на сервер Jenkins -> Настроить Jenkins -> Управление плагинами. Во вкладке «Доступные» находим и устанавливаем следующие плагины:

  1. Role-based Authorization Strategy — обеспечение безопасности. Даёт возможность создания групп пользователей с распределением прав;
  2. GitLab Plugin и Gitlab Hook Plugin— плагины для работы с gitlab;
  3. Xcode integration — интеграция с Xcode;
  4. Keychains and Provisioning Profiles Management — облегчает работу с provisioning profile.

Базовая настройка Jenkins

Через веб-интерфейс Jenkins`а заходим в «Настройки», а затем в раздел «Конфигурирование системы». На что здесь стоит обратить внимание:


1. Настройки Xcode Builder. Если вы устанавливали Xcode в стандартную директорию, то настройки менять не нужно. В противном же случае необходимо задать путь для указанных компонентов. Учитывая, что мы запустили сервер с использованием LaunchAgents, то Xcode будет иметь доступ к login.keychain. Но если вы хотите подстраховаться, то можете в этом разделе добавить keychain, как указано на скриншоте (здесь и далее нажмите на изображение для просмотра). 

CI-IOS-1.png#asset:4236

Теперь мы можем загрузить необходимые сертификаты через GUI или через shell в login.keychain, а Xcode будет автоматически подтягивать нужные ему во время сборки.

2. Для настройки доступа по ssh делаем так:

a. генерируем shh-ключ, если его нет (инструкцию для примера можно найти здесь);

b. добавляем ключ в разделе CVS;

CI-IOS-2.png#asset:4237

c. для доступа к GitLab перейдём в раздел Credentials на главной странице веб-интерфейса Jenkins. Здесь необходимо добавить закрытый ключ для нашего пользователя (Jenkins в данном случае пользователь, зарегистрированный в GitLab). Вы можете вставить его непосредственно, как показано на примере ниже, или же указать путь к нему:

CI-IOS-3.png#asset:4238

d. в настройках самого GitLab необходимо указать соответствующий открытый ключ:

CI-IOS-4.png#asset:4239

3. Для настройки безопасности воспользуемся плагином Role-based Authorization Strategy. Перейдём в настройки через веб-интерфейс Jenkins, а далее в раздел Manage and Assign Roles. Здесь мы сможем создать различные роли, которые назначаются для пользователей и групп, и определить для них права на те или иные операции. Здесь каждый всё делает на своё усмотрение. Но, если вы настроили внешний доступ к серверу, настоятельно рекомендую убрать все права для пользователя «гость». 

Создание job’а и настройка сборки проекта

1. На главной странице веб-интерфейса Jenkins выбираем «Создать Item». Далее выбираем пункт «Создать задачу со свободной конфигурацией» и вводим название проекта;
2. на странице настройки job’а первое, что мы сделаем — это перейдём во вкладку «Управление исходным кодом» и настроим загрузку проекта с GitLab. Здесь нам нужно ввести адрес репозитория нашего проекта, указать, с помощью каких полномочий (credentials) сервер получит доступ к GitLab и какую ветку проекта необходимо собрать:

CI-IOS-5png.png#asset:4240

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

4. Добавляем шаг сборки: Xcode. Если вы используете систему управления зависимостями CocoaPods, то вам не следует указывать значение в поле Target, а в разделе Advanced Xcode build options указать название исполняемой схемы и файла .xcworkspace. Пример минимальной конфигурации показан на скриншотах ниже:

CI-IOS-6.png#asset:4241

CI-IOS-6.1.png#asset:4366

Стоит отметить, что в настройках вашего проекта исполняемая схема должна быть отмечена как shared (контейнер может быть как workspace, так и project):

CI-IOS-7.png#asset:4242

Работа с сертификатами и provisioning profile

Благодаря плагину Keychains and Provisioning Profiles Management можно значительно упростить работу с установкой provisioning profile. 

Добавление provisioning profile:

  1. переходим в настройки Jenkins;
  2. в списке выбираем Keychains and Provisioning Profiles Management;
  3. нажимаем кнопку «Выберите файл», находим свой provisioning profile и нажимаем Upload.

Чтобы указать конкретный Provisioning Profile, если это необходимо, то в настройках job’a в разделе «Cреда сборки» нужно установить флаг Mobile Provisioning Profiles и выбрать один из загруженных профайлов:

CI-IOS-8.png#asset:4243

Затем в настройках Xcode нужно установить custom xcodebuild arguments, как на скриншоте:

CI-IOS-9.png#asset:4244

Для загрузки сертификата в связку ключей login.keychain можно воспользоваться GUI (просто кликнуть по сертификату), но такая возможность есть не всегда. Поэтому мы рассмотрим чуть более сложный вариант — добавление через ssh:

  1. скачиваем нужный сертификат на свой Mac и устанавливаем в локальный keychain access двойным кликом;
  2. заходим в keychain access, находим нужный сертификат и экспортируем ключ в формат .p12;
  3. передаём сертификат на сервер: 
scp certificate.crt jenkins@server:/Users/jenkins

certificate — название сертификата;

jenkins — имя пользователя; 

server — адрес сервера; 

:/Users/jenkins — путь сохраняемого файла;

(Можно использовать параметр для указания нужного порта в формате: scp -P 20 certificate.crt jenkins@server:/Users/jenkins)

      4. передаём ключ на сервер:

scp privatekey.p12 jenkins@server:/Users/jenkins

      5. подключаемся к серверу по ssh:

ssh jenkins@server

      6. открываем доступ к связке ключей:

security unlock-keychain -p password /Users/jenkins/Library/Keychains/login.keychain

      7. устанавливаем сертификат:

security add-certificates ./certificate.crt

      8. копируем ключ:

security import privatekey.p12 -k /Users/jenkins/Library/Keychains/login.keychain -P password -A

Подробнее о команде security можно прочитать здесь

Более подробную информации о том, как добавить конкретный сертификат в job, можно найти в документации соответствующего плагина.

Перед запуском

Теперь мы готовы нажать на кнопку Built Now на главной странице веб-интерфейса Jenkins или на странице самого проекта. Жмём и переходим в раздел Console Output начавшейся сборки. Здесь в логах можно найти много полезной информации, включая ошибки. 
Если вы всё сделали правильно, то по окончанию сборки в конце лога будет указано: Finished: SUCCESS, а на главной странице Jenkins рядом с названием сборки будет гореть синий индикатор УСПЕХА. 

Установка всех необходимых программ и плагинов.

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

#Определение степени покрытие кода тестами
brew install gcovr
#Счётчик строк кода
brew install cloc
#Счётчик строк кода, альтернативный вариант
brew install sloccount
#Поиск дублирования кода
brew install pmd
#Генерация отчётов о результатах тестов (также генерирует данные для oclint)
sudo gem install xcpretty
#Статический анализ кода
brew tap oclint/formulae
brew install oclint

Далее нам нужно установить плагины для Jenkins, которые будут отображать полученную статистику в удобочитаемом виде:

  1. PMD Plug-in — генерация отчёта по статистической сложности кода;
  2. SLOCCount Plug-in — генерация отчёта по количеству строк кода;
  3. Test Results Analyzer Plugin — генерация отчёта по результатам тестов;
  4. Cobertura Plugin — генерация отчета по покрытию кода тестами;
  5. DRY Plug-in — генерация отчёта по дублированию кода.

Также установим вспомогательные плагины:

  1. Environment Injector Plugin — внедрение переменных в проект;
  2. Pre SCM BuildStep Plugin — внедрение переменных до начала выполнения job’а;
  3. Build Authorization Token Root Plugin — запуск job’а по get-запросу с токеном;
  4. Parameterized Trigger plugin — позволяет запускать job’ы с параметрами по окончанию сборки;
  5. Slack Notification Plugin — отправка сообщений в командный чат Slack;
  6. Publish Over SSH — этот плагин указан здесь в качестве примера. Он подойдет вам, если вы, как и мы, отправляете данные через SFTP на сервер.

Интеграция с чатом Slack

Для получения уведомлений о состоянии сборки в командном чате Slack, нам необходимо добавить соответствующую интеграцию с Jenkins в настройках Slack’а. Это можно сделать здесь
После создания интеграции будет сгенерирован уникальный токен, который необходимо добавить в настройки Jenkins’а (либо в настройки отдельного job’а) — как в примере на скриншоте:

CI-IOS-10.png#asset:4310

Далее настроим запуск сборок с помощью встроенного механизма команд в Slack’е. Для начала нам необходимо добавить интеграцию. Для этого пройём в подраздел Slash commands в разделе Custom Integrations и нажмём на кнопку Add configurations. Эту операцию можно выполнить здесь.
При настройке вам нужно указать название вашей команды, выбрать метод передачи данных POST и указать URL-адрес, на который будет идти запрос.

CI-IOS-11.png#asset:4311

Рассмотрим пример формирования URL для запроса подробнее. Наш URL для примера выглядит так: 

http://server:8080/buildByToken/buildWithParameters?job=JenkinsExecutor&token=XXXXXXXXXXXXXXXXX

Разберём его по составляющим:

  1. server — это внешний адрес вашего сервера. Если необходимо, то здесь также указываем нужный порт (в нашем случае 8080);
  2. buildByToken — возможность, предоставляемая плагином Build Authorization Token Root Plugin. Позволяет запускать job по ссылке с указанием токена (в нашем случае XXXXXXXXXXXXXXXXX);
  3. buildWithParameters — указывает на то, что нужно запустить параметризованную сборку;
  4. JenkinsExecutor — название job’а, который мы создадим и будем использовать для запуска других job’ов. О нём речь пойдет ниже;
  5. XXXXXXXXXXXXXXXXX — значение токена, который устанавливается в настройках плагина в конфигурации каждого отдельного job’а.

В качестве примера будем использовать следующую структуру команды:

/build Example test master
  1. /build — название нашей команды;
  2. Example — название job’a;
  3. test — вспомогательный флаг, связанный с запуском тестов и созданием отчётов с метриками;
  4. master — ветка для сборки.

Рассмотренная конфигурация позволит нам запускать сборку любого проекта с указанием нужной ветки, при этом будет использоваться единая команда: /build.

Конфигурация вспомогательного job’a — JenkinsExecutor

Данный job будет нужен нам для того, чтобы запускать другие job’ы. В нём также можно будет обрабатывать ошибки, если пользователь ввёл не существующий проект, и добавить информацию о команде (своеобразный help). 
Заходим на сервер и создаём новую задачу со свободной конфигурацией и названием JenkinsExecutor. Далее в настройках job’а выставляем флаг, указывающий на то, что сборка является параметризированной и принимает параметр text. При запуске команды в Slack’е все данные (Example master test) будут передаваться единой строкой в переменной text.

CI-IOS-12.png#asset:4312

Далее устанавливаем флаг, отвечающий за запуск сборки удалённо. Здесь нужно указать токен, идентичный тому, который мы установили в настройках команды в Slack’е:


Теперь нам необходимо извлечь значения из переменной text. Для этого переходим в раздел «Сборка» и добавляем шаг сборки «выполнить команду shell». Пример команды:

#Создаём массив из элементов строки, разделённых пробелом
IFS=' ' read -a array <<< "$text"
#Согласно нашему примеру, первое значение — это название проекта
JOB_NAME=${array[0]}
#Флаг, ответственный за тесты
TEST=${array[1]}
#Название ветки проекта
BRANCH=${array[2]}
#Если необходимо, можно также получить другие значения:
USER_NAME=${user_name}
CHANNEL_NAME=${channel_name}

Для запуска сборки с параметрами отправим POST-запрос на исполнение конкретного job’а. Для этого к предыдущей shell-команде добавляем следующую строчку: 

curl -d TEST=${TEST} -d BRANCH=${BRANCH} -X POST \
-u username:password http://127.0.0.1:8080/job/${JOB_NAME}/buildWithParameters

Здесь password — это API key пользователя username (пользователь должен иметь права на запуск job’ов).

Чтобы получить ключ:

  1. Нажмите на username в правом верхнем углу веб-интерфейса Jenkins;
  2. Нажмите на кнопку «Настроить» в левой части экрана;
  3. Нажмите на Show API Key — искомый ключ у нас.

Обратите внимание, что все запускаемые сборки должны быть параметризированными!

Настройка сборки

1. Первое, на что стоит обратить внимание при настройки job’а — это то, что сборка должна быть параметризированной. Для этого выставляем соответствующий флаг, добавляем текстовые параметры BRANCH и TEST и задаём им параметры по умолчанию:

CI-IOS-13.png#asset:4338

Здесь стоит отметить, что для переменной BRANCH нужно дополнительно добавить значение по умолчанию. Дело в том, что если вы запустите сборку из Slack без указания ветки, то в переменной BRANCH будет пустое значение и соответственно будет ошибка. Для этого мы добавим флаг Run buildstep before SCM runs в разделе «Среда сборки». Затем добавим шаг «выполнить команду shell» и шаг inject environment variables. Делаем по примеру:

CI-IOS-14.png#asset:4339

2. Настраиваем взаимодействие с GitLab.
Указываем адрес репозитория проекта. Указываем ветку сборки (в нашем случае это переменная BRANCH).

CI-IOS-15.png#asset:4340

3. Настраиваем сборку по веб-хуку.
В триггерах сборки устанавливаем флаг «Сборка по пушу в GitLab». Добавляем нужные параметры и указываем ветку, для которой будет срабатывать триггер:

CI-IOS-16.png#asset:4341

Затем в настройках проекта на GitLab в категории Web hooks добавляем веб-хук на сервер Jenkins’а:

CI-IOS-17.png#asset:4342

4. Этап сборки начинается с выполнения shell-команды, которая устанавливает Pod’ы, если файл был обновлён:

if [ $(( $(date +"%s") - $(stat -f %m Podfile) )) -le 60 ]; then
    pod install
fi

Затем для удобства установим некоторые переменные для проекта и запишем их в файл:

#Название .ipa-файла
PROJECT_NAME="Example"
#Название файла .xcworkspace 
WORKSPACE_NAME="Example" 
#Название исполняемой схемы
SCHEME_NAME="Example" 
#Название папки с исходниками. Будет использоваться для подсчёта количества строк кода
FOLDER_NAME_WITH_CODE="Example" 
#Записываем переменные в файл, чтобы использовать в других этапах сборки
echo PROJECT_NAME=$PROJECT_NAME > build.properties
echo WORKSPACE_NAME=$WORKSPACE_NAME >> build.properties
echo SCHEME_NAME=$SCHEME_NAME >> build.properties

В зависимости от установленного параметра TEST запускаем или пропускаем этап тестирования и генерацию отчётов. Пример того, как это может выглядеть:

if [ "$TEST" == "test" ]; then
        
	#Создание папки reports, в которую мы будем складывать отчёты
if [ ! -d "reports" ]; then
    		mkdir "reports"
	fi

    #Тестирование и создание отчётов для анализа   
	xcodebuild -workspace ${WORKSPACE_NAME}.xcworkspace \
		-scheme ${SCHEME_NAME} \
		-configuration Debug \
		-sdk iphonesimulator \
		-destination 'platform=iOS Simulator,name=iPhone 6' \
        -IDECustomDerivedDataLocation="build_ccov" \
		GCC_GENERATE_TEST_COVERAGE_FILES=YES \
		GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES \
		clean test | xcpretty -r junit -o reports/junit.xml -r json-compilation-database -o compile_commands.json
	#Publish JUNIT test = **/reports/junit.xml

    #Анализ синтаксической сложности кода
	oclint-json-compilation-database -v -e Pods -- \
    	-rc=LONG_LINE=200 \
    	-rc=NCSS_METHOD=60 \
    	-rc=LONG_METHOD=100 \
    	-rc=MINIMUM_CASES_IN_SWITCH=1 \
    	-report-type pmd \
    	-o reports/oclint.xml \
    	-max-priority-1 1000 \
    	-max-priority-2 1000 \
    	-max-priority-3 1000 
	#Publish PMD analysis = **/reports/oclint.xml

    #Анализ покрытия кода тестами
    gcovr --object-directory="build_ccov/${SCHEME_NAME}/Build/Intermediates/${SCHEME_NAME}.build/"\
"Debug-iphonesimulator/${SCHEME_NAME}.build/Objects-normal/x86_64/" \
	--xml \
    --print-summary \
    --exclude '.*Tests.*' \
    --exclude '.*Libs.*' \
    --exclude '.*ExternalFrameworks.*' \
    --exclude '.*Platforms.*' \
    --output=reports/coverage.xml
    #Publish Cobertura Coverage = **/reports/coverage.xml
    
	#Подсчёт строк кода (два варианта):
    cloc ${WORKSPACE}/${FOLDER_NAME_WITH_CODE} -by-file -skip-uniqueness -xml -out=${WORKSPACE}/reports/cloc.xml
#Publish SLOCCount analysis = **/reports/cloc.xml
    sloccount --duplicates --wide --details ${WORKSPACE}/${FOLDER_NAME_WITH_CODE} -v > reports/sloccount.sc
    	#Publish SLOCCount analysis = **/reports/sloccount.sc

	#Анализ дублирования кода
	pmd cpd --files ${WORKSPACE}/${FOLDER_NAME_WITH_CODE} \
    --minimum-tokens 10 --language objectivec \
    --encoding UTF-8 \
    --format net.sourceforge.pmd.cpd.XMLRenderer | iconv -f macRoman -t utf-8 | sed 's/MacRoman/UTF-8/g' > reports/duplicated-code.xml
	#Publish duplicate code = **/reports/duplicated-code.xml
else
    	touch reports/junit.xml
	#Данная строчка нужна, чтобы избежать провала при сборке из-за генерации отчета плагином Publish JUNIT test result report
fi

Подробную информацию по синтаксису команд ищите на соответствующих страницах документации:

  1. OCLint
  2. Gcovr
  3. CLOC
  4. SLOCount
  5. PMD (CPD)
  6. Xcode Build

5. Записанные ранее переменные необходимо внедрить в процесс сборки. Для этого добавляем шаг сборки Inject environment variables и указываем нужный путь:

CI-IOS-18.png#asset:4343

6. Следующий этап — создание сборки и архивирование в .ipa-файл. Для этого воспользуемся плагином Xcode. Делаем по примеру:

CI-IOS-19.png#asset:4344

CI-IOS-20.png#asset:4345

7. Последний шаг — добавить послесборочные операции. 

Мы сгенерировали файлы для пяти отчетов, и теперь нам нужно передать эти файлы соответствующим плагинам:

  1. Publish PMD analysis results = **/reports/oclint.xml
  2. Publish duplicate code analysis results = **/reports/duplicated-code.xml
  3. Publish Cobertura Coverage analysis results = **/reports/coverage.xml
  4. Publish SLOCCount analysis results = в зависимости от используемого модуля:
    1. **/reports/cloc.xml
    2. **/reports/sloccount.sc
  5. Publish JUNIT test result report = **/reports/junit.xml (Примечание: В расширенных настройках плагина нужно установить флаг Do not fail the build on empty test results. Это поможет избежать fail-статуса для сборки, если она была запущена без запуска тестов)

На этом этапе мы можем отправить полученный в случае успеха .ipa-файл туда, куда нам нужно (на сервер, по e-mail и т.д.). Если вы хотите отправить файлы на сервер по SFTP и вы используете плагин Publish Over SSH, то нужно перейти в раздел «Среда сборки», установить флаг для Send files or execute commands over SSH after the build runs и настроить плагин в соответствии с вашими требованиями. 

Последний шаг, который нам нужно добавить — Slack Notification, который, как вы догадались, отправляет уведомления в Slack. В расширенных настройках плагина можно указать индивидуальные настройки для текущего job’а. Стоит заметить, что в качестве сообщения можно указать переменную (пример: $MESSAGE), значение которой менять на разных этапах сборки и тем самым отправлять более информативные сообщения в Slack.

На этом внедрение CI можно считать оконченным. Мы надеемся, что наша статья будет для вас полезной.

Jenkins
CI
iOS