Skip to content

Github Action workflows

The starter comes with over 10 GitHub Actions workflows that help you automate the process of checking, testing, releasing, and much more.

All those workflows are self-documented and contain all details directly inside the workflow file as comments. This approach makes it easier to understand what each workflow does and how to use it without having to go back and forth between the documentation and the workflow file.

This page lists all workflows and actions we use and adds more details on the approach we are using to make those workflows as simple as possible.

All files related to GitHub Actions are located in the .github/workflows and .github/actions folders.

Actions

You can think of actions as functions that are used by workflows to perform a specific task and allow you to reuse the same task in multiple workflows without duplicating the code which made them easier to maintain.

All actions are located in the .github/actions folder, and here is the complete list:

⚙️ Node setup and PNM install

.github/actions/setup-node-pnpm-install/action.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/actions/setup-node-pnpm-install/action.yml
# Composite actions docs: https://docs.github.com/en/actions/creating-actions/creating-a-composite-action
# ✍️ Description:
# This is a composite action, which means it can be used in other actions.
# It is used in almost all workflows to set up the environment and install dependencies.
# Updating the package manager or Node version here will be reflected in all workflows.
# 👀 Example usage:
# - name : 📦 Setup Node + PNPM + install deps
# uses: ./.github/actions/setup-node-pnpm-install
name: 'Setup Node + PNPM + Install Dependencies'
description: 'Setup Node + PNPM + Install Dependencies'
runs:
using: 'composite'
steps:
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: 📦 Install Project Dependencies
run: pnpm install --frozen-lockfile
shell: bash

⚙️ Setup JDK && Generate APK

.github/actions/setup-jdk-generate-apk/action.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/actions/setup-jdk-generate-apk/action.yml
# Composite actions docs: https://docs.github.com/en/actions/creating-actions/creating-a-composite-action
# ✍️ Description:
# This is a composite action, which means it can be used in other actions.
# This action is used to set up the JDK environment and generate an Android APK for testing.
# This action accepts one input: `APP_ENV`, which is used to generate an APK for a specific environment (development, staging, production). We use staging by default.
# Before generating the APK, we run a pre-build script to generate the necessary native folders based on the APP_ENV.
# On success, the APK is generated at `./android/app/build/outputs/apk/release/app-release.apk`.
# 👀 Example usage:
# - name : 📦 Set Up JDK + Generate Test APK
# uses: ./.github/actions/setup-jdk-generate-apk
# with:
# APP_ENV: 'staging'
name: 'Setup JDK + GRADLE + Generate APK'
description: 'Setup JDK + GRADLE + Generate APK'
inputs:
APP_ENV:
description: 'APP_ENV (one of): development, staging, production'
required: true
default: 'staging'
runs:
using: 'composite'
steps:
- name: Set Up JDK
uses: actions/setup-java@v3
with:
distribution: 'zulu' # See 'Supported distributions' for available options
java-version: '17'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Generate Test APK
run: |
pnpm prebuild:${{ inputs.APP_ENV }}
cd android
chmod +x ./gradlew
./gradlew assembleRelease --no-daemon
cd ..
shell: bash
env:
EXPO_NO_DOTENV: '1'
APP_ENV: ${{ inputs.APP_ENV }}
CI: 'true'

⚙️ EAS Build

