- Overview
- Prerequisites
- Initial Play Console Setup
- Keystore Management
- Service Account Setup
- GitHub Secrets Configuration
- Deployment Process
- Release Tracks
- Staged Rollouts
- Troubleshooting
- Post-Deployment
- Best Practices
This guide provides step-by-step instructions for deploying CompareApp to the Google Play Store. The deployment process is automated via GitHub Actions, but requires initial setup of credentials and configurations.
- ✅ Google Play Console account ($25 one-time fee)
- ✅ Google Cloud Platform account (free tier available)
- ✅ GitHub account with repository access
- ✅ Android keystore file (for signing APKs)
- ✅ Java JDK 17+ (for keystore creation)
- ✅ Git (for version control)
- ✅ Text editor (for editing secrets)
- Basic understanding of Android app deployment
- Familiarity with GitHub Actions
- Basic command-line skills
- Go to Google Play Console
- Sign in with your Google account
- Accept the Developer Agreement
- Pay the $25 registration fee
- Complete your account details
- Click "Create app" in Play Console
- Fill in app details:
- App name: CompareApp (or your preferred name)
- Default language: English (United States)
- App or game: App
- Free or paid: Free
- Review declarations:
- ✅ Accept Play Developer Program Policies
- ✅ Confirm US export laws compliance
- Click "Create app"
Navigate to "Store presence" → "Main store listing" and fill in:
Short description (80 characters max):
Compare Uber and Bolt prices side-by-side instantly
Full description (4000 characters max):
CompareApp makes choosing the best ride-sharing service quick and easy. Simply enter your pickup and dropoff locations, tap Compare, and both Uber and Bolt will open side-by-side so you can see prices and features at a glance.
FEATURES
• Split Screen Comparison - View Uber and Bolt simultaneously
• Smart Location Entry - Type addresses or use current location
• Instant Price Comparison - See both apps with one tap
• Modern Design - Clean, intuitive Material Design interface
HOW IT WORKS
1. Enter your pickup location
2. Enter your dropoff location
3. Tap the Compare button
4. Compare prices and choose the best option
REQUIREMENTS
• Android 7.0 or higher
• Uber app installed
• Bolt app installed
Save time and money by comparing ride-sharing options before you book!
App icon:
- Size: 512 x 512 px
- Format: PNG (32-bit)
- No transparency
Feature graphic:
- Size: 1024 x 500 px
- Format: PNG or JPEG
Screenshots (minimum 2, up to 8):
- Size: 16:9 or 9:16 aspect ratio
- Min dimension: 320 px
- Max dimension: 3840 px
- Format: PNG or JPEG
- App category: Tools
- Tags: travel, transportation, price comparison
- Email: your-email@example.com
- Phone: (optional)
- Website: https://github.com/neteinstein/CompareUberVsBoltPriceApp
- Navigate to "Policy" → "App content"
- Click "Start questionnaire" under Content rating
- Enter your email address
- Select category: Utility, Productivity, Communication, or Other
- Answer questionnaire questions:
- Violence: No
- Sexual content: No
- Profanity: No
- Controlled substances: No
- Gambling: No
- User-generated content: No
- Save and submit
- Create a privacy policy (required for apps that access location data)
- Host it on a public URL (GitHub Pages, your website, etc.)
- In Play Console: "Policy" → "Privacy Policy"
- Enter your privacy policy URL
Sample Privacy Policy Content:
# Privacy Policy for CompareApp
Last updated: [Date]
## Information Collection
CompareApp does not collect, store, or transmit any personal information.
All location data is processed locally on your device and is only used to
create deep links to Uber and Bolt apps.
## Location Data
The app may request location permission to auto-fill your pickup location.
This data is never stored or transmitted to our servers.
## Third-Party Apps
When you use CompareApp to open Uber or Bolt, you are subject to those
apps' respective privacy policies.
## Contact
For questions about this privacy policy, contact: your-email@example.comNavigate to "Policy" → "Data safety" and declare:
Data collection:
- Location: Yes (approximate and precise)
- Purpose: App functionality
- Collection method: Required
- Not shared with third parties
- Not collected (only used, not stored)
Security practices:
- Data is encrypted in transit: Yes
- Users can request data deletion: N/A (no data stored)
- Committed to Google Play Families Policy: No
Important: Keep this file secure and backed up. If you lose it, you cannot update your app.
keytool -genkeypair -v \
-keystore release.keystore \
-alias compareapp \
-keyalg RSA \
-keysize 2048 \
-validity 10000Prompts:
- Keystore password: Choose a strong password (save it!)
- Key password: Choose a strong password (can be same as keystore)
- First and last name: Your name or company name
- Organizational unit: Your team/department
- Organization: Your company
- City: Your city
- State: Your state/province
- Country code: Two-letter country code (e.g., US)
Example:
Enter keystore password: MySecurePassword123!
Re-enter new password: MySecurePassword123!
What is your first and last name?
[Unknown]: John Doe
What is the name of your organizational unit?
[Unknown]: Development
What is the name of your organization?
[Unknown]: CompareApp Inc
What is the name of your City or Locality?
[Unknown]: San Francisco
What is the name of your State or Province?
[Unknown]: California
What is the two-letter country code for this unit?
[Unknown]: US
Is CN=John Doe, OU=Development, O=CompareApp Inc, L=San Francisco, ST=California, C=US correct?
[no]: yes
Enter key password for <compareapp>
(RETURN if same as keystore password):
Output: release.keystore file
-
Backup: Store keystore in multiple secure locations
- Encrypted cloud storage (Google Drive with encryption)
- Password manager (1Password, LastPass)
- Hardware security key
- Encrypted USB drive in safe
-
Never:
- Commit keystore to Git
- Email keystore
- Store keystore unencrypted
- Share keystore password in plain text
-
Password Management:
- Use a password manager
- Choose strong, unique passwords
- Never reuse passwords
- Document passwords securely
Linux/macOS:
base64 release.keystore | tr -d '\n' > keystore.base64.txt
cat keystore.base64.txtWindows PowerShell:
[Convert]::ToBase64String([IO.File]::ReadAllBytes("release.keystore")) | Out-File -FilePath keystore.base64.txt -NoNewline
Get-Content keystore.base64.txtWindows Command Prompt:
certutil -encode release.keystore keystore.base64.txt(Then manually remove header/footer lines)
Output: Long base64 string (copy this for GitHub secret)
Service accounts allow GitHub Actions to publish to Play Store without manual intervention.
- Go to Google Cloud Console
- Click "Select a project" → "New Project"
- Project name: CompareApp-PlayStore
- Click "Create"
- Wait for project creation (30-60 seconds)
- In Cloud Console, go to "APIs & Services" → "Library"
- Search for "Google Play Android Developer API"
- Click on it
- Click "Enable"
- Wait for API to enable (10-30 seconds)
- Go to "IAM & Admin" → "Service Accounts"
- Click "Create Service Account"
- Service account name:
compareapp-publisher - Service account ID:
compareapp-publisher(auto-generated) - Description: "GitHub Actions service account for Play Store publishing"
- Click "Create and Continue"
- Role: Skip this step (permissions granted in Play Console)
- Click "Continue" then "Done"
- Find your service account in the list
- Click on it to view details
- Go to "Keys" tab
- Click "Add Key" → "Create new key"
- Key type: JSON
- Click "Create"
- JSON file downloads automatically
- Save this file securely (you'll need it for GitHub)
JSON file structure:
{
"type": "service_account",
"project_id": "compareapp-playstore",
"private_key_id": "abc123...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "compareapp-publisher@compareapp-playstore.iam.gserviceaccount.com",
"client_id": "123456789...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
...
}- Open Google Play Console
- Go to "Setup" → "API access"
- Click "Link a project"
- Select your Cloud project: CompareApp-PlayStore
- Click "Link"
- Under "Service accounts", find
compareapp-publisher - Click "Grant access"
- Account permissions:
- ✅ View app information and download bulk reports (read-only)
- ✅ Create, edit, and delete draft apps
- App permissions:
- Select "CompareApp"
- ✅ Create and edit releases to testing tracks
- ✅ Release apps to testing tracks
- ✅ Release apps to production
- Click "Invite user"
- Click "Send invite"
Verification: Service account should show "Active" status
- Go to your repository on GitHub
- Click "Settings" tab
- Navigate to "Secrets and variables" → "Actions"
- Click "New repository secret"
Add each of these secrets:
- Name:
KEYSTORE_FILE - Value: Contents of
keystore.base64.txt(the base64-encoded keystore) - Example:
MIIJKgIBAzCCCOYGCSqGSIb3DQE...(very long string)
Steps:
- Open
keystore.base64.txtin a text editor - Copy the entire contents (may be 2000+ characters)
- Paste into GitHub secret value field
- Click "Add secret"
- Name:
KEYSTORE_PASSWORD - Value: The password you used when creating the keystore
- Example:
MySecurePassword123!
- Name:
KEY_ALIAS - Value: The alias you used when creating the keystore
- Example:
compareapp
- Name:
KEY_PASSWORD - Value: The key password (often same as keystore password)
- Example:
MySecurePassword123!
- Name:
PLAY_STORE_SERVICE_ACCOUNT_JSON - Value: Entire contents of the service account JSON file
Steps:
- Open the downloaded service account JSON file
- Copy the entire file contents (including
{and}) - Paste into GitHub secret value field
- Click "Add secret"
Important:
- Must be valid JSON
- Include all curly braces
- No extra whitespace at start/end
- Should be ~2500 characters
After adding all secrets, you should see:
KEYSTORE_FILE Updated now by you
KEYSTORE_PASSWORD Updated now by you
KEY_ALIAS Updated now by you
KEY_PASSWORD Updated now by you
PLAY_STORE_SERVICE_ACCOUNT_JSON Updated now by you
Google Play requires the first APK to be manually uploaded.
# Set environment variables
export KEYSTORE_FILE="/path/to/release.keystore"
export KEYSTORE_PASSWORD="your_password"
export KEY_ALIAS="compareapp"
export KEY_PASSWORD="your_key_password"
# Build release APK
./gradlew assembleRelease
# APK location:
# app/build/outputs/apk/release/app-release.apk- Go to Play Console → "Release" → "Testing" → "Internal testing"
- Click "Create new release"
- Upload APK:
- Click "Upload"
- Select
app-release.apk - Wait for upload to complete
- Release name: Version 1.0 (Build 1)
- Release notes (optional):
Initial release - Compare Uber and Bolt prices side-by-side - Modern Material Design interface - Smart location entry - Click "Review release"
- Click "Start rollout to Internal testing"
- Click "Rollout"
First release is now live on internal track!
After the first manual upload, all subsequent releases use GitHub Actions.
Edit app/build.gradle:
android {
defaultConfig {
versionCode 2 // Increment by 1
versionName "1.1" // Update as needed
}
}Commit and push:
git add app/build.gradle
git commit -m "Bump version to 1.1 (build 2)"
git push- Go to GitHub repository → "Actions" tab
- Select "Deploy to Play Store" workflow
- Click "Run workflow"
- Configure inputs:
- track: Select release track (internal/alpha/beta/production)
- inAppUpdatePriority: 0-5 (default: 2)
- userFraction: 0.0-1.0 (default: 1.0)
- Click "Run workflow"
- Click on the running workflow
- Watch progress in real-time
- Check for errors in log output
Successful deployment log:
✓ APK built and signed
✓ APK verified
✓ Uploaded to Play Store (internal track)
✓ Release status: completed
✓ User fraction: 100%
- Go to Play Console
- Navigate to selected track (internal/alpha/beta/production)
- Verify new version is listed
- Check release status
Purpose: Small team testing (up to 100 testers)
Use Cases:
- Initial testing
- QA team validation
- Quick iterations
Deployment Time: Minutes to hours
How to Access:
- Add testers via email in Play Console
- Testers get email with opt-in link
- Testers can install from Play Store
Purpose: Broader testing (custom tester list)
Use Cases:
- Early adopters
- Beta testers
- Feedback gathering
Deployment Time: Hours
How to Access: Same as internal testing
Purpose: Public or closed testing
Use Cases:
- Pre-release testing
- Soft launch in specific countries
- Gather public feedback
Options:
- Closed: Email list (up to 10,000 testers)
- Open: Anyone with link can join
Deployment Time: Hours
Purpose: Public release to all users
Use Cases:
- Official launch
- General availability
Deployment Time: Hours to days (review may be required)
Staged Rollout: Recommended (10% → 25% → 50% → 100%)
Promote releases between tracks in Play Console:
Internal → Alpha → Beta → Production
↓ ↓ ↓ ↓
Minutes Hours Hours Hours-Days
How to Promote:
- Go to source track (e.g., Beta)
- Click "Promote release"
- Select target track (e.g., Production)
- Configure rollout
- Click "Promote"
Release app to a percentage of users, gradually increasing over time.
- ✅ Catch critical bugs before full release
- ✅ Monitor crash rates with limited impact
- ✅ Gather feedback from real users
- ✅ Reduce risk of widespread issues
Production releases:
| Day | User Fraction | Users (example) | Action |
|---|---|---|---|
| 1 | 0.1 (10%) | 100 users | Deploy, monitor closely |
| 2-3 | 0.25 (25%) | 250 users | Check crash rates |
| 4-5 | 0.5 (50%) | 500 users | Review feedback |
| 6-7 | 1.0 (100%) | All users | Full rollout |
Pause between stages if:
- Crash rate > 1%
- ANR rate > 0.5%
- Negative reviews spike
- Critical bug reported
# Run workflow with:
track: production
userFraction: 0.1 # 10%Option 1: Via GitHub Actions
Re-run workflow with higher percentage:
track: production
userFraction: 0.5 # 50%Option 2: Via Play Console
- Go to "Release" → "Production"
- Click "Manage rollout"
- Select "Increase rollout"
- Choose new percentage
- Click "Update rollout"
If critical issue found:
Via Play Console:
- Go to production track
- Click "Manage rollout"
- Click "Halt rollout"
- New users won't get update (existing users keep it)
Fix and Re-release:
- Increment version code
- Fix bug
- Deploy new version
- Resume rollout
Cause: KEYSTORE_FILE secret not set or invalid
Solution:
- Verify secret is set in GitHub
- Re-encode keystore:
base64 release.keystore | tr -d '\n'
- Update GitHub secret
- Re-run workflow
Cause: Wrong KEYSTORE_PASSWORD or KEY_PASSWORD
Solution:
- Test password locally:
keytool -list -v -keystore release.keystore
- Update GitHub secret with correct password
- Re-run workflow
Cause: Version code not incremented
Solution:
// app/build.gradle
versionCode 3 // IncrementCause: Service account JSON incorrect or expired
Solution:
- Download new service account JSON from Cloud Console
- Update
PLAY_STORE_SERVICE_ACCOUNT_JSONsecret - Re-run workflow
Cause: Service account lacks required permissions
Solution:
- Go to Play Console → API access
- Click on service account
- Verify permissions:
- ✅ Create and edit releases
- ✅ Release to production
- Save changes
- Wait 5-10 minutes for propagation
- Re-run workflow
Cause: Package name in workflow doesn't match Play Console
Solution: Verify in workflow file:
packageName: org.neteinstein.compareapp # Must match Play ConsoleCauses:
- App not published to production
- Store listing incomplete
- Content rating incomplete
Solution:
- Complete all setup steps (store listing, content rating, etc.)
- Publish to production track
- Wait 2-24 hours for indexing
What it means: Google is reviewing your app
Timeline: Usually 1-7 days
Actions:
- Wait patiently
- Don't upload new versions during review
- Check for emails from Google Play
Check: "Quality" → "Android vitals"
Key Metrics:
- Crash rate: Should be < 1%
- ANR rate: Should be < 0.5%
- Excessive wakeups: Monitor battery usage
Check: "Quality" → "Crashes & ANRs"
Actions:
- Review crash stack traces
- Prioritize by affected users
- Fix high-impact crashes first
Check: "Quality" → "Reviews"
Best Practices:
- Respond to reviews (boosts rating)
- Address common complaints
- Thank positive reviewers
GitHub releases are automatically generated with dynamic release notes based on merged pull requests and commits. When you push to the main branch:
- The release workflow automatically creates a GitHub release
- Release notes are generated from commits since the last release
- Changes are categorized by labels (features, bug fixes, documentation, etc.)
To customize how changes are categorized, edit .github/release.yml.
Important: Play Store release notes are NOT automated and must be manually updated before each deployment.
Before deploying to Play Store:
- Review the CHANGELOG.md file in the repository root
- Check recent GitHub Releases for changes
- Update
distribution/whatsnew/en-US.txtwith user-facing changes - Ensure the content is under 500 characters (Play Store limit)
- Optionally add translations in other language files (e.g.,
es-ES.txt,fr-FR.txt)
Example distribution/whatsnew/en-US.txt:
Version 1.1
• Added support for current location
• Improved geocoding accuracy
• Fixed split screen issues
• Performance improvements
Character limit: 500 per language
Keep the CHANGELOG.md file updated with notable changes:
- Add entries to the
[Unreleased]section as you make changes - When releasing a new version, move unreleased changes to a new version section
- Follow the Keep a Changelog format
- Use categories: Added, Changed, Deprecated, Removed, Fixed, Security
Improve Discoverability:
- Keywords: Add relevant keywords to description
- "ride sharing", "price comparison", "uber", "bolt"
- Screenshots: Show key features
- Feature graphic: Eye-catching design
- Icon: Professional, recognizable
- Ratings: Encourage happy users to rate
Versioning Strategy:
versionCode: Integer that increments each release (1, 2, 3...)
versionName: Semantic version (1.0, 1.1, 2.0...)
When to Increment:
versionCode: Every release (required by Play Store)versionName: Major/minor/patch changes (user-facing)
Before deploying to production:
- All tests passing
- Lint checks pass
- Version code incremented
- Release notes written
- Tested on internal track
- No critical bugs
- Crash rate < 1%
- Review feedback from beta testers
Protect Sensitive Data:
- ✅ Never commit keystore
- ✅ Never commit passwords
- ✅ Rotate service account keys yearly
- ✅ Use GitHub secret scanning
- ✅ Review workflow logs for leaks
Keystore Backup:
- Store in 3+ locations
- Use encryption
- Document password securely
- Test restore process
User Communication:
- In-app changelog for major updates
- Respond to Play Store reviews
- Social media announcements
- Email newsletter (if applicable)
Team Communication:
- Document deployment dates
- Share release notes
- Coordinate with support team
- Monitor metrics together
-
Halt Rollout (if staged):
- Play Console → Manage rollout → Halt
-
Assess Impact:
- Check crash reports
- Review user feedback
- Estimate affected users
-
Fix:
- Create hotfix branch
- Fix bug
- Test thoroughly
- Increment version code
-
Deploy Hotfix:
# Bump version versionCode 4 versionName "1.1.1" # Commit git commit -m "Hotfix: Critical bug fix" git push # Deploy via GitHub Actions # track: production # userFraction: 1.0 (override previous)
-
Monitor:
- Watch crash rate
- Check user reviews
- Verify fix effectiveness
Prevention is Key: This cannot be recovered
If Lost:
- Cannot update existing app
- Options:
- Publish as new app (lose users)
- Contact Google Play support (rarely helps)
Prevention:
- Multiple encrypted backups
- Document storage locations
- Test backup restoration
- Use password manager
Check keystore details:
keytool -list -v -keystore release.keystoreVerify APK signature:
jarsigner -verify -verbose -certs app-release.apkGet APK info:
aapt dump badging app-release.apk- Play Console
- Google Cloud Console
- Android App Signing
- Google Play Publishing API
- Play Store Policies
Google Play Support:
- Help Center
- Email: Via Play Console
- Phone: Enterprise support only
GitHub Actions Support: