Складання Android-проекту в Docker-контейнері

Розробляючи проект під платформу Android, навіть невеликий, рано чи пізно доводиться стикатися з оточенням для розробки. Крім Android SDK, необхідно, щоб була остання версія Kotlin, Gradle, platform-tools, build-tools. І якщо на машині розробника всі ці залежності вирішуються переважно за допомогою Android Studio IDE, то на сервері CI/CD кожне оновлення може перетворитися на головний біль. І якщо в web-розробці, вирішенням проблеми оточення стандартом став Docker, то чому б не спробувати вирішити за допомогою нього аналогічну проблему і в Android-розробці.

Для тих, хто не знає, що таке Docker — якщо зовсім просто, то це інструмент створення т.зв. «Контейнерів» де міститься мінімальне ядро ​​ОС і необхідний набір ПЗ, які ми можемо розгортати де захочемо, зберігаючи при цьому оточення. Що саме буде в нашому контейнері визначається в Dockerfile, який потім збирається в образ запускається де завгодно і має властивості ідемпотентності.

Процес встановлення та основи Docker чудово описані на його офіційному сайті. Тому, забігаючи трохи вперед, такий Dockerfile у нас вийшов

# Т.к. основным инструментом для сборки Android-проектов является Gradle, 
# и по счастливому стечению обстоятельств есть официальный Docker-образ 
# мы решили за основу взять именно его с нужной нам версией Gradle
FROM gradle:5.4.1-jdk8

# Задаем переменные с локальной папкой для Android SDK и 
# версиями платформы и инструментария
ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-3859397.zip" 
    ANDROID_HOME="/usr/local/android-sdk" 
    ANDROID_VERSION=28 
    ANDROID_BUILD_TOOLS_VERSION=28.0.3

# Создаем папку, скачиваем туда SDK и распаковываем архив,
# который после сборки удаляем
RUN mkdir "$ANDROID_HOME" .android 
    && cd "$ANDROID_HOME" 
    && curl -o sdk.zip $SDK_URL 
    && unzip sdk.zip 
    && rm sdk.zip 
# В следующих строчках мы создаем папку и текстовые файлы 
# с лицензиями. На оф. сайте Android написано что мы 
# можем копировать эти файлы с машин где вручную эти 
# лицензии подтвердили и что автоматически 
# их сгенерировать нельзя
    && mkdir "$ANDROID_HOME/licenses" || true 
    && echo "24333f8a63b6825ea9c5514f83c2829b004d1" > "$ANDROID_HOME/licenses/android-sdk-license" 
    && echo "84831b9409646a918e30573bab4c9c91346d8" > "$ANDROID_HOME/licenses/android-sdk-preview-license"    

# Запускаем обновление SDK и установку build-tools, platform-tools
RUN $ANDROID_HOME/tools/bin/sdkmanager --update
RUN $ANDROID_HOME/tools/bin/sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" 
    "platforms;android-${ANDROID_VERSION}" 
    "platform-tools"

Зберігаємо його в папку з нашим Android-проектом та запускаємо складання контейнера командою

docker build -t android-build:5.4-28-27 .

Параметр -t задає tag або ім'я нашого контейнера, яке зазвичай складається з його назви та версії. У нашому випадку ми назвали його android-build, а у версії вказали сукупність версій gradle, android-sdk і platform-tools. Надалі нам простіше шукатиме потрібний нам образ на ім'я використовуючи таку «версію».

Після того, як складання пройшло ми можемо використовувати наш образ локально, можемо завантажити його командою docker push у публічний або приватний репозиторій образів, щоб завантажувати його на інші машини.

Як приклад зберемо локально проект. Для цього у папці з проектом виконаємо команду

docker run --rm -v "$PWD":/home/gradle/ -w /home/gradle android-build:5.4.1-28-27 gradle assembleDebug

Розберемо що вона означає:

докер-біг - Сама команда запуску образу
-рм - означає, що після зупинки контейнера він видаляє за собою все, що створювалося в процесі його життя
-v $PWD:/home/gradle/ - монтує поточну папку з нашим Android-проектом у внутрішню папку контейнера /home/gradle/
-w /home/gradle - Задає робочу директорію контейнера
android-build:5.4.1-28-27 - Ім'я нашого контейнера, який ми зібрали
gradle assembleDebug — власне команда збирання, яка збирає наш проект

Якщо все скластися вдало, то через пару секунд/хвилин ви побачите у себе на екрані щось на зразок BUILD SUCCESSFUL in 8m 3s! А в папці app/build/output/apk лежатиме зібрана програма.

Аналогічно можна виконувати інші завдання gradle - перевіряти проект, запускати тести і т.д. Основна перевага - при необхідності складання проекту на будь-якій іншій машині, нам не потрібно турбуватися про встановлення всього оточення і достатньо завантажити необхідний образ і запустити в ньому складання.

Контейнер не зберігає жодних змін, і кожне складання запускається з нуля, що з одного боку гарантує ідентичність складання незалежно від місця його запуску, з іншого боку щоразу доводиться завантажувати всі залежності та компілювати весь код заново, а це іноді може займати суттєвий час. Тому, крім звичайного «холодного» запуску, у нас є варіант запуску збірки зі збереженням т.зв. "кеша", де ми зберігаємо папку ~/.gradle просто копіюючи її в робочу папку проекту, а на початку наступного складання повертаємо її назад. Всі процедури копіювання ми винесли в окремі скрипти і сама команда запуску у нас стала виглядати так

docker run --rm -v "$PWD":/home/gradle/ -w /home/gradle android-build:5.4.1-28-27 /bin/bash -c "./pre.sh; gradle assembleDebug; ./post.sh"

У підсумку, середній час складання проекту у нас скоротився в кілька разів (залежно від кількості залежностей на проекті, але середній проект таким чином став збиратися за 1 хвилину замість 5 хвилин).

Все це само собою має сенс тільки якщо у вас є власний внутрішній CI/CD-сервер, підтримкою якого ви самі і займаєтеся. Але зараз є багато хмарних сервісів, у яких всі ці проблеми вирішені і вам не треба про це переживати і потрібні властивості складання можна також вказати в налаштуваннях проекту.

Тільки зареєстровані користувачі можуть брати участь в опитуванні. Увійдіть, будь ласка.

Чи тримаєте ви систему CI/CD всередині або користуєтеся стороннім сервісом

  • Використовуємо внутрішній сервер

  • Використовуємо зовнішній сервіс

  • Не використовуємо CI/CD

  • Інше

Проголосували 42 користувачів. Утрималися 16 користувачів.

Джерело: habr.com

Додати коментар або відгук