Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 117 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
# Android emulator configuration
ANDROID_API_LEVEL: '35'
ANDROID_ARCH: 'x86_64'
ANDROID_PROFILE: 'pixel_6'
ANDROID_AVD_NAME: 'Pixel_8_API_35'
# iOS simulator configuration
IOS_DEVICE_MODEL: 'iPhone 17 Pro'
IOS_VERSION: '26.0'
XCODE_VERSION: '26.1.1'

jobs:
lint:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -44,7 +55,7 @@ jobs:
- name: Build package
run: yarn prepare

build-android:
harness-android:
runs-on: ubuntu-latest

env:
Expand All @@ -65,28 +76,43 @@ jobs:
restore-keys: |
${{ runner.os }}-turborepo-android-

- name: Check turborepo cache for Android
- name: Check turborepo cache status
run: |
TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
# Check if harness tests can be skipped entirely
HARNESS_CACHE_STATUS=$(node -p "($(yarn turbo run test:harness:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'test:harness:android').cache.status")
if [[ $HARNESS_CACHE_STATUS == "HIT" ]]; then
echo "harness_cache_hit=1" >> $GITHUB_ENV
fi

if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
echo "turbo_cache_hit=1" >> $GITHUB_ENV
# Check if native build can be skipped (to avoid installing JDK/Gradle)
BUILD_CACHE_STATUS=$(node -p "($(yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:android').cache.status")
if [[ $BUILD_CACHE_STATUS == "HIT" ]]; then
echo "build_cache_hit=1" >> $GITHUB_ENV
fi

- name: Reclaim disk space
if: env.harness_cache_hit != 1
uses: AdityaGarg8/remove-unwanted-software@v5
with:
remove-dotnet: true
remove-haskell: true
remove-codeql: true
remove-docker-images: true

- name: Install JDK
if: env.turbo_cache_hit != 1
if: env.harness_cache_hit != 1 && env.build_cache_hit != 1
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
with:
distribution: 'zulu'
java-version: '17'

- name: Finalize Android SDK
if: env.turbo_cache_hit != 1
if: env.harness_cache_hit != 1 && env.build_cache_hit != 1
run: |
/bin/bash -c "yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager --licenses > /dev/null"

- name: Cache Gradle
if: env.turbo_cache_hit != 1
if: env.harness_cache_hit != 1 && env.build_cache_hit != 1
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: |
Expand All @@ -97,16 +123,64 @@ jobs:
${{ runner.os }}-gradle-

- name: Build example for Android
if: env.harness_cache_hit != 1
env:
JAVA_OPTS: "-XX:MaxHeapSize=6g"
run: |
yarn turbo run build:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"

build-ios:
- name: Enable KVM group perms
if: env.harness_cache_hit != 1
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
ls /dev/kvm

- name: AVD cache
if: env.harness_cache_hit != 1
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
id: avd-cache
with:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-${{ env.ANDROID_API_LEVEL }}-${{ env.ANDROID_ARCH }}

- name: Create AVD and generate snapshot for caching
if: env.harness_cache_hit != 1 && steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ env.ANDROID_API_LEVEL }}
arch: ${{ env.ANDROID_ARCH }}
profile: ${{ env.ANDROID_PROFILE }}
disk-size: 1G
heap-size: 1G
force-avd-creation: false
avd-name: ${{ env.ANDROID_AVD_NAME }}
disable-animations: true
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: echo "Generated AVD snapshot for caching."

- name: Run Harness E2E tests
if: env.harness_cache_hit != 1
uses: reactivecircus/android-emulator-runner@v2
with:
working-directory: example
api-level: ${{ env.ANDROID_API_LEVEL }}
arch: ${{ env.ANDROID_ARCH }}
force-avd-creation: false
avd-name: ${{ env.ANDROID_AVD_NAME }}
disable-animations: true
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
script: |
adb install -r "./android/app/build/outputs/apk/debug/app-debug.apk"
yarn turbo run test:harness:android --cache-dir="${{ env.TURBO_CACHE_DIR }}"

harness-ios:
runs-on: macos-latest

env:
XCODE_VERSION: 16.3
TURBO_CACHE_DIR: .turbo/ios
RCT_USE_RN_DEP: 1
RCT_USE_PREBUILT_RNCORE: 1
Expand All @@ -126,28 +200,55 @@ jobs:
restore-keys: |
${{ runner.os }}-turborepo-ios-

- name: Check turborepo cache for iOS
- name: Check turborepo cache status
run: |
TURBO_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
# Check if harness tests can be skipped entirely
HARNESS_CACHE_STATUS=$(node -p "($(yarn turbo run test:harness:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'test:harness:ios').cache.status")
if [[ $HARNESS_CACHE_STATUS == "HIT" ]]; then
echo "harness_cache_hit=1" >> $GITHUB_ENV
fi

