iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🔥

[React Native] Distributing to Firebase App Distribution via GitHub Actions (Android Only)

に公開

Overview

In this article, I will walk through the process of distributing an existing React Native app—which currently only runs on Expo Go—to Firebase App Distribution specifically for Android.

This is a method to generate, build, and distribute an Android project without using EAS.

Environment

The environments assumed for this walk-through are as follows. In this guide, we will proceed with the goal of distributing the DEV version of the app.

DEV PROD
App Name AppName (Dev) AppName
BundleID com.my.app.dev com.my.app

Preparation

Generating the android Directory

Generate the android directory using the following command. The -p flag specifies the platform (android in this case), and we explicitly specify the use of yarn.

expo prebuild -p android --yarn

prebuild creates the android/ directory using the Continuous Native Generation (CNG) mechanism.

Modifying android/app/build.gradle

Add the flavor and signingConfigs settings.

android {
    // ....
    flavorDimensions "flavor-type"

    productFlavors {
        dev {
            dimension "flavor-type"
            applicationId "com.my.app.dev"
            resValue "string", "app_name", "AppName (Dev)"
        }
        prod {
            dimension "flavor-type"
            applicationId "com.my.app"
            resValue "string", "app_name", "AppName"
        }
    }
    signingConfigs {
        release {
            if (System.getenv()["CI"]) {
                storeFile file(System.getenv()["KEYSTORE_PATH"])
                storePassword System.getenv()["KEYSTORE_PASSWORD"]
                keyAlias System.getenv()["KEYSTORE_KEY_ALIAS"]
                keyPassword System.getenv()["KEYSTORE_KEY_PASSWORD"]
            } else {
                // ...
            }
        }
    }
}

Creating app.config.js

Create app.config.js with the following content to override specific parts of app.json.

export default ({ config }) => {
  const isProduction = process.env.APP_ENV === 'production';
  return {
    ...config,
    expo: {
      name: isProduction ? 'AppName' : 'AppName (Dev)',
      ios: {
        bundleIdentifier: isProduction ? 'com.my.app' : 'com.my.app.dev',
      },
      android: {
        package: isProduction ? 'com.my.app' : 'com.my.app.dev',
      },
    },
  };
};

Working in the Firebase Console

In the console of the target Firebase project, select "Add app" and fill in the required fields.

image1.png

For now, "Register app", download google-services.json, and proceed to the end.

Next, go to the "App Distribution" screen and click "Get started". Click "Add group" under "Testers & Groups".

image2.png

In this case, I created a group named "Developer".

image3.png

Finally, register testers by entering their email addresses in "Add testers".

Creating a Service Account Authentication File

To perform authentication for your Firebase project, open the Google Cloud Service Accounts page and select the target project. Next, click "Create Service Account", fill in the "Service account name" and "Service account ID", and then select "Create and Continue".

image4.png

For the role in the next step, select "Firebase App Distribution Admin SDK".

image5.png

Finally, click "Done" to register the service account.

Select "Manage keys" from the menu of the created service account.

image6.png

Select "Add Key" > "Create new key".

image7.png

Confirm that the key type is "JSON", and click "Create" to download and save the JSON file.

image8.png

Generating a Keystore for Distribution

Generate a keystore for distribution. Replace the XXXXX parts as appropriate.

$JAVA_HOME/bin/keytool -J-Dkeystore.pkcs12.legacy -genkey -v -keystore dev.keystore -keyalg RSA -storepass XXXXX -alias XXXXX -validity 9125 -dname "CN=Developer, O=XXXX, C=Japan"

Registering GitHub Repository Secrets

Register the following secrets.

SECRET Name Value
ANDROID_KEYSTORE_FILE The keystore created earlier converted to base64: `cat dev.keystore
ANDROID_KEYSTORE_PASSWORD The password set during keystore generation
KEYSTORE_KEY_ALIAS The alias set during keystore generation
ANDROID_KEY_PASSWORD The password set during keystore generation
FIREBASE_APP_ID Available in the Firebase console: Project settings → General → Android apps → App ID
FIREBASE_SERVICE_CREDENTIALS_JSON The content of the service account authentication file (copy/paste)

GitHub Actions

I created the final GitHub Actions configuration file for beta distribution as .github/workflows/deploy.yml with the following content.

name: Firebase App Distribution

on:
  workflow_dispatch:
  push:
    branches:
      - "develop"

jobs:
  distribute:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      # Read version settings from the volta field in package.json
      - uses: volta-cli/action@v4
        with:
          package-json-path: "package.json"
      - name: Cache Yarn
        uses: actions/cache@v4
        with:
          path: ~/.yarn/cache
          key: yarn-${{ hashFiles('yarn.lock') }}-${{ runner.os }}
          restore-keys: yarn-${{ hashFiles('yarn.lock') }}-
      - name: Install dependencies
        run: yarn install --frozen-lockfile --network-timeout 300000
      # Android SDK & Cache
      - uses: android-actions/setup-android@v3
        with: { cache: true }
      # JDK + Gradle (Build/Configuration Cache enabled)
      - uses: gradle/actions/setup-gradle@v3
      # Restore the previously created keystore
      - name: Write dev.keystore
        env:
          ANDROID_KEYSTORE_FILE: ${{ secrets.ANDROID_KEYSTORE_FILE }}
        run: |
          echo "$ANDROID_KEYSTORE_FILE" | base64 --decode > android/app/dev.keystore
      - name: Bump gradle version code
        uses: chkfung/android-version-actions@v1.2.1
        with:
          gradlePath: android/app/build.gradle
          versionCode: ${{github.run_number}}
      # Replace the key alias with the one configured during creation
      - name: Build with Gradle
        env:
          CI: true
          KEYSTORE_PATH: dev.keystore
          KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
          KEYSTORE_KEY_ALIAS: xxxxxx
          KEYSTORE_KEY_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
        run: cd android && ./gradlew assembleDevRelease
      - name: Upload a Build Artifact
        id: upload_artifact
        uses: actions/upload-artifact@v4
        with:
          name: app-dev-release
          path: android/app/build/outputs/apk/dev/release/app-dev-release.apk
      - name: Deploy to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{ secrets.FIREBASE_APP_ID }}
          serviceCredentialsFileContent: ${{ secrets.FIREBASE_SERVICE_CREDENTIALS_JSON }}
          groups: Developer
          file: android/app/build/outputs/apk/dev/release/app-dev-release.apk
  • Since I used Volta for version management this time, I am using the following GitHub Action:

https://github.com/volta-cli/action

With this, although it's only for Android, I believe you will be able to perform beta distribution to Firebase App Distribution.

Reference URLs

https://zenn.dev/cureapp/articles/firebase-app-distribution

https://tudotechnologies.medium.com/setting-up-ci-cd-for-react-native-with-github-actions-and-firebase-app-distribution-86aa416e4beb

https://zenn.dev/steelydylan/articles/expo-app-distribution

Discussion