Skip to content

Commit

Permalink
chore(ios): add scripts to upload dsyms
Browse files Browse the repository at this point in the history
  • Loading branch information
Adwin Ronald Ross committed Feb 25, 2025
1 parent fdb2b64 commit 5d7aa65
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 0 deletions.
24 changes: 24 additions & 0 deletions ios/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* [Self host compatibility](#self-host-compatibility)
* [Quick reference](#quick-reference)
* [Getting started](#getting-started)
* [Uploading DSYMs](#uploading-dsyms)
* [Custom events](#custom-events)
* [Screen view](#screen-view)
* [Features](#features)
Expand Down Expand Up @@ -92,6 +93,29 @@ Reopen the app and check the Measure dashboard—you should see the crash report
🎉 Congratulations! You have successfully integrated Measure into your app!

# Uploading DSYMs

Measure requires DSYM files to symbolicate crash reports. You can upload DSYM files using a standalone shell script via Xcode’s Build Phases.

## Using Shell Script

Run the `upload_dsyms.sh` script to manually upload DSYM files after building your app.

```sh
sh upload_dsyms.sh <path_to_ipa> <path_to_dsym_folder> <api_url> <api_key>
```

## Using Build Phases

Add the `upload_dsym_build_phases.sh` script as a **New Run Script Phase** in Xcode to upload DSYM files automatically.

```sh
sh "${SRCROOT}/path/to/upload_dsym_build_phases.sh" <api_url> <api_key>
```

> [!CAUTION]
> If you are using Build Phases to upload DSYMs make sure to **upload DSYMs only for release builds**.
# Custom Events

Custom events provide more context on top of automatically collected events. They provide the context specific to the app to debug issues and analyze impact.
Expand Down
106 changes: 106 additions & 0 deletions ios/Scripts/upload_dsym_build_phases.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/bin/bash

# Ensure script is called with api_url and api_key parameters
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <api_url> <api_key>"
exit 1
fi

# Assign parameters
api_url="$1"
api_key="$2"

# Set hardcoded values
MAPPING_TYPE="dsym"
BUILD_TYPE="ipa"

# Collect version name and version code from the Info.plist
INFO_PLIST="${TARGET_BUILD_DIR}/${INFOPLIST_PATH}"
VERSION_NAME=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" "$INFO_PLIST" 2>/dev/null)
VERSION_CODE=$(/usr/libexec/PlistBuddy -c "Print CFBundleVersion" "$INFO_PLIST" 2>/dev/null)

# Path to the app bundle
APP_BUNDLE_PATH="${TARGET_BUILD_DIR}/${FULL_PRODUCT_NAME}"

# Print key paths
echo "TARGET_BUILD_DIR=${TARGET_BUILD_DIR}"
echo "FULL_PRODUCT_NAME=${FULL_PRODUCT_NAME}"
echo "APP_BUNDLE_PATH=${APP_BUNDLE_PATH}"
echo "INFO_PLIST=${INFO_PLIST}"

# Ensure app bundle exists before proceeding
if [ ! -d "$APP_BUNDLE_PATH" ]; then
echo "Error: App bundle not found at $APP_BUNDLE_PATH"
exit 1
fi

# Calculate build size using `du` (disk usage)
BUILD_SIZE=$(du -sk "$APP_BUNDLE_PATH" | awk '{print $1}')
BUILD_SIZE=$((BUILD_SIZE * 1024)) # Convert kilobytes to bytes

# Locate all dSYM files in the build directory
echo "Searching for dSYM files in ${DWARF_DSYM_FOLDER_PATH}"
DSYM_FILES=($(find "${DWARF_DSYM_FOLDER_PATH}" -name "*.dSYM" -type d))

if [ ${#DSYM_FILES[@]} -eq 0 ]; then
echo "Error: No dSYM files found in ${DWARF_DSYM_FOLDER_PATH}"
exit 1
fi

# Log found dSYM files with full paths
echo "Found dSYM files:"
for DSYM in "${DSYM_FILES[@]}"; do
FULL_DSYM_PATH=$(realpath "$DSYM") # Get full absolute path
echo " - $FULL_DSYM_PATH"
done

# Compress dSYM files to .tgz format
TGZ_FILES=()
for DSYM in "${DSYM_FILES[@]}"; do
TGZ_FILE="$(basename "$DSYM").tgz"
echo "Compressing $DSYM to $(pwd)/$TGZ_FILE"

tar -czf "$TGZ_FILE" -C "$(dirname "$DSYM")" "$(basename "$DSYM")"

if [ -f "$TGZ_FILE" ]; then
echo "Successfully created $(pwd)/$TGZ_FILE"
TGZ_FILES+=("$(pwd)/$TGZ_FILE")
else
echo "Error: Failed to create $(pwd)/$TGZ_FILE"
exit 1
fi
done

# Print generated .tgz files
echo "Generated dSYM archives:"
for TGZ in "${TGZ_FILES[@]}"; do
echo " - $TGZ"
done

# Construct the curl command
CURL_COMMAND="curl --request PUT \
--url $api_url/builds \
--header 'Authorization: Bearer $api_key' \
--header 'Content-Type: multipart/form-data' \
--form version_name=$VERSION_NAME \
--form version_code=$VERSION_CODE \
--form mapping_type=$MAPPING_TYPE \
--form build_size=$BUILD_SIZE \
--form build_type=$BUILD_TYPE \
--form app_unique_id=sh.measure.DemoApp"

# Attach each dSYM .tgz file
for TGZ in "${TGZ_FILES[@]}"; do
CURL_COMMAND="$CURL_COMMAND --form mapping_file=@$TGZ"
done

# Execute the curl command
eval "$CURL_COMMAND"

# Clean up generated .tgz files
for TGZ in "${TGZ_FILES[@]}"; do
echo "Removing $TGZ"
rm -f "$TGZ"
done

echo "Script execution completed."
105 changes: 105 additions & 0 deletions ios/Scripts/upload_dsyms.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/bash

# Check if required parameters are provided
if [ "$#" -ne 4 ]; then
echo "Usage: $0 <path_to_ipa> <path_to_dsym_folder> <api_url> <api_key>"
exit 1
fi

IPA_PATH=$1
DSYM_FOLDER=$2
API_URL=$3
API_KEY=$4

SCRIPT_DIR=$(pwd) # Get the directory where the script is running

# Validate the IPA file
if [ ! -f "$IPA_PATH" ]; then
echo "Error: IPA file not found at $IPA_PATH"
exit 1
fi

# Validate the dSYM folder
if [ ! -d "$DSYM_FOLDER" ]; then
echo "Error: dSYM folder not found at $DSYM_FOLDER"
exit 1
fi

# Unzip IPA file
TMP_DIR=$(mktemp -d)
unzip -q "$IPA_PATH" -d "$TMP_DIR"

echo "Contents of unzipped IPA:"
ls "$TMP_DIR"

# Locate the main app's Info.plist
INFO_PLIST=$(find "$TMP_DIR" -path "*/Payload/*.app/Info.plist" -type f | head -n 1)

if [ -z "$INFO_PLIST" ]; then
echo "Error: Info.plist not found in the IPA"
rm -rf "$TMP_DIR"
exit 1
fi

echo "Correct Info.plist found at: $INFO_PLIST"

# Extract version information
VERSION_NAME=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$INFO_PLIST" 2>/dev/null)
VERSION_CODE=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$INFO_PLIST" 2>/dev/null)
APP_UNIQUE_ID=$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$INFO_PLIST" 2>/dev/null)

# Handle missing version information gracefully
[ -z "$VERSION_NAME" ] && VERSION_NAME="Unknown"
[ -z "$VERSION_CODE" ] && VERSION_CODE="Unknown"
[ -z "$APP_UNIQUE_ID" ] && APP_UNIQUE_ID="Unknown"

echo "Extracted VERSION_NAME=$VERSION_NAME"
echo "Extracted VERSION_CODE=$VERSION_CODE"
echo "Extracted APP_UNIQUE_ID=$APP_UNIQUE_ID"

# Create .tgz archives for all dSYMs in the script's directory
DSYM_TGZ_FILES=()
for DSYM_DIR in "$DSYM_FOLDER"/*.dSYM; do
if [ -d "$DSYM_DIR" ]; then
DSYM_TGZ="$SCRIPT_DIR/$(basename "$DSYM_DIR").tgz"
tar -czf "$DSYM_TGZ" -C "$DSYM_FOLDER" "$(basename "$DSYM_DIR")"
DSYM_TGZ_FILES+=("$DSYM_TGZ")
echo "Created dSYM archive at $DSYM_TGZ"
fi
done

if [ "${#DSYM_TGZ_FILES[@]}" -eq 0 ]; then
echo "Error: No dSYM files found in the folder"
rm -rf "$TMP_DIR"
exit 1
fi

# Calculate build size
BUILD_SIZE=$(stat -f%z "$IPA_PATH")
BUILD_TYPE="ipa"

# Construct the curl command
CURL_COMMAND="curl --request PUT \
--url $API_URL/builds \
--header 'Authorization: Bearer $API_KEY' \
--form version_name=$VERSION_NAME \
--form version_code=$VERSION_CODE \
--form mapping_type=dsym \
--form build_size=$BUILD_SIZE \
--form build_type=$BUILD_TYPE \
--form app_unique_id=$APP_UNIQUE_ID"

# Add each dSYM .tgz file to the curl command
for DSYM_TGZ in "${DSYM_TGZ_FILES[@]}"; do
CURL_COMMAND="$CURL_COMMAND --form mapping_file=@$DSYM_TGZ"
done

# Execute the curl command
echo "Executing curl command..."
eval "$CURL_COMMAND"

# Clean up
rm -rf "$TMP_DIR"
for DSYM_TGZ in "${DSYM_TGZ_FILES[@]}"; do
rm -f "$DSYM_TGZ"
done

0 comments on commit 5d7aa65

Please sign in to comment.