Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .changeset/calm-dsyms-wait.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"posthog-ios": patch
---

Wait for the current app dSYM and use source Info.plist versions before uploading symbols.
Comment thread
marandaneto marked this conversation as resolved.
Outdated
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build buildSdk buildExamples format swiftLint swiftFormat swiftLintCheck swiftFormatCheck installSwiftLint installSwiftFormat test recordEventShapeSnapshots testDowngradeCompatibility testOniOSSimulator testOnMacSimulator maskSnapshots recordMaskSnapshots checkMaskSnapshotRuntime lint bootstrap releaseCocoaPods api apiCheck apiUpdate buildIOS
.PHONY: build buildSdk buildExamples format swiftLint swiftFormat swiftLintCheck swiftFormatCheck installSwiftLint installSwiftFormat test testUploadSymbols recordEventShapeSnapshots testDowngradeCompatibility testOniOSSimulator testOnMacSimulator maskSnapshots recordMaskSnapshots checkMaskSnapshotRuntime lint bootstrap releaseCocoaPods api apiCheck apiUpdate buildIOS

build: buildSdk buildExamples

Expand Down Expand Up @@ -154,7 +154,10 @@ recordMaskSnapshots: checkMaskSnapshotRuntime
# Examples:
# make test # Run all tests
# make test filter=PostHogPropertiesSerializationTests # Run specific test suite, class or method
test:
testUploadSymbols:
build-tools/upload-symbols.test.sh

test: testUploadSymbols
set -o pipefail && swift test --no-parallel -Xswiftc -DTESTING $(if $(filter),--filter $(filter))

recordEventShapeSnapshots:
Expand Down
71 changes: 68 additions & 3 deletions build-tools/upload-symbols.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,38 @@ if [ -n "${CONFIGURATION}" ] && [ "${CONFIGURATION}" != "Release" ]; then
exit 0
fi

# Xcode can start this phase before dsymutil finishes. Declaring the dSYM as an input can create
Comment thread
marandaneto marked this conversation as resolved.
Outdated
# dependency cycles for apps with embedded extensions, so wait until the dSYM belongs to the current
# executable instead. If it never becomes ready, skip the upload rather than sending stale symbols.
if [ "${DEBUG_INFORMATION_FORMAT:-}" = "dwarf-with-dsym" ] && [ -n "${DWARF_DSYM_FOLDER_PATH:-}" ] && [ -n "${DWARF_DSYM_FILE_NAME:-}" ] && [ -n "${EXECUTABLE_NAME:-}" ] && [ -n "${TARGET_BUILD_DIR:-}" ] && [ -n "${EXECUTABLE_PATH:-}" ]; then
POSTHOG_MAIN_DWARF="${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${EXECUTABLE_NAME}"
POSTHOG_APP_EXECUTABLE="${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
POSTHOG_DSYM_ATTEMPT=1
POSTHOG_DSYM_MAX_ATTEMPTS=60
POSTHOG_DSYM_READY=0

while [ "$POSTHOG_DSYM_ATTEMPT" -le "$POSTHOG_DSYM_MAX_ATTEMPTS" ]; do
if [ -s "$POSTHOG_MAIN_DWARF" ] && [ -s "$POSTHOG_APP_EXECUTABLE" ]; then
POSTHOG_DSYM_UUIDS=$(xcrun dwarfdump --uuid "$POSTHOG_MAIN_DWARF" 2>/dev/null | awk '/^UUID: / {print $2}' | sort)
POSTHOG_APP_UUIDS=$(xcrun dwarfdump --uuid "$POSTHOG_APP_EXECUTABLE" 2>/dev/null | awk '/^UUID: / {print $2}' | sort)
if [ -n "$POSTHOG_DSYM_UUIDS" ] && [ "$POSTHOG_DSYM_UUIDS" = "$POSTHOG_APP_UUIDS" ]; then
POSTHOG_DSYM_READY=1
break
fi
fi

