CI: add SwiftPM iOS e2e workflows (RNTester, HelloWorld, new app)#57659
CI: add SwiftPM iOS e2e workflows (RNTester, HelloWorld, new app)#57659chrfalch wants to merge 4 commits into
Conversation
Adds three standalone workflows that scaffold + convert an iOS app to
SwiftPM using the prebuilt XCFrameworks and compile it:
- test-ios-spm-rntester.yml (packages/rn-tester)
- test-ios-spm-helloworld.yml (private/helloworld)
- test-ios-spm-newapp.yml (fresh copy of helloworld, wired to a
Verdaccio-published local monorepo build)
None of these use @react-native-community/cli. Each workflow builds the
prebuilt dependencies + core XCFrameworks via the existing reusable
prebuild-ios-* workflows, primes both flavor slots, then runs the shared
logic in two committed, locally-runnable scripts:
- scripts/e2e/spm-prime-artifacts.js (lay CI tarballs + hermes into the
--artifacts <dir>/<flavor>/ layout)
- scripts/e2e/spm-ios-e2e.js (scaffold -> spm add --deintegrate
-> xcodebuild, per app)
Triggered manually (workflow_dispatch) and nightly (schedule).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an optional `slices` input to prebuild-ios-dependencies.yml and prebuild-ios-core.yml, driving their build matrix from it. It defaults to the full platform set, so existing callers (test-all.yml, releases) are unchanged. The three SwiftPM iOS e2e workflows only ever compile for the iOS simulator, so they now request `["ios-simulator"]` from both prebuild workflows — cutting the dependencies matrix from 8 slices to 1 and the core matrix from 3 to 1 (per flavor), which is the dominant cost of those e2e runs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- spm-ios-e2e.js: build the execFileSync `env` from process.env's string entries only (process.env is Flow-typed to allow numbers, which the execFileSync `env` option rejects), and omit `env` entirely when there is no override so the child inherits process.env as-is. - Both scripts: suppress the parseArgs [incompatible-type] error with the standard "Natural Inference rollout" $FlowFixMe, matching set-version.js and init-project-e2e.js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
execFileSync's `env` option is invariantly typed {[string]: string |
number | void}, so neither a plain process.env spread ({[string]: string
| void}) nor a string-only object satisfies it. Build the env object
into a correctly-typed indexer instead.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cipolleschi
left a comment
There was a problem hiding this comment.
Thanks for hardening our CI. However, I think that we are not quite there. This approach does not follow the patterns we are currently using in CI. I left a few comments to improve this approach.
| prebuild_apple_dependencies: | ||
| uses: ./.github/workflows/prebuild-ios-dependencies.yml | ||
| with: | ||
| # e2e only compiles for the iOS simulator — no need for device/catalyst/tv/xr slices. | ||
| slices: '["ios-simulator"]' | ||
| secrets: inherit | ||
|
|
||
| prebuild_react_native_core: | ||
| needs: [prebuild_apple_dependencies] | ||
| if: ${{ needs.prebuild_apple_dependencies.result == 'success' }} | ||
| uses: ./.github/workflows/prebuild-ios-core.yml | ||
| with: | ||
| use-hermes-prebuilt: ${{ !endsWith(github.ref_name, '-stable') }} | ||
| slices: '["ios-simulator"]' | ||
| secrets: inherit |
There was a problem hiding this comment.
I think you have to depend on the job that creates the XCFramework and download the XCFramework directly and use that one. We don't want to run the prebuild for these jobs. We want to consume the prebuilds we are creating.
| - name: Download ReactCore (Debug) | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: ReactCoreDebug.xcframework.tar.gz | ||
| path: /tmp/rc-debug | ||
| - name: Download ReactCore (Release) | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: ReactCoreRelease.xcframework.tar.gz | ||
| path: /tmp/rc-release |
There was a problem hiding this comment.
you don't have to download both. You can have a matrix and run Debug and Release in parallel.
| prebuild_apple_dependencies: | ||
| uses: ./.github/workflows/prebuild-ios-dependencies.yml | ||
| with: | ||
| # e2e only compiles for the iOS simulator — no need for device/catalyst/tv/xr slices. | ||
| slices: '["ios-simulator"]' | ||
| secrets: inherit | ||
|
|
||
| prebuild_react_native_core: | ||
| needs: [prebuild_apple_dependencies] | ||
| if: ${{ needs.prebuild_apple_dependencies.result == 'success' }} | ||
| uses: ./.github/workflows/prebuild-ios-core.yml | ||
| with: | ||
| use-hermes-prebuilt: ${{ !endsWith(github.ref_name, '-stable') }} | ||
| slices: '["ios-simulator"]' | ||
| secrets: inherit |
| build_npm_package: | ||
| runs-on: 8-core-ubuntu | ||
| container: | ||
| image: reactnativecommunity/react-native-android:latest | ||
| env: | ||
| TERM: 'dumb' | ||
| LC_ALL: C.UTF8 | ||
| GRADLE_OPTS: '-Dorg.gradle.daemon=false' | ||
| REACT_NATIVE_DOWNLOADS_DIR: /opt/react-native-downloads | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v6 | ||
| - name: Build NPM Package | ||
| uses: ./.github/actions/build-npm-package | ||
| with: | ||
| release-type: dry-run | ||
| gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }} | ||
| skip-apple-prebuilts: 'true' |
There was a problem hiding this comment.
I don't think we rerun the build npm package when running the template app e2e tests. we should not do it here either.
| @@ -0,0 +1,115 @@ | |||
| name: Test iOS SwiftPM - Hello World | |||
There was a problem hiding this comment.
i would just create the workflow and call it from test-all. I would not add a cron job here. We should rely on the nightly trigger.
| @@ -0,0 +1,155 @@ | |||
| name: Test iOS SwiftPM - New App | |||
| - name: Download ReactCore (Debug) | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: ReactCoreDebug.xcframework.tar.gz | ||
| path: /tmp/rc-debug | ||
| - name: Download ReactCore (Release) | ||
| uses: actions/download-artifact@v7 | ||
| with: | ||
| name: ReactCoreRelease.xcframework.tar.gz | ||
| path: /tmp/rc-release |
There was a problem hiding this comment.
same as above, Debug and release should run in parallel.
| @@ -0,0 +1,115 @@ | |||
| name: Test iOS SwiftPM - RN Tester | |||
| @@ -0,0 +1,525 @@ | |||
| /** | |||
There was a problem hiding this comment.
do we need this? can't we run the commands you show during the AppJS talk?
| @@ -0,0 +1,185 @@ | |||
| /** | |||
There was a problem hiding this comment.
I don't think we need this for the testing. We know in advance what we are running and we can have two parallel jobs running one debug and one release.
Summary:
Adds CI coverage for the SwiftPM (SPM) iOS path, which currently has none. Three standalone workflows scaffold + convert an app to SPM using the prebuilt XCFrameworks and compile it for the iOS simulator:
test-ios-spm-rntester.yml—packages/rn-testertest-ios-spm-helloworld.yml—private/helloworldtest-ios-spm-newapp.yml— a fresh copy ofprivate/helloworld, wired to a Verdaccio-published build of this monorepoNone of them use
@react-native-community/cli. The shared logic lives in two committed, locally-runnable scripts (scripts/e2e/spm-prime-artifacts.js,scripts/e2e/spm-ios-e2e.js); the workflows are thin wrappers. They run manually (workflow_dispatch) and nightly (schedule).This also adds an optional
slicesinput toprebuild-ios-dependencies.ymlandprebuild-ios-core.yml. It defaults to the full platform set, so existing callers (test-all.yml, releases) are unchanged; the e2e workflows request onlyios-simulator, since that is all they compile against.Changelog:
[INTERNAL] [ADDED] - CI workflows validating the SwiftPM iOS build (RNTester, HelloWorld, new app)
Test Plan:
spm add+xcodebuild. Theslicesinput correctly collapsed the prebuild matrices to the singleios-simulatorslice per flavor.node --check,prettier --check, andeslintclean on both scripts; all workflow YAML validated.