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
14 changes: 14 additions & 0 deletions e2e/BITRISE.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@ These secrets are configured in Bitrise.io; they cannot live in the repository.
| `STOREFRONT_DOMAIN` | Storefront domain for sample app builds. |
| `STOREFRONT_ACCESS_TOKEN` | Storefront access token for sample app builds. |

## Code signing

React Native iOS IPA generation uses Bitrise's certificate and profile installer before running `xcodebuild archive` and `xcodebuild -exportArchive`.

Upload the signing certificate and provisioning profile for the React Native sample app to the Bitrise app; the iOS artifact workflow installs them before archiving. The iOS build reads the following signing values with the defaults shown, and each can be overridden with a matching Bitrise environment variable:

| Variable | Default | Purpose |
|---|---|---|
| `E2E_IOS_EXPORT_METHOD` | `development` | Export method for the React Native iOS IPA. |
| `E2E_IOS_BUNDLE_ID` | `com.shopify.checkoutkit.reactnativedemo` | Bundle identifier used for iOS archive and export signing. |
| `E2E_IOS_DEVELOPMENT_TEAM` | `A7XGC83MZE` | Apple development team used for iOS archive signing. |
| `E2E_IOS_CODE_SIGN_IDENTITY` | `Apple Development` | Code signing identity used for iOS archive and export signing. |
| `E2E_IOS_PROVISIONING_PROFILE_SPECIFIER` | `bitrise-checkout-kit-e2e` | Provisioning profile specifier installed by Bitrise and passed to `xcodebuild`; override it if the Bitrise-installed profile uses a different name. |

## Caching

React Native Android E2E builds use the released native Maven artifact versions declared by the React Native sample and module configuration. Do not pass the React Native `--local` flag or set local native SDK override environment variables for these builds.
Expand Down
72 changes: 54 additions & 18 deletions e2e/bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,40 @@ step_bundles:
inputs:
- key: *rn_node_modules_cache_key
- paths: platforms/react-native/node_modules
install-ruby-gems:
steps:
- restore-cache@3:
inputs:
- key: &rn_ruby_gems_cache_key |-
rn-ruby-gems-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/sample/Gemfile.lock" }}
- script@1:
title: Install iOS Ruby dependencies
inputs:
- content: |-
set -euo pipefail
cd platforms/react-native/sample
bundle install
- save-cache@1:
inputs:
- key: *rn_ruby_gems_cache_key
- paths: platforms/react-native/sample/vendor/bundle
install-cocoapods:
steps:
- restore-cache@3:
inputs:
- key: &rn_cocoapods_cache_key |-
rn-cocoapods-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
- script@1:
title: Install CocoaPods dependencies
inputs:
- content: |-
set -euo pipefail
cd platforms/react-native/sample/ios
bundle exec pod install --deployment
- save-cache@1:
inputs:
- key: *rn_cocoapods_cache_key
- paths: platforms/react-native/sample/ios/Pods

workflows:
e2e-produce-browserstack-run-plan:
Expand All @@ -67,7 +101,7 @@ workflows:
- script@1:
title: Produce BrowserStack run plan placeholder
timeout: 1800
no_output_timeout: 1800
no_output_timeout: 900
inputs:
- content: |-
set -euo pipefail
Expand All @@ -93,31 +127,33 @@ workflows:
$BITRISE_DEPLOY_DIR/browserstack-run-plan.json:E2E_BROWSERSTACK_RUN_PLAN_JSON

