Налаштування CI/CD для Android-додатку через Gradle
Збірка триває 30 хвилин? Це проблема, яку ми вирішуємо
Конфігурація CI/CD для Android піднімає більше питань, ніж здається. Часта ситуація: розробник пушить код, CI падає з помилкою "Keystore not found" або збірка триває 15 хвилин, а реліз затримується на день. Нещодавно команда з 5 розробників звернулася до нас: їхня збірка тривала 30 хвилин, реліз виходив раз на два тижні. Після впровадження CI/CD з Gradle кешуванням та паралельною збіркою час скоротився до 10 хвилин, частота релізів зросла до щотижневих. Скорочення часу збірки безпосередньо знижує витрати на CI-раннери та прискорює вихід оновлень, що економить бюджет команди.
Основні проблеми, які ми вирішуємо
Основні граблі: keystore в репозиторії, ручне версіонування, відсутність кешу на CI, неправильна конфігурація build variants. Кожна з цих помилок здатна заблокувати реліз або призвести до витоку секретів. Наша послуга з налаштування CI/CD включає повний аудит поточної збірки, конфігурацію Gradle з урахуванням найкращих практик, інтеграцію з CI-системою та деплой в Google Play. Інвестиції в CI/CD окупаються за рахунок прискорення релізного циклу та зниження ручної праці. Гарантуємо стабільну роботу після впровадження.
Як правильно підписати APK у CI?
Keystore в Git — критична помилка, навіть у приватному репозиторії. Правильна схема: keystore Base64-encoded → 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. Процес складається з кількох кроків:
- Завантажте keystore в CI secrets у вигляді Base64-рядка.
- Створіть job для декодування та збірки.
- Налаштуйте signingConfig у build.gradle.kts з використанням змінних середовища.
- Після збірки видаліть keystore з файлової системи.
Чому автоматичне версіонування — стандарт?
versionCode в Android має бути унікальним для кожної збірки. Помилка — використовувати ручне оновлення. На 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}"
}
}Порівняння методів:
| Метод | Опис | Монотонність | Залежність від CI |
|---|---|---|---|
| CI_BUILD_NUMBER | Номер збірки від провайдера | Так, якщо не скидається | Так (при зміні CI — проблеми) |
| git commit count | Кількість комітів | Завжди монотонно | Ні (універсально) |
Рекомендуємо git commit count — не прив'язаний до CI-системи.
Як пришвидшити Gradle збірку в 3-4 рази?
Повільний Gradle — головна скарга Android CI. Кілька конкретних налаштувань у gradle.properties:
-
org.gradle.parallel=true— паралельна збірка модулів. -
org.gradle.configureondemand=true— конфігурування лише потрібних модулів. -
org.gradle.caching=true— локальний кеш завдань. При незмінному коді модуля Gradle бере результат з кешу без перекомпіляції. На CI це працює черезactions/cache(GitHub) з ключем за хешем*.gradle*файлів. -
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC— збільшений heap. -
android.defaults.buildfeatures.buildconfig=false— генерація BuildConfig лише для активної збірки.
Детальніше про налаштування кешу — у документації Gradle Gradle Build Cache. Порівняння часу збірки з різними конфігураціями на ubuntu-latest раннері:
| Конфігурація | Час збірки (хвилини) |
|---|---|
| Без оптимізацій | 10–15 |
| Паралельність + configureondemand | 6–8 |
| Повне кешування + JVM heap 4GB | 2–4 |
Правильне кешування скорочує час у 3–4 рази — це краще, ніж без нього. Використовуйте R8 замість ProGuard — він швидший. Наприклад, R8 кращий за ProGuard, оскільки працює в 2 рази швидше при однаковому коді.
Які build variants налаштовувати?
Різні 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)
}Credentials JSON — service account з Google Play Console з доступом Release manager. На CI передаємо через environment variable, аналогічно keystore. Використання gradle-play-publisher краще, ніж ручне завантаження APK, оскільки воно автоматизує процес і зменшує ймовірність помилок.
Що входить у налаштування CI/CD?
| Етап | Опис | Строки |
|---|---|---|
| Аналіз | Аудит поточної збірки, інфраструктури та CI-конфігурації | 1 день |
| Конфігурація Gradle | Налаштування підписання, версіонування, флейворів, кешування | 1–2 дні |
| Інтеграція CI | Налаштування workflow (GitHub Actions/GitLab CI/Bitrise), секретів, деплою | 1–2 дні |
| Оптимізація | Прискорення збірки, впровадження тестів, паралельних завдань | 1–2 дні |
| Документація | Опис процесу, керівництво для команди | 1 день |
Строки та вартість
Базове налаштування CI/CD (підписання, версіонування, один CI) займає 2–4 дні. Повна конфігурація з build variants, деплоєм в Google Play, оптимізацією збірки — 1–2 тижні. Вартість розраховується індивідуально. Наприклад, базове налаштування коштує від 800$, повна — від 1500$. Економія на CI-раннерах може сягати 300$ на місяць завдяки скороченню часу збірки.
Якщо ваша збірка займає занадто багато часу або релізи постійно зриваються, пора впроваджувати професійний CI/CD. Зв'яжіться з нами — ми проведемо аудит і налаштуємо процес під ключ, оцінимо ваш проект. Гарантуємо стабільний результат.
Типові помилки та чек-лист
- Keystore в Git — використовуйте CI secrets.
- Ручне версіонування — автоматизуйте через git commit count.
- Відсутність кешу — налаштуйте Gradle caching.
- Неправильні build variants — визначте dev/prod з окремими jobs.
- Ігнорування деплою — інтегруйте gradle-play-publisher.
Детальний чек-лист налаштування CI/CD (натисніть, щоб розгорнути)
1. Keystore в CI secrets (Base64). 2. Автоматичне версіонування через git commit count. 3. Налаштування Gradle caching. 4. Роздільні build variants dev/prod. 5. Деплой через gradle-play-publisher. 6. Паралельна збірка. 7. Тести для кожної збірки.Дотримуйтесь цих рекомендацій — і ваш CI/CD буде працювати стабільно та швидко.







