Skip to content
Merged
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
118 changes: 111 additions & 7 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
name: Check

env:
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
NDK_VERSION: 27.1.12297006

on:
push:
branches:
- main
pull_request:
types: [labeled, opened, synchronize, reopened]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -21,13 +26,66 @@ jobs:
node-version: lts/jod
- run: npm ci
- run: npm run lint
test:
unit-tests:
strategy:
fail-fast: false
matrix:
runner: [ubuntu-latest, windows-latest, macos-latest]
name: Test (${{ matrix.runner }})
runner:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.runner }}
name: Unit tests (${{ matrix.runner }})
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/jod
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "temurin"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm guessing rustup just skips the nonsensical toolchain combinations (ios-sim on windows, etc)?

Copy link
Collaborator Author

@kraenhansen kraenhansen Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure you can cross-compile any of these. Or at the very least install the targets on any system.

- run: npm ci
- run: npm run bootstrap
- run: npm test
test-ios:
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Apple 🍎')
name: Test app (iOS)
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/jod
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "temurin"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
with:
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
- run: npm ci
- run: npm run bootstrap
- run: npm run pod-install
working-directory: apps/test-app
- name: Run tests (iOS)
run: npm run test:ios
# TODO: Enable release mode when it works
# run: npm run test:ios -- --mode Release
working-directory: apps/test-app
test-android:
if: github.ref == 'refs/heads/main' || contains(github.event.pull_request.labels.*.name, 'Android 🤖')
name: Test app (Android)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand All @@ -40,9 +98,55 @@ jobs:
distribution: "temurin"
- name: Setup Android SDK
uses: android-actions/setup-android@v3
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
- run: sdkmanager --install "ndk;27.1.12297006"
- run: rustup target add x86_64-linux-android aarch64-linux-android aarch64-apple-ios-sim
with:
packages: tools platform-tools ndk;${{ env.NDK_VERSION }}
- run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi i686-linux-android aarch64-apple-ios-sim
- run: npm ci
- run: npm run bootstrap
- run: npm test
- name: Clone patched Hermes version
shell: bash
run: |
REACT_NATIVE_OVERRIDE_HERMES_DIR=$(npx react-native-node-api vendor-hermes --silent)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could being silent here hide any warnings that are early signs of issues?

echo "REACT_NATIVE_OVERRIDE_HERMES_DIR=$REACT_NATIVE_OVERRIDE_HERMES_DIR" >> $GITHUB_ENV
working-directory: apps/test-app
# - name: Setup Android Emulator cache
# uses: actions/cache@v4
# id: avd-cache
# with:
# path: |
# ~/.android/avd/*
# ~/.android/adb*
# key: ${{ runner.os }}-avd-29
# See https://github.com/marketplace/actions/android-emulator-runner#running-hardware-accelerated-emulators-on-linux-runners
- name: Enable KVM group perms
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
- name: Build weak-node-api for all architectures
run: npm run build-weak-node-api -- --android
working-directory: packages/host
- name: Build ferric-example for all architectures
run: npm run build -- --android
working-directory: packages/ferric-example
- name: Run tests (Android)
timeout-minutes: 75
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 29
force-avd-creation: false
emulator-options: -no-snapshot-save -no-metrics -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
arch: x86
ndk: ${{ env.NDK_VERSION }}
cmake: 3.22.1
working-directory: apps/test-app
script: |
# Setup port forwarding to Mocha Remote
adb reverse tcp:8090 tcp:8090
# Uninstall the app if already in the snapshot (unlikely but could result in a signature mismatch failure)
adb uninstall com.microsoft.reacttestapp || true
# Build, install and run the app
npm run test:android -- --mode Release
# Wait a bit for the sub-process to terminate, before terminating the emulator
sleep 5
93 changes: 55 additions & 38 deletions apps/test-app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,73 @@
import React from "react";
import { StyleSheet, Text, View, Button } from "react-native";
import { StyleSheet, View, SafeAreaView } from "react-native";

