Skip to content

Instantly share code, notes, and snippets.

@husnaintahir
Created October 2, 2025 15:01
Show Gist options
  • Select an option

  • Save husnaintahir/41636cea2d0ffd163a52e522080fc24d to your computer and use it in GitHub Desktop.

Select an option

Save husnaintahir/41636cea2d0ffd163a52e522080fc24d to your computer and use it in GitHub Desktop.
CI/CD for React Native Android: Build, Sign, Version, and Upload to Google Play with GitHub Actions
name: Android Signed Build & Upload
on:
workflow_dispatch:
inputs:
buildType:
description: "Select build type"
required: true
default: "aab"
type: choice
options:
- aab
- apk
versionCode:
description: "Build number (integer)"
required: true
default: "346"
versionName:
description: "Version name (e.g. 1.0.0)"
required: true
default: "0.1.4"
release:
description: "Upload to Google Play?"
required: true
default: "false"
type: choice
options:
- "true"
- "false"
track:
description: "Google Play track"
required: true
default: "internal"
type: choice
options:
- internal
- alpha
- beta
- production
jobs:
build-apk:
name: Build Signed APK
if: ${{ github.event.inputs.buildType == 'apk' }}
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
# 📝 Update versionCode & versionName in build.gradle
- name: Update Android version
run: |
sed -i "s/versionCode [0-9]\+/versionCode ${{ github.event.inputs.versionCode }}/" android/app/build.gradle
sed -i "s/versionName \".*\"/versionName \"${{ github.event.inputs.versionName }}\"/" android/app/build.gradle
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install CMake, Ninja & NDK
run: |
sudo apt-get update
sudo apt-get install -y ninja-build cmake
sdkmanager "cmake;3.22.1" "ndk;25.2.9519653"
- name: Cache Gradle
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: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm install --legacy-peer-deps
- name: Decode and save keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
- name: Make gradlew executable
run: chmod +x android/gradlew
- name: Clean build
working-directory: android
run: ./gradlew clean
- name: Assemble Signed Release APK
working-directory: android
env:
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android/app/release.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/25.2.9519653
run: ./gradlew assembleRelease --no-daemon --max-workers=2 -x lint -x test
- name: Upload Signed APK
uses: actions/upload-artifact@v4
with:
name: signed-app-release-apk
path: android/app/build/outputs/apk/release/app-release.apk
build-aab:
name: Build Signed AAB
if: ${{ github.event.inputs.buildType == 'aab' }}
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Update Android version
run: |
sed -i "s/versionCode [0-9]\+/versionCode ${{ github.event.inputs.versionCode }}/" android/app/build.gradle
sed -i "s/versionName \".*\"/versionName \"${{ github.event.inputs.versionName }}\"/" android/app/build.gradle
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 18
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: 17
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Install CMake, Ninja & NDK
run: |
sudo apt-get update
sudo apt-get install -y ninja-build cmake
sdkmanager "cmake;3.22.1" "ndk;25.2.9519653"
- name: Cache Gradle
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: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-
- name: Install dependencies
run: npm install --legacy-peer-deps
- name: Decode and save keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/release.keystore
- name: Make gradlew executable
run: chmod +x android/gradlew
- name: Clean build
working-directory: android
run: ./gradlew clean
- name: Bundle Signed Release AAB
working-directory: android
env:
ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/android/app/release.keystore
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_NDK_HOME: /usr/local/lib/android/sdk/ndk/25.2.9519653
run: ./gradlew bundleRelease --no-daemon -x lint -x test
- name: Upload AAB artifact
uses: actions/upload-artifact@v4
with:
name: app-release-aab
path: android/app/build/outputs/bundle/release/app-release.aab
upload-to-play:
name: Upload to Google Play
runs-on: ubuntu-latest
needs: build-aab
if: ${{ github.event.inputs.release == 'true' }}
steps:
- name: Download AAB artifact
uses: actions/download-artifact@v4
with:
name: app-release-aab
path: ./release
- name: Decode service account key
run: echo "${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}" | base64 --decode > service_account.json
- name: Upload to Google Play
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJson: service_account.json
packageName: com.example.app # <-- replace with your app ID
releaseFiles: ./release/app-release.aab
track: ${{ github.event.inputs.track }}
status: completed
releaseName: v${{ github.event.inputs.versionName }} - b${{ github.event.inputs.versionCode }}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment