If you’re still manually running ./gradlew assembleRelease on your local machine and uploading the APK to a shared folder, you’re wasting precious development time. Learning how to setup GitHub Actions for android is one of the highest-leverage moves you can make for your mobile workflow. In my experience, moving to a CI/CD pipeline doesn’t just save time; it eliminates the ‘it works on my machine’ syndrome that plagues many Android teams.
Whether you are building a side project or managing complex mobile application lifecycle management tools, GitHub Actions provides a native, powerful way to automate your testing and deployment without needing to manage your own build servers.
Prerequisites
Before we dive into the YAML configuration, ensure you have the following ready:
- An Android project hosted on a GitHub repository.
- A basic understanding of Gradle (the build system Android uses).
- A Keystore file (if you plan to build signed production APKs).
- GitHub Secrets access to securely store your signing keys.
Step 1: Creating the Workflow File
GitHub Actions are defined in YAML files located in a specific directory of your repo. I recommend starting with a basic build check to ensure your environment is correct.
Create a file at .github/workflows/android.yml and add the following configuration:
name: Android CI
on:
push:
branches: [ "main", "develop" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
cache: 'gradle'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew assembleDebug
In this setup, I’m using ubuntu-latest because it’s faster and cheaper than macOS runners for standard Android builds. I also included the cache: 'gradle' option, which is critical. Without it, GitHub will redownload every single dependency on every run, adding minutes to your build time.
Step 2: Implementing Automated Testing
A build that compiles but crashes is useless. To make your pipeline truly effective, you need to run your unit tests. Add this step before your build step:
- name: Run Unit Tests
run: ./gradlew test
If you’re debating between different CI tools, you might be looking at Jenkins vs GitLab CI for mobile, but for most Android developers, the tight integration of GitHub Actions with the PR process makes it the superior choice for velocity.
Step 3: Handling Secrets for Signed Releases
To generate a production APK, you can’t check your .jks keystore file into version control. Here is how I handle this securely:
- Convert your keystore file to Base64:
base64 -i upload-keystore.jks. - Go to your GitHub Repo → Settings → Secrets and variables → Actions.
- Add the following secrets:
KEYSTORE(the base64 string),KEYSTORE_PASSWORD,KEY_ALIAS, andKEY_PASSWORD.
Now, update your workflow to decode the keystore and sign the app. As shown in the image below, you’ll need to ensure the keystore is written to the filesystem before Gradle runs.
- name: Decode Keystore
run: echo "${{ secrets.KEYSTORE }}" | base64 --decode > app/release.keystore
- name: Build Release
run: ./gradlew assembleRelease
env:
SIGNING_KEY_STORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
SIGNING_KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
SIGNING_KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
Step 4: Uploading the Artifact
Once the build is complete, you want to be able to download the APK without digging through logs. Use the upload-artifact action:
- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: app-release
path: app/build/outputs/apk/release/app-release.apk
Pro Tips for Android CI/CD
- Use Matrix Builds: If you support multiple JDK versions or API levels, use the
strategy: matrixfeature to run tests across all of them simultaneously. - Optimize Gradle: Add
-Dorg.gradle.daemon=falseto your Gradle commands in CI to prevent memory leaks on the runner. - Linting: Run
./gradlew lintas a separate job. This prevents style regressions from entering your main branch.
Troubleshooting Common Issues
Issue: Out of Memory (OOM) Errors
Android builds are memory-hungry. If your job fails with an OOM, add a gradle.properties file to your project or set an environment variable: org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m.
Issue: Permission Denied (gradlew)
This usually happens because the gradlew script lost its execution bit. Always include chmod +x gradlew in your workflow steps to be safe.
What’s Next?
Now that you know how to setup GitHub Actions for Android, the next step is automating your distribution. I recommend looking into Fastlane. You can integrate Fastlane into your GitHub Action to automatically push your build to the Google Play Console (Internal Testing track) as soon as a tag is pushed to the repository.