.github/actions/eas-build/action.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/actions/eas-build/action.yml
# EAS Build docs: https://docs.expo.dev/eas-update/github-actions/
# ✍️ Description:
# This is a composite action, which means it can be used in other actions.
# This action is used to trigger an EAS Build for a specific environment (development, staging, production).
# This action accepts those inputs:
# `APP_ENV`, which is used to generate an APK for a specific environment (development, staging, production). We use staging by default.
# `AUTO_SUBMIT`, false by default, set to true if you want to automatically submit your build to stores.
# `EXPO_TOKEN`, required, access token for your Expo account. https://expo.dev/settings/access-tokens
# `VERSION`, required, version of the app to build. used as the build message.
# `ANDROID`, true by default, set to true if you don't want to trigger build for Android.
# `IOS`, false by default, set to true if you want to trigger build for IOS.
# Before triggering the build, we run a pre-build script to generate the necessary native folders based on the APP_ENV.
# Based on the ANDROID and IOS inputs, we trigger the build for the corresponding platform with the corresponding flags.
# 👀 Example usage:
# - name: ⏱️ EAS Build
# uses: ./.github/actions/eas-build
# with:
# APP_ENV: staging
# EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
# VERSION: ${{ github.event.release.tag_name }}
# IOS: false
name: 'Setup EAS Build + Trigger Build'
description: 'Setup EAS Build + Trigger Build'
inputs:
APP_ENV:
description: 'APP_ENV (one of): development, staging, production'
required: true
default: 'staging'
AUTO_SUBMIT: ## TODO: we need to handle this too
description: 'AUTO_SUBMIT (one of): true, false'
required: true
default: 'false'
ANDROID:
description: 'run for ANDROID (one of): true, false'
required: true
default: 'true'
VERSION:
description: 'VERSION'
required: true
default: '0.0.0'
IOS:
description: 'run for IOS (one of): true, false'
required: true
default: 'false'
EXPO_TOKEN:
description: 'EXPO_TOKEN'
required: true
default: 'false'
runs:
using: 'composite'
steps:
- name: 💯 Check for EXPO_TOKEN
run: |
if [ -z "${{ inputs.EXPO_TOKEN }}" ]; then
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
exit 1
fi
shell: bash
- name: 📦 Setup Expo and EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ inputs.EXPO_TOKEN }}
- name: ⚙️ Run Prebuild
run: pnpm prebuild:${{ inputs.APP_ENV }}
shell: bash
- name: 📱 Run Android Build
if: ${{ inputs.ANDROID == 'true' }}
run: pnpm build:${{ inputs.APP_ENV }}:android --non-interactive --no-wait --message "Build ${{ inputs.APP_ENV }} ${{ inputs.VERSION }}"
shell: bash
- name: 📱 Run IOS Build
if: ${{ inputs.IOS == 'true' }}
run: pnpm build:${{ inputs.APP_ENV }}:ios --non-interactive --no-wait --message "Build ${{ inputs.APP_ENV }} ${{ inputs.VERSION }}"
shell: bash

Workflows

⚙️ Linting and formatting

.github/workflows/lint-ts.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/lint-ts.yml
# ✍️ Description:
# This action is used to run eslint checks
# Runs on pull requests and pushes to the main/master branches
# Based on the event type:
# - If it's a pull request, it will run eslint, then add the check to the PR as well as annotate the code with the errors and warnings.
# - If it's a push to main/master, it will run the type checking and fail if there are any errors.
# 🚨 GITHUB SECRETS REQUIRED: NONE
name: Lint TS (eslint, prettier)
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
lint:
name: Lint TS (eslint, prettier)
runs-on: ubuntu-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 🏃‍♂️ Run ESLint PR
if: github.event_name == 'pull_request'
uses: reviewdog/action-eslint@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
reporter: github-pr-review
eslint_flags: '. --ext .js,.jsx,.ts,.tsx'
- name: 🏃‍♂️ Run ESLint PR
if: github.event_name != 'pull_request'
run: pnpm run lint

⚙️ Type checking

