Skip to content

Instantly share code, notes, and snippets.

@Clumsy-Coder
Created August 6, 2020 17:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Clumsy-Coder/dc8c35e5402d214510a24a6e1c3db8f2 to your computer and use it in GitHub Desktop.
Save Clumsy-Coder/dc8c35e5402d214510a24a6e1c3db8f2 to your computer and use it in GitHub Desktop.
React Native Android build using github-actions
name: Android APK Build
on: push
env:
YARN_MODULES_CACHE_KEY: v1
YARN_PACKAGE_CACHE_KEY: v1
YARN_CACHE_FOLDER: .cache/yarn
FORCE_COLOR: true # display terminal colors
jobs:
install:
runs-on: ubuntu-latest
timeout-minutes: 10
if: "! contains(toJSON(github.event.head_commit.message), '[skip ci]')" # skip job if git message contains [skip ci]
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-
- name: Install npm dependencies
run: yarn install
lint:
needs: install
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-
- name: Lint project
run: yarn lint
test:
needs: install
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
${{ runner.os }}-build-${{ env.cache-name }}-
- name: Test project
run: yarn test --maxWorkers=2 --ci --coverage
- name: Archive test results
uses: actions/upload-artifact@v1
with:
name: test-results
path: test-results/test-report.html
- name: Archive code coverage
uses: actions/upload-artifact@v1
with:
name: code-coverage
path: coverage/lcov-report/
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1.0.7
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: coverage/lcov.info
build:
needs: install
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-
- name: Create properties file
run: |
printf 'RELEASE_KEY_STORE=%s\n' ${{ secrets.RELEASE_KEY_STORE }} >> android/release-keystore.properties
printf 'RELEASE_KEY_ALIAS_NAME=%s\n' '${{ secrets.RELEASE_KEY_ALIAS_NAME }}' >> android/release-keystore.properties
printf 'RELEASE_KEY_ALIAS_PASSWORD=%s\n' '${{ secrets.RELEASE_KEY_ALIAS_PASSWORD }}' >> android/release-keystore.properties
printf 'RELEASE_KEY_STORE_PASSWORD=%s\n' '${{ secrets.RELEASE_KEY_STORE_PASSWORD }}' >> android/release-keystore.properties
- name: Decode Android key
run: |
echo "${{ secrets.KEYSTORE }}" > keystore-base64
base64 -d -i keystore-base64 > android/app/release.keystore
- name: Cache Android dependencies
uses: actions/cache@v2
env:
cache-name: cache-android-dependencies
with:
path: |
~/.gradle/
~/.m2/
key: jars-{{ hashFiles('android/gradle/wrapper/gradle-wrapper.properties') }}-{{ hashFiles("android/build.gradle") }}-{{ hashFiles('android/app/build.gradle') }}
restore-keys: jars-{{ hashFiles('android/gradle/wrapper/gradle-wrapper.properties') }}-{{ hashFiles("android/build.gradle") }}-
- name: Download Android dependencies
run: |
cd android
./gradlew androidDependencies
cd ..
- name: Generate .ENV file
run: |
printf 'BUILD_VERSION=%s\n' $(echo ${{ github.sha }} | cut -c -7) >> .env
printf 'API_KEY=%s\n' '${{ secrets.API_KEY }}' >> .env
- name: Build Android APK
run: yarn build:android:release
- name: Upload Artifact
uses: actions/upload-artifact@v1
with:
name: app-release.apk
path: android/app/build/outputs/apk/release/
release:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master'
steps:
- uses: actions/checkout@v2
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: '**/node_modules'
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}-
- uses: actions/download-artifact@v2
with:
name: app-release.apk
path: android/app/build/outputs/apk/release/
- name: semantic-release
if: github.ref == 'refs/heads/master'
run: yarn semantic-release --dry-run ${{github.ref != 'refs/heads/master'}} --ci ${{github.ref == 'refs/heads/master'}}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@Clumsy-Coder
Copy link
Author

Clumsy-Coder commented Aug 6, 2020

