build(r8): obfuscate google release builds (#7493)

This commit is contained in:
James Rich authored and GitHub committed 2026-09-30 14:40:07 +00:00
1 parent 700ff601fb
commit 2c075e292f
4 files changed
+64 -26

No files matched your search

+30 -7
View File
@@ -28,6 +28,8 @@ on:
required: true
DATADOG_CLIENT_TOKEN:
required: true
DATADOG_API_KEY:
required: false
GOOGLE_MAPS_API_KEY:
required: true
GOOGLE_PLAY_JSON_KEY:
@@ -106,17 +108,25 @@ jobs:
echo "MAPS_API_KEY=$GOOGLE_MAPS_API_KEY"
} >> ./secrets.properties
# Build only: publish-play uploads the bundle once every leg has built.
# Build only: publish-play uploads the bundle once every leg has built. The Datadog
# mapping upload runs in the same invocation so its build ID matches the bundle's.
- name: Build the Google release
env:
VERSION_NAME: ${{ inputs.version_name }}
VERSION_CODE: ${{ inputs.version_code }}
run: >
./gradlew :androidApp:bundleGoogleRelease :androidApp:assembleGoogleRelease
-Pandroid.injected.version.name="$VERSION_NAME"
-Pandroid.injected.version.code="$VERSION_CODE"
-PaboutLibraries.release=true
-Pmeshtastic.disableAbiSplits=true
DD_API_KEY: ${{ secrets.DATADOG_API_KEY }}
run: |
tasks=(:androidApp:bundleGoogleRelease :androidApp:assembleGoogleRelease)
if [ -n "$DD_API_KEY" ]; then
tasks+=(:androidApp:uploadMappingGoogleRelease)
else
echo "::warning::DATADOG_API_KEY is not set, so Datadog gets no R8 mapping for this release"
fi
./gradlew "${tasks[@]}" \
-Pandroid.injected.version.name="$VERSION_NAME" \
-Pandroid.injected.version.code="$VERSION_CODE" \
-PaboutLibraries.release=true \
-Pmeshtastic.disableAbiSplits=true
- name: List outputs
run: ls -R androidApp/build/outputs/
@@ -137,6 +147,19 @@ jobs:
path: androidApp/build/outputs/apk/google/release/*.apk
retention-days: 1
# github-release attaches it, so anyone can retrace a pasted google-flavor stack.
- name: Compress the R8 mapping
env:
VERSION_CODE: ${{ inputs.version_code }}
run: gzip -c androidApp/build/outputs/mapping/googleRelease/mapping.txt > "mapping-google-$VERSION_CODE.txt.gz"
- name: Upload the R8 mapping artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: google-mapping
path: mapping-google-*.txt.gz
retention-days: 1
- name: Attest Google AAB provenance
if: success()
uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4
+3 -12
View File
@@ -172,6 +172,9 @@ configure<ApplicationExtension> {
// what a real build carries comes from the plugin. See #6883.
manifestPlaceholders["MAPS_API_KEY"] = "dummy"
}
if (name == "fdroid") {
proguardFile("proguard-rules-fdroid.pro")
}
}
}
@@ -211,18 +214,6 @@ androidComponents {
onVariants(selector().withBuildType("debug")) { variant ->
variant.flavorName?.let { flavor -> variant.applicationId.set("com.geeksville.mesh.$flavor.debug") }
}
onVariants(selector().withBuildType("release")) { variant ->
if (variant.flavorName == "google") {
val variantNameCapped = variant.name.replaceFirstChar { it.uppercase() }
val minifyTaskName = "minify${variantNameCapped}WithR8"
val uploadTaskName = "uploadMapping$variantNameCapped"
// Use tasks.names to check existence without eagerly realizing tasks
if (tasks.names.contains(uploadTaskName) && tasks.names.contains(minifyTaskName)) {
tasks.named(minifyTaskName).configure { finalizedBy(uploadTaskName) }
}
}
}
}
dependencies {
+3
View File
@@ -0,0 +1,3 @@
# F-Droid has no crash backend to retrace an obfuscated stack, so its builds keep
# their names. Play's DEX optimization check only sees the google flavor.
-dontobfuscate
+28 -7
View File
@@ -1,8 +1,10 @@
# ============================================================================
# Meshtastic Android — ProGuard / R8 rules for release minification
# ============================================================================
# Open-source project: obfuscation is disabled (readable stack traces). We rely
# on R8 optimization + tree-shaking (unused code removal) for APK size reduction.
# Release builds are shrunk and optimized. The google flavor is also
# obfuscated; its mapping goes to Crashlytics and Datadog and is attached to
# each GitHub release. The fdroid flavor adds proguard-rules-fdroid.pro, which
# keeps it unobfuscated.
#
# Cross-platform library rules (Koin, kotlinx-serialization, Wire, Room,
# Ktor, Coil, Kable, Kermit, Okio, DataStore, Paging, Lifecycle, Navigation 3,
@@ -14,11 +16,7 @@
# ---- General ----------------------------------------------------------------
# Open-source — no need to obfuscate
-dontobfuscate
# R8 optimization is ENABLED. Obfuscation stays off (-dontobfuscate above), so
# stack traces remain readable; tree-shaking plus the full optimization pass
# R8 optimization is ENABLED: tree-shaking plus the full optimization pass
# (method inlining, class merging, Composer/ComposerImpl devirtualization,
# unused-argument removal) all run.
#
@@ -39,6 +37,29 @@
# for auditing. Inspect this file after a release build to see what libraries inject.
-printconfiguration build/outputs/mapping/r8-merged-config.txt
# ---- Names read at runtime --------------------------------------------------
# Each name below is looked up by string, so obfuscation must leave it alone.
# KableGattCacheRefresh reads these private Kable fields by reflection.
-keepclassmembernames class com.juul.kable.BluetoothDeviceAndroidPeripheral {
kotlinx.coroutines.flow.MutableStateFlow connection;
}
-keepclassmembernames class com.juul.kable.Connection {
android.bluetooth.BluetoothGatt gatt;
}
# rumViewName() reports a route's class name as its Datadog view name.
-keepnames class * implements androidx.navigation3.runtime.NavKey
# isDeprecatedEnumEntry() finds each constant's field by name to read @Deprecated.
-keepclassmembernames enum org.meshtastic.** {
<fields>;
}
# GooglePlatformAnalytics drops logging frames from Crashlytics stacks by class-name prefix.
-keepnames class org.meshtastic.app.analytics.GooglePlatformAnalytics*
-keepnames class co.touchlab.kermit.**
# ---- Networking (transitive references from Ktor on Android) ----------------
-dontwarn org.conscrypt.**