Skip to content

Commit ea5902b

Browse files
committed
fix(ios): Add wait logic for dSYM generation in Xcode build phase
1 parent 77018cc commit ea5902b

File tree

5 files changed

+619
-0
lines changed

5 files changed

+619
-0
lines changed

packages/core/scripts/sentry-xcode-debug-files.sh

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,139 @@ EXTRA_ARGS="$SENTRY_CLI_EXTRA_ARGS $SENTRY_CLI_DEBUG_FILES_UPLOAD_EXTRA_ARGS $IN
5858

5959
UPLOAD_DEBUG_FILES="\"$SENTRY_CLI_EXECUTABLE\" debug-files upload $EXTRA_ARGS \"$DWARF_DSYM_FOLDER_PATH\""
6060

61+
# Function to wait for dSYM files to be generated
62+
# This addresses a race condition where the upload script runs before dSYM generation completes
63+
wait_for_dsym_files() {
64+
local max_attempts="${SENTRY_DSYM_WAIT_MAX_ATTEMPTS:-10}"
65+
local wait_interval="${SENTRY_DSYM_WAIT_INTERVAL:-2}"
66+
local attempt=1
67+
68+
# Check if we should wait for dSYM files
69+
if [ "$SENTRY_DSYM_WAIT_ENABLED" == "false" ]; then
70+
echo "SENTRY_DSYM_WAIT_ENABLED=false, skipping dSYM wait check"
71+
return 0
72+
fi
73+
74+
# Warn if DWARF_DSYM_FILE_NAME is not set - we can't verify the main app dSYM
75+
if [ -z "$DWARF_DSYM_FILE_NAME" ]; then
76+
echo "warning: DWARF_DSYM_FILE_NAME not set, cannot verify main app dSYM specifically"
77+
echo "warning: Will proceed when any dSYM bundle is found"
78+
fi
79+
80+
echo "Checking for dSYM files in: $DWARF_DSYM_FOLDER_PATH"
81+
82+
# Debug information to help diagnose issues
83+
if [ -n "${SENTRY_DSYM_DEBUG}" ]; then
84+
echo "DEBUG: DWARF_DSYM_FOLDER_PATH=$DWARF_DSYM_FOLDER_PATH"
85+
echo "DEBUG: DWARF_DSYM_FILE_NAME=$DWARF_DSYM_FILE_NAME"
86+
echo "DEBUG: PRODUCT_NAME=$PRODUCT_NAME"
87+
if [ -d "$DWARF_DSYM_FOLDER_PATH" ]; then
88+
echo "DEBUG: Contents of dSYM folder:"
89+
ls -la "$DWARF_DSYM_FOLDER_PATH" 2>/dev/null || echo "Cannot list folder"
90+
else
91+
echo "DEBUG: dSYM folder does not exist yet"
92+
fi
93+
fi
94+
95+
while [ $attempt -le $max_attempts ]; do
96+
# Check if the dSYM folder exists
97+
if [ -d "$DWARF_DSYM_FOLDER_PATH" ]; then
98+
# Check if there are any .dSYM bundles in the folder
99+
local dsym_count=$(find "$DWARF_DSYM_FOLDER_PATH" -name "*.dSYM" -type d 2>/dev/null | wc -l | tr -d ' ')
100+
101+
if [ "$dsym_count" -gt 0 ]; then
102+
echo "Found $dsym_count dSYM bundle(s) in $DWARF_DSYM_FOLDER_PATH"
103+
104+
# If DWARF_DSYM_FILE_NAME is set, verify the main app dSYM exists and is complete
105+
if [ -n "$DWARF_DSYM_FILE_NAME" ]; then
106+
local main_dsym="$DWARF_DSYM_FOLDER_PATH/$DWARF_DSYM_FILE_NAME"
107+
108+
if [ -d "$main_dsym" ]; then
109+
# Directory exists, now verify the actual DWARF binary exists inside
110+
local dwarf_dir="$main_dsym/Contents/Resources/DWARF"
111+
112+
if [ -d "$dwarf_dir" ]; then
113+
# Check if there are any files in the DWARF directory
114+
local dwarf_files=$(find "$dwarf_dir" -type f 2>/dev/null | head -1)
115+
116+
if [ -n "$dwarf_files" ]; then
117+
# Verify the DWARF file is not empty (still being written)
118+
local dwarf_size=$(find "$dwarf_dir" -type f -size +0 2>/dev/null | head -1)
119+
120+
if [ -n "$dwarf_size" ]; then
121+
echo "Verified main app dSYM is complete: $DWARF_DSYM_FILE_NAME"
122+
return 0
123+
else
124+
echo "Main app dSYM DWARF binary is empty (still being written): $DWARF_DSYM_FILE_NAME (attempt $attempt/$max_attempts)"
125+
fi
126+
else
127+
echo "Main app dSYM DWARF directory is empty: $DWARF_DSYM_FILE_NAME (attempt $attempt/$max_attempts)"
128+
fi
129+
else
130+
echo "Main app dSYM structure incomplete (missing DWARF directory): $DWARF_DSYM_FILE_NAME (attempt $attempt/$max_attempts)"
131+
fi
132+
else
133+
echo "Main app dSYM not found yet: $DWARF_DSYM_FILE_NAME (attempt $attempt/$max_attempts)"
134+
fi
135+
else
136+
# DWARF_DSYM_FILE_NAME not set, check if any dSYM has valid DWARF content
137+
# This is less strict but better than nothing
138+
local has_valid_dsym=false
139+
for dsym in "$DWARF_DSYM_FOLDER_PATH"/*.dSYM; do
140+
if [ -d "$dsym/Contents/Resources/DWARF" ]; then
141+
local dwarf_files=$(find "$dsym/Contents/Resources/DWARF" -type f -size +0 2>/dev/null | head -1)
142+
if [ -n "$dwarf_files" ]; then
143+
has_valid_dsym=true
144+
break
145+
fi
146+
fi
147+
done
148+
149+
if [ "$has_valid_dsym" = true ]; then
150+
echo "Found dSYM bundle(s) with valid DWARF content"
151+
return 0
152+
else
153+
echo "Found dSYM bundle(s) but none have complete DWARF content yet (attempt $attempt/$max_attempts)"
154+
fi
155+
fi
156+
else
157+
echo "No dSYM bundles found yet in $DWARF_DSYM_FOLDER_PATH (attempt $attempt/$max_attempts)"
158+
fi
159+
else
160+
echo "dSYM folder does not exist yet: $DWARF_DSYM_FOLDER_PATH (attempt $attempt/$max_attempts)"
161+
fi
162+
163+
if [ $attempt -lt $max_attempts ]; then
164+
# Progressive backoff: quick checks first, longer waits later
165+
# Attempts 1-3: 0.5s (total 1.5s)
166+
# Attempts 4-6: 1s (total 3s)
167+
# Attempts 7+: 2s (remaining time)
168+
local current_interval="$wait_interval"
169+
if [ -z "${SENTRY_DSYM_WAIT_INTERVAL}" ]; then
170+
# Only use progressive intervals if user hasn't set custom interval
171+
if [ $attempt -le 3 ]; then
172+
current_interval=0.5
173+
elif [ $attempt -le 6 ]; then
174+
current_interval=1
175+
else
176+
current_interval=2
177+
fi
178+
fi
179+
180+
echo "Waiting ${current_interval}s for dSYM generation to complete..."
181+
sleep $current_interval
182+
fi
183+
184+
attempt=$((attempt + 1))
185+
done
186+
187+
# Timeout reached
188+
echo "warning: Timeout waiting for dSYM files after $((max_attempts * wait_interval))s"
189+
echo "warning: This may result in incomplete debug symbol uploads"
190+
echo "warning: To disable this check, set SENTRY_DSYM_WAIT_ENABLED=false"
191+
return 1
192+
}
193+
61194
XCODE_BUILD_CONFIGURATION="${CONFIGURATION}"
62195

63196
if [ "$SENTRY_DISABLE_AUTO_UPLOAD" == true ]; then
@@ -67,6 +200,12 @@ elif [ "$SENTRY_DISABLE_XCODE_DEBUG_UPLOAD" == true ]; then
67200
elif echo "$XCODE_BUILD_CONFIGURATION" | grep -iq "debug"; then # case insensitive check for "debug"
68201
echo "Skipping debug files upload for *Debug* configuration"
69202
else
203+
# Wait for dSYM files to be generated (addresses race condition in EAS builds)
204+
# Don't fail the script if wait times out - we still want to attempt upload
205+
set +e
206+
wait_for_dsym_files
207+
set -e
208+
70209
# 'warning:' triggers a warning in Xcode, 'error:' triggers an error
71210
set +x +e # disable printing commands otherwise we might print `error:` by accident and allow continuing on error
72211
SENTRY_UPLOAD_COMMAND_OUTPUT=$(/bin/sh -c "\"$LOCAL_NODE_BINARY\" $UPLOAD_DEBUG_FILES" 2>&1)
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/bin/bash
2+
# Test script to verify dSYM upload fix
3+
# Usage: ./test-dsym-fix.sh [test-project-path]
4+
5+
set -e
6+
7+
PROJECT_PATH="${1:-.}"
8+
SDK_PATH="$(cd "$(dirname "$0")/../../.." && pwd)"
9+
10+
echo "=== Sentry React Native dSYM Fix Testing ==="
11+
echo ""
12+
echo "SDK Path: $SDK_PATH"
13+
echo "Project Path: $PROJECT_PATH"
14+
echo ""
15+
16+
# Function to check Sentry debug files
17+
check_sentry_debug_files() {
18+
echo "=== Checking Sentry Debug Files ==="
19+
echo ""
20+
echo "Please check Sentry Debug Files manually:"
21+
echo "1. Go to: https://sentry.io"
22+
echo "2. Navigate to: Settings > Projects > [Your Project] > Debug Files"
23+
echo "3. Look for recent uploads with 'debug' tag"
24+
echo ""
25+
echo "Expected to see:"
26+
echo " ✓ Main app dSYM with 'debug' tag (~145MB)"
27+
echo " ✓ Framework dSYMs"
28+
echo ""
29+
read -p "Press Enter to continue..."
30+
}
31+
32+
# Test with current version
33+
test_current_version() {
34+
echo "=== Phase 1: Testing with v7.12.1 (current stable) ==="
35+
echo ""
36+
37+
cd "$PROJECT_PATH"
38+
39+
echo "Installing @sentry/react-native@7.12.1..."
40+
yarn add @sentry/react-native@7.12.1 || npm install @sentry/react-native@7.12.1
41+
42+
echo ""
43+
echo "Cleaning and regenerating native code..."
44+
npx expo prebuild --clean
45+
46+
echo ""
47+
echo "Building with EAS..."
48+
echo "Watch for 'Upload Debug Symbols to Sentry' in logs"
49+
echo ""
50+
51+
eas build --platform ios --profile production --local 2>&1 | tee build-v7.12.1.log
52+
53+
echo ""
54+
check_sentry_debug_files
55+
}
56+
57+
# Test with our fix
58+
test_with_fix() {
59+
echo "=== Phase 2: Testing with dSYM wait fix ==="
60+
echo ""
61+
62+
cd "$SDK_PATH"
63+
echo "Building SDK..."
64+
yarn build
65+
66+
cd "$PROJECT_PATH"
67+
68+
echo ""
69+
echo "Linking to local SDK..."
70+
yarn link "$SDK_PATH/packages/core" || npm link "$SDK_PATH/packages/core"
71+
72+
echo ""
73+
echo "Cleaning and regenerating native code..."
74+
npx expo prebuild --clean
75+
76+
echo ""
77+
echo "Building with debug logging enabled..."
78+
echo "Look for:"
79+
echo " - 'DEBUG: DWARF_DSYM_FOLDER_PATH=...'"
80+
echo " - 'DEBUG: DWARF_DSYM_FILE_NAME=...'"
81+
echo " - 'Verified main app dSYM is complete'"
82+
echo ""
83+
84+
SENTRY_DSYM_DEBUG=true eas build --platform ios --profile production --local 2>&1 | tee build-with-fix.log
85+
86+
echo ""
87+
check_sentry_debug_files
88+
}
89+
90+
# Main menu
91+
echo "Choose test to run:"
92+
echo "1) Test current v7.12.1 (reproduce issue)"
93+
echo "2) Test with fix (verify solution)"
94+
echo "3) Run both tests"
95+
echo ""
96+
read -p "Enter choice [1-3]: " choice
97+
98+
case $choice in
99+
1)
100+
test_current_version
101+
;;
102+
2)
103+
test_with_fix
104+
;;
105+
3)
106+
test_current_version
107+
echo ""
108+
echo "========================================"
109+
echo ""
110+
test_with_fix
111+
;;
112+
*)
113+
echo "Invalid choice"
114+
exit 1
115+
;;
116+
esac
117+
118+
echo ""
119+
echo "=== Testing Complete ==="
120+
echo ""
121+
echo "Build logs saved:"
122+
echo " - build-v7.12.1.log (if tested)"
123+
echo " - build-with-fix.log (if tested)"
124+
echo ""
125+
echo "Please compare the results in Sentry Debug Files"
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/bin/bash
2+
# Manual test script for dSYM wait functionality
3+
# This simulates the wait behavior without needing a full Xcode build
4+
5+
set -x
6+
7+
# Create a test directory
8+
TEST_DIR="/tmp/sentry-dsym-wait-test-$$"
9+
mkdir -p "$TEST_DIR"
10+
11+
echo "=== Test 1: dSYM appears immediately ==="
12+
DSYM_DIR="$TEST_DIR/test1"
13+
mkdir -p "$DSYM_DIR/TestApp.app.dSYM"
14+
export DWARF_DSYM_FOLDER_PATH="$DSYM_DIR"
15+
export DWARF_DSYM_FILE_NAME="TestApp.app.dSYM"
16+
export SENTRY_DSYM_WAIT_MAX_ATTEMPTS=3
17+
export SENTRY_DSYM_WAIT_INTERVAL=1
18+
19+
# Source the wait function
20+
source "$(dirname "$0")/sentry-xcode-debug-files.sh" 2>/dev/null || {
21+
# If sourcing fails, extract just the wait function
22+
eval "$(sed -n '/^wait_for_dsym_files()/,/^}/p' "$(dirname "$0")/sentry-xcode-debug-files.sh")"
23+
}
24+
25+
wait_for_dsym_files
26+
echo "Test 1 result: $?"
27+
echo ""
28+
29+
echo "=== Test 2: dSYM appears after delay ==="
30+
DSYM_DIR2="$TEST_DIR/test2"
31+
mkdir -p "$DSYM_DIR2"
32+
export DWARF_DSYM_FOLDER_PATH="$DSYM_DIR2"
33+
export DWARF_DSYM_FILE_NAME="DelayedApp.app.dSYM"
34+
35+
# Create dSYM in background after 2 seconds
36+
(sleep 2 && mkdir -p "$DSYM_DIR2/DelayedApp.app.dSYM" && echo "Background: Created dSYM") &
37+
38+
wait_for_dsym_files
39+
echo "Test 2 result: $?"
40+
echo ""
41+
42+
echo "=== Test 3: dSYM never appears (timeout) ==="
43+
DSYM_DIR3="$TEST_DIR/test3"
44+
mkdir -p "$DSYM_DIR3"
45+
export DWARF_DSYM_FOLDER_PATH="$DSYM_DIR3"
46+
export DWARF_DSYM_FILE_NAME="NeverExists.app.dSYM"
47+
export SENTRY_DSYM_WAIT_MAX_ATTEMPTS=2
48+
49+
wait_for_dsym_files
50+
echo "Test 3 result: $?"
51+
echo ""
52+
53+
echo "=== Test 4: Wait disabled ==="
54+
export SENTRY_DSYM_WAIT_ENABLED=false
55+
wait_for_dsym_files
56+
echo "Test 4 result: $?"
57+
echo ""
58+
59+
# Cleanup
60+
rm -rf "$TEST_DIR"
61+
echo "=== All tests complete ==="

0 commit comments

Comments
 (0)