Налаштування 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 буде працювати стабільно та швидко.







