From e5659ed5e87510205fda962f3b2a06bb031ff705 Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 24 Sep 2026 22:05:40 +0200 Subject: [PATCH] ci: attach the r8 mapping to android releases Release APKs are obfuscated since R8 was turned on, and nothing kept the mapping that decodes their stack traces. The workflow now copies `android/app/build/outputs/mapping/release/mapping.txt` to `bulwark-mobile---mapping.txt`, uploads it as a workflow artifact and attaches it to the release next to the APK. The in-app updater only picks `.apk` assets, so it ignores the new file. If R8 is on (`android.enableMinifyInReleaseBuilds=true`) and no mapping was written, the step fails instead of publishing an APK whose crashes cannot be read. docs/android-release.md explains how to decode a trace with `retrace`. --- .github/workflows/release-android.yml | 35 ++++++++++++++++++++++++--- docs/android-release.md | 17 +++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-android.yml b/.github/workflows/release-android.yml index 6b23ce74..0329d31c 100644 --- a/.github/workflows/release-android.yml +++ b/.github/workflows/release-android.yml @@ -230,15 +230,37 @@ jobs: fi aapt2 dump badging "$APK" | grep '^package:' || true - - name: Rename APK with version + # The R8 mapping goes out with the APK: without the one from this exact + # build, the obfuscated frames in a release stack trace cannot be + # decoded (docs/android-release.md, "Stack traces"). + - name: Rename APK and R8 mapping with version id: artifact run: | + set -euo pipefail VERSION=$(cat VERSION | tr -d '[:space:]') SRC=android/app/build/outputs/apk/release/app-release.apk DEST="bulwark-mobile-${VERSION}-${GITHUB_SHA::7}.apk" cp "$SRC" "$DEST" + + MAPPING_SRC=android/app/build/outputs/mapping/release/mapping.txt + MAPPING="" + if [ -f "$MAPPING_SRC" ]; then + MAPPING="bulwark-mobile-${VERSION}-${GITHUB_SHA::7}-mapping.txt" + cp "$MAPPING_SRC" "$MAPPING" + elif grep -Eq '^android\.enableMinifyInReleaseBuilds=true\s*$' android/gradle.properties; then + echo "::error title=R8 mapping missing::R8 is on but the build wrote no $MAPPING_SRC, so stack traces from this APK could not be decoded." + exit 1 + fi + echo "path=$DEST" >> "$GITHUB_OUTPUT" + echo "mapping=$MAPPING" >> "$GITHUB_OUTPUT" echo "version=$VERSION" >> "$GITHUB_OUTPUT" + { + echo "files<> "$GITHUB_OUTPUT" - uses: actions/upload-artifact@v4 with: @@ -246,6 +268,13 @@ jobs: path: ${{ steps.artifact.outputs.path }} if-no-files-found: error + - uses: actions/upload-artifact@v4 + if: steps.artifact.outputs.mapping != '' + with: + name: ${{ steps.artifact.outputs.mapping }} + path: ${{ steps.artifact.outputs.mapping }} + if-no-files-found: error + - name: Resolve target release tag id: target_tag env: @@ -265,9 +294,9 @@ jobs: echo "No release tag resolved - APK is in workflow artifacts only." fi - - name: Attach APK to release + - name: Attach APK and R8 mapping to release if: steps.target_tag.outputs.tag != '' uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.target_tag.outputs.tag }} - files: ${{ steps.artifact.outputs.path }} + files: ${{ steps.artifact.outputs.files }} diff --git a/docs/android-release.md b/docs/android-release.md index 24eb05de..3e5769e3 100644 --- a/docs/android-release.md +++ b/docs/android-release.md @@ -237,6 +237,23 @@ either. With neither, a release build stops and lists what is missing. Debug builds and `npx expo run:android` never need any of this. +## Stack traces + +Release builds are shrunk and obfuscated by R8, so library frames in a crash +report look like `com.facebook.react.uimanager.U.a`. The app's own classes +(`com.anonymous.bulwarkmobile`) keep their names. The workflow attaches +`bulwark-mobile---mapping.txt` to the release next to the APK +and keeps it as a workflow artifact. Decode a trace with the mapping of that +exact build: + +```bash +retrace bulwark-mobile-0.1.63-abc1234-mapping.txt stacktrace.txt +``` + +`retrace` is in the Android SDK command-line tools (`cmdline-tools/latest/bin`). +A local build writes its mapping to +`android/app/build/outputs/mapping/release/mapping.txt`. + ## versionCode `versionCode` was `1` for every release up to 0.1.62. It is now derived from