This guide will walk you through setting up the Android Modular Template for local development, CI/CD, and eventual Play Store deployment.
For Current Development (Required Now):
- Firebase configured (
google-services.jsonadded) - Local build working (
./gradlew :app:assembleDevDebug) - GitHub secrets configured (for CI/CD)
- Build Release APKs workflow working (unsigned APKs)
For Play Store Deployment (When Ready):
- Keystore generated (for signed release builds)
- Play Store service account (for deployment)
- Deploy workflow enabled
Before you begin, ensure you have:
- Android Studio Ladybug (2024.2.1) or newer
- JDK 11 or higher
- Android SDK (API 30+)
- Git installed
- Firebase account (free tier is fine)
- Google Play Developer account (only when ready to publish - $25 one-time fee)
Option A: Quick Rebrand (Recommended)
# Clone the template
git clone https://github.com/yourusername/android-modular-template.git MyApp
cd MyApp
# Interactive rebrand
./rebrand.sh
# Or specify values directly
./rebrand.sh --project-name MyApp \
--package-name com.mycompany.myapp \
--app-name "My Awesome App"
# Preview changes first (dry run)
./rebrand.sh --project-name MyApp \
--package-name com.mycompany.myapp \
--dry-runOption B: Manual Setup
git clone https://github.com/yourusername/android-modular-template.git
cd android-modular-template
# Manually edit template.properties and rebuildThis is required - the app will NOT build without it.
- Go to Firebase Console
- Click "Add project" or select an existing project
- Name it "MyApp" (or your preferred name)
- Follow the wizard (Google Analytics is optional)
Important: This template uses product flavors (dev/prod) and build types (debug/release), which create different package names. You need to add all 4 package variants to Firebase for full functionality.
Add these 4 Android apps to your Firebase project:
-
Production Release (main app for Play Store):
- Click Android icon (⚙️ → Add app)
- Package name:
com.example.myapp(base package fromtemplate.properties) - Nickname: "MyApp - Production"
- Click "Register app"
-
Production Debug (for testing production config):
- Click Add another app → Android icon
- Package name:
com.example.myapp.debug(base +.debug) - Nickname: "MyApp - Prod Debug"
- Click "Register app"
-
Dev Release (for development environment):
- Click Add another app → Android icon
- Package name:
com.example.myapp.dev(base +.dev) - Nickname: "MyApp - Dev"
- Click "Register app"
-
Dev Debug (for day-to-day development):
- Click Add another app → Android icon
- Package name:
com.example.myapp.dev.debug(base +.dev.debug) - Nickname: "MyApp - Dev Debug"
- Click "Register app"
Why 4 apps?
- Product flavors (
dev,prod) let you test against different backend environments - Build types (
debug,release) control optimization and debug tools - Each combination gets a unique package name (via
applicationIdSuffix) - Firebase services (Analytics, Crashlytics, FCM) are isolated per package
- This prevents dev/debug data from polluting production metrics
Important: After adding all 4 apps, Firebase will generate a single google-services.json file that contains configurations for all of them.
- Go to Project Settings → Your apps → Select any of the apps
- Click "Download google-services.json" button
- Place it in the
app/directory:mv ~/Downloads/google-services.json app/google-services.json - DO NOT commit this file - it's already in
.gitignore
Note: The Google Services plugin automatically selects the correct configuration based on your build variant's applicationId.
In Firebase Console, enable these services:
Crashlytics (Crash Reporting):
- Go to: Build → Crashlytics
- Click "Enable Crashlytics"
Analytics (User Analytics):
- Automatically enabled when project is created
- Go to: Analytics → Dashboard to verify
Cloud Messaging (Push Notifications):
- Automatically enabled when project is created
- Go to: Build → Cloud Messaging to verify
Remote Config (Feature Flags):
- Go to: Build → Remote Config
- Click "Get started"
Performance Monitoring (Performance Tracking):
- Go to: Build → Performance Monitoring
- Click "Get started"
./gradlew :app:assembleDevDebugIf successful, you'll see:
BUILD SUCCESSFUL in 2m 15s
Without these secrets, CI builds will fail.
# macOS/Linux
cat app/google-services.json | base64 | pbcopy
# On Linux without pbcopy
cat app/google-services.json | base64 | xclip -selection clipboard- Go to your GitHub repo
- Navigate to: Settings → Secrets and variables → Actions
- Click "New repository secret"
- Name:
GOOGLE_SERVICES_JSON - Value: Paste the base64 string from clipboard
- Click "Add secret"
Push any change to trigger CI:
git add .
git commit -m "test: Verify CI with Firebase config"
git push origin mainCheck: Actions tab → Your workflow should now build successfully
Good news! You don't need a keystore yet. Use the Build Release APKs workflow for development and testing.
✅ Build unsigned release APKs via GitHub Actions ✅ Test app on devices without signing ✅ Create GitHub releases with downloadable APKs ✅ Use CI/CD for builds, tests, and lint checks
- Go to your GitHub repo → Actions tab
- Select "Build Release APKs" workflow
- Click "Run workflow"
- Choose flavor:
dev,prod, orboth - Wait for build to complete (~5-10 minutes)
- Download APKs from Artifacts section
- Install:
adb install -r app-dev-release.apk
# Dev release (unsigned)
./gradlew :app:assembleDevRelease
# Prod release (unsigned)
./gradlew :app:assembleProdRelease
# Find APKs in:
# app/build/outputs/apk/dev/release/*.apk
# app/build/outputs/apk/prod/release/*.apkThese unsigned APKs are perfect for:
- Internal testing
- Beta testing with team
- Device testing (real devices, Firebase Test Lab)
- QA before Play Store submission
Only complete this step when you're ready to publish to Google Play Store.
keytool -genkey -v \
-keystore myapp-release.jks \
-alias myapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000You'll be prompted for:
- Keystore password: Choose a strong password (save it!)
- Key password: Can be same as keystore password
- Name, organization, etc.: Fill in your details
CRITICAL:
- 🔒 Backup this file - if you lose it, you can't update your app!
- 🔒 Save passwords securely (use a password manager)
- 🔒 Never commit the keystore to git
# Move to parent directory (outside git repo)
mv myapp-release.jks ../myapp-release.jksCreate this file in the project root (it's gitignored):
cat > keystore.properties << EOF
storeFile=../myapp-release.jks
storePassword=YOUR_KEYSTORE_PASSWORD
keyAlias=myapp
keyPassword=YOUR_KEY_PASSWORD
EOFReplace YOUR_KEYSTORE_PASSWORD and YOUR_KEY_PASSWORD with actual passwords.
Find this section (around line 68) and uncomment:
// Signing configuration for release builds
// Uncomment and configure when ready to sign release builds
signingConfigs {
create("release") {
val keystorePropertiesFile = rootProject.file("keystore.properties")
if (keystorePropertiesFile.exists()) {
val keystoreProperties = java.util.Properties()
keystoreProperties.load(java.io.FileInputStream(keystorePropertiesFile))
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
}
}
}
// In buildTypes.release, add:
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
// ... existing config ...
}
}./gradlew :app:assembleProdReleaseFind APK at: app/build/outputs/apk/prod/release/app-prod-release.apk
For automated deployment via GitHub Actions:
cat ../myapp-release.jks | base64 | pbcopyGo to: Settings → Secrets and variables → Actions
Add these secrets:
| Secret Name | Value | Description |
|---|---|---|
ANDROID_KEYSTORE_BASE64 |
Paste base64 keystore | Encoded keystore file |
KEYSTORE_PASSWORD |
Your keystore password | Password for keystore |
KEY_ALIAS |
myapp |
Alias from keytool command |
KEY_PASSWORD |
Your key password | Password for key |
Without this, you cannot deploy to Google Play Store via CI/CD.
- Go to Google Cloud Console
- Select your project (or create one linked to Play Console)
- Navigate to: IAM & Admin → Service Accounts
- Click "Create Service Account"
- Name:
myapp-github-actions - Description:
Service account for GitHub Actions deployment
- Name:
- Click "Create and Continue"
- Role: Service Account User
- Click "Continue" → "Done"
- Find your service account in the list
- Click the ⋮ menu → "Manage keys"
- Click "Add Key" → "Create new key"
- Choose JSON format
- Click "Create" - file will download automatically
- Go to Google Play Console
- Navigate to: Setup → API access
- Scroll to Service accounts
- Click "Link Google Cloud project" (if not already linked)
- Grant access to the service account you created:
- Click "Grant access" next to your service account
- Permissions: Admin (all permissions) or Release manager
- Click "Apply"
# Copy JSON content
cat ~/Downloads/your-service-account-key.json | pbcopyAdd to GitHub:
- Name:
PLAY_STORE_SERVICE_ACCOUNT - Value: Paste entire JSON content
- Click "Add secret"
# The deploy workflow is currently disabled
mv .github/workflows/deploy.yml.disabled .github/workflows/deploy.yml
git add .github/workflows/deploy.yml
git commit -m "feat: Enable Play Store deployment workflow"
git push- Go to Actions tab → "Deploy to Play Store"
- Click "Run workflow"
- Choose track:
internal,beta, orproduction - Wait for deployment to complete
- Check Play Console to verify
fastlane deploy_internal # Internal testing
fastlane deploy_beta # Closed testing
fastlane deploy_production # Production release# Development builds
./gradlew :app:assembleDevDebug # Dev debug (with Chucker, LeakCanary)
./gradlew :app:assembleDevRelease # Dev release (unsigned)
# Production builds (requires keystore setup)
./gradlew :app:assembleProdRelease # Production release APK
./gradlew :app:bundleProdRelease # Production AAB (for Play Store)
# Run all tests
./gradlew test
# Code quality checks
./gradlew detekt # Static analysis
./gradlew lint # Android lint
# Clean build
./gradlew clean build./scripts/bump_version.sh patch # 1.0.0 → 1.0.1 (bug fixes)
./scripts/bump_version.sh minor # 1.0.0 → 1.1.0 (new features)
./scripts/bump_version.sh major # 1.0.0 → 2.0.0 (breaking changes)./gradlew createFeature -PfeatureName=dashboardThis automatically creates:
:feature:dashboard- Feature implementation:feature:dashboard:api- Navigation routes- Build files, manifests, and boilerplate
./install-hooks.shPre-commit hooks run:
- Detekt static analysis
- Unit tests
Solution: Download from Firebase Console and place in app/google-services.json
See Step 1.2: Firebase Configuration
Solution: This error means you're missing one of the 4 required package variants in Firebase.
Example error:
No matching client found for package name 'com.example.myapp.dev.debug'
Fix:
- Check which variant is failing (look at the package name in the error)
- Go to Firebase Console → Project Settings → Your apps
- Verify you have all 4 apps registered:
com.example.myapp(prod release)com.example.myapp.debug(prod debug)com.example.myapp.dev(dev release)com.example.myapp.dev.debug(dev debug)
- If missing, add the missing app variant (see Step 1.2)
- Download the updated
google-services.jsonand replace the old one - Sync Gradle and rebuild
Alternative (if package changed after rebranding):
- Re-run
./rebrand.shwith your new package name - Delete all apps from Firebase and re-add with new package names
Solution: Add secret in GitHub Settings → Secrets and variables → Actions
See Step 2.1: Add Firebase Config to GitHub Secrets
Solution:
- Check
keystore.propertieshas correctstoreFilepath - Verify
myapp-release.jksexists at that path - Ensure path is relative to project root (e.g.,
../myapp-release.jks)
Solution:
- Verify service account is linked in Play Console
- Verify it has "Admin" or "Release manager" permissions
- Verify JSON key is correct in GitHub secret
- Check that the service account email matches the one in Play Console
Solution:
- Verify Crashlytics is enabled in Firebase Console
- Trigger a test crash in the app
- Wait 5-10 minutes for first crash to appear
- Ensure you're running a release build (Crashlytics disabled in debug)
Solution:
- Verify intent filters in
AndroidManifest.xml - For custom scheme (
myapp://):adb shell am start -W -a android.intent.action.VIEW \ -d "myapp://content/123" com.example.myapp.dev - For App Links (
https://):- Verify
assetlinks.jsonis accessible athttps://yourdomain.com/.well-known/assetlinks.json - Get SHA-256 fingerprint:
keytool -list -v -keystore ../myapp-release.jks - Test:
adb shell am start -W -a android.intent.action.VIEW -d "https://example.com/content/123"
- Verify
# Stop Gradle daemon
./gradlew --stop
# Clean and rebuild
./gradlew clean build
# If still failing, in Android Studio:
# File → Invalidate Caches → Invalidate and Restart# Stop Gradle daemon (fixes most KSP issues)
./gradlew --stop
# Rebuild
./gradlew clean build
# Ensure KSP plugin is applied in module's build.gradle.kts./install-hooks.shTo bypass (not recommended):
git commit --no-verify- Firebase project created
-
google-services.jsondownloaded and placed inapp/ - Debug build successful:
./gradlew :app:assembleDevDebug - App runs on emulator/device
- Firebase services enabled (Crashlytics, Analytics, FCM, Remote Config)
-
GOOGLE_SERVICES_JSONsecret added to GitHub - CI builds passing on GitHub Actions
- Unsigned release APKs building successfully
- Keystore generated (
myapp-release.jks) - Keystore backed up securely (external drive, password manager)
-
keystore.propertiescreated - Signing config uncommented in
app/build.gradle.kts - Release build successful:
./gradlew :app:assembleProdRelease
- GitHub secrets added:
ANDROID_KEYSTORE_BASE64KEYSTORE_PASSWORDKEY_ALIASKEY_PASSWORDPLAY_STORE_SERVICE_ACCOUNT
- Play Store service account created and linked
- Deploy workflow enabled (
.github/workflows/deploy.yml) - Deployment workflow tested
You can do this right now:
-
Local Development:
./gradlew :app:assembleDevDebug
-
Build Unsigned Release APKs (via GitHub Actions):
- Go to Actions tab → "Build Release APKs"
- Click "Run workflow" → Choose flavor
- Download from artifacts
-
Test on Devices:
adb install -r app-dev-release.apk
-
Develop Features:
- Full CI/CD support
- Automatic testing and linting
- Firebase Analytics and Crashlytics
- Push notifications
When ready to publish:
-
Generate Keystore (see Step 4.1)
-
Create Signed Builds:
./gradlew :app:assembleProdRelease # Signed APK ./gradlew :app:bundleProdRelease # Signed AAB for Play Store
-
Setup Play Store (see Step 5):
- Create app listing
- Upload screenshots, description, privacy policy
- Complete content rating questionnaire
- Upload first release to internal testing
-
Enable Automated Deployment:
- Add GitHub secrets
- Enable deploy workflow
- Deploy via GitHub Actions or Fastlane
- README.md - Project overview and features
- CONTRIBUTING.md - Contribution guidelines
- CLAUDE.md - Architecture and technical details
- docs/architecture/ - Architecture Decision Records (ADRs)
- docs/api/ - API endpoint documentation
- scripts/README.md - Version management scripts
- .github/workflows/README.md - CI/CD workflows
- fastlane/README.md - Fastlane deployment
- build-logic/README.md - Convention plugins
- Build issues: See Troubleshooting above
- CI/CD issues: Check
.github/workflows/README.md - Architecture questions: Read
CLAUDE.mdanddocs/architecture/ - Contributing: See
CONTRIBUTING.md - Open an issue: GitHub Issues
Congratulations! 🎉 You're all set to start building amazing Android apps with this template.