CI/CD для Android-додатків через Gradle
Gradle—система сборки Android, та більшість CI/CD для Android-проектів будується навколо кількох ключових Gradle-команд. Але «запустити ./gradlew assembleRelease» та «настроїти правильний CI/CD»—різні завдання. Підписання APK/AAB, версіонування, управління build variants, кеш залежностей—всё це вимагає явної конфігурації.
Підписання у CI без зберігання keystore у репозиторії
Keystore в Git—критична помилка, навіть у приватному репозиторії. Правильна схема: keystore Base64-кодований → CI secret → декодуємо на льоту.
# GitHub Actions
- name: Decode Keystore
run: |
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > app/release.keystore
- name: Build Release AAB
run: ./gradlew bundleRelease
env:
SIGNING_STORE_FILE: release.keystore
SIGNING_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
SIGNING_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
В app/build.gradle.kts:
signingConfigs {
create("release") {
storeFile = file(System.getenv("SIGNING_STORE_FILE") ?: "debug.keystore")
storePassword = System.getenv("SIGNING_STORE_PASSWORD") ?: ""
keyAlias = System.getenv("SIGNING_KEY_ALIAS") ?: ""
keyPassword = System.getenv("SIGNING_KEY_PASSWORD") ?: ""
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
Keystore не потрапляє в артефакти—видаліть після сборки: rm -f app/release.keystore.
Версіонування через CI
Android versionCode повинен бути унікальним для кожної сборки. Помилка—вручну використовувати 1, 2, 3 або забути оновити. На CI—автоматично:
// build.gradle.kts
val ciPipelineNumber = System.getenv("CI_BUILD_NUMBER")?.toIntOrNull() ?: 1
val gitCommitCount = "git rev-list --count HEAD".runCommand().trim().toIntOrNull() ?: 1
android {
defaultConfig {
versionCode = ciPipelineNumber.takeIf { it > 1 } ?: gitCommitCount
versionName = "2.4.${gitCommitCount}"
}
}
gitCommitCount як versionCode—надійний варіант: монотонно растет, не залежить від CI-системи.
Gradle Performance: основні рычаги
Повільний Gradle—поширена скарга. Кілька конкретних настройок у gradle.properties:
# Паралельна сборка модулів
org.gradle.parallel=true
# Конфігурування тільки потрібних модулів
org.gradle.configureondemand=true
# Кеш сборки
org.gradle.caching=true
# JVM heap
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC
# Build config тільки для активної сборки
android.defaults.buildfeatures.buildconfig=false
org.gradle.caching=true—локальний кеш завдань. При незмінному коді модуля Gradle бере результат з кешу без перекомпіляції. На CI це працює через actions/cache (GitHub) з ключем за хешем .gradle файлів.
Типовий до/після з правильним кешуванням на ubuntu-latest раннері:
- Без кешу: ./gradlew assembleRelease—8–12 хвилин
- З Gradle daemon + dependency cache: 2–4 хвилини
Build Variants та CI
Різні variant-и для різних окружень—стандарт:
flavorDimensions += "env"
productFlavors {
create("dev") {
applicationIdSuffix = ".dev"
buildConfigField("String", "API_URL", "\"https://api.dev.example.com\"")
}
create("prod") {
buildConfigField("String", "API_URL", "\"https://api.example.com\"")
}
}
На CI—окремі jobs для кожного variant-а:
- PR → assembleDevDebug + тесты
- merge to develop → assembleDevRelease + Firebase App Distribution
- release tag → bundleProdRelease + Google Play
Загрузка в Google Play через Gradle
gradle-play-publisher plugin:
// build.gradle.kts
plugins {
id("com.github.triplet.play") version "3.9.1"
}
play {
serviceAccountCredentials.set(file(System.getenv("PLAY_STORE_JSON") ?: "play-store-credentials.json"))
track.set("internal")
defaultToAppBundles.set(true)
}
./gradlew publishProdReleaseBundle
Credentials JSON—service account з Google Play Console з доступом Release manager. На CI—передаємо через environment variable або тимчасовий файл, як keystore.
Часова шкала
Настройка Gradle з підписанням, версіонуванням та базовим CI (GitHub Actions/GitLab CI): 2–4 дні. Повна конфігурація з build variants, Google Play deploy, Gradle performance-оптимізацією, тест-раннерами: 1–1.5 тижня. Стоимость рассчитывается индивидуально.







