Internal testing React Native Android apps
Tags: reactnative, android, githubactions
Published: 2023-01-21 17:34:01

---
title: Internal testing React Native Android apps
published: true
tags: ['reactnative', 'android', 'githubactions']
canonical_url: https://sevic.dev/notes/internal-testing-react-native-android/
---

Internal testing on Google Play Console is used for testing new app versions before releasing them to the end users. This post covers the main notes from setting up the app (on the Google Play Console) to automatic uploads.

### Prerequisites

- bootstrapped app
- installed Android studio
- verified developer account on Google Play Console
- paid one-time fee (25$)

### Google Play Console setup

Create an app with the essential details, such as app name, default language, and app type, and choose if it is paid or free.

#### Testers

Add email addresses to the email list.

### Release

#### Signing config

Generate a private signing key with a password using `keytool`

```bash
sudo keytool -genkey -v -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
```

Move the generated file to `android/app` directory.

Edit `~/.gradle/gradle.properties` to add the following keys and replace the alias and password with the correct values.

```properties
MYAPP_UPLOAD_STORE_FILE=my-upload-key.keystore
MYAPP_UPLOAD_KEY_ALIAS=my-key-alias
MYAPP_UPLOAD_STORE_PASSWORD=*****
MYAPP_UPLOAD_KEY_PASSWORD=*****
```

Edit `android/app/build.gradle` to add the release signing config, which uses the generated key.

```gradle
android {
  ...
  defaultConfig { ... }
  signingConfigs {
    ...
    release {
      if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
          storeFile file(MYAPP_UPLOAD_STORE_FILE)
          storePassword MYAPP_UPLOAD_STORE_PASSWORD
          keyAlias MYAPP_UPLOAD_KEY_ALIAS
          keyPassword MYAPP_UPLOAD_KEY_PASSWORD
      }
    }
  }
  buildTypes {
    ...
    release {
      ...
      signingConfig signingConfigs.release
    }
  }
}
```

#### Versioning

Update `versionCode` and `versionName` fields in `android/app/build.gradle` file before generating the bundle. The version code should be incremented by 1.

```gradle
android {
  ...
  defaultConfig {
    ...
    versionCode 2
    versionName "1.1.0"
    ...
  }
...
```

#### Android App Bundle (aab file)

Generate the Android app bundle with the following command.

```bash
cd android
./gradlew bundleRelease
```

`android/app/build/outputs/bundle/release/app-release.aab` is the path for the generated file.

### Manual uploads

Upload the `aab` file and write the release name and notes. The link for downloading the app should be available on the Testers tab.

### Automatic uploads

This section configures the necessary steps for the pipeline with [Github actions](https://sevic.dev/notes/github-actions-101/).

#### Versioning

Version apps with `np` and `react-native-version` packages running `np` script.

```json
// package.json
{
  "scripts": {
    "np": "np --no-publish",
    "postversion": "react-native-version -t android"
  },
  "repository": {
    "type": "git",
    "url": "<REPOSITORY_URL>"
  }
}
```

#### Signing config

Remove the release signing config from `android/app/build.gradle` file. An app will be bundled first and signed after that.

#### Credentials

Get the signing key with the following command and set it as `ANDROID_SIGNING_KEY` Github action secret in the Settings &#8594; Secrets &#8594; Actions page.

```bash
cd android/app
openssl base64 < my-upload-key.keystore  | tr -d '\n' | tee my-upload-key.keystore.base64.txt
```

Reuse credentials from `~/.gradle/gradle.properties` file and set them as Github secrets.

- MYAPP_UPLOAD_STORE_PASSWORD &#8594; ANDROID_KEY_STORE_PASSWORD
- MYAPP_UPLOAD_KEY_ALIAS &#8594; ANDROID_ALIAS
- MYAPP_UPLOAD_KEY_PASSWORD &#8594; ANDROID_KEY_PASSWORD

For automatic uploads, create a service account by following the next steps.

- go to Google Play console &#8594; Setup &#8594; API Access &#8594; Google Cloud project, create a Google Cloud project, and link it
- on the same page, go to Credentials &#8594; Service accounts heading and click on `Learn how to create service accounts`, follow the mentioned steps
  - create a service account with a service account user role
  - click on Actions &#8594; Manage keys button and create JSON key, which will be downloaded
- set downloaded JSON file as `ANDROID_SERVICE_ACCOUNT_JSON_TEXT` Github secret

#### Pipeline

The following pipeline sets up the necessary tools, does CI checks (linting, testing, audit), generates the app bundle, signs it, and uploads it to the Google Play console.

```yml
name: Android Build

on:
  push:
    branches:
      - release

jobs:
  android-build:
    name: Android Build
    runs-on: ubuntu-latest

    steps:
      - name: Check out Git repository
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v3
        with:
          java-version: 18
          distribution: temurin

      - name: Set up Android SDK
        uses: android-actions/setup-android@v3

      - name: Use Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci

      - run: npm run lint

      - run: npm test

      - run: npm audit

      - name: Make Gradlew Executable
        run: cd android && chmod +x ./gradlew

      - name: Generate App Bundle
        run: |
          cd android && ./gradlew clean && \
          ./gradlew bundleRelease --no-daemon

      - name: Sign App Bundle
        id: sign_aab
        uses: r0adkll/sign-android-release@v1
        with:
          releaseDirectory: android/app/build/outputs/bundle/release
          signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}
          alias: ${{ secrets.ANDROID_ALIAS }}
          keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}
          keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}

      - name: Upload App Bundle to Google Play
        uses: r0adkll/upload-google-play@v1
        with:
          serviceAccountJsonPlainText: ${{ secrets.ANDROID_SERVICE_ACCOUNT_JSON_TEXT }}
          packageName: com.flatmeapp
          releaseFiles: android/app/build/outputs/bundle/release/*.aab
          track: internal
          status: draft
          inAppUpdatePriority: 2
```

Edit the draft release and roll it out.

### Troubleshooting

- In case of a problem with signatures not matching the previously installed version, uninstall the app with the following commands.

```bash
adb devices
# adb -s <DEVICE_KEY> uninstall <PACKAGE_NAME>
adb -s emulator-5554 uninstall "com.yourapp"
```

- If the link for downloading the app installs some old version, clear the cache and data of the Google Play Store app on your device

### Boilerplate

Here is the [link](https://sevic.dev/react-native-starter?ref=devto) to the boilerplate I use for the development.