Статьи

Полностью автоматизированный конвейер сборки Android

В ходе моей текущей работы мне приходилось автоматизировать рабочие места для создания приложений Android. Этот пост посвящен описанию болевых точек, с которыми я столкнулся, чтобы вы, читатели, не тратили свое время впустую, если вы собираетесь это сделать.

Среда выглядит следующим образом:

  • Кукольный для автоматизации инфраструктуры
  • Дженкинс для  CI-  сервера
  • Проект Android
  • Файл сборки Gradle для его сборки
  • Робоэлектрик как основной каркас тестирования

Кукольный и Дженкинс

Моя отправная точка была действительно здоровой. Коллеги уже автоматизировали установку сервера Jenkins, необходимые пакеты, включая Java, а также предоставили повторно используемые классы Puppet для создания заданий. Задания Jenkins полагаются на один  config.xml файл, который представляет собой сборку из разных разделов. Каждый раздел обрабатывается специальным шаблоном. В этот момент я подумал, что создание простой работы в Gradle будет сродни прогулке в парке, что займет максимум несколько дней, и что вскоре мне будет поручено другое задание.

Первый шаг был достаточно легким: просто обновите существующий манифест Puppet, чтобы добавить плагин Gradle в Jenkins.

Обертка Gradle

Постоянные читатели этого блога знают мое мнение о Gradle. Тем не менее, я признаю, что гарантированная сборка, которая работает независимо от версии установленного инструмента, — это то, чего не хватает Maven — и оно должно быть. Для этого Gradle предоставляет так называемый механизм-оболочку через JAR, сценарий оболочки и файл свойств, последний из которых содержит URL-адрес для распространения Gradle ZIP. Все три должны быть сохранены в  SCM .

Это было началом моих неприятностей. Необходимость загрузки в корпоративной среде означает прохождение и аутентификацию на прокси. Самый простой вариант — установить все в конфигурации задания … включая учетные данные прокси. Однако идти по этому пути не очень разумно с точки зрения безопасности, поскольку любой, имеющий доступ к интерфейсу Jenkins или файловой системе, сможет прочитать эти учетные данные. Есть необходимость в другом варианте.

У клиента уже есть работающий репозиторий Nexus с настроенным прокси. Было очень просто загрузить нужный дистрибутив Gradle и обновить свойства gradle.properties, чтобы они указывали на него.

Android SDK

Android SDK — это просто ZIP. Я использовал ту же тактику: скачай и загрузи в Nexus. На этом этапе существующий сценарий Puppet позаботился о его загрузке, распаковке и установке необходимых разрешений.

Этот шаг — начало реальных проблем, как бы то ни было. Разработчики Android знают, что Android SDK — всего лишь менеджер: нужно вручную проверить нужные платформы и инструменты, чтобы загрузить их в локальную файловую систему. Простой шаг для разработчика Android на его компьютере — это кошмар для автоматизации, хотя есть командная строка, эквивалентная установке / обновлению пакетов через SDK (с  --no-ui параметром). Для полного описания, пожалуйста, проверьте  эту ссылку .

Инженеры Google не предоставляют 2 важных параметра:

  • Учетные данные прокси — логин / пароль
  • Принятие лицензионных соглашений

В Интернете много неработающих ответов, наиболее привлекательным из которых является файл конфигурации. Я не нашел ни одного из них, чтобы работать. Однако я нашел  креативное решение  с помощью  expect команды. Expect —  изящная команда,  которая читает стандартный вывод и соответственно вводит стандартный ввод. Хорошая вещь о ожидаемом, что это принимает регулярное выражение. Таким образом, когда он запрашивает прокси-сервер, вы вводите логин, когда он запрашивает пароль, вы делаете то же самое, а когда он запрашивает принятие лицензии, вы набираете «y». Это довольно просто — хотя мне потребовалось много проб и ошибок, чтобы достичь желаемых результатов.

Первоначально я планировал установить все необходимые пакеты Android с Puppet в рамках подготовки сервера. Для стандартных операций, таких как создание файла или установка системного пакета, Puppet может определить, требуется ли подготовка,  например,  если файл существует, его не нужно создавать, а также, если пакет установлен. В конце Puppet сообщает о каждой операции, которую он выполнил в журнале. Сначала я попытался внедрить этот тип кэширования, рассказав Puppet о том, какие пакеты были созданы во время инициализации, поскольку Android SDK создает одну папку на пакет. Первая проблема заключается в том, что Puppet принимает только одну папку для проверки. Кроме того, для некоторых пакетов информация о версии отсутствует (например, это относится к сервисам Google Play).

Таким образом, у коллеги возникла идея перейти от этого обновления от обеспечения Puppet к предварительному этапу в каждой работе. Это исправляет неидемпотентную проблему. Кроме того, это делает запуск обновления настраиваемым для каждого задания.

Robolectric

На данный момент я думал, что это будет сделано. К сожалению, это было не так, из-за библиотеки — Robolectric.

В то время я не знал о  Robolectric  , я просто знал, что это тестовая библиотека для Android, которая позволяет запускать тесты без подключенного физического устройства. Пытаясь запустить сборку на Jenkins, я наткнулся на «интересную» проблему: хотя Roboletric предоставляет POM с полным набором зависимостей,  MavenDependencyResolver класс жестко кодирует репозиторий, откуда его можно загрузить.

Единственный  предоставленный обходной путь  — расширить вышеуказанный класс, чтобы взломать вашу собственную реализацию. Мой использовал хранилище Nexus для предприятий, указанное выше

Загрузить и выпустить задачи

Конец реализации был относительно легким. Не хватало только загрузки артефактов в репозиторий Nexus и тега релиза в SCM.

Чтобы достичь первого, я просто добавил пользовательскую задачу Gradle, чтобы получить настройки Nexus из  settings.xml (предоставленных Puppet). Тогда мне удалось, чтобы  upload задача зависела от этого. Наконец, для каждого варианта выполнения  assemble задачи я добавил выходной файл в набор артефактов для загрузки. Таким образом, следующая команда будет загружать только варианты XXX и YYY независимо от того, какие варианты настроены в файле сборки:

./gradlew assembleXXX assembleYYY upload

Для выпуска, это даже проще: требуется только , что было установить  этот  Gradle плагин, который добавляет  release задачу, сродни развертывание в Maven.

Вывод

Как бэкэнд-разработчик, я привык к настройке Continuous Integration и был почти уверен, что смогу справиться с процессом Android CI за несколько дней. Я был довольно удивлен отсутствием зрелости экосистемы Android относительно CI. Каждый шаг болезненный, плохо документированный (если вообще) и решения кажутся больше взломами, чем что-либо еще. Если вы хотите пойти по этому пути, вас предупредили … и я желаю вам удачи.