הגדרת CI/CD לאנדרואיד מעלה יותר שאלות ממה שנדמה. מצב נפוץ: מפתח דוחף קוד, CI נכשל עם שגיאת 'Keystore not found', או שהבנייה אורכת 15 דקות, והשחרור מתעכב ביום. לאחרונה פנתה אלינו צוות של 5 מפתחים: הבנייה שלהם ארכה 30 דקות, ושחרורים יצאו פעם בשבועיים. לאחר יישום CI/CD עם מטמון Gradle ובנייה מקבילית, זמן הבנייה ירד ל-10 דקות, ותדירות השחרורים עלתה לשבועית. הפחתת זמן הבנייה מורידה ישירות את עלויות רצי ה-CI ומאיצה את אספקת העדכונים, וחוסכת בתקציב הצוות.
המלכודות העיקריות: keystore במאגר, ניהול גרסאות ידני, חוסר מטמון ב-CI, והגדרת build variant שגויה. כל אחת מהטעויות האלה יכולה לחסום שחרור או להוביל לדליפת סודות. שירות הגדרת ה-CI/CD שלנו כולל ביקורת מלאה של הבנייה הנוכחית שלך, הגדרת Gradle לפי שיטות עבודה מומלצות, אינטגרציה עם מערכת CI, ופריסה ל-Google Play. ההשקעה בהגדרת CI/CD משתלמת דרך מחזורי שחרור מהירים יותר והפחתת עבודה ידנית. אנו מבטיחים פעילות יציבה לאחר היישום.
איך לחתום כראוי על קבצי APK ב-CI?
אחסון ה-keystore ב-Git הוא טעות קריטית, אפילו במאגרים פרטיים. הגישה הנכונה: keystore מקודד ב-Base64 → סוד CI → פענוח תוך כדי ריצה.
---
# 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 }}
ב-# 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 }} :
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 לא מגיע לארטיפקטים — אנו מוחקים אותו לאחר הבנייה: app/build.gradle.kts.
התהליך מורכב מכמה שלבים:
- העלאת ה-keystore לסודות CI כמחרוזת Base64.
- יצירת job לפענוח ובנייה.
- הגדרת signingConfig ב-build.gradle.kts באמצעות משתני סביבה.
- לאחר הבנייה, מחיקת ה-keystore ממערכת הקבצים.
למה ניהול גרסאות אוטומטי הוא הסטנדרט?
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") } } באנדרואיד חייב להיות ייחודי לכל בנייה. טעות היא לעדכן אותו ידנית. ב-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) |
| ספירת commit של git | מספר ה-commits | תמיד מונוטוני | לא (אוניברסלי) |
אנו ממליצים על ספירת commit של git — היא לא קשורה למערכת CI.
איך להאיץ בניות Gradle?
Gradle איטי הוא התלונה העיקרית על CI לאנדרואיד. מספר הגדרות ספציפיות ב-rm -f app/release.keystore:
-
versionCode— בנייה מקבילית של מודולים. -
// 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}" } }— הגדרת רק את המודולים הדרושים. -
gradle.properties— מטמון משימות מקומי. כשקוד המודול לא השתנה, Gradle לוקח את התוצאה מהמטמון ללא קומפילציה מחדש. ב-CI זה עובד דרךorg.gradle.parallel=true(GitHub) עם מפתח המבוסס על hash של קבציorg.gradle.configureondemand=true. -
org.gradle.caching=true— הגדלת heap. -
actions/cache— יצירת BuildConfig רק עבור הבנייה הפעילה.
מידע נוסף על הגדרת מטמון: ראו Gradle Build Cache. השוואת זמן בנייה עם הגדרות שונות על רץ *.gradle*:
| הגדרה | זמן בנייה (דקות) |
|---|---|
| ללא אופטימיזציות | 10–15 |
| מקביליות + configureondemand | 6–8 |
| מטמון מלא + JVM heap 4GB | 2–4 |
מטמון נכון מפחית את זמן הבנייה פי 3-4. השתמשו ב-R8 במקום ProGuard — הוא מהיר יותר.
אילו build variants יש להגדיר?
וריאנטים שונים לסביבות שונות הוא סטנדרט:
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 נפרדים לכל וריאנט:
- PR →
org.gradle.jvmargs=-Xmx4g -XX:+UseParallelGC+ בדיקות - merge ל-develop →
android.defaults.buildfeatures.buildconfig=false+ Firebase App Distribution - release tag →
ubuntu-latest+ Google Play
העלאה ל-Google Play דרך Gradle
תוסף 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\"") } } :
// 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)
}אישורים JSON — חשבון שירות מ-Google Play Console עם גישת assembleDevDebug. העבירו דרך משתנה סביבה ב-CI, בדומה ל-keystore.
מה כלול בהגדרת CI/CD?
| שלב | תיאור | לוח זמנים |
|---|---|---|
| ניתוח | ביקורת של הבנייה הנוכחית, התשתית והגדרות CI | יום אחד |
| הגדרת Gradle | הגדרת חתימה, גרסאות, flavors, מטמון | 1–2 ימים |
| אינטגרציית CI | הגדרת workflows (GitHub Actions/GitLab CI/Bitrise), סודות, פריסה | 1–2 ימים |
| אופטימיזציה | האצת בניות, הכנסת בדיקות, משימות מקביליות | 1–2 ימים |
| תיעוד | תיאור התהליך, מדריך לצוות | יום אחד |
לוח זמנים
הגדרת CI/CD בסיסית (חתימה, גרסאות, CI אחד) אורכת 2–4 ימים. הגדרה מלאה עם build variants, פריסה ל-Google Play, אופטימיזציית בנייה — 1–2 שבועות. העלות מחושבת באופן אישי.
אם הבנייה שלכם אורכת יותר מדי זמן או שחרורים ממשיכים להיכשל, הגיע הזמן ליישם CI/CD מקצועי. צרו קשר — אנו נבצע ביקורת ונגדיר את התהליך כך שתוכלו לשכוח מבעיות שחרור. אנו מבטיחים תוצאות יציבות.