.github/workflows/type-check.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/type-check.yml
# ✍️ Description:
# This action is used to run the type-check on the project.
# Runs on pull requests and pushes to the main/master branches
# Based on the event type:
# - If it's a pull request, it will run type checking, then add the check to the PR as well as annotate the code with the errors using reviewdog.
# - If it's a push to main/master, it will run the type checking and fail if there are any errors.
# 🚨 GITHUB SECRETS REQUIRED: NONE
name: Type Check (tsc)
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
type-check:
name: Type Check (tsc)
runs-on: ubuntu-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 📦 Install Reviewdog
if: github.event_name == 'pull_request'
uses: reviewdog/action-setup@v1
- name: 🏃‍♂️ Run TypeScript PR # Reviewdog tsc errorformat: %f:%l:%c - error TS%n: %m
# We only need to add the reviewdog step if it's a pull request
if: github.event_name == 'pull_request'
run: |
pnpm type-check | reviewdog -name="tsc" -efm="%f(%l,%c): error TS%n: %m" -reporter="github-pr-review" -filter-mode="nofilter" -fail-on-error -tee
env:
REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name:
🏃‍♂️ Run TypeScript Commit
# If it's not a Pull Request then we just need to run the type-check
if: github.event_name != 'pull_request'
run: pnpm type-check

⚙️ Expo Doctor

.github/workflows/expo-doctor.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/expo-doctor.yml
# ✍️ Description:
# This workflow runs the expo doctor command to check if your project dependencies are aligned with the expo sdk version you are using.
# Can be triggered manually from the Actions tab in your project.
# Runs Also on pull requests and pushes to the main/master branch, but only if the `package.json` or `pnpm-lock.yaml` files have been changed.
# 🚨 GITHUB SECRETS REQUIRED: NONE
name: Expo Doctor (expo)
on:
push:
branches:
- main
- master
paths:
- 'package.json'
- 'pnpm-lock.yaml'
pull_request:
paths:
- 'package.json'
- 'pnpm-lock.yaml'
jobs:
doctor:
name: Expo Doctor (expo)
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: Run prebuild
run: pnpm run prebuild
- name: 🚑 Run Doctor Checks
run: |
chmod +x .github/scripts/expo-doctor.sh
rm -rf ios android
.github/scripts/expo-doctor.sh
- name: Add doctor report as comment on PR
if: github.event_name == 'pull_request' && always()
uses: marocchino/sticky-pull-request-comment@v2
with:
header: expo-doctor
path: .expo/expo-doctor.md

⚙️ Compress Images

.github/workflows/compress-images.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/compress-images.yml
# ✍️ Description:
# This workflow is used to compress images in the repo.
# This workflow will trigger on a push to the "master" or "main" branch and only run when a new image is added or updated.
# If it's the case, it will compress those images and create a pull request with the compressed images.
# 🚨 GITHUB SECRETS REQUIRED: None
name: Compress images
on:
push:
branches:
- master
- main
paths:
- '**.jpg'
- '**.jpeg'
- '**.png'
- '**.webp'
workflow_dispatch:
jobs:
build:
name: calibreapp/image-actions
runs-on: ubuntu-latest
steps:
- name: Checkout Branch
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Compress Images
id: calibre
uses: calibreapp/image-actions@main
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
compressOnly: true
ignorePaths: 'node_modules/**,ios/**,android/**'
- name: Create Pull Request
if: steps.calibre.outputs.markdown != ''
uses: peter-evans/create-pull-request@v3
with:
title: Auto Compress Images
branch-suffix: timestamp
commit-message: Compress Images
body: ${{ steps.calibre.outputs.markdown }}

⚙️ Running Tests + Coverage Reports

.github/workflows/test.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/test.yml
# ✍️ Description:
# This action is used to run unit tests
# Runs on pull requests and pushes to the main/master branches
# Based on the event type:
# - If it's a pull request, it will run the tests and post a comment with coverage details.
# - If it's a push to main/master, it will run the tests and add the check to the commit.
# 🚨 GITHUB SECRETS REQUIRED: NONE
name: Tests (jest)
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
test:
name: Tests (jest)
runs-on: ubuntu-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 🏃‍♂️ Run Tests
run: pnpm run test:ci
- name: Jest Coverage Comment
uses: MishaKav/jest-coverage-comment@main
if: (success() || failure()) && github.event_name == 'pull_request'
with:
coverage-summary-path: ./coverage/coverage-summary.json
summary-title: '💯 Test Coverage'
badge-title: Coverage
create-new-comment: false
junitxml-title: 😎 Tests Results
junitxml-path: ./coverage/jest-junit.xml
coverage-title: 👀 Tests Details
coverage-path: ./coverage/coverage.txt
report-only-changed-files: true

