Автономный CI/CD на базе Termux — это реалистичный способ ускорить разработку Android‑приложений, не завися от внешних CI‑провайдеров. В этой статье покажем, как собрать полностью автономную среду: Termux + Docker‑контейнеры + GitLab‑Runner, а также как организовать автоматическое тестирование Android‑приложений (unit-тесты и инструментальные тесты) в согласованном пайплайне.
Подход ориентирован на локальную разработку и внутренние стенды. Мы будем говорить о настройках, которые повышают воспроизводимость сборок и предсказуемость результатов.
Архитектура автономного CI/CD в Termux
Схема решения состоит из четырёх слоёв:
- Termux — среда управления, запуск фоновых процессов и оркестрация.
- Docker — изоляция окружений под разные стадии (сборка, тесты, статический анализ).
- GitLab Runner — исполнитель job’ов, подключённый к вашему GitLab (или локальному GitLab).
- Android build/test — инструменты Gradle/Android SDK внутри контейнеров (или в виде образов с заранее подготовленным SDK).
Ключевая идея: Runner запускает jobs, каждый job исполняется в контейнере, а зависимости (SDK/Gradle/JDK) закреплены в образах. Это минимизирует «дрейф» окружения между запусками.
Требования и подготовка окружения
Перед началом убедитесь, что у вас есть:
- Устройство с Termux и достаточным объёмом памяти под Android SDK и кэш Gradle.
- Доступ к Docker в Termux (Docker должен быть установлен и работоспособен в вашем окружении).
- Рабочий GitLab (локальный или корпоративный), чтобы зарегистрировать Runner.
Рекомендуется выделить отдельные каталоги в Termux:
mkdir -p ~/ci/gitlab-runner ~/ci/docker ~/ci/android-sdk ~/ci/cache/gradle
Установка Docker и запуск Docker Engine в Termux
Точные шаги зависят от версии Termux и способа установки Docker в вашей среде, но общая логика одинаковая:
pkg update
pkg upgrade
pkg install docker
Дальше запустите Docker Engine. На практике часто используют запуск сервиса/демона или старт вручную (в зависимости от сборки и политик безопасности). После запуска проверьте доступность:
docker version
docker info
Важно: обеспечьте права и корректный доступ текущего пользователя к Docker (чтобы Runner мог запускать контейнеры).
Создание базового Docker‑образа для сборки Android
Создадим образ, который будет содержать нужный JDK, Android SDK и базовые утилиты. В промышленной практике SDK лучше «закреплять» через заранее построенный образ, чтобы jobs стартовали быстро.
Ниже пример Dockerfile. В нём показан принцип: образ готовит инструменты, а также предусматривает место под кэш Gradle. Вам потребуется адаптировать версии JDK/Android SDK под ваш проект.
# ~/ci/docker/Dockerfile.android-ci
FROM debian:stable-slim
ARG DEBIAN_FRONTEND=noninteractive
# Базовые пакеты
RUN apt-get update && apt-get install -y --no-install-recommends \
curl unzip ca-certificates git bash file \
&& rm -rf /var/lib/apt/lists/
# Пример установки JDK (подберите версию под ваш Gradle/AGP)
# На практике удобнее использовать официальные образы, но тут — демонстрационный шаблон.
RUN apt-get update && apt-get install -y --no-install-recommends \
openjdk-17-jdk-headless \
&& rm -rf /var/lib/apt/lists/
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
ENV PATH=$JAVA_HOME/bin:$PATH
# Android SDK путь
ENV ANDROID_SDK_ROOT=/opt/android-sdk
ENV PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/emulator:$ANDROID_SDK_ROOT/tools:$ANDROID_SDK_ROOT/tools/bin
# Подготовка SDK (подставьте корректные команды/лицензию/версии)
# Упрощённый шаблон: в реальном Dockerfile чаще используют скачивание command-line tools.
RUN mkdir -p $ANDROID_SDK_ROOT
# Убедитесь, что будет доступ к cmdline-tools и скачивание будет корректным для вашей среды.
# Для примера оставим место под установку.
# COPY cmdline-tools/ $ANDROID_SDK_ROOT/cmdline-tools/
# В продакшн-образах обычно фиксируют:
# - platform-tools
# - build-tools (несколько версий)
# - platforms;android-XX
# - cmdline-tools
# - emulator (если нужны instrumented tests)
# Создадим директорию под кэш Gradle
ENV GRADLE_USER_HOME=/cache/gradle
RUN mkdir -p /cache/gradle
WORKDIR /work
# По умолчанию команда — не требуется, Runner выполнит скрипт
CMD ["bash", "-lc", "echo Container ready"]
После сборки:
docker build -t android-ci:local -f ~/ci/docker/Dockerfile.android-ci ~/ci/docker
Если в вашем окружении нужны эмулятор/устройства для instrumented tests, добавьте в образ то, что требуется для запуска. Обычно instrumented tests требуют более сложной инфраструктуры (эмулятор, Android system image, настройки сети и т.п.). В рамках автономного сценария часто начинают с unit-тестов и затем расширяют окружение под инструментальные.
Устройство GitLab Runner для автономной работы
GitLab Runner — это агент, который принимает задания от GitLab и выполняет их. В автономном решении есть два популярных пути:
- Runner как процесс в Termux (с использованием Docker executor).
- Runner как контейнер (когда Termux управляет Docker хостом).
Обычно удобен первый вариант: Runner в Termux с Docker executor. Общая логика:
- Установить runner в Termux.
- Зарегистрировать его в GitLab.
- Настроить executor: docker.
- Подключить volume для кэшей (Gradle).
Установка и регистрация GitLab Runner
Для установки runner используйте доступный способ в вашем окружении (в зависимости от доступности пакетов для Termux). После установки создайте конфигурационный файл и зарегистрируйте runner через gitlab-runner register.
Примерно так выглядит последовательность (значения URL/токена — ваши):
gitlab-runner --version
gitlab-runner register
Во время регистрации вам потребуется выбрать:
- URL GitLab: адрес вашего сервера (локальный или корпоративный).
- Registration token: токен проекта/группы.
- Executor: Docker.
- Docker image (если используется напрямую): можно указывать
android-ci:localили базовый образ, а затем в job — собрать всё остальное.
После регистрации проверьте конфиг: обычно он в ~/.gitlab-runner/config.toml (путь зависит от установки).
Настройка config.toml для Docker executor
Ниже упрощённый пример секции runner. Смысл: обеспечить корректный запуск контейнеров, задать кэш Gradle и рабочие директории.
# ~/.gitlab-runner/config.toml (пример фрагмента)
[[runners]]
name = "termux-android-ci"
url = "https://gitlab.local/"
token = "REPLACE_ME"
executor = "docker"
[runners.docker]
image = "android-ci:local"
privileged = false
disable_cache = false
# Хостовые директории монтируются в контейнер для кэша
volumes = [
"/home/YOUR_USER/ci/cache/gradle:/cache/gradle",
"/home/YOUR_USER/ci/gitlab-runner:/work"
]
[runners.cache]
# В Docker executor кэш часто решают volume/Gradle caching.
# При желании можно настроить отдельное хранилище.
Подставьте ваш путь и пользователя. Идея: чтобы Gradle кэш оставался на хосте и не скачивался заново при каждом job.
Организация pipeline для Android: сборка, unit-тесты, статический анализ
Далее — ключевой файл .gitlab-ci.yml. Мы сделаем пайплайн с несколькими стадиями и чёткими артефактами.
Базовый пример для unit-тестов и сборки:
# .gitlab-ci.yml
stages:
- prepare
- test
- build
variables:
# Держим кэш Gradle внутри примонтированного volume
GRADLE_USER_HOME: "/cache/gradle"
# Ускорение логов, при необходимости
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
before_script:
- java -version
- ./gradlew --version || true
prepare:
stage: prepare
script:
- chmod +x ./gradlew
- ./gradlew tasks --all
artifacts:
expire_in: 1 day
paths:
- app/build
unit_tests:
stage: test
script:
- ./gradlew testDebugUnitTest --stacktrace
artifacts:
when: always
expire_in: 7 days
reports:
junit: "/build/test-results/test//.xml"
paths:
- app/build/test-results
static_analysis:
stage: test
script:
# Пример: настройте под ваш инструмент (ktlint/detekt/checkstyle/lint)
- ./gradlew lintDebug --stacktrace
artifacts:
when: always
expire_in: 7 days
paths:
- app/build/reports
build_apk:
stage: build
script:
- ./gradlew assembleDebug --stacktrace
artifacts:
expire_in: 14 days
paths:
- app/build/outputs/apk/debug/*.apk
Примечания:
- Если у вас multi-module проект, корректируйте команды (например,
module:test). - Если Gradle Wrapper не используется, добавьте скачивание Gradle или закрепите версию в образе.
- Артефакты помогают быстро понять, что именно «сломалось» на этапе тестов/линта.
Автоматическое тестирование Android: unit vs instrumented
В автономной среде важно разделять типы тестов:
- Unit-тесты (например,
testDebugUnitTest) обычно не требуют устройства и могут запускаться сразу в контейнере. - Instrumented tests (например, через AndroidJUnitRunner на эмуляторе) требуют эмулятора или физического устройства и обычно дополнительных компонентов (system image, запуск эмулятора, виртуальные устройства/ускорение).
Для начала рекомендовано выстроить надёжный контур unit-тестов, а затем отдельно добавлять стадию для instrumented tests после того, как эмуляторная часть будет стабильна в вашем окружении.
Добавление инструментальных тестов (эскиз)
Ниже — общий шаблон стадию instrumented_tests. В реальности нужно адаптировать запуск эмулятора под ваше устройство/образ, а также обеспечить таймауты и ожидание готовности.
instrumented_tests:
stage: test
script:
# Примеры шагов (адаптируйте под ваш SDK/версии):
# 1) Создать AVD
# 2) Запустить emulator в фоне
# 3) Дождаться boot completion
# 4) Выполнить connectedAndroidTest / managed device tests
- echo "TODO: implement emulator start and waits"
- ./gradlew connectedDebugAndroidTest --stacktrace
artifacts:
when: always
expire_in: 7 days
paths:
- app/build/outputs/androidTest-results
Практический совет: если эмулятор нестабилен, лучше держать instrumented tests в отдельной ветке/триггере (например, по расписанию или вручную), чтобы не блокировать разработку на unit-тестах.
Кэширование и ускорение: Gradle, зависимости, SDK
Автономный CI/CD в Termux выигрывает за счёт кэширования:
- Кэш Gradle: монтируйте
/cache/gradleиз хоста в контейнер. - Вместо установки SDK на каждом job — фиксируйте SDK в Docker‑образе.
- Вынесите «тяжёлые» шаги в подготовительные стадии, чтобы уменьшить время первого запуска.
Проверяйте, что кэш реально используется: Gradle при корректной настройке GRADLE_USER_HOME начнёт переиспользовать зависимости.
Надёжность пайплайна: повторяемость и диагностика
Чтобы CI/CD работал предсказуемо:
- Фиксируйте версии: JDK, Android Gradle Plugin, Build Tools и платформы Android.
- Используйте
--stacktraceи включайте корректные отчёты тестов. - Делайте артефакты: отчёты JUnit, логи сборки, отчёты линтера.
- Добавляйте отдельные jobs для диагностики (например,
gradlew test --tests ...при необходимости).
Безопасность и корректная эксплуатация
Даже локальный CI/CD требует дисциплины:
- Не храните секреты прямо в
.gitlab-ci.yml. Используйте переменные GitLab. - Ограничивайте доступ Runner’а: минимальные права на томах и файловой системе.
- Избегайте запуска контейнеров в привилегированном режиме без необходимости.
- Следите за обновлениями образов и зависимостей.
Если вы подключаете локальную сеть для обмена данными между устройствами (например, чтобы Runner мог обращаться к GitLab/репозиторию по локальному адресу), можно использовать VPN только для организации локальной сети — без целей обхода блокировок.
Пример: полный локальный цикл разработки
Рекомендуемый рабочий процесс может выглядеть так:
- Разработчик пушит изменения в GitLab.
- GitLab вызывает ваш автономный Runner.
- Runner запускает контейнер
android-ci:local. - Внутри контейнера выполняются: unit tests, lint, сборка APK.
- Результаты тестов и отчёты сохраняются как артефакты.
Это даёт быстую обратную связь и снижает вероятность сюрпризов в более «тяжёлых» средах.
Заключение
Мы рассмотрели, как построить полностью автономный CI/CD в Termux: поднять Docker‑контейнеры, подключить GitLab Runner с Docker executor и организовать автоматическое тестирование Android‑приложений в пайплайне. Такой подход помогает ускорить разработку, улучшить воспроизводимость сборок и сделать контроль качества частью ежедневной рутины.
Если хотите, чтобы мы помогли вам собрать решение под ваш проект (версии Android/Gradle, структура модулей, unit vs instrumented, настройка кэшей и оптимизация времени сборки), обратитесь в РыбинскЛАБ — мы ведём внедрение и сопровождение DevOps‑практик для Android и локальных CI/CD.