if [[ $TURBO_CACHE_STATUS == "HIT" ]]; then
echo "turbo_cache_hit=1" >> $GITHUB_ENV
# Check if native build can be skipped (to avoid installing Xcode/CocoaPods)
BUILD_CACHE_STATUS=$(node -p "($(yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}" --dry=json)).tasks.find(t => t.task === 'build:ios').cache.status")
if [[ $BUILD_CACHE_STATUS == "HIT" ]]; then
echo "build_cache_hit=1" >> $GITHUB_ENV
fi

- name: Use appropriate Xcode version
if: env.turbo_cache_hit != 1
if: env.harness_cache_hit != 1 && env.build_cache_hit != 1
uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
with:
xcode-version: ${{ env.XCODE_VERSION }}

- name: Install cocoapods
if: env.turbo_cache_hit != 1 && steps.cocoapods-cache.outputs.cache-hit != 'true'
if: env.harness_cache_hit != 1 && env.build_cache_hit != 1
run: |
cd example
bundle install
bundle exec pod repo update --verbose
bundle exec pod install --project-directory=ios

- name: Build example for iOS
if: env.harness_cache_hit != 1
run: |
yarn turbo run build:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"

- name: Setup iOS Simulator
if: env.harness_cache_hit != 1
uses: futureware-tech/simulator-action@v4
with:
model: ${{ env.IOS_DEVICE_MODEL }}
os: iOS
os_version: ${{ env.IOS_VERSION }}
wait_for_boot: true
erase_before_boot: false

- name: Install app on simulator
if: env.harness_cache_hit != 1
run: |
xcrun simctl install booted example/ios/build/Build/Products/Debug-iphonesimulator/WebworkerExample.app

- name: Run Harness E2E tests
if: env.harness_cache_hit != 1
run: |
yarn turbo run test:harness:ios --cache-dir="${{ env.TURBO_CACHE_DIR }}"
4 changes: 2 additions & 2 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native start",
"build:android": "react-native build-android --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a\"",
"build:ios": "react-native build-ios --mode Debug",
"build:android": "react-native build-android --tasks assembleDebug --extra-params \"--no-daemon --console=plain -PreactNativeArchitectures=x86_64\"",
"build:ios": "react-native build-ios --mode Debug --buildFolder build",
"test:harness:ios": "react-native-harness --harnessRunner ios",
"test:harness:android": "react-native-harness --harnessRunner android"
},
Expand Down
4 changes: 2 additions & 2 deletions example/rn-harness.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const config = {
runners: [
androidPlatform({
name: 'android',
device: androidEmulator('Pixel_8_API_33', {
device: androidEmulator('Pixel_8_API_35', {
apiLevel: 35,
profile: 'pixel_6',
diskSize: '1G',
Expand All @@ -24,7 +24,7 @@ const config = {
}),
applePlatform({
name: 'ios',
device: appleSimulator('iPhone 16 Pro', '26.0'),
device: appleSimulator('iPhone 17 Pro', '26.0'),
bundleId: 'webworker.example',
}),
],
Expand Down
41 changes: 38 additions & 3 deletions turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,16 @@
"env": ["ANDROID_HOME", "ORG_GRADLE_PROJECT_newArchEnabled"],
"inputs": [
"package.json",
"cpp",
"android",
"!android/build",
"src/*.ts",
"src/*.tsx",
"example/package.json",
"example/android",
"!example/android/.gradle",
"!example/android/build",
"!example/android/app/build"
],
"outputs": []
"outputs": ["example/android/app/build/outputs/apk/debug/app-debug.apk"]
},
"build:ios": {
"env": [
Expand All @@ -28,11 +27,47 @@
],
"inputs": [
"package.json",
"cpp",
"*.podspec",
"ios",
"example/package.json",
"example/ios",
"!example/ios/build",
"!example/ios/Pods"
],
"outputs": [
"example/ios/build/Build/Products/Debug-iphonesimulator/WebworkerExample.app/**"
]
},
"test:harness:android": {
"dependsOn": ["build:android"],
"inputs": [
"package.json",
"cpp",
"android",
"!android/build",
"src/*.ts",
"src/*.tsx",
"example/package.json",
"example/src",
"example/android",
"!example/android/.gradle",
"!example/android/build",
"!example/android/app/build"
],
"outputs": []
},
"test:harness:ios": {
"dependsOn": ["build:ios"],
"inputs": [
"package.json",
"cpp",
"*.podspec",
"ios",
"src/*.ts",
"src/*.tsx",
"example/package.json",
"example/src",
"example/ios",
"!example/ios/build",
"!example/ios/Pods"
Expand Down
Loading