⚙️ New App Version

.github/workflows/new-app-version.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/lint-ts.yml
# Starter releasing process: https://starter.obytes.com/ci-cd/app-releasing-process/
# ✍️ Description:
# This workflow is used to create a new version of the app and push a new tag to the repo.
# As this workflow will push code to the repo, we set up GitHub Bot as a Git user.
# This Workflow need to be triggered manually from the Actions tab in the repo.
# 1. Choose your release type (patch, minor, major)
# 2. The workflow will run the np-release script which runs the following steps:
# - Bump the version in package.json based on the release type using np
# - Run the prebuild of the app to align the package.json version with the native code
# - Create a new tag with the new version
# - Push the new tag to the repo
#
# 🚨 GITHUB SECRETS REQUIRED:
# - GH_TOKEN: A GitHub token with write repo access.
# You can generate one from here: https://docs.github.com/en/[email protected]/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
# make sure to add it to the repo secrets with the name GH_TOKEN
name: New App Version
on:
workflow_dispatch:
inputs:
release-type:
type: choice
description: 'Release type (one of): patch, minor, major'
required: true
default: 'patch'
options:
- patch
- minor
- major
jobs:
release:
name: Create New Version and push new tag
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 🔍 GH_TOKEN
if: env.GH_TOKEN == ''
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: echo "GH_TOKEN=${GITHUB_TOKEN}" >> $GITHUB_ENV
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN }}
- name: 📝 Git User Setup
run: |
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 🏃‍♂️ Run App release
run: |
pnpm app-release ${{ github.event.inputs.release-type }}

⚙️ New Github Release

.github/workflows/new-github-release.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/new-github-release.yml
# Starter releasing process: https://starter.obytes.com/ci-cd/app-releasing-process/
# ✍️ Description:
# This workflow will be triggered automatically after the new app version workflow has been executed successfully.
# It will create a new GitHub release with the new app version and the release notes.
# 🚨 GITHUB SECRETS REQUIRED: None
name: New GitHub Release
on:
push:
tags:
- '*'
jobs:
release:
name: New GitHub Release
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 🏃‍♂️Create A Draft Github Release
uses: ncipollo/release-action@v1
with:
generateReleaseNotes: true
draft: false

⚙️ EAS Build QA

.github/workflows/eas-build-qa.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/eas-build-qa.yml
# Starter releasing process: https://starter.obytes.com/ci-cd/app-releasing-process/
# ✍️ Description:
# This workflow is used to trigger a build on EAS for the QA environment.
# It will run on every GitHub release published on the repo or can be triggered manually from the actions tab.
# This workflow will use ./actions/eas-build action to trigger the build on EAS with staging env.
# 🚨 GITHUB SECRETS REQUIRED:
# - EXPO_TOKEN: Expo token to authenticate with EAS
# - You can get it from https://expo.dev/settings/access-tokens
name: EAS QA Build (Android & IOS) (EAS)
on:
workflow_dispatch:
release:
types: [published]
jobs:
Build:
name: EAS QA Build (Android & IOS) (EAS)
runs-on: ubuntu-latest
steps:
- name: Check for EXPO_TOKEN
run: |
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
exit 1
fi
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: ⏱️ EAS Build
uses: ./.github/actions/eas-build
with:
APP_ENV: staging
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
VERSION: ${{ github.event.release.tag_name }}
IOS: false # TODO: set as true when IOS account is ready

⚙️ EAS Build Prod

