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
57 changes: 57 additions & 0 deletions .github/actions/setup-spm-artifacts/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: setup-spm-artifacts
description: >-
Downloads the ReactCore and ReactNativeDependencies XCFrameworks built in this
run, in both flavors, into the SwiftPM artifact root /tmp/spm-artifacts. Pass
`--artifacts /tmp/spm-artifacts --download skip` to the `react-native spm`
command that follows.
inputs:
working-directory:
description: The app directory that `npx react-native spm download` runs from.
required: true
runs:
using: composite
steps:
# Both flavors are needed by every build, not just the one it builds:
# `spm` stages both flavor framework trees and lets a per-configuration
# build setting pick one at build time, so it validates the debug/ and
# release/ artifact slots together (and `--download skip` refuses an
# incomplete slot). Please don't "optimise" this down to one flavor.
- name: Download ReactCore (Debug)
uses: ./.github/actions/download-artifact
with:
name: ReactCoreDebug.xcframework.tar.gz
path: /tmp/rc-debug
- name: Download ReactCore (Release)
uses: ./.github/actions/download-artifact
with:
name: ReactCoreRelease.xcframework.tar.gz
path: /tmp/rc-release
- name: Download ReactNativeDependencies (Debug)
uses: ./.github/actions/download-artifact
with:
name: ReactNativeDependenciesDebug.xcframework.tar.gz
path: /tmp/deps-debug
- name: Download ReactNativeDependencies (Release)
uses: ./.github/actions/download-artifact
with:
name: ReactNativeDependenciesRelease.xcframework.tar.gz
path: /tmp/deps-release
# The ordinary `spm download`, pointed at the XCFrameworks this run just
# built rather than at the published nightly (RN_CORE_TARBALL_PATH /
# RN_DEPS_TARBALL_PATH are overrides download-spm-artifacts.js already
# supports). It fills both flavor slots per call and skips any slot that
# already validates, so it runs twice: the first pass fills both slots
# from the Release tarballs, then debug/ is dropped and refilled from the
# Debug tarballs while release/ is left alone. hermes-engine is fetched
# from Maven by the command itself.
- name: Stage the XCFrameworks for SwiftPM (Debug + Release)
shell: bash
working-directory: ${{ inputs.working-directory }}
run: |
RN_CORE_TARBALL_PATH=/tmp/rc-release/ReactCoreRelease.xcframework.tar.gz \
RN_DEPS_TARBALL_PATH=/tmp/deps-release/ReactNativeDependenciesRelease.xcframework.tar.gz \
npx react-native spm download --artifacts /tmp/spm-artifacts
rm -rf /tmp/spm-artifacts/debug
RN_CORE_TARBALL_PATH=/tmp/rc-debug/ReactCoreDebug.xcframework.tar.gz \
RN_DEPS_TARBALL_PATH=/tmp/deps-debug/ReactNativeDependenciesDebug.xcframework.tar.gz \
npx react-native spm download --artifacts /tmp/spm-artifacts
45 changes: 7 additions & 38 deletions .github/actions/test-ios-helloworld/action.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
name: test-ios-helloworld
description: Test iOS Hello World
inputs:
use-frameworks:
description: Whether to build with dynamic frameworks.
default: false
ruby-version:
description: The version of ruby that must be used
default: 2.6.10
flavor:
description: The flavor of the build. Must be one of "Debug", "Release".
default: Debug
Expand All @@ -19,46 +13,21 @@ runs:
uses: ./.github/actions/setup-node
- name: Run yarn install
uses: ./.github/actions/yarn-install
- name: Setup ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ inputs.ruby-version }}
- name: Set Hermes prebuilt version
shell: bash
run: |
node ./scripts/releases/use-hermes-prebuilt.js
- name: Run yarn install again, with the correct hermes version
uses: ./.github/actions/yarn-install
- name: Download ReactNativeDependencies
uses: ./.github/actions/download-artifact
with:
name: ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/third-party
- name: Print third-party folder
shell: bash
run: ls -lR /tmp/third-party
- name: Download React Native Prebuilds
uses: ./.github/actions/download-artifact
- name: Download the XCFrameworks built in this run
uses: ./.github/actions/setup-spm-artifacts
with:
name: ReactCore${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/ReactCore
- name: Print ReactCore folder
working-directory: private/helloworld/ios
# RN_STRICT_API=1 keeps the C++ stable API enforced, as `yarn bootstrap ios` does.
- name: Set up the Swift packages
shell: bash
run: ls -lR /tmp/ReactCore
- name: Install iOS dependencies - Configuration ${{ inputs.flavor }};
shell: bash
run: |
cd private/helloworld
args=()

if [[ "${{ inputs.use-frameworks }}" == "true" ]]; then
args+=(--frameworks dynamic)
fi

export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"

yarn bootstrap ios "${args[@]}" | cat
working-directory: private/helloworld/ios
run: RN_STRICT_API=1 npx react-native spm update --artifacts /tmp/spm-artifacts --download skip

- name: Run Helloworld tests
shell: bash
Expand Down
150 changes: 52 additions & 98 deletions .github/actions/test-ios-rntester/action.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: test-ios-rntester
description: Test iOS RNTester
inputs:
ruby-version:
description: The version of ruby that must be used
default: 2.6.10
run-unit-tests:
description: whether unit tests should run or not.
default: false
Expand All @@ -14,19 +11,6 @@ inputs:
description: Whether we want to run E2E tests or not
required: false
default: false
use-frameworks:
description: Whether we have to build with Dynamic Frameworks.
required: false
default: false
use-prebuilds:
description: >-
Whether to consume the prebuilt ReactCore/ReactNativeDependencies
artifacts. 'auto' (default) keeps the historical coupling: prebuilds for
static, source for dynamic frameworks. Pass 'true' with
use-frameworks:true for the prebuilt + dynamic-frameworks lane (the
config of the 2026-07-03 SocketRocket dual-copy regression).
required: false
default: auto

runs:
using: composite
Expand All @@ -37,31 +21,6 @@ runs:
uses: ./.github/actions/setup-node
- name: Run yarn
uses: ./.github/actions/yarn-install
- name: Setup ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ inputs.ruby-version }}
bundler: none
- name: Install Bundler
shell: bash
run: |
BUNDLER_VERSION="$(awk '/BUNDLED WITH/{getline; print $1}' Gemfile.lock)"
if [ -z "$BUNDLER_VERSION" ]; then
echo "Could not determine Bundler version from Gemfile.lock." >&2
exit 1
fi
for attempt in 1 2 3; do
echo "Attempt $attempt of 3"
if gem install bundler --version "$BUNDLER_VERSION" --no-document; then
exit 0
fi
if [ "$attempt" -lt 3 ]; then
echo "Attempt $attempt failed. Retrying..."
sleep "$((attempt * 10))"
fi
done
echo "All 3 attempts failed."
exit 1
- name: Set Hermes prebuilt version
shell: bash
run: |
Expand All @@ -71,77 +30,73 @@ runs:
- name: Prepare IOS Tests
if: ${{ inputs.run-unit-tests == 'true' }}
uses: ./.github/actions/prepare-ios-tests
- name: Resolve prebuilds mode
id: prebuilds
shell: bash
run: |
if [[ "${{ inputs.use-prebuilds }}" == "auto" ]]; then
# Historical coupling: prebuilds for static, source for dynamic frameworks.
if [[ "${{ inputs.use-frameworks }}" == "true" ]]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
else
echo "enabled=true" >> "$GITHUB_OUTPUT"
fi
elif [[ "${{ inputs.use-prebuilds }}" == "true" ]]; then
echo "enabled=true" >> "$GITHUB_OUTPUT"
elif [[ "${{ inputs.use-prebuilds }}" == "false" ]]; then
echo "enabled=false" >> "$GITHUB_OUTPUT"
else
# Don't silently treat a typo as 'disabled' — surface it.
echo "::warning::Unexpected use-prebuilds value '${{ inputs.use-prebuilds }}' (expected auto/true/false); treating as disabled."
echo "enabled=false" >> "$GITHUB_OUTPUT"
fi
- name: Download ReactNativeDependencies
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
uses: ./.github/actions/download-artifact
- name: Download the XCFrameworks built in this run
uses: ./.github/actions/setup-spm-artifacts
with:
name: ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/third-party/
- name: Print third-party folder
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
working-directory: packages/rn-tester
# RNTester is a CocoaPods project in git, so developers can build React
# Native from source. CI converts it to SwiftPM on every run.
- name: Ensure CocoaPods (`spm add --deintegrate` shells out to `pod`)
shell: bash
run: ls -lR /tmp/third-party
- name: Download React Native Prebuilds
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
uses: ./.github/actions/download-artifact
with:
name: ReactCore${{ inputs.flavor }}.xcframework.tar.gz
path: /tmp/ReactCore
- name: Print ReactCore folder
if: ${{ steps.prebuilds.outputs.enabled == 'true' }}
run: pod --version || sudo gem install cocoapods --no-document
# react-native-test-library ships only a podspec; scaffold writes its
# Package.swift.
- name: Scaffold Package.swift manifests for community dependencies
shell: bash
working-directory: packages/rn-tester
run: npx react-native spm scaffold --artifacts /tmp/spm-artifacts --download skip || true
- name: Convert RNTester to SwiftPM
shell: bash
run: ls -lR /tmp/ReactCore
- name: Install CocoaPods dependencies
working-directory: packages/rn-tester
run: |
# pod deintegrate fails on non-ASCII strings without a UTF-8 locale.
LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 RN_STRICT_API=1 \
npx react-native spm add --deintegrate --artifacts /tmp/spm-artifacts --download skip
- name: Assert RNTester is on SwiftPM
shell: bash
working-directory: packages/rn-tester
run: |
if [[ ${{ inputs.use-frameworks }} == "true" ]]; then
export USE_FRAMEWORKS=dynamic
if [[ ! -f RNTesterPods.xcodeproj/.spm-injected.json ]]; then
echo "::error::spm add did not inject SwiftPM: RNTesterPods.xcodeproj/.spm-injected.json is missing"
exit 1
fi
if [[ "${{ steps.prebuilds.outputs.enabled }}" == "true" ]]; then
export RCT_USE_LOCAL_RN_DEP="/tmp/third-party/ReactNativeDependencies${{ inputs.flavor }}.xcframework.tar.gz"
export RCT_TESTONLY_RNCORE_TARBALL_PATH="/tmp/ReactCore/ReactCore${{ inputs.flavor }}.xcframework.tar.gz"
if grep -q 'Pods-' RNTesterPods.xcodeproj/project.pbxproj; then
echo "::error::spm add --deintegrate left CocoaPods references in RNTesterPods.xcodeproj"
exit 1
fi

