- Setting up GitLab CI for Android projects
- Our sample project
- Testing
- Setting up GitLab CI
- Understanding .gitlab-ci.yml
- Defining the Docker Image
- Defining variables
- Installing packages
- Installing the Android SDK
- Setting up the environment
- Defining the stages
- Building the app
- Running tests
- Run your new CI setup
- Conclusion
- Master your CI/CD
- Андрей Алеев
- Настройка CI в GitLab для Android проекта
- 3 комментария
- Настройка GitLab CI CD для Java приложения
- Коротко о Gitlab Pipline
- Кэширование и его особенности
- Артефакты
- Установка Gitlab Runner
- Что хотим получить от CI CD?
- Настройка GitLab CI для Maven
- Сборка — Build
- Запуск unit тестов — test
- Упаковка — package
- Деплой — deploy
- Настройка деплоя на прод
- Бонус: уведомления о деплое в Telegram
Setting up GitLab CI for Android projects
Note: This is a new version of a previously published blog post, updated for the current Android API level (28). Thanks Grayson Parrelli for authoring the original post!
Have you ever accidentally checked on a typo that broke your Android build or unknowingly broke an important use case with a new change? Continuous Integration is a way to avoid these headaches, allowing you to confirm that changes to your app compile, and your tests pass before they’re merged in.
GitLab CI/CD is a wonderful Continuous Integration built-in solution, and in this post we’ll walk through how to set up a basic config file ( .gitlab-ci.yml ) to ensure your Android app compiles and passes unit and functional tests. We assume that you know the process of creating an Android app, can write and run tests locally, and are familiar with the basics of the GitLab UI.
Our sample project
We’ll be working with a real-world open source Android project called Materialistic to demonstrate how easy it is to get up and running with GitLab CI for Android. Materialistic currently uses Travis CI with GitHub, but switching over is a breeze. If you haven’t seen Materialistic before, it’s a fantastic open source Android reader for Hacker News.
Testing
Unit tests are the fundamental tests in your app testing strategy, from which you can verify that the logic of individual units is correct. They are a fantastic way to catch regressions when making changes to your app. They run directly on the Java Virtual Machine (JVM), so you don’t need an actual Android device to run them.
If you already have working unit tests, you shouldn’t have to make any adjustments to have them work with GitLab CI. Materialistic uses Robolectric for tests, Jacoco for coverage, and also has a linting pass. We’ll get all of these easily running in our .gitlab-ci.yml example except for Jacoco, since that requires a secret token we do not have — though I will show you how to configure that in your own projects.
Setting up GitLab CI
We want to be able to configure our project so that our app is built, and it has the complete suite of tests run upon check-in. To do so, we have to create our GitLab CI config file, called .gitlab-ci.yml , and place it in the root of our project.
So, first things first: If you’re just here for a snippet to copy-paste, here is a .gitlab-ci.yml that will build and test the Materialistic app:
Well, that’s a lot of code! Let’s break it down.
Understanding .gitlab-ci.yml
Defining the Docker Image
This tells GitLab Runners (the things that are executing our build) what Docker image to use. If you’re not familiar with Docker, the TL;DR is that Docker provides a way to create a completely isolated version of a virtual operating system running in its own container. Anything running inside the container thinks it has the whole machine to itself, but in reality there can be many containers running on a single machine. Unlike full virtual machines, Docker containers are super fast to create and destroy, making them great choices for setting up temporary environments for building and testing.
This Docker image ( openjdk:8-jdk ) works perfectly for our use case, as it is just a barebones installation of Debian with Java pre-installed. We then run additional commands further down in our config to make our image capable of building Android apps.
Defining variables
These are variables we’ll use throughout our script. They’re named to match the properties you would typically specify in your app’s build.gradle .
- ANDROID_COMPILE_SDK is the version of Android you’re compiling with. It should match compileSdkVersion .
- ANDROID_BUILD_TOOLS is the version of the Android build tools you are using. It should match buildToolsVersion .
- ANDROID_SDK_TOOLS is a little funny. It’s what version of the command line tools we’re going to download from the official site. So, that number really just comes from the latest version available there.
Installing packages
This starts the block of the commands that will be run before each job in our config.
These commands ensure that our package repository listings are up to date, and it installs packages we’ll be using later on, namely: wget , tar , unzip , and some packages that are necessary to allow 64-bit machines to run Android’s 32-bit tools.
Installing the Android SDK
Here we’re downloading the Android SDK tools from their official location, using our ANDROID_SDK_TOOLS variable to specify the version. Afterwards, we’re unzipping the tools and running a series of sdkmanager commands to install the necessary Android SDK packages that will allow our app to build.
Setting up the environment
Finally, we wrap up the before_script section of our config with a few remaining tasks. First, we set the ANDROID_HOME environment variable to the SDK location, which is necessary for our app to build. Next, we add the platform tools to our PATH , allowing us to use the adb command without specifying its full path, which is important when we run a downloaded script later. Next, we ensure that gradlew is executable, as sometimes Git will mess up permissions.
The next command yes | android-sdk-linux/tools/bin/sdkmanager —licenses is responsible for accepting the SDK licenses. Because the unix yes command results in an EPIPE error once the pipe is broken (when the sdkmanager quits normally), we temporarily wrap the command in +o pipefile so that it does not terminate script execution when it fails.
Defining the stages
Here we’re defining the different stages of our build. We can call these anything we want. A stage can be thought of as a group of jobs. All of the jobs in the same stage happen in parallel, and all jobs in one stage must be completed before the jobs in the subsequent stage begin. We’ve defined two stages: build and test . They do exactly what you think: the build stage ensures the app compiles, and the test stage runs our unit and functional tests.
Building the app
This defines our first job, called build . It has two parts — a linter to ensure that the submitted code is up to snuff, and the actual compilation of the code (and configuration of the artifacts that GitLab should expect to find). These are run in parallel for maximum efficiency.
Running tests
This defines a job called debugTests that runs during the test stage. Nothing too crazy here about setting something simple like this up!
If we had wanted to get Jacoco also working, that would be very straightforward. Simply adding a section as follows would work — the only additional thing you’d need to do is add a secret variable containing your personal COVERALLS_REPO_TOKEN :
Run your new CI setup
After you’ve added your new .gitlab-ci.yml file to the root of your directory, just push your changes and off you go! You can see your running builds in the Pipelines tab of your project. You can even watch your build execute live and see the runner’s output, allowing you to debug problems easily.
After your build is done, you can retrieve your build artifacts:
- First, click on your completed build, then navigate to the Jobs tab:
From here, simply click on the download button to download your build artifacts.
Conclusion
So, there you have it! You now know how to create a GitLab CI config that will ensure your app:
- Compiles
- Passes tests
- Allows you to access your build artifacts (like your APK) afterwards.
You can take a look at my local copy of the Materialistic repository, with everything up and running, at this link
Enjoy your newfound app stability 🙂
Master your CI/CD
Watch this webcast and learn to deliver faster with CI/CD.
Источник
Андрей Алеев
Настройка CI в GitLab для Android проекта
CI — continuous integration — можно описать как практику автоматизированного внедрения результатов работы программиста по целевому месту назначения и использования (деплой). CI имеет множество преимуществ, и особенностей, среди которых отметим следующие:
- Единая кодовая база, в которую сливаются ветки разработчиков — достигается с помощью GitFlow
- Автоматизация сборок — сборка запускается автоматически после вливания в нее нового кода с помощью pipeline в GitLab
- Автоматизация тестирования — как один из шагов в pipeline-e
- Удобный доступ к сборкам
- Автоматизированный деплой — дополнительный скрипт может публиковать сборки в магазинах приложений или по пути дистрибуции.
CI как и любая автоматизация позволяет устранить человеческий фактор и автоматизировать повторяющиеся действия.
Поэтому CI желательно настраивать на любом серьезном проекте. А в случае, когда разработчиков несколько и релизы необходимо собирать часто, CI должен быть настроен в обязательном порядке.
Обычно настройка CI лежит в зоне ответственности DevOps инженера. Однако, хотя в большинстве компаний такой человек есть, он может быть недоступен или не уметь настраивать CI именно для Android проекта. Поэтому Android-разработчику необходимо уметь делать это самостоятельно. Как это делается, расскажу и покажу в этой статьте.
Итак, приступим.
С точки зрения GitLab, CI состоит из
- Jobs, которые описывают что сделать. например, собрать код или протестировать
- Stages, которые определяют когда стартовать Job-ы, например, стадия тестирования
Обычно stages бывают
— buiild job под названием compile
— test — запуск тестов
— staging — деплой на stage
— production -деплой на prod
Всю эту информацию о jobs и stages нам нужно передать GitLab-у. Сделаем мы это с помощью файла .gitlab-ci, который положим в корне проекта. В нем будут описаны все шаги, а также выполнятся необходимые скрипты.
Во-первых укажем образ, который будет выполнять Job
Затем в разделе под названием before_script напишем скрипты, которые сервер должен выполнить, чтобы настроить сборочное окружение.
Никакой магии здесь нет, и, если вы знакомы unix-системами, и их командами, то вам все должно быть понятно
Не забудьте указать номера версий SDK, которые сервер должен установить. Для этого перед блоком before_script добавьте
Далее, указываем типы этапов сборки в блоке stages
Теперь мы готовы к выполнению собственно сборок.
Я рекомендую использовать два типа сборок — релизную и дебажную. Вы можете сделать столько вариантов, сколько вам угодня, исходя из ваших задач
Итак, для начала пропишем дебажную сборку
Тег only позволяет триггерить запуск сборки в случае пуша в конкретнкю ветку или по тегу, как мы увидим далее
Переменная BUILD_NUMBER будет считываться с помощью токена CI_PIPELINE_IID_TOKEN из настроек Variables в разделе CI в GitLab. Она позволяет нам добавлять автоинкрементируемый номер сборки в название версии. Подробнее этот подход описан здесь: https://gitlab.com/jmarcos.cano/CI_PIPELINE_IID/snippets/1675434. А ниже я покажу как настроить build.gradle для генерирования названия версии и номера сборки. А пока продолжим с .gitlab-ci
Следующий блок будет script
Теперь остается только указать как и где хранить артефакты, то есть apk:
Вот и все, а для релизной сборки укажем такой же блок, но stage и него будет deploy, а сборка будет собираться еще и с app:assembleRelease, и собираться такая сборка будет только при создании тэга в GitLab.
Дополнительно нам нужно настроить автоматическое инкрементирование номеров сборок
Для этого в Ваш build.gradle добавьте функции, которые будут генерировать versionCode и versionName на основе минимальной версии target api
Естественно, в корневом build.gradle у вас должны быть прописаны номера версии в виде
Также добавьте функции для получения номера сборки из CI GitLab, а в случае локальной сборки — из гита
// Этот метод получает номер сборки из специального файла, который мы генерировали выше в скрипте .gitlab-ci
def getBuildNumberFromFile() <
Остается вызывать этот метод для названия версии, а также для номера сборки
Готово с кодом, теперь нужно добавить переменную в GitLab, идея взята отсюда
Заходим в Settings->CI/CD->Variables, далее добавляем две переменные BUILD_NUMBER и CI_PIPELIBE_ID_TOKEB
BUILD_NUMBER присваиваем 1, а в CI_PIPELIBE_ID_TOKEB положим значение токена, который нужно сгенерировать через раздел Access Tokens профиля
Остается запушить изменения в GitLab, и он автоматически будет стартовать Pipeline
Для создания релизной сборки, которую мы настроили выше, нужно создать Тэг через Repository->Tags-> New Tag
Выбираете ветку, из которой хотите создать сборку и запускаете. Все готово!
3 комментария
Спасибо большое за статью, но есть вопрос, что делать если в образе image: openjdk:8-jdk готовит, что не может найти apt-get, как тут быть?
Добрый день, так же как и коментатор выше получаю ошибку «apt-get: command not found»
Возможно есть какие то тонкости?
Возможно, у вас другой дистрибутив linux. Попробуйте использовать dpkg или установить APT
Источник
Настройка GitLab CI CD для Java приложения
Из-за прекращения поддержи Bitbucket Setver пришлось переехать на GitLab.
В Bitbucket Server не было встроенного CI/CD, поэтому использовали Teamcity. Из-за проблемы интеграции Teamcity с GitLab, мы попробовали GitLab Pipline. И остались довольны.
Disclamer: У меня не так много опыта в CI/CD, так что статья скорее для новичков. Мы рассмотрим самый простой пример деплоя на несколько контуров с доставкой с помощью scp и запуском linux сервиса. Буду рад услышать конструктивную критику с предложениями по оптимизации скрипта сборки.
Коротко о Gitlab Pipline
Если говорить простыми словами: сборка делится на задачи. Какие-то задачи выполняются последовательно и передают результаты следующим задачам, какие-то задачи распаралеливаются: например можно одновременно деплоить на сервер и в nexus.
Не обязательно один раннер отвечает за все задачи в сборке. Если на проекте два раннера, то первую задачу может взять один ранер, после ее выполнения вторую задачу возьмет другой раннер.
Раннеры бывают разных типов. Мы рассмотрим executor docker. Для каждой задачи создается новый чистый контейнер. Но между контейнерами можно передавать промежуточные результаты — это называется кэширование.
Кэширование и его особенности
Механизм кэширования разрабатывал какой-то одаренный человек. Поэтому сразу разобраться, как оно работает, будет не просто. В отдельной статье можно прочитать о кэшировании подробнее.
Каждый раннер хранит кэш в папке /cache . Для каждого проекта в этой папке создается еще папка. Сам кэш хранится в виде zip архива.
Из-за наличия у каждого раннера своей папки для кэша, возникает проблема. Если один раннер кэшировал папку в первой задаче, а второй раннер взялся за выполнение второй задачи, то у него не окажется кэша из первой задачи. Чуть далее рассмотрим как это исправить.
Можно определить только один набор папок для кэша. Также только для всего набора можно выбрать политику кэширования. Например если вы уверены, что задача не изменит кэш, то можно его не загружать. Но нельзя запретить загружать например две папки из четырех.
Нет возможности установить глобальный кэш и добавить к нему дополнительные папки для конкретных задач.
Так же стоит знать, что кэш не удаляется после окончания сборки. При следующей такой же сборке старый кэш будет доступен.
Эти особенности усложняют создание инструкций для CI CD.
Артефакты
Помимо кэша между сборками можно передавать артефакты.
Артефакт — это файлы, которые считаются законченным продуктом сборки. Например .jar файлы приложения.
В отличие от кэша, артефакт передается между раннерами. Из-за этого многие злоупотребляют использованием артефактов там, где стоит использовать кэш.
Следуйте следующим правилам:
- Если текущее задание может выполняться самостоятельно, но в присутствии контента работа будет идти быстрее, используйте кэш.
- Если текущее задание зависит от результатов предыдущего, то есть не может выполняться само по себе, используйте артефакты и зависимости.
Установка Gitlab Runner
Перед установкой ранера создадим папку с конфигурацией, чтобы не приходилось для изменений лезть в контейнер.
Само создание ранера выполняется в одну команду. Можно создать сколько угодно ранеров.
Мало создать раннер, теперь его нужно зарегистрировать в GitLab. Зарегистрировать можно на уровне всего GitLab, тогда сборки будут выполняться для любого проекта; на уровне группы — выполнятся только для группы, и на уровне проекта.
Заходим в контейнер.
Внутри контейнера выполним команду регистрации. Регистрация происходит в интерактивном режиме.
Отвечаем на вопросы:
- Адрес вашего gitlab.
- Токен авторизации. Посмотреть его можно в настройках гурппы/проекта в разделе CI/CD Runners.
- Название раннера.
- Теги ранера, можно пропустить нажав Enter.
- Исполнитель сборки. Вводим docker.
- Образ, который будет использоваться по умолчанию, если не установлен другой.
После этого в настройках проекта можно посмотреть доступные раннеры.
После регистрации, в папке /home/user/runner_name появится файл с настройками конфигурации config.toml . Нам нужно добавить docker volume для кэширования промежуточных результатов.
Проблема кэширования
В начале статьи я рассказал о проблеме кеширования. Ее можно решить с помощью монтирования одного volume к разным раннерам. То есть во втором своем раннере так же укажите volumes = [«gitlab-runner-cache:/cache»] . Таким образом разные раннеры будут иметь единый кэш.
В итоге файл конфигурации выглядит так:
После изменения перезапускаем раннер.
Что хотим получить от CI CD?
У нас на проекте было 3 контура:
- dev-сервер, на него все попадает сразу после MR;
- пре-прод-сервер, на него все попадает перед попаданием на прод, там проходит полное регресс тестирование;
- прод-сервер, собственно сама прод среда.
Что нам было необходимо от нашего CI/CD:
- Запуск unit-тестов для всех MergeRequest
- При мерже в dev ветку повторный запуск тестов и автоматический деплой на dev-сервер.
- Автоматическая сборка, тестирвоание и деплой веток формата release/* на пре-прод-сервер.
- При мерже в master ничего не происходит. Релиз собирается при обнаружении тега формата release-* . Одновременно с деплоем на прод-сервер будет происходить загрузка в корпоративный nexus.
- Бонусом настроим уведомления о статусе деплоя в Telegram.
Настройка GitLab CI для Maven
Что делать если у вас Gradle? Загляните в оригинал статьи, там я рассказываю про настройку и для Gradle. Она не сильно отличается.
Создайте в корне проекта файл .gitlab-ci.yml .
Настройку буду объяснять блоками, в конце прикреплю единый файл.
Вы можете указать нужный образ, вместо дефолтного образа у раннера.
Устанавливаем место хранения папки .m2 через переменные среды variables . Это понадобится, когда будем настраивать кэширование.
Далее указываются этапы сборки. Они позволяют группировать задачи для одновременного выполнения.
- build — стадия сборки.
- test — стадия тестирования.
- package — стадия упаковки в jar.
- deploy — стадия деплоя на сервер.
- notify — стадия уведомления о провале.
Указываем непосредственно задачи для каждой стадии.
Сборка — Build
Раздел script выполняет linux команды.
Переменная GitLab CI/CD $MAVEN_SETTINGS необходима для передачи файла settings.xml , если вы используете нестандартные настройки, например корпоративные репозитории. Переменная создается в настройках CI/CD для группы/проекта. Тип переменной File.
Раздел only указывает для каких веток и тегов выполнять задачу. Чтобы не собирать каждую запушенную ветку устанавливаем: dev , merge_requests и ветки формата /release/* .
Раздел only не разделяет ветка это или тег. Поэтому мы указываем параметр except , который исключает теги. Из-за этого поведения за сборку на прод отвечают отдельные задачи. В противном случае, если бы кто-то создал тег формата release/* , то он бы запустил сборку.
Для защиты от случайного деплоя в прод джуном, рекомендую установить защиту на ветки dev , master , /release/* , а так же на теги release-* . Делается это в настройках проекта GitLab.
Раздел cache отвечает за кэширование. Чтобы каждый раз не выкачивать зависимости добавляем в кэш папку ./target и ./.m2 .
Запуск unit тестов — test
Создаем задачу для запука тестирования.
Ничем не отличается от стадии сборки, только команда мавена test . Кэш необходимо указать и в этой задаче, чтобы получить его и отправить дальше.
Упаковка — package
Следом добавляем задачу упаковки с выключенными тестами. Она уже выполняется только для веток dev и release/* . Упаковывать Merge Request смысла нет.
Обратите внимание на policy: pull и artifacts . В следующих задачах не нужны исходники и зависимости, так что policy: pull отключает кэширование. Для сохранения результатов сборки используем artifacts , который сохраняет .jar файлы и передает их следующим задачам.
Деплой — deploy
Теперь осталось задеплоить артефакт на dev-сервер с помощью ssh и scp.
Не забудьте создать переменные в GitLab CI CD:
- $DEV_USER — пользователь системы, от чьего имени будет происходить деплой.
- $DEV_HOST — ip адрес сервера.
- $DEV_APP_PATH — путь до папки приложения.
- $SSH_PRIVATE_KEY — приватный ключ.
Раздел before_script отвечает за выполнение настроек перед основным скриптом. Мы проверяем наличие ssh-agent, устанавливаем его при отсутствии. После чего добавляем приватный ключ, устанавливаем правильные права на папки.
В разделе script происходит деплой на сервер:
- Проверяем наличие старого jar и переименовываем его. $CI_PIPELINE_ID — это глобальный номер сборки Pipeline.
- Копируем новый jar на сервер.
- Останавливаем и запускаем службу, отвечающую за приложение.
- Отправляем уведомление об успехе в телеграм. Об этом ниже.
На пре-прод делаем по аналогии, только меняем переменные на $PRE_PROD_* .
Настройка деплоя на прод
Для прод-деплоя можно объединить сборку, тестирование и упаковку в одну задачу.
Мы защищаемся от срабатывания на ветки формата release-* , нам нужно срабатывание только по тегу.
Деплой аналогичен пре-проду, изменяется только only и переменные.
Дополнительно появляется задача с отправкой артефакта в корпоративный nexus. Деплой на сервер и в нексус работает параллельно.
Бонус: уведомления о деплое в Telegram
Полезная фишка для уведомления менеджеров. После сборки в телеграм отправляется статус сборки, название проекта и ветка сборки.
В задачах деплоя последней командой пропишите:
Сам файл необходимо добавить в корень проекта, рядом с файлом .gitlab-ci.yml .
Скрипт отправляет запрос к API Telegram, через curl. Параметром скрипта передается emoji статуса билда.
Не забудьте добавить новые параметры в CI/CD:
- $TELEGRAM_BOT_TOKEN — токен бота в телеграмм. Получить его можно при создании своего бота.
- $TELEGRAM_CHAT_ID — идентификатор чата или беседы, в которую отправляется сообщение. Узнать идентификатор можно с помощью специального бота.
Необходимо создать задачи для уведомления о падении сборки.
По сложившейся традиции у нас отдельная задача для прод-среды. Благодаря параметру when: on_failure задача отрабатывает только, когда одна из предыдущих завершилась неудачно.
Удобно, что теперь и код и сборка находятся в одном месте. Несмотря на недостатки GitLab CI, пользоваться им в целом удобно.
Источник