e2e-build-react-native-ios:
meta:
bitrise.io:
# Keep in sync with dev.yml's xcode.version (26.2). Bitrise stable stacks
# only receive Xcode patch updates, so this pins Xcode 26.2 / iOS 26.2
# (23C54) to match local development.
stack: osx-xcode-26.2.x
machine_type_id: g2.mac.4large
steps:
- git-clone@8: {}
- restore-cache@3:
inputs:
- key: |-
rn-ios-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
- certificate-and-profile-installer@1: {}
- bundle::install-node-modules: {}
- bundle::install-ruby-gems: {}
- bundle::install-cocoapods: {}
- script@1:
title: Create React Native iOS artifact placeholder
title: Build React Native iOS IPA artifact
timeout: 5400
no_output_timeout: 1800
inputs:
- content: |-
set -euo pipefail
mkdir -p "$BITRISE_DEPLOY_DIR/e2e"
echo "Phase 2 placeholder for React Native iOS IPA" > "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
envman add --key E2E_REACT_NATIVE_IOS_APP_PATH --value "$BITRISE_DEPLOY_DIR/e2e/react-native-ios.ipa"
e2e/scripts/build_react_native_ios
- deploy-to-bitrise-io@2:
is_always_run: false
inputs:
- pipeline_intermediate_files: |-
$E2E_REACT_NATIVE_IOS_APP_PATH:E2E_REACT_NATIVE_IOS_APP_PATH
- save-cache@1:
inputs:
- key: |-
rn-ios-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/pnpm-lock.yaml" }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}
- paths: |-
platforms/react-native/node_modules
platforms/react-native/sample/ios/Pods
$E2E_REACT_NATIVE_IOS_SIMULATOR_APP_PATH:E2E_REACT_NATIVE_IOS_SIMULATOR_APP_PATH

e2e-build-react-native-android:
meta:
Expand All @@ -134,7 +170,7 @@ workflows:
- script@1:
title: Build React Native Android APK artifact
timeout: 3600
no_output_timeout: 3600
no_output_timeout: 1800
inputs:
- content: |-
set -euo pipefail
Expand All @@ -161,7 +197,7 @@ workflows:
- script@1:
title: Resolve BrowserStack run plan row placeholder
timeout: 2700
no_output_timeout: 2700
no_output_timeout: 1350
inputs:
- content: |-
set -euo pipefail
Expand Down
6 changes: 6 additions & 0 deletions e2e/scripts/bitrise_ci_helpers
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ e2e_enable_corepack() {
corepack enable
}

e2e_deploy_dir() {
local dir="$BITRISE_DEPLOY_DIR/e2e"
mkdir -p "$dir"
printf '%s\n' "$dir"
}