.github/workflows/eas-build-prod.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/eas-build-prod.yml
# Starter releasing process: https://starter.obytes.com/ci-cd/app-releasing-process/
# ✍️ Description:
# This workflow is used to trigger a build on EAS for Prod environment.
# Can be triggered manually from the actions tab.
# This workflow will use ./actions/eas-build action to trigger the build on EAS with production env.
# 🚨 GITHUB SECRETS REQUIRED:
# - EXPO_TOKEN: Expo token to authenticate with EAS
# - You can get it from https://expo.dev/settings/access-tokens
name: EAS Production Build (Android & IOS) (EAS)
on:
workflow_dispatch:
jobs:
Build:
name: EAS Production Build (Android & IOS) (EAS)
runs-on: ubuntu-latest
steps:
- name: Check for EXPO_TOKEN
run: |
if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
exit 1
fi
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: ⏱️ EAS Build
uses: ./.github/actions/eas-build
with:
APP_ENV: production
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}

⚙️ E2E Test for Android

.github/workflows/e2e-android.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/e2e-android.yml
# End-to-end testing: https://starter.obytes.com/testing/end-to-end-testing/
# ✍️ Description:
# This workflow is used to run end-to-end tests on Android using Maestro.
# As a first step, it will generate a test APK using the Gradle build and then upload it as an artifact.
# A new job will be started to run the tests using the test APK generated in the previous job.
# To test the app, we set up an Android emulator using the `reactivecircus/android-emulator-runner` action. This runner requires macOS as the operating system for the runner.
# This workflow will be triggered on pull requests (PRs) with the label "android-test-github" or can be manually triggered from the Actions tab.
#
# 🚨 GITHUB SECRETS REQUIRED: None
name: E2E Tests Android (Maestro + Github Action)
on:
workflow_dispatch:
pull_request:
branches: [main, master]
jobs:
generate-test-apk:
if: github.event_name != 'pull_request' || ( github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'android-test-github'))
name: Generate Test APK (Gradle)
runs-on: ubuntu-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 📦 Set Up JDK + Generate Test APK
uses: ./.github/actions/setup-jdk-generate-apk
with:
APP_ENV: staging
- name: Upload Test APK
uses: actions/upload-artifact@v3
with:
name: test-apk
path: ./android/app/build/outputs/apk/release/app-release.apk
test-android:
name: Run Android E2E Tests (Maestro + Github Action)
needs: generate-test-apk
runs-on: macOS-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Install Maestro
run: npm run install-maestro ## We use npm because we don't need to install deps again
- name: Download Test APK
uses: actions/download-artifact@v3
with:
name: test-apk
path: ${{ github.workspace }}
- name: Gradle cache
uses: gradle/gradle-build-action@v2
- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-cache
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
cores: 2
ram-size: 4096M
profile: Nexus 6
script: echo "Generated AVD snapshot for caching."
- name: Run tests with Maestro
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
cores: 2
ram-size: 4096M
profile: Nexus 6
script: |
adb install "${{ github.workspace }}/app-release.apk"
$HOME/.maestro/bin/maestro test .maestro/ --env=APP_ID=com.obytes.staging --format junit
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: e2e-test-logs
path: ~/.maestro/tests/**/*
- name: Store tests result
uses: actions/upload-artifact@v3
with:
name: e2e_android_report
path: |
report.xml

⚙️ E2E Test for Android using Maestro Cloud

