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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@

When a root span ends (idle nav spans from `reactNavigationIntegration` / `expoRouterIntegration`, or a user's own `Sentry.startSpan(...)`), the integration writes `turbo_module.<name>.<method>.{call_count,duration_ms,error_count}` attributes plus summary keys (`turbo_module.total_call_count`, `turbo_module.total_duration_ms`, `turbo_module.top_module`). Async calls above `slowCallThresholdMs` (default 500ms) additionally record a `native.turbo_module` breadcrumb. Both surfaces enabled by default; new knobs `enableSpanAttribution`, `slowCallThresholdMs`, `maxTopModulesPerSpan` on `turboModuleContextIntegration`.

### Fixes

- Make the `RNSentry` SPEC CHECKSUM in `Podfile.lock` machine-independent ([#6474](https://github.com/getsentry/sentry-react-native/pull/6474))

The prebuilt `Sentry.xcframework` is now referenced through a `$(PODS_ROOT)/sentry-xcframeworks/โ€ฆ` symlink instead of the absolute per-user cache path, so `Podfile.lock` no longer churns between developers and CI. Expect a one-time `RNSentry` checksum change on the next `pod install`; the `SENTRY_XCFRAMEWORK_CACHE_DIR=/tmp/โ€ฆ` workaround is no longer needed.

### Changes

- Expose `instrumentStateGraph` for manual LangGraph instrumentation ([#6520](https://github.com/getsentry/sentry-react-native/pull/6520))
Expand Down
27 changes: 18 additions & 9 deletions packages/core/RNSentry.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,25 @@ Pod::Spec.new do |s|
# sentry-cocoa's `Sentry.xcframework` layout is stable across releases.
# Add a slice there if a future release ships one.
#
# Point the search paths at the pod-install-time absolute path to the
# xcframework. `${PODS_TARGET_SRCROOT}` is only defined in per-pod
# xcconfigs, not in aggregate/user-target xcconfigs, and a
# `${PODS_ROOT}`-relative fallback works for one Podfile layout but
# breaks for another (e.g. the RN sample apps put node_modules at a
# different depth from RNSentryCocoaTester). Using the absolute path
# avoids the layout-detection dance โ€” the path is regenerated on
# every `pod install`, so it's not something anyone commits.
# Reference the xcframework through a `Pods/sentry-xcframeworks/โ€ฆ`
# symlink so the search paths are machine-independent (see
# `stage_sentry_xcframework_in_pods`) โ€” the values below feed the
# `RNSentry` SPEC CHECKSUM in `Podfile.lock`, so an absolute
# `~/Library/Caches/โ€ฆ` path here made the lockfile churn between
# machines (#6467). `${PODS_TARGET_SRCROOT}` can't be used instead: it
# is only defined in per-pod xcconfigs, not in aggregate/user-target
# xcconfigs, and a `${PODS_ROOT}`-relative path to node_modules works
# for one Podfile layout but breaks for another (e.g. the RN sample
# apps put node_modules at a different depth from RNSentryCocoaTester).
# When the link can't be staged โ€” or the podspec is evaluated outside a
# `pod install`, where `$(PODS_ROOT)` would point at an unrelated
# sandbox โ€” fall back to the absolute pod-install-time path: functional,
# but with a machine-specific checksum.
sentry_xcframework_ref =
stage_sentry_xcframework_in_pods(sentry_xcframework_dir, sentry_cocoa_version, 'Sentry') ||
sentry_xcframework_dir
xcframework_search_paths = SENTRY_XCFRAMEWORK_SLICES_BY_SDK.each_with_object({}) do |(sdk, slice_ids), acc|
paths = slice_ids.map { |slice| %("#{File.join(sentry_xcframework_dir, slice)}") }
paths = slice_ids.map { |slice| %("#{File.join(sentry_xcframework_ref, slice)}") }
acc["FRAMEWORK_SEARCH_PATHS[sdk=#{sdk}*]"] = (['$(inherited)'] + paths).join(' ')
end

Expand Down
68 changes: 68 additions & 0 deletions packages/core/scripts/sentry_utils.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,71 @@ def ensure_sentry_xcframework(version, product = 'Sentry')
target_dir
end

# Directory inside the CocoaPods sandbox (`Pods/`) that holds the symlinks to
# the cached xcframeworks staged by `stage_sentry_xcframework_in_pods`.
SENTRY_XCFRAMEWORK_STAGING_DIR = 'sentry-xcframeworks'.freeze

# Stage the cached xcframework behind a machine-independent `$(PODS_ROOT)`
# reference.
#
# Everything assigned to `pod_target_xcconfig`/`user_target_xcconfig` is part
# of the evaluated spec, and CocoaPods derives the pod's `SPEC CHECKSUM` in
# `Podfile.lock` from exactly that evaluated spec. Pointing
# `FRAMEWORK_SEARCH_PATHS` at the per-user cache directory
# (`~/Library/Caches/โ€ฆ`) therefore leaked `$HOME` into the checksum: two
# machines installing the same SDK version produced different `RNSentry`
# checksums and `Podfile.lock` churned on every `pod install` (#6467).
#
# Instead, symlink `Pods/sentry-xcframeworks/<version>/<product>.xcframework`
# to the cached bundle and reference it as
# `$(PODS_ROOT)/sentry-xcframeworks/โ€ฆ` โ€” a string that is identical on every
# machine (including ones overriding `SENTRY_XCFRAMEWORK_CACHE_DIR`).
# `$(PODS_ROOT)` is defined by CocoaPods in both the per-pod and the
# user/aggregate xcconfigs, unlike `$(PODS_TARGET_SRCROOT)`, and the link
# lives inside `Pods/` so no Podfile-layout detection is needed.
#
# Returns the `$(PODS_ROOT)`-based path, or nil when the link can't be staged
# โ€” callers must then fall back to the absolute cache path (previous
# behaviour, functional but with a machine-specific checksum).
def stage_sentry_xcframework_in_pods(xcframework_dir, version, product = 'Sentry')
# Only stage when we're inside a real `pod install`, i.e. CocoaPods has
# located a Podfile and `sandbox_root` therefore points at the `Pods/`
# directory of the project that will consume the xcconfig. Anywhere else
# (`pod ipc spec`, `pod lib lint`, plain `Specification.from_file`) the
# sandbox root is unrelated to that project โ€” CocoaPods evaluates a podspec
# with the CWD set to the podspec's own directory, so a guessed
# `<cwd>/Pods` would stage the link inside `node_modules/@sentry/
# react-native/` and `$(PODS_ROOT)/โ€ฆ` would dangle at build time. Bail out
# and let the caller use the absolute path, which is always correct.
return nil unless defined?(Pod::Config) &&
Pod::Config.instance.respond_to?(:podfile_path) &&
Pod::Config.instance.podfile_path

staging_root = File.join(Pod::Config.instance.sandbox_root.to_s, SENTRY_XCFRAMEWORK_STAGING_DIR)
staging_dir = File.join(staging_root, version)
link_path = File.join(staging_dir, "#{product}.xcframework")

FileUtils.mkdir_p(staging_dir)
# Recreate the link when it's missing or points at a stale location (e.g.
# `SENTRY_XCFRAMEWORK_CACHE_DIR` changed between installs). `rm_rf` unlinks
# the symlink itself, it does not follow it into the shared cache.
unless File.symlink?(link_path) && File.readlink(link_path) == xcframework_dir
FileUtils.rm_rf(link_path)
File.symlink(xcframework_dir, link_path)
end

# Drop links staged for other sentry-cocoa versions (left behind by SDK
# upgrades) so the directory doesn't collect dangling symlinks.
Dir.glob(File.join(staging_root, '*')).each do |stale|
FileUtils.rm_rf(stale) unless File.basename(stale) == version
end

"$(PODS_ROOT)/#{SENTRY_XCFRAMEWORK_STAGING_DIR}/#{version}/#{product}.xcframework"
rescue StandardError, NotImplementedError => e
if defined?(Pod::UI)
Pod::UI.warn "[Sentry] Could not link the #{product} xcframework into Pods/ " \
"(#{e.class}: #{e.message}). Falling back to the absolute cache path; " \
"the RNSentry checksum in Podfile.lock will be machine-specific."
end
nil
end
Loading