diff --git a/e2e/BITRISE.md b/e2e/BITRISE.md
index 281fc049b..55c8372fb 100644
--- a/e2e/BITRISE.md
+++ b/e2e/BITRISE.md
@@ -60,9 +60,10 @@ Duplicate in-progress PR pipelines are cancelled by Bitrise native Rolling build
Nightly pipelines ship the sample apps to the stores from the same E2E test storefront the PR pipeline uses, so they need no storefront configuration of their own.
-| Pipeline | App | Destination |
-| ------------------------------ | ---------------------- | ----------- |
-| `nightly-swift-ios-testflight` | `CheckoutKitSwiftDemo` | TestFlight |
+| Pipeline | App | Destination |
+| ------------------------------------- | ---------------------------- | ----------- |
+| `nightly-swift-ios-testflight` | `CheckoutKitSwiftDemo` | TestFlight |
+| `nightly-react-native-ios-testflight` | `CheckoutKitReactNativeDemo` | TestFlight |
These pipelines are deliberately absent from `trigger_map`, so nothing starts them on a pull request. Create a daily **scheduled build** under **Project settings > Scheduled builds**, targeting `main` and selecting the pipeline. The schedule is the one part of this design that Bitrise keeps outside the repository.
@@ -76,7 +77,7 @@ The gate asks whether HEAD was committed inside `NIGHTLY_COMMIT_WINDOW`, which d
The build number is `$BITRISE_BUILD_NUMBER`, injected as an `xcodebuild` build-setting override. No committed file changes value, so nothing has to be bumped by hand and no two uploads can collide.
-This only works because the sample's XcodeGen spec binds `CFBundleVersion` to `$(CURRENT_PROJECT_VERSION)`. Without that binding XcodeGen writes the setting's current value into the plist as a literal, an override is silently discarded, and App Store Connect rejects every upload after the first. `e2e/scripts/build_swift_ios_testflight` re-reads the archived plist and fails the build if the number did not land.
+This only works because each sample binds `CFBundleVersion` to `$(CURRENT_PROJECT_VERSION)` rather than to a literal. `CheckoutKitSwiftDemo` binds it in its XcodeGen spec, and `CheckoutKitReactNativeDemo` binds it in its committed `Info.plist`. Without that binding the literal wins, the override is silently discarded, and App Store Connect rejects every upload after the first. Both build scripts call `e2e_assert_archived_build_number`, which re-reads the archived plist and fails the build if the number did not land.
### Signing
@@ -84,9 +85,10 @@ The nightly iOS build passes its signing arguments explicitly and calls `e2e_rej
Each nightly iOS workflow names its profile in `NIGHTLY_IOS_PROVISIONING_PROFILE_SPECIFIER`, under that workflow's `envs` in `e2e/bitrise.yml`. The build script has no default and exits if the variable is missing, so a renamed profile fails the build immediately instead of signing with the wrong identity. The variable is workflow-scoped rather than an `app.envs` entry, because each app needs its own profile.
-| App | Profile | Export method |
-| ---------------------- | ---------------------------------------------- | ------------------- |
-| `CheckoutKitSwiftDemo` | `PP-Bitrise-com.shopify.checkoutkit.swiftdemo` | `app-store-connect` |
+| App | Profile | Export method |
+| ---------------------------- | ---------------------------------------------------- | ------------------- |
+| `CheckoutKitSwiftDemo` | `PP-Bitrise-com.shopify.checkoutkit.swiftdemo` | `app-store-connect` |
+| `CheckoutKitReactNativeDemo` | `PP-Bitrise-com.shopify.checkoutkit.reactnativedemo` | `app-store-connect` |
Required Bitrise code signing assets, beyond the E2E development assets:
diff --git a/e2e/bitrise.yml b/e2e/bitrise.yml
index e4a3b1440..046d1735d 100644
--- a/e2e/bitrise.yml
+++ b/e2e/bitrise.yml
@@ -94,6 +94,15 @@ pipelines:
depends_on:
- nightly-decide-should-build
+ nightly-react-native-ios-testflight:
+ workflows:
+ nightly-decide-should-build: {}
+ nightly-release-react-native-ios:
+ run_if:
+ expression: '{{ enveq "NIGHTLY_SHOULD_BUILD" "true" }}'
+ depends_on:
+ - nightly-decide-should-build
+
step_bundles:
install-node-modules:
steps:
@@ -482,3 +491,31 @@ workflows:
- ipa_path: $NIGHTLY_SWIFT_IOS_IPA_PATH
- deploy-to-bitrise-io@2:
is_always_run: false
+
+ nightly-release-react-native-ios:
+ meta:
+ bitrise.io:
+ stack: *macos_stack
+ machine_type_id: g2.mac.4large
+ envs:
+ - NIGHTLY_IOS_PROVISIONING_PROFILE_SPECIFIER: PP-Bitrise-com.shopify.checkoutkit.reactnativedemo
+ steps:
+ - git-clone@8: {}
+ - certificate-and-profile-installer@1: {}
+ - bundle::install-node-modules: {}
+ - bundle::install-ruby-gems: {}
+ - bundle::install-cocoapods: {}
+ - script@1:
+ title: Build the React Native iOS App Store IPA
+ timeout: 5400
+ no_output_timeout: 1800
+ inputs:
+ - content: |-
+ set -euo pipefail
+ e2e/scripts/build_react_native_ios_testflight
+ - deploy-to-itunesconnect-application-loader@2:
+ inputs:
+ - connection: automatic
+ - ipa_path: $NIGHTLY_REACT_NATIVE_IOS_IPA_PATH
+ - deploy-to-bitrise-io@2:
+ is_always_run: false
diff --git a/e2e/scripts/build_react_native_ios_testflight b/e2e/scripts/build_react_native_ios_testflight
new file mode 100755
index 000000000..7097f831f
--- /dev/null
+++ b/e2e/scripts/build_react_native_ios_testflight
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+source "$script_dir/bitrise_ci_helpers"
+source "$script_dir/ios_build_helpers"
+
+: "${BITRISE_BUILD_NUMBER:?BITRISE_BUILD_NUMBER is required. App Store Connect rejects a build number it has already seen.}"
+: "${NIGHTLY_IOS_PROVISIONING_PROFILE_SPECIFIER:?NIGHTLY_IOS_PROVISIONING_PROFILE_SPECIFIER is required. Set it to the name of the App Store provisioning profile installed on the Bitrise app.}"
+
+e2e_reject_ios_signing_overrides
+
+e2e_configure_storefront
+
+app_name="CheckoutKitReactNativeDemo"
+archive_path="$(e2e_deploy_dir)/${app_name}.xcarchive"
+
+cd platforms/react-native/sample/ios
+
+e2e_build_ios_device_ipa \
+ --workspace "${app_name}.xcworkspace" \
+ --scheme "$app_name" \
+ --app-name "$app_name" \
+ --bundle-id com.shopify.checkoutkit.reactnativedemo \
+ --code-sign-identity "Apple Distribution" \
+ --export-method app-store-connect \
+ --profile-specifier "$NIGHTLY_IOS_PROVISIONING_PROFILE_SPECIFIER" \
+ --archive-path "$archive_path" \
+ --build-setting "CURRENT_PROJECT_VERSION=$BITRISE_BUILD_NUMBER"
+
+e2e_assert_archived_build_number \
+ "$archive_path" \
+ "$app_name" \
+ "$BITRISE_BUILD_NUMBER" \
+ "platforms/react-native/sample/ios/${app_name}/Info.plist"
+
+e2e_log "Publishing React Native iOS IPA path"
+envman add --key NIGHTLY_REACT_NATIVE_IOS_IPA_PATH --value "$E2E_IOS_PROVISIONED_IPA"
diff --git a/e2e/scripts/build_swift_ios_testflight b/e2e/scripts/build_swift_ios_testflight
index f866c7a14..21a21ab31 100755
--- a/e2e/scripts/build_swift_ios_testflight
+++ b/e2e/scripts/build_swift_ios_testflight
@@ -32,16 +32,11 @@ e2e_build_ios_device_ipa \
--archive-path "$archive_path" \
--build-setting "CURRENT_PROJECT_VERSION=$BITRISE_BUILD_NUMBER"
-# A wrong build number is otherwise only reported by App Store Connect, long after
-# this build has been marked green.
-e2e_log "Verifying the archived build number"
-archived_info_plist="$archive_path/Products/Applications/${app_name}.app/Info.plist"
-archived_build_number="$(plutil -extract CFBundleVersion raw -o - "$archived_info_plist")"
-if [ "$archived_build_number" != "$BITRISE_BUILD_NUMBER" ]; then
- e2e_log "Archived CFBundleVersion is ${archived_build_number}, expected ${BITRISE_BUILD_NUMBER}"
- echo "Check that CFBundleVersion in ${app_name}/project.yml still reads \$(CURRENT_PROJECT_VERSION)." >&2
- exit 1
-fi
+e2e_assert_archived_build_number \
+ "$archive_path" \
+ "$app_name" \
+ "$BITRISE_BUILD_NUMBER" \
+ "platforms/swift/Samples/${app_name}/project.yml"
e2e_log "Publishing Swift iOS IPA path"
envman add --key NIGHTLY_SWIFT_IOS_IPA_PATH --value "$E2E_IOS_PROVISIONED_IPA"
diff --git a/e2e/scripts/ios_build_helpers b/e2e/scripts/ios_build_helpers
index 81fca73ee..79a715d60 100755
--- a/e2e/scripts/ios_build_helpers
+++ b/e2e/scripts/ios_build_helpers
@@ -178,3 +178,22 @@ PLIST
E2E_IOS_PROVISIONED_IPA="$provisioned_ipa"
}
+
+# A wrong build number is otherwise only reported by the store, long after this
+# build has been marked green.
+e2e_assert_archived_build_number() {
+ local archive_path="$1"
+ local app_name="$2"
+ local expected="$3"
+ local version_source="$4"
+
+ e2e_log "Verifying the archived build number"
+ local info_plist="$archive_path/Products/Applications/${app_name}.app/Info.plist"
+ local archived
+ archived="$(plutil -extract CFBundleVersion raw -o - "$info_plist")"
+ if [ "$archived" != "$expected" ]; then
+ e2e_log "Archived CFBundleVersion is ${archived}, expected ${expected}"
+ echo "Check that CFBundleVersion in ${version_source} still reads \$(CURRENT_PROJECT_VERSION)." >&2
+ return 1
+ fi
+}
diff --git a/platforms/react-native/sample/ios/CheckoutKitReactNativeDemo/Info.plist b/platforms/react-native/sample/ios/CheckoutKitReactNativeDemo/Info.plist
index ab7ec2279..70c614377 100644
--- a/platforms/react-native/sample/ios/CheckoutKitReactNativeDemo/Info.plist
+++ b/platforms/react-native/sample/ios/CheckoutKitReactNativeDemo/Info.plist
@@ -34,6 +34,8 @@
CFBundleVersion
$(CURRENT_PROJECT_VERSION)
+ ITSAppUsesNonExemptEncryption
+
LSApplicationQueriesSchemes
rn