Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,11 +502,31 @@ class NetworkRepository @Inject constructor(
1. **Create Release Branch**: `git checkout -b release/vX.Y.Z` (e.g., `release/v1.17`)
2. **Update Version**: Increment `versionCode` and `versionName` in `app/build.gradle.kts`
3. **Update Release Notes**: Add new release section in `project-resources/google-play/release-notes.md`
4. **Build & Test**: Run `./gradlew clean assembleRelease bundleRelease`
4. **Build & Test**: Run `./gradlew clean assembleRelease bundleRelease` **on your local machine**
5. **Commit Changes**: Commit version bump and release notes on the release branch
6. **Create PR**: Open pull request from release branch to `main`
7. **Merge & Tag**: After PR approval, merge to `main` and create git tag `vX.Y.Z`
8. **Upload to Play Store**: Upload AAB to Google Play Console
8. **Upload to Play Store**: Upload the locally-built AAB (`app/release/app-release.aab`) to Google Play Console

### ⚠️ Critical: App Bundle Signing

**DO NOT upload release builds from GitHub Actions.** The release AAB must be built locally with the correct signing certificate registered in Google Play Console.

**Why:** GitHub Actions builds and local builds use different keystores, which causes signing key mismatch errors when uploading to Google Play Console. See [Issue #438](https://github.com/hossain-khan/android-remote-notify/issues/438) for details.

**Correct Process:**
- Build release bundle locally on your machine with correct `local.properties` keystore path
- Verify `local.properties` contains the correct keystore location and password
- Upload the generated `app/release/app-release.aab` directly from your local machine
- This ensures the bundle is signed with the certificate registered in Google Play

**Troubleshooting Signing Errors:**
If you encounter "Your App Bundle is signed with the wrong key" error:
1. Verify the SHA1 fingerprint in Google Play Console
2. Check that `local.properties` points to the correct keystore file
3. Ensure the keystore password is correct
4. Rebuild locally: `./gradlew clean bundleRelease`
5. Re-upload the newly generated AAB

### Version Management
- Update `versionCode` (increment by 1) and `versionName` in `app/build.gradle.kts`
Expand All @@ -519,7 +539,7 @@ class NetworkRepository @Inject constructor(
- [ ] Update version code and name in `app/build.gradle.kts`
- [ ] Update `release-notes.md` with new release section
- [ ] Check `local.properties` for correct keystore path and password
- [ ] Build release: `./gradlew clean assembleRelease bundleRelease`
- [ ] Build release **locally**: `./gradlew clean assembleRelease bundleRelease`
- [ ] Test release build locally
- [ ] Run full test suite: `./gradlew test`
- [ ] Check ProGuard/R8 obfuscation
Expand All @@ -528,17 +548,19 @@ class NetworkRepository @Inject constructor(
- [ ] Create pull request to `main`
- [ ] After merge, create git tag: `git tag -a vX.Y.Z -m "Release vX.Y.Z"`
- [ ] Push tag: `git push origin vX.Y.Z`
- [ ] Upload AAB to Firebase Test Lab
- [ ] Upload AAB to Google Play Console
- [ ] **Upload AAB to Google Play Console from your local machine** (not from GitHub Actions)
- Verify keystore SHA1 matches Google Play Console certificate fingerprint
- Upload from `app/release/app-release.aab`
- [ ] Create GitHub release from tag with release notes

## Troubleshooting

### Common Issues
1. **Build Failures**: Ensure `local.properties` contains required API keys
2. **WorkManager Issues**: Check device battery optimization settings
3. **Firebase Issues**: Verify `google-services.json` is present and valid
4. **Compose Issues**: Clean and rebuild project, check Compose compiler version
2. **Signing Key Mismatch**: If Google Play rejects your bundle with "signed with the wrong key" error, ensure you built locally with the correct keystore. Never upload AAB from GitHub Actions (see Issue #438)
3. **WorkManager Issues**: Check device battery optimization settings
4. **Firebase Issues**: Verify `google-services.json` is present and valid
5. **Compose Issues**: Clean and rebuild project, check Compose compiler version

### Debugging Tools
- Use `Timber.d()` for debug logging
Expand Down
87 changes: 80 additions & 7 deletions .github/workflows/android-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,25 @@ name: Android Release Build
# 1. Snapshot builds: Each time code changes on main branch
# 2. Release builds: When a GitHub release is published (automatically attaches APK and AAB to release)
# This allows developers and testers to easily download the latest release APK.
# AAB (Android App Bundle) is provided for Google Play Store distribution.
# AAB (Android App Bundle) is provided for GitHub release artifacts.
#
# See:
# ⚠️ IMPORTANT: App Bundle Signing for Google Play Store
# =====================================================
# The AAB built in this workflow is signed with a CI keystore, NOT the keystore registered
# in Google Play Console. DO NOT upload this AAB directly to Google Play Store.
#
# For Play Store releases, you MUST:
# 1. Build locally: ./gradlew bundleRelease
# 2. Verify local.properties has the correct keystore path and password
# 3. Upload app/release/app-release.aab from your local machine to Google Play Console
#
# See Issue #438 for more details on signing key mismatches:
# https://github.com/hossain-khan/android-remote-notify/issues/438
#
# References:
# - https://github.com/hossain-khan/android-remote-notify/tree/main/keystore
# - https://github.com/hossain-khan/android-remote-notify/blob/main/app/build.gradle.kts (signing configuration)
# - https://github.com/hossain-khan/android-remote-notify/blob/main/app/build.gradle.kts (signing config)
# - https://github.com/hossain-khan/android-remote-notify/.github/copilot-instructions.md (release process)

on:
push:
Expand Down Expand Up @@ -62,20 +76,48 @@ jobs:
id: keystore
run: |
if [ -z "${{ secrets.KEYSTORE_BASE64 }}" ]; then
echo "⚠️ KEYSTORE_BASE64 secret is not set, using debug keystore for signing"
echo "⚠️ WARNING: KEYSTORE_BASE64 secret is not set"
echo "Using debug keystore for signing instead of release keystore"
echo "This build should NOT be uploaded to Google Play Store"
echo "USE_RELEASE_KEYSTORE=false" >> $GITHUB_OUTPUT
else
echo "📝 Attempting to decode release keystore from base64..."
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore/remote-notify-release.keystore
if [ ! -s keystore/remote-notify-release.keystore ]; then
echo "❌ Failed to decode keystore file"
echo "❌ ERROR: Failed to decode keystore file or keystore is empty"
exit 1
fi
echo "✅ Release keystore decoded successfully"
# Verify keystore file
KEYSTORE_SIZE=$(stat -f%z keystore/remote-notify-release.keystore 2>/dev/null || stat -c%s keystore/remote-notify-release.keystore 2>/dev/null)
echo "✅ Release keystore decoded successfully (size: $KEYSTORE_SIZE bytes)"
echo "⚠️ NOTE: AAB signed with CI keystore, not Google Play registr​ed keystore"
echo "For Play Store uploads, use locally-built AAB with correct keystore"
echo "USE_RELEASE_KEYSTORE=true" >> $GITHUB_OUTPUT
fi

- name: Build Release APK and AAB
run: ./gradlew assembleRelease bundleRelease --parallel --daemon
run: |
echo "🏗️ Building Release APK and AAB..."
if [ "${{ steps.keystore.outputs.USE_RELEASE_KEYSTORE }}" = "true" ]; then
echo "📦 Signing with CI release keystore (NOT Google Play registered keystore)"
else
echo "📦 Signing with debug keystore (development only)"
fi

./gradlew assembleRelease bundleRelease --parallel --daemon

if [ -f app/build/outputs/apk/release/app-release.apk ]; then
APK_SIZE=$(stat -f%z app/build/outputs/apk/release/app-release.apk 2>/dev/null || stat -c%s app/build/outputs/apk/release/app-release.apk 2>/dev/null)
echo "✅ APK built successfully (size: $APK_SIZE bytes)"
fi

if [ -f app/build/outputs/bundle/release/app-release.aab ]; then
AAB_SIZE=$(stat -f%z app/build/outputs/bundle/release/app-release.aab 2>/dev/null || stat -c%s app/build/outputs/bundle/release/app-release.aab 2>/dev/null)
echo "✅ AAB built successfully (size: $AAB_SIZE bytes)"
echo "⚠️ REMINDER: This AAB is signed with CI keystore"
echo "⚠️ DO NOT upload this AAB to Google Play Store"
echo "✅ For Play Store, build locally: ./gradlew bundleRelease"
fi
env:
# Only set KEYSTORE_FILE when release keystore was decoded successfully
# Otherwise, build.gradle.kts will fall back to debug keystore
Expand Down Expand Up @@ -113,6 +155,20 @@ jobs:
path: artifact/remote-notify-v${{ steps.version.outputs.VERSION }}.aab
retention-days: 30

# ⚠️ IMPORTANT: Signing & Google Play Store Upload
# ================================================
# The APK and AAB built here are for GitHub releases and testing ONLY.
# They are signed with the CI keystore, NOT the keystore registered in Google Play Console.
#
# FOR GOOGLE PLAY STORE RELEASES:
# 1. Do NOT use the AAB from this workflow
# 2. Build locally on your machine: ./gradlew bundleRelease
# 3. Verify local.properties has correct keystore path and password
# 4. Verify keystore SHA1 fingerprint matches Google Play Console
# 5. Upload app/release/app-release.aab to Google Play Console from your local machine
#
# See Issue #438 and copilot-instructions.md for detailed release process.

# If this is running due to a GitHub release, attach the APK and AAB to the release
- name: Attach APK and AAB to GitHub Release
if: github.event_name == 'release'
Expand Down Expand Up @@ -182,3 +238,20 @@ jobs:
cat upload_response_aab.json
exit 1
fi

echo ""
echo "=================================="
echo "🔐 SIGNING NOTICE"
echo "=================================="
echo "✅ APK and AAB successfully built and attached to GitHub release"
echo "⚠️ These artifacts are signed with the CI keystore"
echo "⚠️ They are for testing and GitHub releases ONLY"
echo "❌ DO NOT upload to Google Play Store from this workflow"
echo ""
echo "📝 For Google Play Store releases:"
echo " 1. Build locally: ./gradlew bundleRelease"
echo " 2. Upload from local machine: app/release/app-release.aab"
echo ""
echo "📖 See .github/copilot-instructions.md for detailed release process"
echo "🔗 Issue #438: https://github.com/hossain-khan/android-remote-notify/issues/438"
echo "=================================="
Loading