Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,6 @@ e2e/.env.local

# Decrypted ejson output must never be committed.
config/secrets/*.json

# SCCACHE_DIR on the Bitrise React Native iOS workflows.
.sccache/
2 changes: 2 additions & 0 deletions e2e/BITRISE.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,6 @@ The pipeline uses Bitrise cache steps for key-based pnpm/CocoaPods/Gradle cache

Do not add `activate-build-cache-for-xcode` or `activate-build-cache-for-gradle`; the Bitrise Build Cache add-on is disabled for Shopify Bitrise apps.

The `ci-ios` macOS jobs add two more caches. DerivedData is keyed on `Package.resolved` and the relevant `Podfile.lock`, and the build scripts no longer pass `xcodebuild clean`, which would delete the restored copy. sccache is keyed on the branch and commit, with branch-then-architecture prefixes as fallbacks, so each build starts from the last one on its branch. `restore-cache` takes a single `key` input holding one key per line in priority order; there is no plural `keys` input, and `bitrise validate` does not catch that mistake.

Ruby and Node versions are pinned in `e2e/bitrise.yml` via the Bitrise `tools:` configuration (`ruby: "3.4:installed"`, `nodejs: 22.14.0`), which Bitrise installs before each workflow runs. The `:installed` suffix tells each stack to use its own preinstalled 3.4.x rather than compiling one from source. Pin exact versions that the target stacks preinstall so setup stays fast and reproducible; a version the stack does not ship is installed on demand and is slower. pnpm is pinned separately through Corepack via the `packageManager` field in `platforms/react-native/package.json`.
51 changes: 51 additions & 0 deletions e2e/bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ step_bundles:
simulator_uuid="$(xcrun simctl create CIDevice "$CI_IOS_SIMULATOR_DEVICE")"
xcrun simctl bootstatus "$simulator_uuid" -b
envman add --key CURRENT_SIMULATOR_UUID --value "$simulator_uuid"
install-sccache:
steps:
- script@1:
title: Install sccache
inputs:
- content: |-
set -euo pipefail
if ! command -v sccache >/dev/null 2>&1; then
brew install sccache
fi
sccache --version
bootstrap-mint:
inputs:
- mintfile_dir: ""
Expand Down Expand Up @@ -368,6 +379,11 @@ workflows:
bitrise.io:
stack: *macos_stack
machine_type_id: g2.mac.4large
envs:
# Opt-in, set here and nowhere shared. A hardcoded default would make a local
# pod install produce a different Pods project than this job does.
- SCCACHE_ENABLED: "1"
- SCCACHE_DIR: $BITRISE_SOURCE_DIR/.sccache
steps:
- git-clone@8: {}
- bundle::create-ios-simulator: {}
Expand All @@ -384,13 +400,25 @@ workflows:
scripts/check_published_podfile_lock sample/ios/Podfile.lock
- bundle::install-node-modules: {}
- bundle::install-ruby-gems: {}
- bundle::install-sccache: {}
- bundle::install-cocoapods: {}
# The 89 pods here are the React Native New Architecture C++ core. They dominate the
# build and only change when the lockfile or the Swift package pins do.
- restore-cache@3:
inputs:
- key: &rn_sample_derived_data_cache_key |-
rn-sample-derived-data-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/sample/ios/Podfile.lock" }}-{{ checksum "Package.resolved" }}
# Keyed on the branch so each build starts from the last one on the same branch and
# ends up saved under its own key. A lockfile key would freeze the compiler cache at
# its first contents, which is the opposite of what a compiler cache is for.
# restore-cache takes one `key` input; extra lines are fallback prefixes tried in
# order. save-cache takes a single key, so line 1 is repeated below verbatim.
- restore-cache@3:
inputs:
- key: |-
rn-sample-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-{{ .CommitHash }}
rn-sample-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-
rn-sample-sccache-{{ .OS }}-{{ .Arch }}-
- script@1:
title: Build the React Native iOS sample
timeout: 5400
Expand All @@ -401,16 +429,25 @@ workflows:
cd platforms/react-native
pnpm module build
pnpm sample build:ios
sccache --show-stats
- save-cache@1:
inputs:
- key: *rn_sample_derived_data_cache_key
- paths: ~/Library/Developer/Xcode/DerivedData
- save-cache@1:
inputs:
- key: |-
rn-sample-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-{{ .CommitHash }}
- paths: $SCCACHE_DIR

ci-ios-react-native-test-ios:
meta:
bitrise.io:
stack: *macos_stack
machine_type_id: g2.mac.4large
envs:
- SCCACHE_ENABLED: "1"
- SCCACHE_DIR: $BITRISE_SOURCE_DIR/.sccache
steps:
- git-clone@8: {}
- bundle::create-ios-simulator: {}
Expand All @@ -432,10 +469,18 @@ workflows:
# Not the install-cocoapods bundle: that one owns the sample app's Pods tree.
# test_ios runs `pod install` for the integration app itself, so this pair only
# carries the resulting Pods directory across builds.
- bundle::install-sccache: {}
- restore-cache@3:
inputs:
- key: &rct_integration_cocoapods_cache_key |-
rct-integration-cocoapods-{{ .OS }}-{{ .Arch }}-{{ checksum "platforms/react-native/test/rct-integration-app/Podfile.lock" }}
# See the sample build job for why the exact key is repeated on save-cache.
- restore-cache@3:
inputs:
- key: |-
rct-integration-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-{{ .CommitHash }}
rct-integration-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-
rct-integration-sccache-{{ .OS }}-{{ .Arch }}-
- script@1:
title: Run the React Native iOS tests
timeout: 5400
Expand All @@ -446,10 +491,16 @@ workflows:
cd platforms/react-native
pnpm module build
pnpm sample test:ios
sccache --show-stats
- save-cache@1:
inputs:
- key: *rct_integration_cocoapods_cache_key
- paths: platforms/react-native/test/rct-integration-app/Pods
- save-cache@1:
inputs:
- key: |-
rct-integration-sccache-{{ .OS }}-{{ .Arch }}-{{ .Branch }}-{{ .CommitHash }}
- paths: $SCCACHE_DIR

ci-ios-report:
steps:
Expand Down
3 changes: 1 addition & 2 deletions e2e/test/ios_ci_run_plan_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ class IOSCIRunPlanTest < Minitest::Test
SWIFT_FILTERS = ["swift", "protocolSwift", "protocolShared", "packageSwift", "ciFilters", "iosCiConfig"].freeze
REACT_NATIVE_FILTERS = ["reactNative", "protocolTypescript", "protocolShared", "packageSwift", "ciFilters", "iosCiConfig"].freeze

# The real config carries only the jobs already ported to Bitrise, so the four-job
# selection rules are exercised against a fixture that names all four macOS jobs.
# The explicit fixture keeps selection tests independent from loading the real config.
def four_job_config
{
"version" => 1,
Expand Down
28 changes: 24 additions & 4 deletions platforms/react-native/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ Local mode has to stay an explicit command-line choice. Hardcoding it makes loca

## Optional: Speed up builds with sccache

For faster native compilation (especially on incremental builds), you can install [sccache](https://github.com/mozilla/sccache), a shared compilation cache:
Install [sccache](https://github.com/mozilla/sccache), a shared compilation cache:

```sh
# macOS (using Homebrew)
Expand All @@ -109,14 +109,34 @@ cargo install sccache
# Other systems: see https://github.com/mozilla/sccache#installation
```

The build scripts will automatically detect and use sccache if available. On Android, React Native's CMake files look for a command named `ccache`, so the sample Android scripts put an sccache-backed compatibility command first on `PATH`. If you encounter any build issues, you can temporarily disable it:
The Android build scripts detect it automatically. React Native's CMake files
look for a command named `ccache`, so the sample Android scripts put an
sccache-backed compatibility command first on `PATH`. To disable it for one build:

```sh
# Disable sccache for a single build
SCCACHE=false pnpm sample ios
SCCACHE=false pnpm sample android
```

The iOS integration is opt-in because CocoaPods writes compiler wrappers into
the generated Xcode projects:

```sh
SCCACHE_ENABLED=1 pnpm pod-install
SCCACHE_ENABLED=1 pnpm sample build:ios
sccache --show-stats
```

Set `SCCACHE_ENABLED=1` for `pod install` and the build. CI enables it only on
the Bitrise React Native iOS workflows.

An sccache-enabled `pod install` writes `CC`, `CXX`, `LD`, and `LDPLUSPLUS` into
the tracked `sample/ios/CheckoutKitReactNativeDemo.xcodeproj/project.pbxproj`.
Never commit the generated changes. Restore the project with:

```sh
git checkout -- sample/ios/CheckoutKitReactNativeDemo.xcodeproj/project.pbxproj
```

## Making changes to the Native Module

If your intentions are to modify the TS code for the Native Module under
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@
baseConfigurationReference = 7A4F0E4A2E4B123400000001 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CC = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -489,7 +489,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "";
CXX = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang++";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
Expand All @@ -510,8 +510,8 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
LD = "";
LDPLUSPLUS = "";
LD = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang";
LDPLUSPLUS = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang++";
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
Expand Down Expand Up @@ -545,7 +545,7 @@
baseConfigurationReference = 7A4F0E4A2E4B123400000001 /* Config.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CC = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -573,7 +573,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CXX = "";
CXX = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang++";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
Expand All @@ -590,8 +590,8 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 16.6;
LD = "";
LDPLUSPLUS = "";
LD = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang";
LDPLUSPLUS = "/Users/ko/.claudex/worktrees/Shopify@checkout-kit/ios-build-on-bitrise/platforms/react-native/scripts/sccache-clang++";
LD_RUNPATH_SEARCH_PATHS = (
/usr/lib/swift,
"$(inherited)",
Expand Down
5 changes: 5 additions & 0 deletions platforms/react-native/sample/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ require Pod::Executable.execute_command('node', ['-p',
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
require_relative '../../scripts/react_native_sccache'

platform :ios, '16.6'

Expand Down Expand Up @@ -46,5 +47,9 @@ target 'CheckoutKitReactNativeDemo' do
:mac_catalyst_enabled => false,
:ccache_enabled => false
)
ReactNativeSccache.configure(
installer,
:enabled => ENV.fetch('SCCACHE_ENABLED', '0') == '1'
)
end
end
2 changes: 1 addition & 1 deletion platforms/react-native/sample/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3002,6 +3002,6 @@ SPEC CHECKSUMS:
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: a742cc68e8366fcfc681808162492bc0aa7a9498

PODFILE CHECKSUM: 5f421561b67cd1e99762e104b6021b68fffe9aaa
PODFILE CHECKSUM: 9736d8ba4e3d20c45d9a66c69768ecd1e330d222

COCOAPODS: 1.15.2
16 changes: 3 additions & 13 deletions platforms/react-native/sample/scripts/build_ios
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,9 @@
set -ex
set -eo pipefail

# Add sccache for faster compilation outside of CI environments
# Can be disabled with SCCACHE=false for debugging or matching CI behavior locally
if [ "$CI" = "true" ] || [ "$SCCACHE" = "false" ]; then
# CI detected or sccache explicitly disabled - skipping sccache for clean builds
if command -v sccache >/dev/null 2>&1; then
sccache --stop-server 2>/dev/null || true
fi
else
if command -v sccache >/dev/null 2>&1; then
export RUSTC_WRAPPER=sccache
export CC="sccache clang"
export CXX="sccache clang++"
fi
if [ "${SCCACHE_ENABLED:-0}" = "1" ]; then
SCCACHE_BINARY="${SCCACHE_BINARY:-$(command -v sccache)}"
export SCCACHE_BINARY
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand Down
5 changes: 5 additions & 0 deletions platforms/react-native/sample/scripts/test_ios
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ for arg in "$@"; do
fi
done

if [ "${SCCACHE_ENABLED:-0}" = "1" ]; then
SCCACHE_BINARY="${SCCACHE_BINARY:-$(command -v sccache)}"
export SCCACHE_BINARY
fi

source "$SCRIPT_DIR/simulator"
dest="$(get_sim_destination)"

Expand Down
33 changes: 33 additions & 0 deletions platforms/react-native/scripts/react_native_sccache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

module ReactNativeSccache
CLANG_WRAPPER = File.expand_path("sccache-clang", __dir__)
CLANGXX_WRAPPER = File.expand_path("sccache-clang++", __dir__)
BUILD_SETTINGS = {
"CC" => CLANG_WRAPPER,
"LD" => CLANG_WRAPPER,
"CXX" => CLANGXX_WRAPPER,
"LDPLUSPLUS" => CLANGXX_WRAPPER,
}.freeze

def self.configure(installer, enabled:)
projects = installer.aggregate_targets
.map(&:user_project)
.push(installer.pods_project)
.uniq(&:path)

projects.each do |project|
project.build_configurations.each do |configuration|
BUILD_SETTINGS.each do |name, wrapper|
if enabled
configuration.build_settings[name] = wrapper
elsif configuration.build_settings[name] == wrapper
configuration.build_settings.delete(name)
end
end
end

project.save
end
end
end
11 changes: 11 additions & 0 deletions platforms/react-native/scripts/sccache-clang
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env sh

set -eu

SCCACHE_BINARY="${SCCACHE_BINARY:-$(command -v sccache || true)}"
if [ -z "$SCCACHE_BINARY" ]; then
echo "sccache is required when SCCACHE_ENABLED=1" >&2
exit 1
fi

exec "$SCCACHE_BINARY" clang "$@"
11 changes: 11 additions & 0 deletions platforms/react-native/scripts/sccache-clang++
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env sh

set -eu

SCCACHE_BINARY="${SCCACHE_BINARY:-$(command -v sccache || true)}"
if [ -z "$SCCACHE_BINARY" ]; then
echo "sccache is required when SCCACHE_ENABLED=1" >&2
exit 1
fi

exec "$SCCACHE_BINARY" clang++ "$@"
5 changes: 5 additions & 0 deletions platforms/react-native/test/rct-integration-app/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ require Pod::Executable.execute_command('node', ['-p',
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
require_relative '../../scripts/react_native_sccache'

platform :ios, '16.6'

Expand Down Expand Up @@ -39,5 +40,9 @@ target 'RCTIntegrationApp' do
:mac_catalyst_enabled => false,
:ccache_enabled => false
)
ReactNativeSccache.configure(
installer,
:enabled => ENV.fetch('SCCACHE_ENABLED', '0') == '1'
)
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -2469,6 +2469,6 @@ SPEC CHECKSUMS:
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: a742cc68e8366fcfc681808162492bc0aa7a9498

PODFILE CHECKSUM: f548eaa9f294170b473f3f02f14b645383e0e378
PODFILE CHECKSUM: 0b157e8468364100309509db332664b5a8e88dfe

COCOAPODS: 1.15.2
Loading