-
-
Notifications
You must be signed in to change notification settings - Fork 358
Expand file tree
/
Copy pathsentry-xcode-debug-files.sh
More file actions
executable file
·230 lines (191 loc) · 9 KB
/
sentry-xcode-debug-files.sh
File metadata and controls
executable file
·230 lines (191 loc) · 9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#!/bin/bash
# Upload Debug Symbols to Sentry Xcode Build Phase
# PWD=ios
# print commands before executing them
set -x
# REACT_NATIVE_PATH first used in RN 0.74.0 Template https://github.com/facebook/react-native/commit/289e78388a87408e215a25108cb02511a05f5c80
LOCAL_REACT_NATIVE_PATH="${REACT_NATIVE_PATH:-"../node_modules/react-native"}"
[ -z "$WITH_ENVIRONMENT" ] && WITH_ENVIRONMENT="${LOCAL_REACT_NATIVE_PATH}/scripts/xcode/with-environment.sh"
if [ -f "$WITH_ENVIRONMENT" ]; then
# load envs if loader file exists (since rn 0.68)
. "$WITH_ENVIRONMENT"
fi
# stop on first error (we can't use -e before as any failed command in WITH_ENVIRONMENT would stop the debug files upload)
set -e
LOCAL_NODE_BINARY=${NODE_BINARY:-node}
# The project root by default is one level up from the ios directory
RN_PROJECT_ROOT="${PROJECT_DIR}/.."
[ -z "$SENTRY_PROPERTIES" ] && export SENTRY_PROPERTIES=sentry.properties
[ -z "$SENTRY_DOTENV_PATH" ] && [ -f "$RN_PROJECT_ROOT/.env.sentry-build-plugin" ] && export SENTRY_DOTENV_PATH="$RN_PROJECT_ROOT/.env.sentry-build-plugin"
[ -z "$SENTRY_CLI_EXECUTABLE" ] && SENTRY_CLI_PACKAGE_PATH=$("$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json'))")
[ -z "$SOURCEMAP_FILE" ] && export SOURCEMAP_FILE="$DERIVED_FILE_DIR/main.jsbundle.map"
if [ -z "$SENTRY_CLI_EXECUTABLE" ]; then
# Try standard resolution safely
RESOLVED_PATH=$(
"$LOCAL_NODE_BINARY" --print "require('path').dirname(require.resolve('@sentry/cli/package.json'))" 2>/dev/null
) || true
if [ -n "$RESOLVED_PATH" ]; then
SENTRY_CLI_PACKAGE_PATH="$RESOLVED_PATH/bin/sentry-cli"
else
# Fallback: parse NODE_PATH from the .bin/sentry-cli shim (file generated by PNPM)
PNPM_BIN_PATH="$PWD/../node_modules/@sentry/react-native/node_modules/.bin/sentry-cli"
if [ -f "$PNPM_BIN_PATH" ]; then
CLI_FILE_TEXT=$(cat "$PNPM_BIN_PATH")
# Filter where PNPM stored Sentry CLI
NODE_PATH_LINE=$(echo "$CLI_FILE_TEXT" | grep -oE 'NODE_PATH="[^"]+"' | head -n1)
NODE_PATH_VALUE=$(echo "$NODE_PATH_LINE" | sed -E 's/^NODE_PATH="([^"]+)".*/\1/')
SENTRY_CLI_PACKAGE_PATH=${NODE_PATH_VALUE%%/bin*}
fi
fi
fi
[ -z "$SENTRY_CLI_EXECUTABLE" ] && SENTRY_CLI_EXECUTABLE="$SENTRY_CLI_PACKAGE_PATH"
[[ $SENTRY_INCLUDE_NATIVE_SOURCES == "true" ]] && INCLUDE_SOURCES_FLAG="--include-sources" || INCLUDE_SOURCES_FLAG=""
EXTRA_ARGS="$SENTRY_CLI_EXTRA_ARGS $SENTRY_CLI_DEBUG_FILES_UPLOAD_EXTRA_ARGS $INCLUDE_SOURCES_FLAG"
UPLOAD_DEBUG_FILES="\"$SENTRY_CLI_EXECUTABLE\" debug-files upload $EXTRA_ARGS \"$DWARF_DSYM_FOLDER_PATH\""
# Print a message only when SENTRY_DSYM_DEBUG is set
_sentry_dsym_log() {
if [ -n "${SENTRY_DSYM_DEBUG}" ]; then
echo "$1"
fi
}
# Check if dSYM files are fully generated and ready to upload.
# Returns 0 (ready) or 1 (not ready yet), printing a status message in either case.
_sentry_check_dsym_ready() {
local dsym_folder="$1"
local dsym_file_name="$2"
local attempt="$3"
local max_attempts="$4"
if [ ! -d "$dsym_folder" ]; then
_sentry_dsym_log "dSYM folder does not exist yet: $dsym_folder (attempt $attempt/$max_attempts)"
return 1
fi
local dsym_count
dsym_count=$(find "$dsym_folder" -name "*.dSYM" -type d 2>/dev/null | wc -l | tr -d ' ')
if [ "$dsym_count" -eq 0 ]; then
_sentry_dsym_log "No dSYM bundles found yet in $dsym_folder (attempt $attempt/$max_attempts)"
return 1
fi
echo "Found $dsym_count dSYM bundle(s) in $dsym_folder"
# DWARF_DSYM_FILE_NAME not set: check if any dSYM has valid DWARF content
if [ -z "$dsym_file_name" ]; then
for dsym in "$dsym_folder"/*.dSYM; do
local dwarf_file
dwarf_file=$(find "$dsym/Contents/Resources/DWARF" -type f -size +0 2>/dev/null | head -1)
if [ -n "$dwarf_file" ]; then
echo "Found dSYM bundle(s) with valid DWARF content"
return 0
fi
done
_sentry_dsym_log "Found dSYM bundle(s) but none have complete DWARF content yet (attempt $attempt/$max_attempts)"
return 1
fi
# DWARF_DSYM_FILE_NAME set: verify the main app dSYM is complete
local main_dsym="$dsym_folder/$dsym_file_name"
if [ ! -d "$main_dsym" ]; then
_sentry_dsym_log "Main app dSYM not found yet: $dsym_file_name (attempt $attempt/$max_attempts)"
return 1
fi
local dwarf_dir="$main_dsym/Contents/Resources/DWARF"
if [ ! -d "$dwarf_dir" ]; then
_sentry_dsym_log "Main app dSYM structure incomplete (missing DWARF directory): $dsym_file_name (attempt $attempt/$max_attempts)"
return 1
fi
local dwarf_files
dwarf_files=$(find "$dwarf_dir" -type f 2>/dev/null | head -1)
if [ -z "$dwarf_files" ]; then
_sentry_dsym_log "Main app dSYM DWARF directory is empty: $dsym_file_name (attempt $attempt/$max_attempts)"
return 1
fi
local dwarf_size
dwarf_size=$(find "$dwarf_dir" -type f -size +0 2>/dev/null | head -1)
if [ -z "$dwarf_size" ]; then
_sentry_dsym_log "Main app dSYM DWARF binary is empty (still being written): $dsym_file_name (attempt $attempt/$max_attempts)"
return 1
fi
echo "Verified main app dSYM is complete: $dsym_file_name"
return 0
}
# Function to wait for dSYM files to be generated
# This addresses a race condition where the upload script runs before dSYM generation completes
wait_for_dsym_files() {
local max_attempts="${SENTRY_DSYM_WAIT_MAX_ATTEMPTS:-10}"
local wait_interval="${SENTRY_DSYM_WAIT_INTERVAL:-2}"
local attempt=1
local total_wait_time=0
# Check if we should wait for dSYM files
if [ "$SENTRY_DSYM_WAIT_ENABLED" == "false" ]; then
echo "SENTRY_DSYM_WAIT_ENABLED=false, skipping dSYM wait check"
return 0
fi
# Warn if DWARF_DSYM_FILE_NAME is not set - we can't verify the main app dSYM
if [ -z "$DWARF_DSYM_FILE_NAME" ]; then
echo "warning: DWARF_DSYM_FILE_NAME not set, cannot verify main app dSYM specifically"
echo "warning: Will proceed when any dSYM bundle is found"
fi
echo "Checking for dSYM files in: $DWARF_DSYM_FOLDER_PATH"
# Debug information to help diagnose issues
_sentry_dsym_log "DEBUG: DWARF_DSYM_FOLDER_PATH=$DWARF_DSYM_FOLDER_PATH"
_sentry_dsym_log "DEBUG: DWARF_DSYM_FILE_NAME=$DWARF_DSYM_FILE_NAME"
_sentry_dsym_log "DEBUG: PRODUCT_NAME=$PRODUCT_NAME"
while [ $attempt -le $max_attempts ]; do
if _sentry_check_dsym_ready "$DWARF_DSYM_FOLDER_PATH" "$DWARF_DSYM_FILE_NAME" "$attempt" "$max_attempts"; then
return 0
fi
if [ $attempt -lt $max_attempts ]; then
# Progressive backoff: quick checks first, longer waits later
# Attempts 1-3: 0.5s (total 1.5s)
# Attempts 4-6: 1s (total 3s)
# Attempts 7+: 2s (remaining time)
local current_interval="$wait_interval"
if [ -z "${SENTRY_DSYM_WAIT_INTERVAL}" ]; then
# Only use progressive intervals if user hasn't set custom interval
if [ $attempt -le 3 ]; then
current_interval=0.5
elif [ $attempt -le 6 ]; then
current_interval=1
else
current_interval=2
fi
fi
echo "Waiting ${current_interval}s for dSYM generation to complete..."
sleep $current_interval
total_wait_time=$(awk "BEGIN {print $total_wait_time + $current_interval}")
fi
attempt=$((attempt + 1))
done
# Timeout reached
echo "warning: Timeout waiting for dSYM files after ${total_wait_time}s ($max_attempts attempts)"
echo "warning: This may result in incomplete debug symbol uploads"
echo "warning: To disable this check, set SENTRY_DSYM_WAIT_ENABLED=false"
return 1
}
XCODE_BUILD_CONFIGURATION="${CONFIGURATION}"
if [ "$SENTRY_DISABLE_AUTO_UPLOAD" == true ]; then
echo "SENTRY_DISABLE_AUTO_UPLOAD=true, skipping debug files upload"
elif [ "$SENTRY_DISABLE_XCODE_DEBUG_UPLOAD" == true ]; then
echo "SENTRY_DISABLE_XCODE_DEBUG_UPLOAD=true, skipping native debug files upload"
elif echo "$XCODE_BUILD_CONFIGURATION" | grep -iq "debug"; then # case insensitive check for "debug"
echo "Skipping debug files upload for *Debug* configuration"
else
# Wait for dSYM files to be generated (addresses race condition in EAS builds)
# Don't fail the script if wait times out - we still want to attempt upload
set +e
wait_for_dsym_files
set -e
# 'warning:' triggers a warning in Xcode, 'error:' triggers an error
set +x +e # disable printing commands otherwise we might print `error:` by accident and allow continuing on error
SENTRY_UPLOAD_COMMAND_OUTPUT=$(/bin/sh -c "\"$LOCAL_NODE_BINARY\" $UPLOAD_DEBUG_FILES" 2>&1)
UPLOAD_EXIT_CODE=$?
if [ $UPLOAD_EXIT_CODE -eq 0 ]; then
echo "$SENTRY_UPLOAD_COMMAND_OUTPUT" | awk '{print "output: sentry-cli - " $0}'
else
if [ "$SENTRY_ALLOW_FAILURE" == true ]; then
echo "warning: sentry-cli - Debug files upload failed, but continuing build because SENTRY_ALLOW_FAILURE=true"
echo "warning: sentry-cli - $SENTRY_UPLOAD_COMMAND_OUTPUT"
else
echo "error: sentry-cli - To disable native debug files auto upload, set SENTRY_DISABLE_AUTO_UPLOAD=true in your environment variables. Or to allow failing upload, set SENTRY_ALLOW_FAILURE=true"
echo "error: sentry-cli - $SENTRY_UPLOAD_COMMAND_OUTPUT"
fi
fi
set -x -e # re-enable
fi