if [ "$POSTHOG_DSYM_ATTEMPT" -lt "$POSTHOG_DSYM_MAX_ATTEMPTS" ]; then
sleep 1
fi
POSTHOG_DSYM_ATTEMPT=$((POSTHOG_DSYM_ATTEMPT + 1))
done

if [ "$POSTHOG_DSYM_READY" -ne 1 ]; then
echo "warning: Main app dSYM was not ready after ${POSTHOG_DSYM_MAX_ATTEMPTS} attempts; skipping upload: $POSTHOG_MAIN_DWARF"
Comment thread
marandaneto marked this conversation as resolved.
Outdated
exit 0
fi
fi

# Validate environment
if [ -z "${DWARF_DSYM_FOLDER_PATH}" ]; then
echo "warning: DWARF_DSYM_FOLDER_PATH not set"
Expand Down Expand Up @@ -110,6 +142,34 @@ if [ "$LOWEST" != "$MIN_POSTHOG_CLI_VERSION" ]; then
exit 1
fi

resolve_source_plist_value() {
local key="$1"
local plist_path="${INFOPLIST_FILE:-}"
local value

if [ -z "$plist_path" ]; then
return
fi
if [[ "$plist_path" != /* ]]; then
if [ -z "${SRCROOT:-}" ]; then
return
fi
plist_path="${SRCROOT}/${plist_path}"
Comment thread
marandaneto marked this conversation as resolved.
fi
if [ ! -f "$plist_path" ]; then
return
fi

value=$(/usr/libexec/PlistBuddy -c "Print :${key}" "$plist_path" 2>/dev/null) || return
if [ -z "$value" ] || [[ "$value" == *"\$("* ]] || [[ "$value" == *"\${"* ]]; then
Comment thread
marandaneto marked this conversation as resolved.
return
fi
printf '%s' "$value"
}

POSTHOG_RELEASE_VERSION=$(resolve_source_plist_value "CFBundleShortVersionString")
POSTHOG_BUILD_VERSION=$(resolve_source_plist_value "CFBundleVersion")

# Build CLI arguments as an array so paths with spaces are preserved.
CLI_ARGS=(--directory "${DWARF_DSYM_FOLDER_PATH}")

Expand All @@ -118,14 +178,19 @@ if [ -n "${DWARF_DSYM_FILE_NAME}" ]; then
CLI_ARGS+=(--main-dsym "${DWARF_DSYM_FILE_NAME}")
fi

# Pass version info from Xcode build settings (overrides plist extraction)
# Prefer literal values from the source Info.plist. EAS remote versioning can update these values
# without changing MARKETING_VERSION or CURRENT_PROJECT_VERSION.
if [ -n "${PRODUCT_BUNDLE_IDENTIFIER}" ]; then
CLI_ARGS+=(--release-name "${PRODUCT_BUNDLE_IDENTIFIER}")
fi
if [ -n "${MARKETING_VERSION}" ]; then
if [ -n "$POSTHOG_RELEASE_VERSION" ]; then
CLI_ARGS+=(--release-version "$POSTHOG_RELEASE_VERSION")
elif [ -n "${MARKETING_VERSION}" ]; then
CLI_ARGS+=(--release-version "${MARKETING_VERSION}")
fi
if [ -n "${CURRENT_PROJECT_VERSION}" ]; then
if [ -n "$POSTHOG_BUILD_VERSION" ]; then
CLI_ARGS+=(--build "$POSTHOG_BUILD_VERSION")
elif [ -n "${CURRENT_PROJECT_VERSION}" ]; then
CLI_ARGS+=(--build "${CURRENT_PROJECT_VERSION}")
fi
# Include source if requested via env var
Expand Down
175 changes: 175 additions & 0 deletions build-tools/upload-symbols.test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/usr/bin/env bash

set -euo pipefail

ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
UPLOAD_SCRIPT="${ROOT_DIR}/build-tools/upload-symbols.sh"
TEMP_DIR=$(mktemp -d)
trap 'rm -rf "$TEMP_DIR"' EXIT

fail() {
echo "error: $*" >&2
exit 1
}

assert_file_contains_line() {
local file="$1"
local expected="$2"
grep -Fxq -- "$expected" "$file" || fail "Expected '$expected' in $file"
}

create_fixture() {
local name="$1"
FIXTURE_DIR="${TEMP_DIR}/${name}"
HOME_DIR="${FIXTURE_DIR}/home"
FAKE_BIN="${FIXTURE_DIR}/bin"
SRC_ROOT="${FIXTURE_DIR}/source"
DSYM_FOLDER="${FIXTURE_DIR}/dSYMs"
DSYM_NAME="ExampleApp.app.dSYM"
EXECUTABLE_NAME="ExampleApp"
MAIN_DWARF="${DSYM_FOLDER}/${DSYM_NAME}/Contents/Resources/DWARF/${EXECUTABLE_NAME}"
TARGET_BUILD_DIR="${FIXTURE_DIR}/build"
EXECUTABLE_PATH="ExampleApp.app/ExampleApp"
APP_EXECUTABLE="${TARGET_BUILD_DIR}/${EXECUTABLE_PATH}"
CLI_ARGS_FILE="${FIXTURE_DIR}/cli-args"
DWARFDUMP_ATTEMPTS="${FIXTURE_DIR}/dwarfdump-attempts"

mkdir -p "$HOME_DIR/.posthog" "$FAKE_BIN" "$SRC_ROOT/Config" "$(dirname "$MAIN_DWARF")" "$(dirname "$APP_EXECUTABLE")"
printf 'dwarf' > "$MAIN_DWARF"
printf 'executable' > "$APP_EXECUTABLE"

cat > "$HOME_DIR/.posthog/posthog-cli" <<'EOF'
#!/bin/sh
if [ "$1" = "--version" ]; then
echo "posthog-cli 0.10.0"
exit 0
fi
printf '%s\n' "$@" > "$TEST_CLI_ARGS_FILE"
EOF
chmod +x "$HOME_DIR/.posthog/posthog-cli"

cat > "$FAKE_BIN/sleep" <<'EOF'
#!/bin/sh
exit 0
EOF
chmod +x "$FAKE_BIN/sleep"
}

write_plist() {
local path="$1"
local release_version="$2"
local build_version="$3"
cat > "$path" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleShortVersionString</key>
<string>${release_version}</string>
<key>CFBundleVersion</key>
<string>${build_version}</string>
</dict>
</plist>
EOF
}

run_upload() {
set +e
OUTPUT=$(env \
HOME="$HOME_DIR" \
PATH="$FAKE_BIN:$PATH" \
CONFIGURATION="${TEST_CONFIGURATION:-Release}" \
DEBUG_INFORMATION_FORMAT="${TEST_DEBUG_INFORMATION_FORMAT:-dwarf-with-dsym}" \
DWARF_DSYM_FOLDER_PATH="$DSYM_FOLDER" \
DWARF_DSYM_FILE_NAME="$DSYM_NAME" \
EXECUTABLE_NAME="$EXECUTABLE_NAME" \
TARGET_BUILD_DIR="$TARGET_BUILD_DIR" \
EXECUTABLE_PATH="$EXECUTABLE_PATH" \
SRCROOT="$SRC_ROOT" \
INFOPLIST_FILE="$TEST_INFOPLIST_FILE" \
PRODUCT_BUNDLE_IDENTIFIER="com.example.app" \
MARKETING_VERSION="1.0" \
CURRENT_PROJECT_VERSION="1" \
TEST_CLI_ARGS_FILE="$CLI_ARGS_FILE" \
TEST_DWARFDUMP_ATTEMPTS="$DWARFDUMP_ATTEMPTS" \
bash "$UPLOAD_SCRIPT" 2>&1)
STATUS=$?
set -e
}

test_waits_for_current_dsym_and_uses_source_plist_versions() {
create_fixture "ready"
write_plist "$SRC_ROOT/Config/Info.plist" "2.10.0" "154"
TEST_INFOPLIST_FILE="Config/Info.plist"

cat > "$FAKE_BIN/xcrun" <<'EOF'
#!/bin/sh
case "$3" in
*.dSYM/*)
attempts=$(cat "$TEST_DWARFDUMP_ATTEMPTS" 2>/dev/null || printf 0)
attempts=$((attempts + 1))
printf '%s' "$attempts" > "$TEST_DWARFDUMP_ATTEMPTS"
if [ "$attempts" -lt 3 ]; then
printf 'UUID: OLD-UUID (arm64) %s\n' "$3"
else
printf 'UUID: CURRENT-UUID (arm64) %s\n' "$3"
fi
;;
*) printf 'UUID: CURRENT-UUID (arm64) %s\n' "$3" ;;
esac
EOF
chmod +x "$FAKE_BIN/xcrun"

run_upload

[ "$STATUS" -eq 0 ] || fail "Expected upload to succeed, got status $STATUS: $OUTPUT"
[ "$(cat "$DWARFDUMP_ATTEMPTS")" = "3" ] || fail "Expected three dSYM readiness attempts"
[ -f "$CLI_ARGS_FILE" ] || fail "Expected posthog-cli to run"
assert_file_contains_line "$CLI_ARGS_FILE" "--release-version"
assert_file_contains_line "$CLI_ARGS_FILE" "2.10.0"
assert_file_contains_line "$CLI_ARGS_FILE" "--build"
assert_file_contains_line "$CLI_ARGS_FILE" "154"
}

test_skips_upload_when_dsym_never_matches() {
create_fixture "not-ready"
write_plist "$SRC_ROOT/Config/Info.plist" "2.10.0" "154"
TEST_INFOPLIST_FILE="Config/Info.plist"

cat > "$FAKE_BIN/xcrun" <<'EOF'
#!/bin/sh
case "$3" in
*.dSYM/*) printf 'UUID: OLD-UUID (arm64) %s\n' "$3" ;;
*) printf 'UUID: CURRENT-UUID (arm64) %s\n' "$3" ;;
esac
EOF
chmod +x "$FAKE_BIN/xcrun"

run_upload

[ "$STATUS" -eq 0 ] || fail "Expected an unavailable dSYM to skip without failing the build"
[[ "$OUTPUT" == *"skipping upload"* ]] || fail "Expected a warning when the dSYM stays unavailable"
[ ! -f "$CLI_ARGS_FILE" ] || fail "posthog-cli must not run with a stale dSYM"
}

test_falls_back_for_unresolved_source_plist_versions() {
create_fixture "fallback"
write_plist "$SRC_ROOT/Config/Info.plist" "\${MARKETING_VERSION}" "\$(CURRENT_PROJECT_VERSION)"
TEST_INFOPLIST_FILE="$SRC_ROOT/Config/Info.plist"
TEST_DEBUG_INFORMATION_FORMAT="dwarf"

run_upload

[ "$STATUS" -eq 0 ] || fail "Expected upload with fallback versions to succeed: $OUTPUT"
assert_file_contains_line "$CLI_ARGS_FILE" "--release-version"
assert_file_contains_line "$CLI_ARGS_FILE" "1.0"
assert_file_contains_line "$CLI_ARGS_FILE" "--build"
assert_file_contains_line "$CLI_ARGS_FILE" "1"
}

bash -n "$UPLOAD_SCRIPT"
test_waits_for_current_dsym_and_uses_source_plist_versions
test_skips_upload_when_dsym_never_matches
test_falls_back_for_unresolved_source_plist_versions

echo "upload-symbols tests passed"
Loading