Skip to main content

Build & Release Using Github Actions

Continuous integration and deployment#

Continuous integration is the practice of frequently committing small changes to a shared repository. This can range from once per day to multiple times per day.

Continuous deployment/delivery picks up after continuous integration. CD automates the delivery of applications to selected infrastructures (like Play Store, Firebase App distribution and App Store in our case). Most teams work with multiple environments other than the production, such as development and testing environments, and CD ensures there is an automated way to push code changes to them.

CI/CD Workflow#

One of the Main Advantages of using React Native is that you can build one app that works on both ios and android platforms, same thing for CI/CD we can setup App Center for automatic builds and also ensuring smooth delivery to the App Store/Play Store. For CI we will use the same GIT Workflow, for Development each time you add a new feature or you fix a bug it should be tested and merged to main branch. If this version is stable you can generate a new github release using npm run release

Configure Release Process#

To create a new app release we are going to use the same tool used to create npm packages and a preview github release.

  1. Add np and react-native-version package.
yarn add  np react-native-version  -D
  1. Go to package.json and add the following scripts
{  "name": "AwesomeProject",  "version": "0.0.1",  "scripts": {    "np": "np --no-publish",    "postversion": "react-native-version"  }}

we added react-native-version command as a postversion script because np can't update ios and android versions. react-native-version will help sync package.json version with android and ios as well as incrementing the build number automatically. The default behavior work as expected but you can add more options

⚠️ Make sure to add github repo in your package.json. it's mandatory to create a github release.

  1. Generate an App release using(we need a simple video here):
npm run npyarn np

Android Github Action#

  1. Create a new github workflow .github/workflows/android_build.yml and add the following content
name: Android Build ## name of the workflow
on:  release:    types: [published] ## only run the workflow when a new release has been published
jobs:  android-build:    name: Android Build    runs-on: ubuntu-latest # using ubuntu latest version / or you can use a specific version
    steps:      - name: Check out Git repository # checkout repository        uses: actions/checkout@v2
      - name: Set up our JDK environment # setup JDK environment: mandatory as we need to build an android project        uses: actions/setup-[email protected]        with:          java-version: 1.8
      - name: Set up Node.js ## setup node and yarn with cache        uses: actions/setup-node@v2        with:          node-version: 14          cache: "yarn"
      - name: Install dependencies ## install project deps with --frozen-lockfile to make sure we will have the same packages version ( very recommended  on running yarn install on ci)        run: yarn install --frozen-lockfile
      ## configure cash for gradle : will help to reduce build time      - name: Cache Gradle Wrapper        uses: actions/cache@v2        with:          path: ~/.gradle/wrapper          key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
      - name: Cache Gradle Dependencies        uses: actions/cache@v2        with:          path: ~/.gradle/caches          key: ${{ runner.os }}-gradle-caches-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}          restore-keys: |            ${{ runner.os }}-gradle-caches-
      - name: Make Gradlew Executable        run: cd android && chmod +x ./gradlew
      ## make sure to generate abb instead if you wand to upload it to play store ./gradlew bundleRelease      - name: Build Android App Bundle        run: |          cd android && ./gradlew assembleRelease --no-daemon
        ## sign generate apk using github secrets      - name: Sign App Bundle        id: sign_app        uses: r0adkll/sign-android-release@v1        with:          releaseDirectory: android/app/build/outputs/apk/release          signingKeyBase64: ${{ secrets.ANDROID_SIGNING_KEY }}          alias: ${{ secrets.ANDROID_ALIAS }}          keyStorePassword: ${{ secrets.ANDROID_KEY_STORE_PASSWORD }}          keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }}
      ## upload artifact to action      - name: Upload Artifact        uses: actions/upload-artifact@v2        with:          name: Gakko App Bundle          path: ${{steps.sign_app.outputs.signedReleaseFile}}
      ## Distribute app to Firebase App Distribution for testing / use google play internal track if you have a google play account      - name: upload artifact to Firebase App Distribution        uses: wzieba/Firebase-Distribution-Github-Action@v1        with:          appId: ${{secrets.ANDROID_FIREBASE_APP_ID}}          token: ${{secrets.ANDROID_FIREBASE_TOKEN}}          groups: testers          file: ${{steps.sign_app.outputs.signedReleaseFile}}
      ## upload abb to google play  using https://github.com/r0adkll/upload-google-play
  1. Generate a keystore and add environment variables as github secrets in your repository

Android Singing Configuration for r0adkll/sign-android-release@v1 action.

You can generate keystore using the following command:

keytool -genkeypair -v -storetype PKCS12 -keystore my-upload-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
  • ANDROID_SIGNING_KEY : The base64 encoded signing key used to sign your app

This action will directly decode this input to a file to sign your release with. You can prepare your key by running this command on *nix systems.

openssl base64 < your_signing_key.jks | tr -d '\n' | tee signing_key.jks.base64.txt

Then copy the contents of the .txt file to your ANDROID_SIGNING_KEY GH secret.

  • ANDROID_ALIAS : The alias of your signing key

  • ANDROID_KEY_STORE_PASSWORD : The password to your signing keystore

  • ANDROID_KEY_PASSWORD : The private key password for your signing keystore