/* eslint-disable @typescript-eslint/no-require-imports -- We're using require to defer crashes */
import {
MochaRemoteProvider,
ConnectionText,
StatusEmoji,
StatusText,
} from "mocha-remote-react-native";

// import { requireNodeAddon } from "react-native-node-api";
import nodeAddonExamples from "react-native-node-addon-examples";
// import * as ferricExample from "ferric-example";
import nodeAddonExamples from "@react-native-node-api/node-addon-examples";

function App(): React.JSX.Element {
function loadTests() {
for (const [suiteName, examples] of Object.entries(nodeAddonExamples)) {
describe(suiteName, () => {
for (const [exampleName, requireExample] of Object.entries(examples)) {
it(exampleName, () => {
requireExample();
});
}
});
}

describe("ferric-example", () => {
it("exports a callable sum function", () => {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const exampleAddon = require("ferric-example");
const result = exampleAddon.sum(1, 3);
if (result !== 4) {
throw new Error(`Expected 1 + 3 to equal 4, but got ${result}`);
}
});
});
}

export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>React Native Node-API Modules</Text>
{Object.entries(nodeAddonExamples).map(([suiteName, examples]) => (
<View key={suiteName} style={styles.suite}>
<Text>{suiteName}</Text>
{Object.entries(examples).map(([exampleName, requireExample]) => (
<Button
key={exampleName}
title={exampleName}
onPress={requireExample}
/>
))}
<MochaRemoteProvider tests={loadTests}>
<SafeAreaView style={styles.container}>
<ConnectionText style={styles.connectionText} />
<View style={styles.statusContainer}>
<StatusEmoji style={styles.statusEmoji} />
<StatusText style={styles.statusText} />
</View>
))}
<View key="ferric-example" style={styles.suite}>
<Text>ferric-example</Text>
<Button
title={"Ferric Example: sum(1, 3)"}
onPress={() =>
console.log("1+3 = " + require("ferric-example").sum(1, 3))
}
/>
</View>
</View>
</SafeAreaView>
</MochaRemoteProvider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
backgroundColor: "#fff",
},
statusContainer: {
flex: 1,
alignItems: "center",
justifyContent: "center",
},
suite: {
borderWidth: 1,
width: "96%",
margin: 10,
padding: 10,
statusEmoji: {
fontSize: 30,
margin: 30,
textAlign: "center",
},
title: {
statusText: {
fontSize: 20,
margin: 20,
textAlign: "center",
},
connectionText: {
textAlign: "center",
},
});

export default App;
13 changes: 9 additions & 4 deletions apps/test-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
"private": true,
"version": "0.1.0",
"scripts": {
"android": "react-native run-android",
"build:android": "react-native bundle --entry-file index.js --platform android --dev true --bundle-output dist/main.android.jsbundle --assets-dest dist/res",
"ios": "react-native run-ios",
"metro": "react-native start --no-interactive",
"android": "react-native run-android --no-packager --active-arch-only",
"ios": "react-native run-ios --no-packager",
"pod-install": "cd ios && pod install",
"start": "react-native start"
"test:android": "mocha-remote --exit-on-error -- concurrently --kill-others-on-fail --passthrough-arguments npm:metro 'npm:android -- {@}' --",
"test:ios": "mocha-remote --exit-on-error -- concurrently --passthrough-arguments --kill-others-on-fail npm:metro 'npm:ios -- {@}' --"
},
"dependencies": {
"@babel/core": "^7.26.10",
Expand All @@ -21,7 +22,11 @@
"@react-native/typescript-config": "0.79.0",
"@rnx-kit/metro-config": "^2.0.1",
"@types/react": "^19.0.0",
"concurrently": "^9.1.2",
"ferric-example": "^0.1.0",
"mocha": "^11.6.0",
"mocha-remote-cli": "^1.13.2",
"mocha-remote-react-native": "^1.13.2",
"react": "19.0.0",
"react-native": "0.79.1",
"react-native-node-addon-examples": "*",
Expand Down
Loading
Loading