Skip to content

fix(nitrogen): match Kotlin's is prefix rule for JVM accessors #70

fix(nitrogen): match Kotlin's is prefix rule for JVM accessors

fix(nitrogen): match Kotlin's is prefix rule for JVM accessors #70

Workflow file for this run

name: Nitro Performance
on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches: [main]
schedule:
- cron: '17 3 * * 1'
workflow_dispatch:
concurrency:
group: performance-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
prepare:
runs-on: ubuntu-24.04
outputs:
base_sha: ${{ steps.metadata.outputs.base_sha }}
head_sha: ${{ steps.metadata.outputs.head_sha }}
pr_number: ${{ steps.metadata.outputs.pr_number }}
relevant: ${{ steps.metadata.outputs.relevant }}
base_benchmark_available: ${{ steps.metadata.outputs.base_benchmark_available }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
fetch-depth: 0
- id: metadata
name: Resolve revisions and relevant paths
env:
EVENT_NAME: ${{ github.event_name }}
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PUSH_BASE_SHA: ${{ github.event.before }}
CURRENT_SHA: ${{ github.sha }}
run: |
set -euo pipefail
if [[ "$EVENT_NAME" == "pull_request" ]]; then
BASE_SHA="$PR_BASE_SHA"
HEAD_SHA="$PR_HEAD_SHA"
NUMBER="$PR_NUMBER"
elif [[ "$EVENT_NAME" == "push" && ! "$PUSH_BASE_SHA" =~ ^0+$ ]]; then
BASE_SHA="$PUSH_BASE_SHA"
HEAD_SHA="$CURRENT_SHA"
NUMBER="0"
else
BASE_SHA="$CURRENT_SHA"
HEAD_SHA="$CURRENT_SHA"
NUMBER="0"
fi
RELEVANT=true
if [[ "$BASE_SHA" != "$HEAD_SHA" ]] && git diff --quiet "$BASE_SHA" "$HEAD_SHA" -- \
'.github/workflows/performance.yml' \
'.github/workflows/performance-report.yml' \
'bun.lock' \
'package.json' \
'scripts/performance' \
'apps/benchmark' \
'packages/react-native-nitro-modules' \
'packages/react-native-nitro-test' \
'packages/react-native-nitro-test-external'; then
RELEVANT=false
fi
echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT"
echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
echo "pr_number=$NUMBER" >> "$GITHUB_OUTPUT"
echo "relevant=$RELEVANT" >> "$GITHUB_OUTPUT"
if git cat-file -e "$BASE_SHA:apps/benchmark/package.json" 2>/dev/null; then
echo 'base_benchmark_available=true' >> "$GITHUB_OUTPUT"
else
echo 'base_benchmark_available=false' >> "$GITHUB_OUTPUT"
fi
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
if: steps.metadata.outputs.relevant == 'true'
with:
bun-version: 1.3.14
- name: Test performance tooling
if: steps.metadata.outputs.relevant == 'true'
run: |
bun install --frozen-lockfile
bun run test:performance-tools
bun run typecheck:performance-tools
bun benchmark typecheck
bun benchmark lint-ci
android:
needs: prepare
if: needs.prepare.outputs.relevant == 'true'
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
BASE_SHA: ${{ needs.prepare.outputs.base_sha }}
HEAD_SHA: ${{ needs.prepare.outputs.head_sha }}
steps:
- name: Checkout base
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ env.BASE_SHA }}
path: base
- name: Checkout head
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ env.HEAD_SHA }}
path: head
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.14
- uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: zulu
java-version: 17
java-package: jdk
- name: Enable KVM for benchmark measurements
run: |
set -euxo pipefail
test -c /dev/kvm
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
sudo udevadm settle --timeout=30
stat --format='%A %U:%G %n' /dev/kvm
test -r /dev/kvm && test -w /dev/kvm
- name: Select trusted controller
run: |
if [[ -f base/scripts/performance/run-sequence.ts ]]; then
echo "PERF_CONTROLLER_ROOT=$GITHUB_WORKSPACE/base" >> "$GITHUB_ENV"
else
echo "PERF_CONTROLLER_ROOT=$GITHUB_WORKSPACE/head" >> "$GITHUB_ENV"
echo "PERF_BOOTSTRAP=true" >> "$GITHUB_ENV"
fi
- name: Build head benchmark APK
working-directory: head
run: |
bun install --frozen-lockfile
cd apps/benchmark/android
./gradlew :app:assembleRelease --no-daemon --no-build-cache -PreactNativeArchitectures=x86_64
- name: Build base benchmark APK
if: env.PERF_BOOTSTRAP != 'true'
working-directory: base
run: |
bun install --frozen-lockfile
cd apps/benchmark/android
./gradlew :app:assembleRelease --no-daemon --no-build-cache -PreactNativeArchitectures=x86_64
- name: Select base artifact
run: |
if [[ "$PERF_BOOTSTRAP" == "true" ]]; then
echo "BASE_APP=$GITHUB_WORKSPACE/head/apps/benchmark/android/app/build/outputs/apk/release/app-release.apk" >> "$GITHUB_ENV"
echo "BASE_ROOT=$GITHUB_WORKSPACE/head" >> "$GITHUB_ENV"
else
echo "BASE_APP=$GITHUB_WORKSPACE/base/apps/benchmark/android/app/build/outputs/apk/release/app-release.apk" >> "$GITHUB_ENV"
echo "BASE_ROOT=$GITHUB_WORKSPACE/base" >> "$GITHUB_ENV"
fi
- name: Run paired Android benchmarks
uses: reactivecircus/android-emulator-runner@4c44018e59b437e86cdfc41da381398f93ed8808 # v2
with:
api-level: 36
arch: x86_64
profile: pixel_7
disable-animations: true
# Never silently fall back to software CPU emulation: it is slow and
# its measurements are not comparable to the KVM-backed testbed.
disable-linux-hw-accel: false
emulator-boot-timeout: 300
pre-emulator-launch-script: '"$ANDROID_HOME/emulator/emulator" -accel-check'
emulator-options: -accel on -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim -camera-back none
script: >-
bun "$PERF_CONTROLLER_ROOT/scripts/performance/run-sequence.ts"
--platform android
--base-app "$BASE_APP"
--head-app "$GITHUB_WORKSPACE/head/apps/benchmark/android/app/build/outputs/apk/release/app-release.apk"
--base-root "$BASE_ROOT"
--head-root "$GITHUB_WORKSPACE/head"
--base-sha "$BASE_SHA"
--head-sha "$HEAD_SHA"
--output-directory "$GITHUB_WORKSPACE/performance-android"
--device-id "$(adb get-serialno)"
--device "Pixel 7 emulator"
--os-version "Android 16 / API 36"
--architecture x86_64
--toolchain "JDK 17 / NDK 29.0.14206865 / KVM"
--mode advisory
- name: Upload Android results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: performance-android
path: performance-android
if-no-files-found: warn
retention-days: 30
# Same-repository contributors already have access to repository workflows.
# Forks never enter this job; their reports use the default-branch workflow_run.
# Keep reporting on a fresh runner with reviewed code, not either app checkout.
publish-pr:
name: Publish performance to PR and Bencher
needs: [prepare, nitro-performance]
if: >-
github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository &&
needs.prepare.outputs.base_benchmark_available == 'true' &&
needs.nitro-performance.result == 'success'
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
actions: read
contents: read
checks: write
pull-requests: write
steps:
- name: Checkout pinned reporting code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: margelo/nitro
ref: f5857e95af6c07a67a6604c44ecbfc6bc365e234
persist-credentials: false
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.14
- name: Download completed benchmark data
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: performance-report
path: untrusted-artifact
- name: Validate against GitHub metadata and rebuild report
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
set -euo pipefail
gh api "repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID" \
--jq '{repository: .repository, workflow_run: .}' > trusted-workflow-event.json
gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER" > trusted-pull-request.json
bun scripts/performance/validate-report.ts \
--artifact-directory untrusted-artifact \
--output-directory validated-report \
--expected-repository "$GITHUB_REPOSITORY" \
--trusted-workflow-event trusted-workflow-event.json \
--trusted-pull-request trusted-pull-request.json
- name: Post paired comparison to the PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bun scripts/performance/github-report.ts --directory validated-report
- name: Install pinned Bencher CLI
if: vars.NITRO_BENCHER_ENABLED == 'true'
uses: bencherdev/bencher@8d75325c3bc59403a2186a056b472c4f49d42838 # v0.6.12
with:
version: '0.6.12'
- name: Verify pinned Bencher binary
if: vars.NITRO_BENCHER_ENABLED == 'true'
run: |
printf '%s %s\n' 'c2d3a6a7fae654246134e5ced1408bdb9ba4e198b0ac3b903af17a06574a7e08' "$(command -v bencher)" | sha256sum --check -
- name: Publish to Bencher and GitHub
if: vars.NITRO_BENCHER_ENABLED == 'true'
env:
BENCHER_API_KEY: ${{ secrets.BENCHER_KEY }}
BENCHER_PROJECT: nitro
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: bun scripts/performance/publish.ts --directory validated-report
- name: Explain disabled Bencher publishing
if: vars.NITRO_BENCHER_ENABLED != 'true'
run: echo 'Bencher uploads are disabled until the exposed key is rotated and NITRO_BENCHER_ENABLED=true. The paired PR comment is still published.' >> "$GITHUB_STEP_SUMMARY"
ios:
needs: prepare
if: needs.prepare.outputs.relevant == 'true'
runs-on: macos-26
timeout-minutes: 120
env:
BASE_SHA: ${{ needs.prepare.outputs.base_sha }}
HEAD_SHA: ${{ needs.prepare.outputs.head_sha }}
steps:
- name: Checkout base
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
ref: ${{ env.BASE_SHA }}
path: base
- name: Checkout head
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ env.HEAD_SHA }}
path: head
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: 1.3.14
- uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1.321.0
with:
ruby-version: 3.3.0
working-directory: head/apps/benchmark
- name: Select Xcode 26.5
run: sudo xcode-select -s /Applications/Xcode_26.5.app/Contents/Developer
- name: Select trusted controller
run: |
if [[ -f base/scripts/performance/run-sequence.ts ]]; then
echo "PERF_CONTROLLER_ROOT=$GITHUB_WORKSPACE/base" >> "$GITHUB_ENV"
else
echo "PERF_CONTROLLER_ROOT=$GITHUB_WORKSPACE/head" >> "$GITHUB_ENV"
echo "PERF_BOOTSTRAP=true" >> "$GITHUB_ENV"
fi
- name: Build head benchmark app
working-directory: head
run: |
bun install --frozen-lockfile
cd apps/benchmark
bundle install
bun pods
cd ios
xcodebuild \
CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ \
-derivedDataPath build-benchmark \
-workspace NitroBenchmark.xcworkspace \
-scheme NitroBenchmark \
-configuration Release \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
CODE_SIGNING_ALLOWED=NO \
COMPILER_INDEX_STORE_ENABLE=NO \
build
- name: Build base benchmark app
if: env.PERF_BOOTSTRAP != 'true'
working-directory: base
run: |
bun install --frozen-lockfile
cd apps/benchmark
bundle install
bun pods
cd ios
xcodebuild \
CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ \
-derivedDataPath build-benchmark \
-workspace NitroBenchmark.xcworkspace \
-scheme NitroBenchmark \
-configuration Release \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=YES \
CODE_SIGNING_ALLOWED=NO \
COMPILER_INDEX_STORE_ENABLE=NO \
build
- name: Select base artifact
run: |
if [[ "$PERF_BOOTSTRAP" == "true" ]]; then
echo "BASE_APP=$GITHUB_WORKSPACE/head/apps/benchmark/ios/build-benchmark/Build/Products/Release-iphonesimulator/NitroBenchmark.app" >> "$GITHUB_ENV"
echo "BASE_ROOT=$GITHUB_WORKSPACE/head" >> "$GITHUB_ENV"
else
echo "BASE_APP=$GITHUB_WORKSPACE/base/apps/benchmark/ios/build-benchmark/Build/Products/Release-iphonesimulator/NitroBenchmark.app" >> "$GITHUB_ENV"
echo "BASE_ROOT=$GITHUB_WORKSPACE/base" >> "$GITHUB_ENV"
fi
- name: Run paired iOS benchmarks
run: |
set -euo pipefail
DEVICE_ID=$(xcrun simctl create \
'Nitro Performance' \
'com.apple.CoreSimulator.SimDeviceType.iPhone-17-Pro' \
'com.apple.CoreSimulator.SimRuntime.iOS-26-5')
echo "DEVICE_ID=$DEVICE_ID" >> "$GITHUB_ENV"
xcrun simctl boot "$DEVICE_ID"
DEVICE_ID="$DEVICE_ID" bun -e '
const child = Bun.spawn(["xcrun", "simctl", "bootstatus", process.env.DEVICE_ID, "-b"], {
stdout: "inherit", stderr: "inherit", timeout: 300_000, killSignal: "SIGKILL",
});
if (await child.exited !== 0) throw new Error("iOS simulator did not boot successfully within five minutes.");
'
bun "$PERF_CONTROLLER_ROOT/scripts/performance/run-sequence.ts" \
--platform ios \
--base-app "$BASE_APP" \
--head-app "$GITHUB_WORKSPACE/head/apps/benchmark/ios/build-benchmark/Build/Products/Release-iphonesimulator/NitroBenchmark.app" \
--base-root "$BASE_ROOT" \
--head-root "$GITHUB_WORKSPACE/head" \
--base-sha "$BASE_SHA" \
--head-sha "$HEAD_SHA" \
--output-directory "$GITHUB_WORKSPACE/performance-ios" \
--device-id "$DEVICE_ID" \
--device 'iPhone 17 Pro simulator' \
--os-version 'iOS 26.5' \
--architecture arm64 \
--toolchain 'Xcode 26.5' \
--mode advisory
- name: Delete simulator
if: always() && env.DEVICE_ID != ''
run: |
xcrun simctl shutdown "$DEVICE_ID" || true
xcrun simctl delete "$DEVICE_ID" || true
- name: Upload iOS results
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: performance-ios
path: performance-ios
if-no-files-found: warn
retention-days: 30
nitro-performance:
name: nitro-performance
needs: [prepare, android, ios]
if: always()
runs-on: ubuntu-24.04
steps:
- name: Require successful preparation
if: needs.prepare.result != 'success'
run: exit 1
- name: Report not applicable
if: needs.prepare.outputs.relevant != 'true'
run: |
echo '## Nitro performance' >> "$GITHUB_STEP_SUMMARY"
echo '' >> "$GITHUB_STEP_SUMMARY"
echo 'No performance-sensitive files changed.' >> "$GITHUB_STEP_SUMMARY"
- name: Require successful platform runs
if: needs.prepare.outputs.relevant == 'true'
env:
ANDROID_RESULT: ${{ needs.android.result }}
IOS_RESULT: ${{ needs.ios.result }}
run: |
if [[ "$ANDROID_RESULT" != "success" || "$IOS_RESULT" != "success" ]]; then
echo "Android: $ANDROID_RESULT, iOS: $IOS_RESULT" >&2
exit 1
fi
- name: Checkout report tooling
if: needs.prepare.outputs.relevant == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
ref: ${{ needs.prepare.outputs.head_sha }}
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
if: needs.prepare.outputs.relevant == 'true'
with:
bun-version: 1.3.14
- name: Download Android results
if: needs.prepare.outputs.relevant == 'true'
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: performance-android
path: artifacts/android
- name: Download iOS results
if: needs.prepare.outputs.relevant == 'true'
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
with:
name: performance-ios
path: artifacts/ios
- name: Build aggregate report
if: needs.prepare.outputs.relevant == 'true'
env:
REPOSITORY: ${{ github.repository }}
PR_NUMBER: ${{ needs.prepare.outputs.pr_number }}
run: |
mkdir -p performance-report/raw/android performance-report/raw/ios
bun scripts/performance/report.ts \
--comparison artifacts/android/comparison-android.json \
--comparison artifacts/ios/comparison-ios.json \
--output performance-report/performance-report.json \
--markdown-output performance-report/performance-summary.md \
--repository "$REPOSITORY" \
--event-name "${{ github.event_name }}" \
--pull-request "$PR_NUMBER"
cp artifacts/android/bencher-android.json performance-report/
cp artifacts/ios/bencher-ios.json performance-report/
cp artifacts/android/base-*.json artifacts/android/head-*.json performance-report/raw/android/
cp artifacts/ios/base-*.json artifacts/ios/head-*.json performance-report/raw/ios/
cat performance-report/performance-summary.md >> "$GITHUB_STEP_SUMMARY"
- name: Upload aggregate report
if: needs.prepare.outputs.relevant == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: performance-report
path: performance-report
if-no-files-found: error
retention-days: 30