Jenkins + Android
Нередко управление «мобильным» CI/CD ложится на плечи разработчиков, хотя это и не относится к их основным обязанностям напрямую. Тогда возникают проблемы, связанные со снижением производительности и неэффективным расходованием времени.
Чтобы оптимизировать управление «мобильными» CI/CD, важно иметь в команде человека, который разбирается и в мобильной разработке, и в CI/CD. Идеальная ситуация, когда это бывший разработчик мобильных приложений, который перешёл на должность инженера. Но, конечно, такой человек есть не везде. В статье дальше делимся правилами и рекомендациями, которые помогут избежать трудностей при настройке Jenkins для Android-проектов.
Особенности «мобильного» CI/CD
Прежде чем переходить непосредственно к настройке Jenkins, разберём особенности CI/CD для мобильной разработки. По данным Developer Nation, 33% разработчиков мобильных приложений не применяют в работе методы CI/CD. На то есть несколько причин:
В отличие от открытой экосистемы интернета, экосистемы мобильных приложений жёстко контролируются поставщиками ОС — Apple и Google. У этих поставщиков есть строгие правила разработки, развёртывания и тестирования, которые зачастую не позволяют применять лучшие практики CI/CD «из коробки».
«Мобильный» CI/CD требует знаний и инструментов, которые могут отсутствовать в типичной DevOps-команде. Поэтому для обслуживания таких пайплайнов нужны специалисты, хорошо разбирающиеся и в практиках CI/CD, и в мобильной разработке.
«Мобильный» CI/CD требует настройки отдельного пайплайна из веб-стеков или бэкенд-стеков, поскольку он основан на развёртывании скомпилированного двоичного файла, который нужно каждый раз заново устанавливать на мобильное устройство. Развёртывания B2C-приложений для конечных пользователей сопровождаются длительными ревью и периодами ожидания релиза, что не характерно для других стеков. Из-за этого ошибки должны обнаруживаться на самой ранней стадии, а анализ кода и тесты, специфичные для мобильных приложений, должны присутствовать на каждом этапе CI/CD.
В отличие от других стеков, подпись кода (code-signing) обязательна при разработке мобильных приложений. Это вводит ещё один уровень сложности поверх и без того сложных процессов «мобильного» CI/CD.
«CI/CD c Jenkins»
Установим Jenkins
Сначала установим Jenkins. Для Windows это будет ZIP-файл, содержащий установочный файл .msi
. Для Mac — файл .pkg
.
Альтернативный вариант: загрузить общий пакет Java (.war) и запустить его из командной строки. Команда для запуска Jenkins из файла .war:
java -jar jenkins.war
Настроим Jenkins
После завершения установки откроется веб-страница:
Если этого не произошло, перейдите по адресу: http://localhost:8080
По умолчанию Jenkins заблокирован и для него требуется ключ разблокировки, записанный в файле initialAdminPassword
. Путь к нему:
Откройте файл в текстовом редакторе, скопируйте ключ разблокировки, вставьте его и нажмите «Продолжить».
После этого откроется страница «Customize Jenkins». Воспользуемся опцией «Install suggestedplugins» («Установить предлагаемые плагины»). Это поможет установить базовые плагины — git, subversion, gradle и др.
После того, как мы выберем «Install suggested plugins», появится страница со списком устанавливаемых плагинов:
Дождёмся, когда установятся все плагины — это займёт буквально пару минут. После завершения установки будет предложено создать первого пользователя-администратора:
Заполним необходимую информацию, а затем нажмём «Save and Continue».
Примечание: имя пользователя и пароль будут использоваться для входа в Jenkins.
Далее откроется страница конфигурации инстанса:
Вы можете изменить URL-адрес Jenkins, но в рамках статьи мы продолжим использовать тот, что задан по умолчанию. Для завершения настройки нажмём «Save and finish». А затем кликнем на «Start Using Jenkins» («Начать использовать Jenkins»), и отобразится панель управления Jenkins:
Настроим Jenkins для Android
Переходим к самому сложному. Создание Android-проекта в Jenkins включает несколько этапов:
Git;
JDK;
Gradle;
Android SDK Tools.
Поскольку мы выбрали установку плагинов по умолчанию, Git и Gradle установятся автоматически. Нам нужно установить инструменты Java и Android SDK.
Настроим JDK
Здесь можно скачать и установить java-jdk для соответствующих платформ. После установки нужно установить переменные среды для JDK в Jenkins:
/jdk/
Чтобы задать переменные среды для Jenkins, откроем панель мониторинга Jenkins и перейдите в раздел Manage Jenkins>Configure System (Управление Jenkins>Настройка системы). Найдём раздел »Global Settings» («Глобальные настройки» и установим флажок напротив »Environment variables»(«Переменные среды»).
Нажмём на кнопку »Add» («Добавить»), чтобы добавить переменную окружения. Затем определим путь к JDK:
И сохраним изменения.
Настроим инструменты Android SDK
Если у вас уже установлен Android SDK, можно пропустить эту часть и сразу переходить к установке переменной среды для ANDROID_HOME.
Если у вас нет Android SDK, можете скачать его отсюда. Загрузите нужный zip-файл = и распакуйте его в подходящем для вас месте на диске.
Когда Android SDK загрузится, нужно принять все лицензии для его использования. Откроем терминал или CMD, перейдём в распакованную папку с именем «tools», а затем — в каталог bin. Обновим sdkmanager.
Для Windows:
// updates sdkmanager
sdkmanager --update
Для Mac:
// updates sdkmanager
./sdkmanager --update
Запустим команду, чтобы принять все лицензии.
Для Windows
// updates sdkmanager
sdkmanager --licenses
Для Mac
// updates sdkmanager
./sdkmanager --licenses
На экране отобразятся выходные данные (как на изображении выше). Введите y
и нажмите Enter. Система попросит принять лицензии одну за другой — всё, что нам нужно делать, это вводить y
и нажимать Enter до тех пор, пока все лицензии не будут приняты. После вы увидите такое сообщение:
Если вы не приняли какую-либо лицензию, снова запустите команду и повторите процесс. Если все лицензии приняты, после запуска команды система оповестит вас об этом.
После настройки инструментов Android SDK, нужно установить переменную окружения ANDROID_HOME
, указывающую на местоположение Android SDK. Процесс такой же, как и при настройке переменной окружения для JDK.
Откроем панель управления Jenkins и перейдём в раздел Manage Jenkins>Configure System. В разделе Global Settings добавим новую переменную среды:
Прокрутим страницу вниз и нажмём «Save».
Настроим Android-проект в Jenkins
Следующий шаг — создание джобы для Android-проекта. Откроем панель управления Jenkins и нажмём «Create new job» или «New item».
Введём название проекта и выберем стиль «Freestyle Project». Нажмём ОК, чтобы продолжить.
На следующей странице перейдём на вкладку »Source Code Management» («Управление исходным кодом»). Здесь настроим наш репозиторий git, в котором будем выполнять сборку. Выберем опцию git, скопируем и вставим URL репозитория. Если репозиторий закрытый, вы увидите что-то вроде этого:
Если репозиторий открыт, нам не нужно предоставлять учётные данные для доступа к нему. И мы не получим эту ошибку. Но если репозиторий частный, нужно предоставить правильные учётные данные. Нажмём »Add» в разделе »Credentials» («Учетные данные»), а затем выберем »Jenkins credentials Provider» («Поставщик учетных данных Jenkins»), чтобы добавить новые учётные данные. Добавим информацию, как показано на скриншоте:
Нажмём «Add», а затем выберем добавленные учётные данные из раскрывающегося списка в разделе «Credentials». Теперь ошибки должны исчезнуть.
Следующий шаг — указать, в какой ветке мы хотим собрать проект:
Jenkins будет использовать эти настройки и клонировать репозиторий каждый раз, когда мы будем создавать проект. Мы пока не устанавливаем Build Triggers и Build Environment для простоты.
Сообщим Jenkins, как «строить» наш проект. Перейдите в раздел »Build» («Сборка»), нажмите кнопку »Add build step». Есть два способа настроить процесс сборки для Android:
Shell-скрипт;
Gradle-скрипт.
Первый способ используется для расширенной настройки. Мы пойдем вторым путем: вызовем Gradle-скрипт Gradle для сборки проекта.
Invoke Gradle Script (вызов Gradle-скрипта) — при выборе этого параметра Jenkins использует Gradle, установленный на хост-компьютере, для сборки проекта. Для начала нужно сообщить Jenkins, какую версию Gradle использовать для сборки проекта. Убедимся, что на компьютере установлен Gradle и в переменных среды заданы требуемые пути.
По умолчанию используется последняя версия. Чтобы воспользоваться другими версиями, откройте панель инструментов Jenkins и перейдите в раздел Manage Jenkins>Global Tool configuration. Перейдите в раздел »Gradle» и нажмите кнопку »Add». Укажите имя и выберите нужную версию Gradle, а затем сохраните изменения.
Use Gradle Wrapper (используйте Gradle-оболочку) — наиболее оптимальный вариант. Поскольку мы используем ту же версию Gradle, которая использовалась для локальной сборки проекта в Android-студии, она менее подвержена ошибкам и препятствует возникновению проблем совместимости с другими версиями.
Установим флажок напротив »Make Gradle executable» («Сделать Gradle исполняемым»). Для этой опции требуется местоположение оболочки Gradle. Найти оболочку можно в каталоге проекта, который находится внутри рабочего пространства Jenkins.
После сообщим Jenkins, какую задачу Gradle мы хотим выполнить. В данном случае мы хотим создать проект и сгенерировать APK.
Создадим Android-проект
Чтобы запустить сборку, откроем панель мониторинга Jenkins и выберем project/job. На панели мониторинга проекта/задания нажмём »Build Now» («Создать сейчас»). После этого отобразится запущенная сборка в разделе »Build History»:
Мигающая серая точка означает, что сборка продолжается. Нажмите на эту точку, чтобы вывести консоль:
Если что-то пойдёт не так, то цвет точки станет красным. Если всё в порядке, и сборка прошла успешно — синим.
Архивируем артефакты (APKs)
Итак, мы успешно построили проект, но как насчёт APK? Как можно получить предыдущий результат сборки? Jenkins предоставляет способ архивирования выходных данных каждой сборки.
Чтобы заархивировать выходные данные сборки, откроем панель управления проекта, перейдем на страницу настройки и щелкните вкладку »Post-build Actions». В этом разделе нажмём кнопку »Add post-build action» и выберите параметр »Archive the artifacts».
Теперь нужно сообщить Jenkins, какие файлы архивировать. Мы хотим заархивировать APK, созданный в результате сборки Grade. Предоставим путь для этого APK:
\app\build\outputs\apk\debug\
Скажем Jenkins заархивировать все файлы APK в этом каталоге. Теперь путь уже имеет контекст рабочей области, поэтому не нужно использовать переменную ${workspace
}. Запишем выходной путь:
Нажмём кнопку »Advance» («Дополнительно») и установим флажок напротив »Archive artifacts only if build is successful» («Архивировать артефакты, только если сборка выполнена успешно»). Сохраним изменения и снова создадим проект. После успешной сборки проекта перейдём на панель управления и выберите последнюю сборку в разделе «Build History». На панели сборки заархивированный артефакт будет отображаться следующим образом:
Теперь при каждой успешной сборке артефакты будут архивироваться.
Тестирование Android-приложений с помощью Jenkins
Android SDK поддерживает два типа тестов: модульные тесты, которые выполняются на JVM, и инструментальные тесты, которые должны выполняться на устройстве Android или эмуляторе. Оба типа тестов могут быть выполнены с помощью Jenkins.
Плагин Android Gradle записывает результаты тестирования на диск в формате JUnit XML. Следовательно, мы можем использовать плагин JUnit для Jenkins, чтобы анализировать результаты: просматривать отчёты о тестировании и получать уведомления о сбоях.
Компилировать и выполнять модульные тесты так же просто, как добавить ещё один шаг сборки — ./gradlew testDebugUnitTest
.
Аналогично, инструментальные тесты можно скомпилировать и выполнять с помощью задачи connectedDebugAndroidTest в Gradle. Но сначала нужно убедиться, что устройство Android подключено к агенту сборки Jenkins. Или можно воспользоваться плагином Android Emulator для автоматической загрузки, создания и запуска эмулятора во время сборки. Также существуют плагины для облачных сервисов тестирования, например, AWS Device Farm.
Сразу после окончания тестов можно использовать шаг junit для анализа результатов: junit '**/TEST-*.xml'
.
Модульные тесты
Чтобы запустить модульные тесты, добавим ещё один шаг сборки с тестовой задачей Gradle:
Интеграционные тесты
Для настройки UI-тестов нужно запустить эмулятор. Хотя у Jenkins есть подключаемый модуль эмулятора Android, он не работает с последними версиями Android SDK. Но есть обходной путь.
Шаг 1: загрузим эмулятор. Чтобы просмотреть список доступных SDK, запустим:
sudo -iu jenkins android-sdk/tools/bin/sdkmanager --list --verbose
Краткое замечание по выбору подходящего образа: образы на базе x86 работают быстрее, но нуждаются в аппаратном ускорении. Возможно, придется включать ускорение KVM (sudo modprobekvm) на сервере, и сервер не может быть виртуальной машиной. По этой причине мы выбираем образ ARM — он медленнее, но работает.
Шаг 2: мы хотим запустить приложение на Android SDK 25-го уровня, поэтому system-images; android-25; google_apis; armeabi-v7a кажется подходящим. Установим его:
sudo -iu jenkins android-sdk/tools/bin/sdkmanager 'system-images;android-25;google_apis;armeabi-v7a'
Шаг 3: после установки создадим виртуальное устройство Android (инстанс эмулятора):
echo no | $ANDROID_SDK_ROOT/tools/bin/avdmanager -v create avd --force --package 'system-images;android-25;google_apis;armeabi-v7a' --name Android25 --tag google_apis --abi armeabi-v7a
Шаг 4: воспользуемся Supervisord для запуска эмулятора Android в качестве системной службы (в фоновом режиме):
http://supervisord.org/
Затем создадим файл конфигурации в /etc/supervisor/conf.d/emulator.conf:
[program:emulator]
command=/var/lib/jenkins/android-sdk/emulator/emulator -avd Android25 -no-window -noaudio -no-boot-anim -accel on -ports 5556,5557
autostart=true
user=jenkins
environment=ANDROID_SDK_ROOT=/var/lib/jenkins/android-sdk
Шаг 5: перезапустим супервизор, чтобы применить изменения:
sudo service supervisor restart
Эмулятор должен запуститься в фоновом режиме. Это может занять 1–2 минуты. устройство-эмулятор будет готово, оно появится в списке устройств:
sudo -iu jenkins android-sdk/platform-tools/adb devices
Шаг 6: в джобе Jenkins добавим шаг сборки Execute shell:
ANDROID_SERIAL=emulator-5556
# wait for emulator to be up and fully booted, unlock screen
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed) ]]; do sleep 1; done; input keyevent 82'
./gradlew connectedAndroidTest
Напоследок: пару слов о безопасности
Для распространения Android-приложение должно быть подписано закрытым ключом, который хранится в безопасном месте (его потеря означает, что мы не сможем публиковать обновления).
Чтобы разработчикам не превращать свои компьютеры в хранилище ключей, можно безопасно хранить ключи и кодовые фразы в Jenkins с помощью плагина Credentials. Это позволяет избежать необходимости вводить кодовую фразу в build.gradle или иным образом проверять её в SCM.
Плагин Credentials позволяет хранить конфиденциальные данные в Jenkins. Эта данные хранятся в зашифрованном виде на диске, когда не используются, и становятся временно доступными во время сборки — либо в виде файла в рабочей области сборки, либо в виде переменной среды.
Можем использовать такие переменные среды в блоке signingConfig в файле build.gradle или использовать Android Signing Plugin, чтобы подписать APK. После создания и подписания готового APK-файла можно автоматически загрузить его в Google Play с помощью плагина Google Play Android Publisher.
«CI/CD c Jenkins»