Firebase App distribution

if you want to distribute App to testers using firebase App distribution using wzieba/Firebase-Distribution-Github-Action@v1 you need to create a firebase application and activate App distribution then add the following Github secrets:

  • ANDROID_FIREBASE_APP_ID : App id can be found on the General Settings page

  • ANDROID_FIREBASE_TOKEN: Upload token - see Firebase CLI Reference (tldr; run firebase login:ci command to get your token).

Upload to Google Play

to upload the Android Application to Google play you can add the following to you github action:

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

First Make sure your android developer account and internal track are ready to accept new build.

Next you should generate A new service account to upload your aab file to Google play. here's a complete guide on how to generate Android service account 👉 Generate Service account json file

⚠️ Only Owners can generate service account json files ⚠️ Make sure to to delete signing the release aab with debug credentials

Then you can json file content to Github secrets under ANDROID_SERVICE_ACCOUNT_JSON_TEXT name.

IOS Github action#

The first step is to generate bundle Id, certification and Provisioning Profile for distribution and install them in your mac. check IOS code sining guide

Configure IOS project on xcode#

  1. Open your project on xcode by double click on ios/XXXX.xcworkspace (⚠️ not .xcodeproj file)

  2. Select project target > Singing & Capabilities > Release

  3. Uncheck Automatically manage singing

  4. In Provisioning Profile, import profile you already downloaded.

  5. If the certificate associated to profile is already installed on your device you should see it in Signing Certificate also you should see the team name appear automatically

Now your project code source is ready for github action build

IOS Build workflow#

name: IOS Build
on:  release:    types: [published]
jobs:  ios-build:    name: IOS Build    runs-on: macOS-latest    defaults:      run:        working-directory: ios
    steps:      - name: Cancel Previous Runs        uses: styfle/cancel-workflow-[email protected]        with:          access_token: ${{ github.token }}      - name: Check out Git repository        uses: actions/checkout@v2
      - name: Set up Node.js        uses: actions/setup-node@v2        with:          node-version: 14          cache: "yarn"
      - name: Install dependencies        run: yarn install --frozen-lockfile
      - name: Restore buildcache        uses: mikehardy/buildcache-action@v1        continue-on-error: true
      - name: Setup Ruby (bundle)        uses: ruby/setup-ruby@v1        with:          ruby-version: 2.6          bundler-cache: true
      - name: Restore Pods cache        uses: actions/cache@v2        with:          path: |            ios/Pods            ~/Library/Caches/CocoaPods            ~/.cocoapods          key: ${{ runner.os }}-pods-${{ hashFiles('ios/Podfile.lock') }}          restore-keys: |            ${{ runner.os }}-pods-      - name: Install Pods        run: yarn setup:ios
      - name: Build IOS App        uses: yukiarrr/ios-build-[email protected]        with:          project-path: ios/Gakko.xcodeproj          p12-base64: ${{ secrets.IOS_P12_BASE64 }}          mobileprovision-base64: ${{ secrets.IOS_MOBILE_PROVISION_BASE64 }}          code-signing-identity: "iPhone Distribution"          team-id: ${{ secrets.IOS_TEAM_ID }}          certificate-password: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}          workspace-path: ios/Gakko.xcworkspace
      - name: Upload Artifact        uses: actions/upload-artifact@v2        with:          name: Gakko IOS IPA          path: "output.ipa"
      - name: "Upload app to TestFlight"        uses: apple-actions/upload-testflight-build@v1        with:          app-path: "output.ipa"          issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}          api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }}          api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}

Add the following environment variable as GH secrets

Sining certs and provisioning profile for yukiarrr/ios-build-action

  • IOS_MOBILE_PROVISION_BASE64 : Base64 encoded Provisioning profile file.

Download it from your Apple developer portal and use the following script to generate base64

openssl base64 < MY_Profile.mobileprovision | tr -d '\n' | tee my-profile.base64.txt
  • P12_BASE64 : Base64 encoded .p12 file (key + cert)

After installing the certificate in your Mac, Open Keychain Access App. Select "My Certificates" on the top, and locate the certificate you've downloaded.

Expand the certificate to see the corresponding private key.Then select the certificate and private key, then right-click for the context menu on the items and choose "Export 2 items…".

Pick a location on disk to save the file as a .p12 and choose a strong password for the file(IOS_CERTIFICATE_PASSWORD)

Generate a base64 for the .p12 file using

openssl base64 < cert.p12 | tr -d '\n' | tee cert.base64.txt

Upload to TestFlight upload-testflight-build

  • APPSTORE_ISSUER_ID : Go to Users and Access > API Keys, the issuer id is something like 598542-36fe-1a63-e073-0824d0166672a

  • APPSTORE_API_KEY_ID: The Key ID for AppStore Connect API.

  • APPSTORE_API_PRIVATE_KEY: The PKCS8 format Private Key for AppStore Connect API. The content of the file AuthKey_xxxxxx.p8 generated

Last updated on by Youssouf EL Azizi