Android auto release
Claude Code skills: auto-detect & start any app, README generator, Docker Hub push, LinkedIn posts, NotebookLM slides, secret scanner, mobile responsiveness testing | Install with npx skills add
npx -y skills add alfredang/skills --skill android-auto-releaseAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Set up CI/CD that auto-builds, signs, and uploads a native Android app (AAB) to the Google Play Console on every push to main (GitHub Actions on ubuntu). The build is signed with your release keystore (from GitHub secrets) and uploaded to a chosen track (internal / closed / production) via a Play Developer API service account, with release notes pulled from distribution/whatsnew (or CHANGELOG.md). Handles auto-incrementing versionCode, the "first upload must be manual" rule, secrets, and the Play API gotchas. Use when a user wants automated Play Store releases / "auto submit to Play on push" / a release pipeline for an Android Gradle app.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
7.6 KB, as published. Nobody here has run it
Android Auto-Release — GitHub Actions → Google Play
Build, sign, and upload an Android AAB to Google Play automatically on every push to
main. Runs on ubuntu-latest. Uploads via the Play Developer API using a service
account, so no manual console clicks per update.
First release is manual. The Play Developer API can only update an app that already has at least one AAB on a track. Do the first submission with the
android-app-submissionskill (console UI), then this pipeline handles every update after.
What you need (one-time setup)
1. A Play service account with Play Console access
- Google Cloud Console → the project linked to Play → IAM & Admin → Service Accounts → Create. No roles needed in GCP. Create a JSON key and download it.
- Play Console → Setup → API access → link the project if needed → grant the service account app access with at least Release to testing tracks / Release apps to production (or "Release manager"). Wait a few minutes for propagation.
2. GitHub repository secrets (Settings → Secrets and variables → Actions)
| Secret | What |
|---|---|
PLAY_SERVICE_ACCOUNT_JSON | the full contents of the service-account JSON key |
KEYSTORE_BASE64 | base64 -i keystore/<app>-release.jks (one line) |
KEYSTORE_PASSWORD | keystore store password |
KEY_ALIAS | key alias |
KEY_PASSWORD | key password (== store password for PKCS12) |
MAPS_API_KEY | (if used) Google Maps Android key |
LTA_DATAMALL_ACCOUNT_KEY | (if used) any app data key surfaced via BuildConfig |
NEVER commit the keystore or JSON. They live only in GitHub secrets. Keep an offline backup of the keystore — losing it means you can never update the app again.
Make the Gradle build CI-friendly
- versionCode must be unique and monotonic. Let CI override it from the run number so you
never hand-bump it. In
app/build.gradle.kts:val ciVersionCode = (project.findProperty("versionCodeOverride") as String?)?.toIntOrNull() defaultConfig { versionCode = ciVersionCode ?: 2 // local default versionName = "1.1" } - Signing already reads
local.properties(per the submission skill). The workflow writes alocal.propertiesfrom secrets so the existingRELEASE_*wiring just works.
The workflow .github/workflows/android-release.yml
name: Android Release to Play
on:
push:
branches: [ main ]
paths-ignore: [ '**.md', 'docs/**', '.claude/**' ]
workflow_dispatch:
inputs:
track:
description: 'Play track'
default: 'internal'
type: choice
options: [ internal, alpha, beta, production ]
concurrency:
group: android-release
cancel-in-progress: false
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with: { distribution: temurin, java-version: '17' }
- name: Decode keystore
run: |
mkdir -p keystore
echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > keystore/release.jks
- name: Write local.properties
run: |
{
echo "sdk.dir=$ANDROID_SDK_ROOT"
echo "MAPS_API_KEY=${{ secrets.MAPS_API_KEY }}"
echo "LTA_DATAMALL_ACCOUNT_KEY=${{ secrets.LTA_DATAMALL_ACCOUNT_KEY }}"
echo "RELEASE_STORE_FILE=keystore/release.jks"
echo "RELEASE_STORE_PASSWORD=${{ secrets.KEYSTORE_PASSWORD }}"
echo "RELEASE_KEY_ALIAS=${{ secrets.KEY_ALIAS }}"
echo "RELEASE_KEY_PASSWORD=${{ secrets.KEY_PASSWORD }}"
} > local.properties
- name: Release notes (from CHANGELOG → whatsnew)
run: |
mkdir -p distribution/whatsnew
# take the bullet lines of the top CHANGELOG entry, cap at 500 chars (Play limit)
awk '/^## \[/{n++} n==1 && /^- /{print}' CHANGELOG.md | head -c 480 \
> distribution/whatsnew/whatsnew-en-US || true
[ -s distribution/whatsnew/whatsnew-en-US ] || echo "Bug fixes and improvements." \
> distribution/whatsnew/whatsnew-en-US
- name: Build signed AAB
run: ./gradlew :app:bundleRelease -PversionCodeOverride=$(( ${{ github.run_number }} + 100 ))
- name: Upload to Google Play
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: <APPLICATION_ID> # e.g. com.alfredang.sgevcharging
releaseFiles: app/build/outputs/bundle/release/*.aab
track: ${{ github.event.inputs.track || 'internal' }}
status: completed
whatsNewDirectory: distribution/whatsnew
Notes:
+ 100offsets the run number so CI versionCodes stay above any you uploaded manually (set the offset above your last manual versionCode).- Track: default
internalrolls out fastest. Usealphafor your closed track (Play's API name for the default closed track isalpha).productiontriggers full review. status: completedrolls out immediately to the track's testers; usedraftif you want to finish/submit manually in the console.- The action fails loudly if the versionCode already exists or the service account lacks permission — read its log; those are the two most common failures.
Deploy
# add the override hook to app/build.gradle.kts (see above), then:
git add app/build.gradle.kts .github/workflows/android-release.yml distribution/whatsnew/
git commit -m "ci: auto-release to Google Play on push to main"
git push origin main
gh workflow enable "Android Release to Play" 2>/dev/null || true
gh secret set PLAY_SERVICE_ACCOUNT_JSON < service-account.json
gh secret set KEYSTORE_BASE64 < <(base64 -i keystore/<app>-release.jks)
gh secret set KEYSTORE_PASSWORD --body '<pw>'
gh secret set KEY_ALIAS --body '<alias>'
gh secret set KEY_PASSWORD --body '<pw>'
# plus MAPS_API_KEY / LTA_DATAMALL_ACCOUNT_KEY if used
gh workflow run "Android Release to Play" # test once via dispatch
gh run watch
Gotchas
- "APK/AAB not allowed: version code already used" → bump the offset or you re-ran a build; versionCode must strictly increase.
- "The caller does not have permission" → service account not granted app access in Play Console, or you're targeting an app with no first manual release.
- "Package not found" → wrong
packageName, or first release never done in the console. - Closed track name in the API is
alpha;betais the open/closed beta; custom tracks use their track id.internalis the Internal testing track (fast, up to 100 testers). - Keep
paths-ignoreso doc-only commits don't burn a versionCode. - This pairs with
android-app-submission(first manual release + policy/store setup) andandroid-feedback-about(in-app About/Feedback UI). MaintainCHANGELOG.mdso the whatsnew notes stay meaningful.