Intro
Let’s be honest.
Publishing an Android app to Google Play manually is already annoying…
but automating it? That’s where things usually get messy.
If you’ve ever tried, you probably ended up with:
- a random JSON key sitting in your repo 😬
- a CI pipeline you don’t fully trust
- or a setup you’re afraid to touch because “it works… somehow”
I’ve been there.
So this post is the version I wish I had earlier:
a clean, secure way to automate Google Play releases without hacks, without leaking secrets, and without overcomplicating everything.
The Key Idea
Instead of storing a Google service account key in your repo (please don’t), we use:
- Workload Identity Federation (WIF)
- Service account impersonation
In plain English:
Your CI logs into Google using short-lived tokens — no permanent secrets needed.
What You’ll Get
By the end of this:
- Your app builds automatically
- Your AAB gets uploaded to Google Play
- No credentials stored in your repo
- A setup you won’t be scared to touch later
Quick Setup (Copy & Paste)
1. Define variables
PROJECT_ID="your-project-id"
PROJECT_NUMBER="your-project-number"
POOL_ID="github"
PROVIDER_ID="ci-provider"
SERVICE_ACCOUNT_NAME="ci-publisher"
SERVICE_ACCOUNT_EMAIL="ci-publisher@your-project-id.iam.gserviceaccount.com"
REPO="your-org/your-repo"
PACKAGE_NAME="com.your.app"
2. Create service account
gcloud iam service-accounts create "$SERVICE_ACCOUNT_NAME" \
--project="$PROJECT_ID"
3. Create identity pool
gcloud iam workload-identity-pools create "$POOL_ID" \
--project="$PROJECT_ID" \
--location="global"
4. Connect your CI (OIDC)
gcloud iam workload-identity-pools providers create-oidc "$PROVIDER_ID" \
--project="$PROJECT_ID" \
--location="global" \
--workload-identity-pool="$POOL_ID" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository,attribute.ref=assertion.ref" \
--attribute-condition="assertion.repository == '$REPO'"
5. Allow impersonation
gcloud iam service-accounts add-iam-policy-binding \
"$SERVICE_ACCOUNT_EMAIL" \
--project="$PROJECT_ID" \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/$POOL_ID/attribute.repository/$REPO"
6. Enable Play API
gcloud services enable androidpublisher.googleapis.com \
--project="$PROJECT_ID"
7. Configure Play Console (manual)
- Go to Users and permissions
- Add the service account email
- Give access to your app
- Grant publish permissions (internal track is enough)
8. Add CI secrets
Workload Identity Provider
projects/YOUR_PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL/providers/PROVIDER
Service Account
ci-publisher@your-project-id.iam.gserviceaccount.com
9. Add CI workflow
name: Publish Android to Play
on:
push:
tags:
- "v*"
permissions:
contents: read
id-token: write
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@vX
- name: Authenticate
uses: google-github-actions/auth@v3
with:
workload_identity_provider: ${{ secrets.WIF_PROVIDER }}
service_account: ${{ secrets.SERVICE_ACCOUNT }}
- name: Build
run: ./gradlew bundleRelease
- name: Upload
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJson: ${{ steps.auth.outputs.credentials_file_path }}
packageName: com.your.app
releaseFiles: app/build/outputs/bundle/release/*.aab
track: internal
10. Trigger a release
git tag v1.0.0
git push origin v1.0.0
Debug Checklist
If something breaks, check this first:
- Is the provider pointing to the correct repo?
- Does your secret include
/providers/...? - Did you add
id-token: write? - Is the service account added in Play Console?
Common Mistakes
Auth works but upload fails
Play Console permissions are wrong.
Auth fails completely
Repository or branch does not match the provider condition.
Token errors
Missing id-token: write permission.
Final Thoughts
This setup might look complex at first…
but once it’s in place, it just works.
And more importantly:
- no leaked keys
- no fragile hacks
- no future headaches
Just a clean pipeline doing its job.
TL;DR
- Don’t store Google service account keys
- Use Workload Identity Federation
- Let CI impersonate a service account
- Upload with a GitHub Action
- Sleep better at night 😄
Top comments (0)