cd packages/rn-tester

bundle install
bundle exec pod update hermes-engine --no-repo-update
echo "SwiftPM injected in place; CocoaPods de-integrated."
- name: Build RNTester
shell: bash
run: |
xcodebuild \
-scheme "RNTester" \
-workspace packages/rn-tester/RNTesterPods.xcworkspace \
-project packages/rn-tester/RNTesterPods.xcodeproj \
-configuration "${{ inputs.flavor }}" \
-sdk "iphonesimulator" \
-destination "generic/platform=iOS Simulator" \
-derivedDataPath "/tmp/RNTesterBuild"

echo "Print path to *.app file"
APP_PATH=$(find "/tmp/RNTesterBuild" -type d -name "*.app")

echo "App found at $APP_PATH"
echo "app-path=$APP_PATH" >> $GITHUB_ENV
APP_PATH="/tmp/RNTesterBuild/Build/Products/${{ inputs.flavor }}-iphonesimulator/RNTester.app"
echo "App found at $APP_PATH"
echo "app-path=$APP_PATH" >> $GITHUB_ENV
- name: Check the embedded React.framework flavor
shell: bash
run: |
BINARY="${{ env.app-path }}/Frameworks/React.framework/React"
if [[ ! -f "$BINARY" ]]; then
echo "::error::No embedded React.framework binary at $BINARY"
exit 1
fi
COUNT=$(nm "$BINARY" | grep -c getDebugProps || true)
echo "getDebugProps symbols in $BINARY: $COUNT"
if [[ "${{ inputs.flavor }}" == 'Debug' && "$COUNT" -eq 0 ]]; then
echo "::error::Debug build embeds a Release React.framework (expected getDebugProps symbols, found none)"
exit 1
fi
if [[ "${{ inputs.flavor }}" == 'Release' && "$COUNT" -ne 0 ]]; then
echo "::error::Release build embeds a Debug React.framework ($COUNT getDebugProps symbols, expected none)"
exit 1
fi
- name: 'Run Tests: iOS Unit and Integration Tests'
if: ${{ inputs.run-unit-tests == 'true' }}
shell: bash
Expand All @@ -158,10 +113,9 @@ runs:
uses: ./.github/actions/upload-artifact
if: ${{ inputs.run-unit-tests == 'true' }}
with:
name: xcresults-${{ inputs.flavor }}-${{ inputs.use-frameworks == 'true' && 'dynamic-frameworks' || 'static-libraries' }}-Ruby${{ inputs.ruby-version }}
name: xcresults-${{ inputs.flavor }}
path: /Users/runner/Library/Developer/Xcode/xcresults.tar.gz
- name: Upload RNTester App
if: ${{ inputs.use-frameworks == 'false' && inputs.ruby-version == '2.6.10' }} # This is needed to avoid conflicts with the artifacts
uses: ./.github/actions/upload-artifact
with:
name: RNTesterApp-NewArch-${{ inputs.flavor }}
Expand All @@ -170,5 +124,5 @@ runs:
if: ${{ inputs.run-unit-tests == 'true' }}
uses: ./.github/actions/upload-artifact
with:
name: test-results-${{ inputs.flavor }}-${{ inputs.use-frameworks == 'true' && 'dynamic-frameworks' || 'static-libraries' }}-Ruby${{ inputs.ruby-version }}
name: test-results-${{ inputs.flavor }}
path: ./reports/junit
4 changes: 1 addition & 3 deletions .github/workflows/e2e-ios-rntester.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,7 @@ jobs:
if [[ "${{ steps.test-state.outputs.should-run }}" == "false" || "${{ steps.run-tests.outcome }}" == "success" ]]; then
RESULT=passed
elif [[ "${{ steps.download-app.outcome }}" == "failure" ]]; then
# test_ios_rntester uses continue-on-error, so it reports success
# even when the build that uploads this app failed.
echo "::error::RNTesterApp-NewArch-${{ matrix.flavor }} is missing. Check the test_ios_rntester (${{ matrix.flavor }}, frameworks: false) job."
echo "::error::RNTesterApp-NewArch-${{ matrix.flavor }} is missing. Check the test_ios_rntester (${{ matrix.flavor }}) job."
RESULT=failed
elif [[ -f "$STATE" ]] && jq -e 'any(.flows[]; .status == "failed")' "$STATE" > /dev/null; then
RESULT=failed
Expand Down
Loading
Loading