e2e_configure_storefront() {
e2e_log "Checking storefront configuration secrets"
: "${STOREFRONT_DOMAIN:?STOREFRONT_DOMAIN is required. Check https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/workflow_editor#!/secrets and enable Expose for pull requests.}"
Expand Down
152 changes: 152 additions & 0 deletions e2e/scripts/build_react_native_ios
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#!/usr/bin/env bash

set -euo pipefail

script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$script_dir/bitrise_ci_helpers"

e2e_configure_storefront

cd platforms/react-native/sample/ios

app_name="CheckoutKitReactNativeDemo"
workspace="${app_name}.xcworkspace"
scheme="$app_name"

e2e_dir="$(e2e_deploy_dir)"
archive_path="$e2e_dir/${app_name}.xcarchive"
export_path="$e2e_dir/react-native-ios-export"
export_options="$e2e_dir/ExportOptions.plist"
provisioned_ipa="$export_path/${app_name}-Provisioned.ipa"
simulator_derived_data="$PWD/build/e2e-simulator"
simulator_app_zip="$e2e_dir/${app_name}-Simulator.zip"

export_method="${E2E_IOS_EXPORT_METHOD:-development}"
ios_bundle_id="${E2E_IOS_BUNDLE_ID:-com.shopify.checkoutkit.reactnativedemo}"
ios_development_team="${E2E_IOS_DEVELOPMENT_TEAM:-A7XGC83MZE}"
ios_code_sign_identity="${E2E_IOS_CODE_SIGN_IDENTITY:-Apple Development}"
ios_profile_specifier="${E2E_IOS_PROVISIONING_PROFILE_SPECIFIER:-bitrise-checkout-kit-e2e}"

xcodebuild_common_args=(
-workspace "$workspace"
-scheme "$scheme"
-configuration Release
-skipPackagePluginValidation
)

# Builds the device archive that export_device_ipa packages into the provisioned IPA.
archive_device_app() {
xcodebuild archive \
"${xcodebuild_common_args[@]}" \
-disableAutomaticPackageResolution \
-sdk iphoneos \
-destination generic/platform=iOS \
-archivePath "$archive_path" \
"CODE_SIGN_STYLE=Manual" \
"DEVELOPMENT_TEAM=$ios_development_team" \
"CODE_SIGN_IDENTITY=$ios_code_sign_identity" \
"PROVISIONING_PROFILE_SPECIFIER=$ios_profile_specifier"
}

# Packages the device archive into the signed IPA uploaded to Bitrise.
export_device_ipa() {
xcodebuild -exportArchive \
-archivePath "$archive_path" \
-exportPath "$export_path" \
-exportOptionsPlist "$export_options"
}

# Builds the simulator app bundle that is zipped for BrowserStack.
build_simulator_app() {
xcodebuild build \
"${xcodebuild_common_args[@]}" \
-disableAutomaticPackageResolution \
-sdk iphonesimulator \
-destination 'generic/platform=iOS Simulator' \
-derivedDataPath "$simulator_derived_data" \
ARCHS=arm64 \
ONLY_ACTIVE_ARCH=NO \
CODE_SIGNING_ALLOWED=NO
}

cat > "$export_options" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>$export_method</string>
<key>signingStyle</key>
<string>manual</string>
<key>teamID</key>
<string>$ios_development_team</string>
<key>signingCertificate</key>
<string>$ios_code_sign_identity</string>
<key>provisioningProfiles</key>
<dict>
<key>$ios_bundle_id</key>
<string>$ios_profile_specifier</string>
</dict>
<key>stripSwiftSymbols</key>
<true/>
<key>compileBitcode</key>
<false/>
</dict>
</plist>
PLIST

e2e_log "Archiving React Native iOS app"
if ! archive_device_app; then
e2e_log "iOS archive failed"
echo "Check Bitrise code signing assets for bundle id ${ios_bundle_id} and team ${ios_development_team}." >&2
echo "Expected provisioning profile specifier: ${ios_profile_specifier}." >&2
echo "Upload an Apple Development certificate and matching development provisioning profile at https://app.bitrise.io/app/f51f9054-053e-40f1-81e9-ae727567ae76/workflow_editor#!/code_signing." >&2
echo "The e2e-build-react-native-ios workflow runs certificate-and-profile-installer before xcodebuild." >&2
exit 1
fi

e2e_log "Exporting React Native iOS IPA"
if ! export_device_ipa; then
e2e_log "iOS IPA export failed"
echo "Check that the installed provisioning profile matches export method ${export_method}." >&2
echo "Export options plist: ${export_options}" >&2
exit 1
fi

ios_ipa="$(find "$export_path" -name "*.ipa" -print -quit)"
if [ -z "$ios_ipa" ] || [ ! -f "$ios_ipa" ]; then
e2e_log "iOS IPA export did not create an IPA"
find "$export_path" -maxdepth 3 -print >&2 || true
exit 1
fi
mv "$ios_ipa" "$provisioned_ipa"

e2e_log "Publishing iOS IPA path"
envman add --key E2E_REACT_NATIVE_IOS_APP_PATH --value "$provisioned_ipa"

e2e_log "Building React Native iOS simulator app"
# Restrict the simulator build to arm64: react-native-quick-crypto@1.0.9
# ships a libQuickCrypto.a whose blake3 dispatch references Intel SIMD
# symbols (avx512/avx2/sse41/sse2) that are not compiled for x86_64, so
# the x86_64 slice fails to link. CI/BrowserStack simulators are Apple
# Silicon, so arm64 is sufficient.
# TODO: Remove ARCHS/ONLY_ACTIVE_ARCH override once the Expo prebuild
# sample app lands (https://github.com/Shopify/checkout-kit/pull/297).
if ! build_simulator_app; then
e2e_log "iOS simulator build failed"
exit 1
fi

simulator_app="$(find "$simulator_derived_data/Build/Products" -maxdepth 2 -name "${app_name}.app" -print -quit)"
if [ -z "$simulator_app" ] || [ ! -d "$simulator_app" ]; then
e2e_log "iOS simulator build did not create an app bundle"
find "$simulator_derived_data/Build/Products" -maxdepth 2 -print >&2 || true
exit 1
fi

e2e_log "Packaging iOS simulator app"
rm -f "$simulator_app_zip"
ditto -c -k --keepParent "$simulator_app" "$simulator_app_zip"

e2e_log "Publishing iOS simulator app path"
envman add --key E2E_REACT_NATIVE_IOS_SIMULATOR_APP_PATH --value "$simulator_app_zip"
Loading