.github/workflows/e2e-android-maestro.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/e2e-android.yml
# End-to-end testing: https://starter.obytes.com/testing/end-to-end-testing/
# ✍️ Description:
# This workflow is used to run end-to-end tests on Android using Maestro Cloud.
# As a first step, it will generate a test APK using the Gradle build and then trigger Maestro Cloud to run the tests on the generated APK.
# This workflow will be triggered on pull requests (PRs) with the label "android-test-maestro-cloud" or can be manually triggered from the Actions tab.
# 🚨 GITHUB SECRETS REQUIRED:
# MAESTRO_CLOUD_API_KEY: API key for Maestro Cloud. You can get it from https://cloud.mobile.dev/ci-integration/github-actions#add-your-api-key-secret
name: E2E Tests Android (Maestro Cloud)
on:
workflow_dispatch:
pull_request:
branches: [main, master]
jobs:
generate-and-test-apk:
if: github.event_name != 'pull_request' || ( github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'android-test-maestro-cloud'))
name: Generate and Test Test APK (Maestro Cloud)
runs-on: ubuntu-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Setup Node + PNPM + install deps
uses: ./.github/actions/setup-node-pnpm-install
- name: 📦 Set Up JDK + Generate Test APK
uses: ./.github/actions/setup-jdk-generate-apk
with:
APP_ENV: staging
- name: Upload Test APK
uses: actions/upload-artifact@v3
with:
name: test-apk
path: ./android/app/build/outputs/apk/release/app-release.apk
- name: 📱 Run E2E Tests with Maestro Cloud
uses: mobile-dev-inc/[email protected]
with:
api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }}
app-file: ./android/app/build/outputs/apk/release/app-release.apk
env: |
APP_ID=com.obytes.staging

⚙️ E2E Test for Android using EAS Build

.github/workflows/e2e-android-eas-build.yml
# 🔗 Links:
# Source file: https://github.com/obytes/react-native-template-obytes/blob/master/.github/workflows/e2e-android-eas-build.yml
# End-to-end testing: https://starter.obytes.com/testing/end-to-end-testing/
# ✍️ Description:
# This workflow is used to run end-to-end tests for EAS build on Android.
# It uses Maestro Cloud to run tests on Android emulator.
# It downloads the APK from EAS build and triggers the tests on Maestro Cloud with the downloaded APK.
# 🚨 GITHUB SECRETS REQUIRED:
# MAESTRO_CLOUD_API_KEY: API key for Maestro Cloud. You can get it from https://cloud.mobile.dev/ci-integration/github-actions#add-your-api-key-secret
name: E2E Tests EAS Build Android (Maestro + Github Action)
on:
workflow_dispatch:
inputs:
apk-url:
type: string
description: 'EAS APK URL'
required: true
default: ''
jobs:
download-eas-apk:
if: github.event_name != 'pull_request' && github.event.inputs.apk-url != ''
name: Download Test APK From EAS Url (wget)
runs-on: ubuntu-latest
steps:
- name: 📦 Download EAS APK
run: wget ${{ github.event.inputs.apk-url }} -O ./app-release.apk
- name: Upload Test APK
uses: actions/upload-artifact@v3
with:
name: test-apk
path: ./app-release.apk
test-android:
name: E2E Tests EAS Build Android (Maestro + Github Action)
needs: download-eas-apk
runs-on: macOS-latest
steps:
- name: 📦 Checkout project repo
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: 📦 Install Maestro
run: npm run install-maestro ## We use npm because we don't need to install deps again
- name: Download Test APK
uses: actions/download-artifact@v3
with:
name: test-apk
path: ${{ github.workspace }}
- name: Gradle cache
uses: gradle/gradle-build-action@v2
- name: AVD cache
uses: actions/cache@v3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-cache
- name: create AVD and generate snapshot for caching
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
cores: 2
ram-size: 4096M
profile: Nexus 6
script: echo "Generated AVD snapshot for caching."
- name: Run tests with Maestro
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: false
cores: 2
ram-size: 4096M
profile: Nexus 6
script: |
adb install "${{ github.workspace }}/app-release.apk"
$HOME/.maestro/bin/maestro test .maestro/ --env=APP_ID=com.obytes.staging --format junit
- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: e2e-test-logs
path: ~/.maestro/tests/**/*
- name: Store tests result
uses: actions/upload-artifact@v3
with:
name: e2e_android_report
path: |
report.xml