Skip to content

React Native CI/CD

React Native CI/CD #83

name: React Native CI/CD
on:
workflow_dispatch:
inputs:
buildType:
type: choice
description: "Build type to run"
options:
- prod-apk
- prod-aab
- ios-prod
- publish-stores
- all
platform:
type: choice
description: "What platform to build for"
options:
- ios
- android
- all
release:
type: choice
description: "The GitHub Release type for this run"
options:
- draft
- prerelease
- release
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
GOOGLE_PLAY_SERVICE_ACCOUNT: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
GOOGLE_SERVICES_JSON_BASE64: ${{ secrets.GOOGLE_SERVICES_JSON_BASE64 }}
GOOGLE_SERVICES_PLIST_BASE64: ${{ secrets.GOOGLE_SERVICES_PLIST_BASE64 }}
EXPO_APPLE_ID: ${{ secrets.EXPO_APPLE_ID }}
EXPO_APPLE_PASSWORD: ${{ secrets.EXPO_APPLE_PASSWORD }}
EXPO_TEAM_ID: ${{ secrets.EXPO_TEAM_ID }}
NODE_OPTIONS: --openssl-legacy-provider
jobs:
check-skip:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, '[skip ci]')"
steps:
- name: Skip CI check
run: echo "Proceeding with workflow"
build:
needs: check-skip
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')) || github.event_name == 'workflow_dispatch'
strategy:
matrix:
platform: ${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.platform == 'all' && fromJSON('["android", "ios"]') || github.event.inputs.platform == 'android' && fromJSON('["android"]') || github.event.inputs.platform == 'ios' && fromJSON('["ios"]')) || fromJSON('["android", "ios"]') }}
runs-on: ${{ matrix.platform == 'ios' && 'macos-26' || 'ubuntu-latest' }}
steps:
- name: πŸ— Checkout repository
uses: actions/checkout@v4
- name: πŸ— Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24"
cache: "npm"
- name: πŸ“¦ Install dependencies
run: |
npm install
npm install -g eas-cli@latest
- name: πŸ”„ Verify EAS CLI installation
run: |
echo "EAS CLI version:"
eas --version
- name: ⏬️ Download Google Services configs
run: |
chmod +x ./utils/downloadGoogleServices.sh
bash ./utils/downloadGoogleServices.sh
- name: πŸ“± Build Production APK
if: (matrix.platform == 'android') && (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'prod-apk' || github.event_name == 'push')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
mkdir -p android_output
eas build --platform android --profile production-apk --local --non-interactive --output=./android_output/app-prod.apk
env:
NODE_ENV: production
- name: πŸ“± Build Production AAB
if: (matrix.platform == 'android') && (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'prod-aab' || github.event_name == 'push')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
mkdir -p android_output
eas build --platform android --profile production --local --non-interactive --output=./android_output/app-prod.aab
env:
NODE_ENV: production
- name: πŸ“± Build iOS Production
if: (matrix.platform == 'ios') && (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'ios-prod' || github.event_name == 'push')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
export EAS_PWD=$PWD
eas build --platform ios --profile production --local --non-interactive --output=./app-ios-prod.ipa
env:
NODE_ENV: production
- name: πŸš€ Submit to Play Store
if: (matrix.platform == 'android') && (github.event.inputs.buildType == 'all' || github.event_name == 'push')
run: |
eas submit -p android --path ./android_output/app-prod.aab --non-interactive
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
GOOGLE_PLAY_SERVICE_ACCOUNT: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT }}
- name: πŸš€ Submit to App Store
if: (matrix.platform == 'ios') && (github.event.inputs.buildType == 'all' || github.event_name == 'push')
run: |
eas submit -p ios --path ./app-ios-prod.ipa --non-interactive
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
EXPO_APPLE_ID: ${{ secrets.EXPO_APPLE_ID }}
EXPO_APPLE_PASSWORD: ${{ secrets.EXPO_APPLE_PASSWORD }}
EXPO_TEAM_ID: ${{ secrets.EXPO_TEAM_ID }}
- name: ⬆️ Upload Android Artifacts
uses: actions/upload-artifact@v4
if: matrix.platform == 'android'
with:
name: android-artifacts
path: |
./android_output/*
- name: ⬆️ Upload iOS Artifact
uses: actions/upload-artifact@v4
if: matrix.platform == 'ios'
with:
name: ios-artifact
path: ./app-ios-prod.ipa
create-release:
needs: build
runs-on: ubuntu-latest
steps:
- name: πŸ— Checkout repository
uses: actions/checkout@v4
# New Step: Generate the version info you were trying to use
- name: πŸ“ Get version info
id: build-info
run: |
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "build_number=${{ github.run_number }}" >> $GITHUB_OUTPUT
- name: ⬇️ Download Android Artifacts (if available)
uses: actions/download-artifact@v4
if: github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android'
with:
name: android-artifacts
path: artifacts/android-artifacts
- name: ⬇️ Download iOS Artifacts (if available)
uses: actions/download-artifact@v4
if: github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios'
with:
name: ios-artifact
path: artifacts/ios-artifact
- name: πŸ“ Create GitHub Release
uses: softprops/action-gh-release@v1
with:
prerelease: ${{ github.event.inputs.release == 'prerelease' }}
draft: ${{ github.event.inputs.release == 'draft' }}
name: "Release v${{ steps.build-info.outputs.version }}-${{ steps.build-info.outputs.build_number }}"
tag_name: "v${{ steps.build-info.outputs.version }}-${{ steps.build-info.outputs.build_number }}"
files: |
artifacts/android-artifacts/*
artifacts/ios-artifact/*
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}