Skip to content

Refactor React Native CI/CD workflow: streamline job definitions, upd… #2

Refactor React Native CI/CD workflow: streamline job definitions, upd…

Refactor React Native CI/CD workflow: streamline job definitions, upd… #2

# Inspired by https://www.expobuilder.app
name: React Native CI/CD
on:
# workflow_dispatch:
# inputs:
# buildType:
# type: choice
# description: "Build type to run"
# options:
# - dev
# - prod-aab
# - ios-dev
# - ios-prod
# - publish-expo
# - publish-stores
# - all
# platform:
# type: choice
# description: "Platform to build"
# default: "all"
# options:
# - android
# - ios
# - all
workflow_call:
secrets:
EXPO_TOKEN:
required: true
EXPO_APPLE_ID:
required: true
EXPO_APPLE_PASSWORD:
required: true
EXPO_TEAM_ID:
required: true
GOOGLE_PLAY_SERVICE_ACCOUNT:
required: false
SLACK_WEBHOOK:
required: false
DISCORD_WEBHOOK:
required: false
GITHUB_TOKEN:

Check failure on line 42 in .github/workflows/react-native-cicd.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/react-native-cicd.yml

Invalid workflow file

secret name `GITHUB_TOKEN` within `workflow_call` can not be used since it would collide with system reserved name
required: true
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 }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
NODE_OPTIONS: --openssl-legacy-provider
jobs:
build-android:
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')) || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
steps:
- name: 🏗 Checkout repository
uses: actions/checkout@v4
- name: 🏗 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
- name: 📦 Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: 📦 Setup yarn cache
uses: actions/cache@v3
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: 📦 Install dependencies
run: |
yarn install
yarn global add eas-cli@latest
- name: 📱 Setup EAS build cache
uses: actions/cache@v3
with:
path: ~/.eas-build-local
key: ${{ runner.os }}-eas-build-local-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-eas-build-local-
- name: 🔄 Verify EAS CLI installation
run: |
echo "EAS CLI version:"
eas --version
- name: 📋 Fix package.json main entry (Linux)
run: |
if ! command -v jq &> /dev/null; then
sudo apt-get update && sudo apt-get install -y jq
fi
cp package.json package.json.bak
jq '.main = "node_modules/expo/AppEntry.js"' package.json > package.json.tmp && mv package.json.tmp package.json
cat package.json | grep "main"
- name: Build Development APK
if: github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'dev' || github.event_name == 'push' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
eas build --platform android --profile development --local --non-interactive --output=./app-dev.apk
env:
NODE_ENV: development
- name: 📱 Build Production AAB
if: github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'prod-aab' || github.event_name == 'push' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'android')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
eas build --platform android --profile production --local --non-interactive --output=./app-prod.aab
env:
NODE_ENV: production
- name: 🚀 Publish to Expo (optional)
if: github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'publish-expo'
run: |
eas update --auto
- name: 📦 Upload Android artifacts
uses: actions/upload-artifact@v4
with:
name: android-builds
path: |
./app-dev.apk
./app-prod.aab
if-no-files-found: ignore
retention-days: 7
build-ios:
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')) || github.event_name == 'workflow_dispatch'
runs-on: macos-latest
steps:
- name: 🏗 Checkout repository
uses: actions/checkout@v4
- name: 🏗 Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "yarn"
- name: 📦 Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- name: 📦 Setup yarn cache
uses: actions/cache@v3
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: 📦 Install dependencies
run: |
yarn install
yarn global add eas-cli@latest
- name: 📱 Setup EAS build cache
uses: actions/cache@v3
with:
path: ~/.eas-build-local
key: ${{ runner.os }}-eas-build-local-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-eas-build-local-
- name: 🔄 Verify EAS CLI installation
run: |
echo "EAS CLI version:"
eas --version
- name: 📋 Fix package.json main entry (macOS)
run: |
if ! command -v jq &> /dev/null; then
brew update || true
brew install jq || true
fi
cp package.json package.json.bak
jq '.main = "node_modules/expo/AppEntry.js"' package.json > package.json.tmp && mv package.json.tmp package.json
cat package.json | grep "main"
- name: 📱 Build iOS Development
if: (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'ios-dev') && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' || github.event_name == 'push')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
eas build --platform ios --profile development --local --non-interactive --output=./app-ios-dev.app
env:
NODE_ENV: development
- name: 📱 Build iOS Production
if: (github.event.inputs.buildType == 'all' || github.event.inputs.buildType == 'ios-prod') && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'ios' || github.event_name == 'push')
run: |
export NODE_OPTIONS="--openssl-legacy-provider --max_old_space_size=4096"
eas build --platform ios --profile production --local --non-interactive --output=./app-ios-prod.ipa
env:
NODE_ENV: production
- name: 📦 Zip iOS .app (development)
run: |
if [ -d "./app-ios-dev.app" ]; then
ditto -c -k --sequesterRsrc --keepParent "./app-ios-dev.app" "./app-ios-dev.zip"
fi
- name: 📦 Upload iOS artifacts
uses: actions/upload-artifact@v4
with:
name: ios-builds
path: |
./app-ios-dev.app
./app-ios-dev.zip
./app-ios-prod.ipa
if-no-files-found: ignore
retention-days: 7
create-release:
needs: [build-android, build-ios]
runs-on: ubuntu-latest
if: always()
steps:
- name: 🏗 Checkout repository
uses: actions/checkout@v4
- name: ⬇️ Download Android artifacts
if: ${{ needs.build-android.result == 'success' }}
uses: actions/download-artifact@v4
with:
name: android-builds
path: ./dist
- name: ⬇️ Download iOS artifacts
if: ${{ needs.build-ios.result == 'success' }}
uses: actions/download-artifact@v4
with:
name: ios-builds
path: ./dist
- name: 🏷️ Generate build information
id: build-info
run: |
VERSION=$(node -p "require('./app.json').expo.version")
BUILD_NUMBER=$(date +%Y%m%d%H%M)
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT
if git describe --tags --abbrev=0 > /dev/null 2>&1; then
LAST_TAG=$(git describe --tags --abbrev=0)
git log $LAST_TAG..HEAD --pretty=format:"- %s" > changelog.md
else
git log --pretty=format:"- %s" -n 10 > changelog.md
fi
- name: 📝 Create or Update GitHub Release
uses: softprops/action-gh-release@v2
with:
draft: true
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: |
./dist/app-dev.apk
./dist/app-prod.aab
./dist/app-ios-dev.zip
./dist/app-ios-prod.ipa
fail_on_unmatched_files: false
body_path: changelog.md
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: 📢 Notify release status
if: always()
uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ env.SLACK_WEBHOOK }}
SLACK_COLOR: ${{ job.status == 'success' && 'good' || 'danger' }}
SLACK_TITLE: Release Results
SLACK_MESSAGE: "Release ${{ job.status == 'success' && 'created/updated ✅' || 'failed ❌' }}"