What does it do?

Used for building React Native Android release APK.

Github action assumptions

  • The github action is used for React Native project
  • The project is written in Typescript
  • Contains .eslintrcjs file to be used by ESlint
  • Contains jest.config.js file to be used by Jest
  • Contains tsconfig.json to be used by Typescript

Github-action Jobs

  • install: install npm packages
  • lint: lint project using typescript and eslint
  • test: test the project using jest
  • build: build Android release APK
  • release: semantic release the project
    • ONLY runs on master branch

Caveats

Android Release Keystore

Android Release keystore file is used to build a Release APK. It's a way sign and certify the APK.
Android release keystore filename is release.keystore
Android release keystore will be placed in <project root>/android/app/ folder

Android release keystore properites

The properties file is used by Gradle to build the APK.
It contains

  • filename of the release keystore file
  • Keystore alias
  • Keystore alias password
  • Keystore password

The Android keystore filename is release-keystore.properties
It will be placed <project root>/android/ folder

NOTE: Gradle must be configured to read the release-keystore-properties content to build the APK

Gradle

<project root>/android/app/build.gradle file will have to be configured for it to be able to use the Release keystore and it's properties

/**
 * Load release keystore properties.
 * Properties are stored in <project_root>/android/release-keystore.properties
 * Used for building a production app.
 */
def keyStorePropertiesFile = rootProject.file("./release-keystore.properties")
def keyStoreProperties = new Properties()
keyStoreProperties.load(new FileInputStream(keyStorePropertiesFile))

android {
  ... /** other configs */

  signingConfig {
    release {
      // release key is located in
      // <project_root>/android/app/release.keystore
      storeFile file(keyStoreProperties['RELEASE_KEY_STORE'])
      keyAlias keyStoreProperties['RELEASE_KEY_ALIAS_NAME']
      keyPassword keyStoreProperties['RELEASE_KEY_ALIAS_PASSWORD']
      storePassword keyStoreProperties['RELEASE_KEY_STORE_PASSWORD']
    }
  }
  buildTypes {
    release {
      // Caution! In production, you need to generate your own keystore file.
      // see https://facebook.github.io/react-native/docs/signed-apk-android.
      signingConfig signingConfigs.release
      minifyEnabled true
      proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
    }
  }
}

Explanation

def keyStorePropertiesFile = rootProject.file("./release-keystore.properties")
def keyStoreProperties = new Properties()
keyStoreProperties.load(new FileInputStream(keyStorePropertiesFile))

Import keystore properties


android {
  signingConfig {
    release {
      // release key is located in
      // <project_root>/android/app/release.keystore
      storeFile file(keyStoreProperties['RELEASE_KEY_STORE'])
      keyAlias keyStoreProperties['RELEASE_KEY_ALIAS_NAME']
      keyPassword keyStoreProperties['RELEASE_KEY_ALIAS_PASSWORD']
      storePassword keyStoreProperties['RELEASE_KEY_STORE_PASSWORD']
    }
  }
}

Use keystore properties when signing the APK


android {
  buildTypes {
    release {
      // Caution! In production, you need to generate your own keystore file.
      // see https://facebook.github.io/react-native/docs/signed-apk-android.
      signingConfig signingConfigs.release
      minifyEnabled true
      proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
    }
  }
}

Use the release signingConfig when building the APK


Credit

This config is based on link medium post


Github secrets

This github action uses the following github secrets

  • API_KEY: api key for the application
  • CODECOV_TOKEN: codecov token
  • KEYSTORE: Android release keystore (note: encoded in base64)
  • RELEASE_KEY_STORE: Android release keystore filename
  • RELEASE_KEY_ALIAS_NAME: release key alias name
  • RELEASE_KEY_ALIAS_PASSWORD: release key alias password
  • RELEASE_KEY_STORE_PASSWORD: release key password
  • GITHUB_TOKEN: token auto generated for every repo

To use this github-action, add the secrets mentioned to repo secrets

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment