404 lines
9.2 KiB
Markdown
404 lines
9.2 KiB
Markdown
# Android Release Process
|
|
|
|
## Keystore Generation
|
|
|
|
### Create Release Keystore
|
|
|
|
```bash
|
|
# Generate a new keystore
|
|
keytool -genkeypair -v -storetype PKCS12 -keystore saayam-release-key.keystore -alias saayam-key-alias -keyalg RSA -keysize 2048 -validity 10000
|
|
|
|
# Verify keystore
|
|
keytool -list -v -keystore saayam-release-key.keystore
|
|
```
|
|
|
|
### Keystore Security
|
|
|
|
```bash
|
|
# Store keystore securely
|
|
mkdir -p ~/.android/keystores
|
|
mv saayam-release-key.keystore ~/.android/keystores/
|
|
|
|
# Set proper permissions
|
|
chmod 600 ~/.android/keystores/saayam-release-key.keystore
|
|
```
|
|
|
|
## Gradle Configuration
|
|
|
|
### Configure Signing
|
|
|
|
**android/gradle.properties:**
|
|
```properties
|
|
# Keystore configuration
|
|
SAAYAM_UPLOAD_STORE_FILE=saayam-release-key.keystore
|
|
SAAYAM_UPLOAD_KEY_ALIAS=saayam-key-alias
|
|
SAAYAM_UPLOAD_STORE_PASSWORD=your_keystore_password
|
|
SAAYAM_UPLOAD_KEY_PASSWORD=your_key_password
|
|
|
|
# Build optimization
|
|
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
|
|
org.gradle.parallel=true
|
|
org.gradle.configureondemand=true
|
|
org.gradle.daemon=true
|
|
|
|
# Android configuration
|
|
android.useAndroidX=true
|
|
android.enableJetifier=true
|
|
android.enableR8.fullMode=true
|
|
```
|
|
|
|
**android/app/build.gradle:**
|
|
```gradle
|
|
android {
|
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
|
|
|
defaultConfig {
|
|
applicationId "com.saayam.app"
|
|
minSdkVersion rootProject.ext.minSdkVersion
|
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
|
versionCode 1
|
|
versionName "1.0.0"
|
|
multiDexEnabled true
|
|
}
|
|
|
|
signingConfigs {
|
|
debug {
|
|
storeFile file('debug.keystore')
|
|
storePassword 'android'
|
|
keyAlias 'androiddebugkey'
|
|
keyPassword 'android'
|
|
}
|
|
release {
|
|
if (project.hasProperty('SAAYAM_UPLOAD_STORE_FILE')) {
|
|
storeFile file(SAAYAM_UPLOAD_STORE_FILE)
|
|
storePassword SAAYAM_UPLOAD_STORE_PASSWORD
|
|
keyAlias SAAYAM_UPLOAD_KEY_ALIAS
|
|
keyPassword SAAYAM_UPLOAD_KEY_PASSWORD
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
signingConfig signingConfigs.debug
|
|
applicationIdSuffix ".debug"
|
|
debuggable true
|
|
minifyEnabled false
|
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
|
}
|
|
release {
|
|
signingConfig signingConfigs.release
|
|
minifyEnabled true
|
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
|
debuggable false
|
|
zipAlignEnabled true
|
|
}
|
|
}
|
|
|
|
splits {
|
|
abi {
|
|
reset()
|
|
enable true
|
|
universalApk false
|
|
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## ProGuard Configuration
|
|
|
|
**android/app/proguard-rules.pro:**
|
|
```proguard
|
|
# React Native
|
|
-keep class com.facebook.react.** { *; }
|
|
-keep class com.facebook.hermes.** { *; }
|
|
-keep class com.facebook.jni.** { *; }
|
|
|
|
# Keep our application class
|
|
-keep class com.saayam.** { *; }
|
|
|
|
# Keep native methods
|
|
-keepclassmembers class * {
|
|
native <methods>;
|
|
}
|
|
|
|
# Keep enums
|
|
-keepclassmembers enum * {
|
|
public static **[] values();
|
|
public static ** valueOf(java.lang.String);
|
|
}
|
|
|
|
# Keep Parcelable implementations
|
|
-keep class * implements android.os.Parcelable {
|
|
public static final android.os.Parcelable$Creator *;
|
|
}
|
|
|
|
# Keep serializable classes
|
|
-keepnames class * implements java.io.Serializable
|
|
-keepclassmembers class * implements java.io.Serializable {
|
|
static final long serialVersionUID;
|
|
private static final java.io.ObjectStreamField[] serialPersistentFields;
|
|
!static !transient <fields>;
|
|
private void writeObject(java.io.ObjectOutputStream);
|
|
private void readObject(java.io.ObjectInputStream);
|
|
java.lang.Object writeReplace();
|
|
java.lang.Object readResolve();
|
|
}
|
|
|
|
# Firebase
|
|
-keep class com.google.firebase.** { *; }
|
|
-keep class com.google.android.gms.** { *; }
|
|
|
|
# OkHttp
|
|
-dontwarn okhttp3.**
|
|
-dontwarn okio.**
|
|
-dontwarn javax.annotation.**
|
|
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
|
|
|
# Retrofit
|
|
-dontwarn retrofit2.**
|
|
-keep class retrofit2.** { *; }
|
|
-keepattributes Signature
|
|
-keepattributes Exceptions
|
|
|
|
# Gson
|
|
-keepattributes Signature
|
|
-keepattributes *Annotation*
|
|
-dontwarn sun.misc.**
|
|
-keep class com.google.gson.** { *; }
|
|
-keep class * implements com.google.gson.TypeAdapterFactory
|
|
-keep class * implements com.google.gson.JsonSerializer
|
|
-keep class * implements com.google.gson.JsonDeserializer
|
|
```
|
|
|
|
## Build Process
|
|
|
|
### Build Commands
|
|
|
|
```bash
|
|
# Clean build
|
|
cd android && ./gradlew clean
|
|
|
|
# Build debug APK
|
|
./gradlew assembleDebug
|
|
|
|
# Build release APK
|
|
./gradlew assembleRelease
|
|
|
|
# Build release AAB (recommended for Play Store)
|
|
./gradlew bundleRelease
|
|
|
|
# Install debug APK
|
|
./gradlew installDebug
|
|
|
|
# Install release APK
|
|
./gradlew installRelease
|
|
```
|
|
|
|
### Build Variants
|
|
|
|
```gradle
|
|
android {
|
|
flavorDimensions "version"
|
|
|
|
productFlavors {
|
|
development {
|
|
dimension "version"
|
|
applicationIdSuffix ".dev"
|
|
versionNameSuffix "-dev"
|
|
buildConfigField "String", "API_BASE_URL", '"https://dev-api.saayam.com"'
|
|
resValue "string", "app_name", "Saayam Dev"
|
|
}
|
|
|
|
staging {
|
|
dimension "version"
|
|
applicationIdSuffix ".staging"
|
|
versionNameSuffix "-staging"
|
|
buildConfigField "String", "API_BASE_URL", '"https://staging-api.saayam.com"'
|
|
resValue "string", "app_name", "Saayam Staging"
|
|
}
|
|
|
|
production {
|
|
dimension "version"
|
|
buildConfigField "String", "API_BASE_URL", '"https://api.saayam.com"'
|
|
resValue "string", "app_name", "Saayam"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Google Play Console Setup
|
|
|
|
### Create Application
|
|
|
|
1. **Go to Google Play Console**
|
|
2. **Create Application**
|
|
- App name: Saayam
|
|
- Default language: English
|
|
- App or game: App
|
|
- Free or paid: Free
|
|
|
|
3. **App Content**
|
|
- Privacy Policy URL
|
|
- App category
|
|
- Content rating questionnaire
|
|
- Target audience
|
|
- Data safety form
|
|
|
|
### Upload Release
|
|
|
|
```bash
|
|
# Build release AAB
|
|
cd android && ./gradlew bundleRelease
|
|
|
|
# AAB file location
|
|
# android/app/build/outputs/bundle/release/app-release.aab
|
|
```
|
|
|
|
### Release Tracks
|
|
|
|
1. **Internal Testing**
|
|
- Upload AAB
|
|
- Add internal testers
|
|
- Release to internal testing
|
|
|
|
2. **Closed Testing**
|
|
- Create closed testing track
|
|
- Add test users via email lists
|
|
- Release to closed testing
|
|
|
|
3. **Open Testing**
|
|
- Create open testing track
|
|
- Set country availability
|
|
- Release to open testing
|
|
|
|
4. **Production**
|
|
- Review release
|
|
- Set rollout percentage
|
|
- Release to production
|
|
|
|
## Version Management
|
|
|
|
### Automated Version Bumping
|
|
|
|
**scripts/bump-android-version.sh:**
|
|
```bash
|
|
#!/bin/bash
|
|
|
|
VERSION_TYPE=${1:-patch} # major, minor, patch
|
|
BUILD_GRADLE="android/app/build.gradle"
|
|
|
|
# Get current version
|
|
CURRENT_VERSION=$(grep "versionName" $BUILD_GRADLE | sed 's/.*versionName "\(.*\)"/\1/')
|
|
CURRENT_CODE=$(grep "versionCode" $BUILD_GRADLE | sed 's/.*versionCode \(.*\)/\1/')
|
|
|
|
echo "Current version: $CURRENT_VERSION ($CURRENT_CODE)"
|
|
|
|
# Calculate new version
|
|
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_VERSION"
|
|
MAJOR=${VERSION_PARTS[0]}
|
|
MINOR=${VERSION_PARTS[1]}
|
|
PATCH=${VERSION_PARTS[2]}
|
|
|
|
case $VERSION_TYPE in
|
|
major)
|
|
MAJOR=$((MAJOR + 1))
|
|
MINOR=0
|
|
PATCH=0
|
|
;;
|
|
minor)
|
|
MINOR=$((MINOR + 1))
|
|
PATCH=0
|
|
;;
|
|
patch)
|
|
PATCH=$((PATCH + 1))
|
|
;;
|
|
esac
|
|
|
|
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
|
|
NEW_CODE=$((CURRENT_CODE + 1))
|
|
|
|
echo "New version: $NEW_VERSION ($NEW_CODE)"
|
|
|
|
# Update build.gradle
|
|
sed -i "s/versionCode $CURRENT_CODE/versionCode $NEW_CODE/" $BUILD_GRADLE
|
|
sed -i "s/versionName \"$CURRENT_VERSION\"/versionName \"$NEW_VERSION\"/" $BUILD_GRADLE
|
|
|
|
echo "Version updated successfully!"
|
|
```
|
|
|
|
### Git Tagging
|
|
|
|
```bash
|
|
# Create and push tag
|
|
git tag -a v1.0.0 -m "Release version 1.0.0"
|
|
git push origin v1.0.0
|
|
|
|
# List tags
|
|
git tag -l
|
|
|
|
# Delete tag
|
|
git tag -d v1.0.0
|
|
git push origin --delete v1.0.0
|
|
```
|
|
|
|
## Release Checklist
|
|
|
|
### Pre-Release Checklist
|
|
|
|
- [ ] Update version number
|
|
- [ ] Update changelog
|
|
- [ ] Run all tests
|
|
- [ ] Test on multiple devices
|
|
- [ ] Check ProGuard configuration
|
|
- [ ] Verify signing configuration
|
|
- [ ] Test release build locally
|
|
- [ ] Update app store metadata
|
|
- [ ] Prepare release notes
|
|
|
|
### Post-Release Checklist
|
|
|
|
- [ ] Monitor crash reports
|
|
- [ ] Check app store reviews
|
|
- [ ] Monitor performance metrics
|
|
- [ ] Update documentation
|
|
- [ ] Create git tag
|
|
- [ ] Notify team of release
|
|
- [ ] Plan next release
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Build Issues
|
|
|
|
```bash
|
|
# Clean and rebuild
|
|
cd android
|
|
./gradlew clean
|
|
./gradlew assembleRelease
|
|
|
|
# Clear React Native cache
|
|
npx react-native start --reset-cache
|
|
|
|
# Clear Metro cache
|
|
npx react-native start --reset-cache
|
|
|
|
# Reset Android build
|
|
cd android
|
|
rm -rf build/
|
|
rm -rf app/build/
|
|
./gradlew clean
|
|
```
|
|
|
|
### Keystore Issues
|
|
|
|
```bash
|
|
# Verify keystore
|
|
keytool -list -v -keystore your-keystore.keystore
|
|
|
|
# Check keystore alias
|
|
keytool -list -keystore your-keystore.keystore
|
|
|
|
# Export certificate
|
|
keytool -export -alias your-alias -keystore your-keystore.keystore -file certificate.crt
|
|
``` |