MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Learn how to streamline your Android app deployment pipeline using GitHub Actions and Fastlane, eliminating manual steps and reducing release errors.
On this page
Manual Android app releases are slow, error-prone, and eat up valuable development time. By automating the entire process with GitHub Actions and Fastlane, you can deploy updates in minutes instead of hours—freeing you to focus on building features. As a solo developer managing 22+ apps, I’ve cut my release overhead by 80% using this exact workflow.
The pain points are clear: updating version codes, signing builds, uploading to Play Store—each step introduces potential failures. When I missed a version bump during a critical bug fix, it cost me 48 hours of app store review time. Automation solves this by creating a repeatable pipeline that handles versioning, signing, and deployment consistently.
GitHub Actions runs your automation directly in your repository. For Android releases, we’ll create a workflow that triggers on push to your release branch. Start by creating
.github/workflows/release.ymlname: Android Release Pipeline
on:
push:
branches: [ "main" ]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle dependencies
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Grant gradlew execute permission
run: chmod +x gradlew
- name: Build release APK
run: ./gradlew assembleRelease
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.1
bundler-cache: true
- name: Run Fastlane release
run: bundle exec fastlane deploy
env:
GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }}
KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}This workflow handles environment setup, dependency caching, and executes Fastlane commands. The
env[!TIP] Add
trigger to manually trigger releases from GitHub’s Actions tab—perfect for hotfixes when you can’t push to main.codeworkflow_dispatch
Fastlane transforms complex deployment tasks into simple Ruby commands. Initialize it in your project root with:
fastlane initThen edit the generated
Fastfiledefault_platform(:android)
platform :android do
desc "Deploy new version to Play Store"
lane :deploy do
# Automatically increment version code
increment_version_code(
gradle_file_path: "app/build.gradle",
bump_type: "patch"
)
# Update version name (e.g., 1.0.0)
gradle(
task: "bumpVersionName",
gradle_command: "bumpVersionName"
)
# Build signed APK
gradle(
task: "assemble",
build_type: "Release",
flags: "-x bundleRelease"
)
# Sign APK with keystore
sign_apk(
apk_path: "app/build/outputs/apk/release/app-release-unsigned.apk",
keystore_path: "keystore.jks",
keystore_password: ENV["KEYSTORE_PASSWORD"],
key_alias: ENV["KEY_ALIAS"],
key_password: ENV["KEY_PASSWORD"]
)
# Upload to Play Store
upload_to_play_store(
track: "production",
release_status: "draft",
aab: "app/build/outputs/bundle/release/app-release.aab",
package_name: "com.your.package.name",
json_key_data: ENV["GOOGLE_PLAY_JSON_KEY"]
)
# Commit version bump
git_commit(
path: ["app/build.gradle"],
message: "Bump version to #{gradle_task("printVersionName")}"
)
push_to_git_remote
end
endThis lane handles version management, signing, and Play Store uploads. The
git_commit[!WARNING] Never commit keystore files to version control. Store them as GitHub secrets instead—exposing keys can compromise your app’s security.
Secure credential management is non-negotiable. Here’s how to handle sensitive data:
| Credential Type | Storage Method | Access in Fastlane |
|---|---|---|
| Keystore | GitHub Secrets | code |
| Play API Key | GitHub Secrets | code |
| Git Token | GitHub Secrets | code |
For version management, automate incrementation based on semantic versioning:
lane :bump_version_name do
current_version = gradle_task("printVersionName")
new_version = semantic_bump(current_version, bump_type: "patch")
replace_in_file(
path: "app/build.gradle",
replace: "versionName '#{current_version}'",
with: "versionName '#{new_version}'"
)
end[!NOTE] Always test your workflow on the internal or beta track before deploying to production. This catches signing issues or build failures before affecting users.
This workflow has transformed my release process. I can now push a commit and have a new live version ready for review in under 20 minutes—no more manual signing, no more forgotten version bumps, no more last-minute stress.
Sudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
ReadPrivate dream journal — structured entry capture, pattern tagging, and optional Claude-powered insight generation. All data stays on-device by default.
ReadWorkout tracker — exercise logging with set/rep/weight history, goal progression, and local Room DB persistence. No account, no cloud sync required.
Read