From 1d594804b571161e9512c1481b0ce02625d9a06a Mon Sep 17 00:00:00 2001 From: Dat Vu Date: Tue, 14 Apr 2026 12:32:03 +0700 Subject: [PATCH] TF-4268 Automate Sentry symbol upload for Android release builds (#4442) --- .github/workflows/release.yaml | 50 +++++++++++++++++++ android/fastlane/Fastfile | 4 +- ...y-source-maps-native-symbols-automation.md | 45 +++++++++++++++++ pubspec.yaml | 1 + 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 docs/adr/0076-android-sentry-source-maps-native-symbols-automation.md diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 65ec1bb0a..638e23217 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -89,3 +89,53 @@ jobs: APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }} run: ../scripts/build-release.sh working-directory: ${{ matrix.os }} + + # --- UPLOAD SENTRY MAPPING AND SYMBOLS FOR ANDROID --- + - name: Upload Android ProGuard Mapping & Dart Debug Symbols to Sentry + if: matrix.os == 'android' + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + run: | + # 1. Install Sentry CLI + curl -sL https://sentry.io/get-cli/ | SENTRY_CLI_VERSION="2.42.2" bash + + # 2. Validate tag version matches pubspec.yaml version + TAG_VERSION="${GITHUB_REF_NAME#v}" + TAG_VERSION="${TAG_VERSION%%-rc*}" + PUBSPEC_VERSION=$(awk '/^version:/{split($2,a,"+"); print a[1]; exit}' pubspec.yaml) + if [ "$PUBSPEC_VERSION" != "$TAG_VERSION" ]; then + echo "::error::Tag version ($TAG_VERSION) does not match pubspec version ($PUBSPEC_VERSION)." + exit 1 + fi + SENTRY_RELEASE="$PUBSPEC_VERSION" + echo "Processing Sentry Release: $SENTRY_RELEASE" + + # 3. Create Release + sentry-cli releases new "$SENTRY_RELEASE" + + # 4. Upload ProGuard Mapping + MAPPING_FILE="build/app/outputs/mapping/release/mapping.txt" + + if [ -f "$MAPPING_FILE" ]; then + echo "Found mapping.txt, uploading ProGuard mapping..." + sentry-cli upload-proguard "$MAPPING_FILE" + else + echo "::error::Could not find mapping.txt at $MAPPING_FILE." + exit 1 + fi + + # 5. Upload Dart Debug Symbols (Native Dart Stacktrace) + SYMBOLS_DIR="build/app/outputs/symbols" + + if [ -d "$SYMBOLS_DIR" ]; then + echo "Uploading Dart debug symbols from $SYMBOLS_DIR..." + sentry-cli debug-files upload "$SYMBOLS_DIR" + else + echo "::error::Symbols directory not found at $SYMBOLS_DIR. Check your Fastfile build arguments." + exit 1 + fi + + # 6. Finalize + sentry-cli releases finalize "$SENTRY_RELEASE" diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index cb2a06447..811d7d07e 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -31,7 +31,9 @@ platform :android do json_key_data: ENV["PLAY_STORE_CONFIG_JSON"] )[0].to_i build_name = last_git_tag.gsub("v", "").split("-").first - sh "flutter build appbundle --verbose --release --dart-define=SERVER_URL=$SERVER_URL --build-number=#{latest_build_number+1} --build-name=#{build_name}" + # Add: --obfuscate --split-debug-info=build/app/outputs/symbols + # Note: We run `cd ..` first, so paths are relative to repository root. + sh "cd .. && flutter build appbundle --verbose --release --obfuscate --split-debug-info=build/app/outputs/symbols --dart-define=SERVER_URL=$SERVER_URL --build-number=#{latest_build_number+1} --build-name=#{build_name}" upload_to_play_store( json_key_data: ENV["PLAY_STORE_CONFIG_JSON"], track: track, diff --git a/docs/adr/0076-android-sentry-source-maps-native-symbols-automation.md b/docs/adr/0076-android-sentry-source-maps-native-symbols-automation.md new file mode 100644 index 000000000..949944b9f --- /dev/null +++ b/docs/adr/0076-android-sentry-source-maps-native-symbols-automation.md @@ -0,0 +1,45 @@ +# 0076. Android Sentry Source Maps & Native Symbols Automation + +Date: 2026-02-11 + +## Status + +Accepted + +## Context + +Android crash reports on Sentry are unreadable due to two levels of obfuscation: + +1. **Java/Kotlin:** Minified by R8/ProGuard (e.g., classes appear as `a.b.c`). +2. **Dart:** ARM binaries lack debug info — only raw memory addresses are shown. + +This makes production crashes nearly impossible to debug. + +## Decision + +Update the Android CI/CD pipeline to automatically upload de-obfuscation artifacts to Sentry on every release. + +### Build (Fastlane) + +Add `--obfuscate --split-debug-info=build/app/outputs/symbols` to the Flutter build command (executed from repository root after `cd ..`). This strips debug info from the binary into a separate `symbols/` directory. + +### CI Pipeline (GitHub Actions) + +After a successful build, the `release.yaml` workflow: + +1. Creates a Sentry release matching the version in `pubspec.yaml`. +2. Uploads `build/app/outputs/mapping/release/mapping.txt` (ProGuard — de-obfuscates Java/Kotlin). +3. Uploads `build/app/outputs/symbols/` (Dart debug symbols — de-obfuscates Flutter stack traces). +4. Finalizes the release. + +## Consequences + +**Benefits:** +- Full readable stack traces for both Dart and Java/Kotlin crashes in Sentry. +- Automated — no manual upload step needed. +- Smaller app binary (debug info stripped from the APK/AAB). + +**Trade-offs:** +- Slightly longer CI build time (symbol separation + upload). +- `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, `SENTRY_PROJECT` must be maintained as CI secrets. +- Symbol files must be uploaded in the **same workflow run** as the build — running `flutter clean` between build and upload will break de-obfuscation. diff --git a/pubspec.yaml b/pubspec.yaml index ff9fc1835..13a35d637 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html version: 0.28.2-rc01 + environment: sdk: ">=3.0.0 <4.0.0"