feat(gradle-plugin): upload native debug symbols via symbol-sets upload - #660
feat(gradle-plugin): upload native debug symbols via symbol-sets upload#660cat-ph wants to merge 10 commits into
Conversation
|
we should add a config to enable|disable this in PostHogCliExecTask (or PostHogUploadNativeSymbolsTask not sure) since debug symbols are quite heavy and not all apps (the majority) dont need this |
|
not sure if this task is under isDebuggable already but we should not upload for debug builds a known gap is that we still wont be able to symbolicate OS frames, which is ok for now |
|
left a few comments, the overall approach does whats supposed to do, noice! |
…task With the directory untracked, outputs.upToDateWhen marked the task UP-TO-DATE after a native rebuild that kept the same app version, so new build ids were never uploaded.
Replaces the externalNativeBuild heuristic, which missed jniLibs and dependency-packaged libraries, with a posthog { uploadNativeSymbols } extension flag, off by default.
… across agp versions
…ariants includeNativeSymbolSources passes --include-source to the CLI, off by default. The automatic hook now excludes debuggable variants so day-to-day debug builds don't upload heavy unoptimized symbol sets; explicit invocation still works for any variant. ApplicationVariant.debuggable is unavailable on the AGP 8.0.x baseline, so the flag is resolved from the finalized DSL build type.
great catch, updated this too! |
|
thanks @marandaneto for the thorough review 💙 updated! 🏓 |
Prompt To Fix All With AI### Issue 1
posthog-android-gradle-plugin/src/main/kotlin/com/posthog/android/PostHogAndroidGradlePlugin.kt:114
**Finalizer uploads after failed builds**
When an opted-in assemble, bundle, or install task fails after producing merged native libraries, `hookWithAssembleTasks` still runs the upload through `finalizedBy`, causing symbols for an artifact that was never successfully built to be uploaded and allowing an upload error to obscure the original failure.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat(gradle-plugin): add source bundling..." | Re-trigger Greptile |
…ails Finalizers run even when the build they finalize fails, which would upload symbols for an artifact that was never built and let upload errors obscure the original failure. Applies to the mapping upload too, which shares the hook.
| // invocations are unaffected: an unexecuted anchor carries no failure. | ||
| this@hookWithAssembleTasks.configure { | ||
| onlyIf("the finalized build succeeded") { | ||
| anchors.none { anchor -> anchor.get().state.failure != null } |
There was a problem hiding this comment.
blocking: Preserve Gradle configuration-cache compatibility
This execution-time predicate closes over anchors and resolves each TaskProvider with get(), causing configuration-cache storage to fail with cannot serialize object of type DefaultTask. This affects opted-in native-symbol uploads and the shared mapping-upload hook. Please keep the condition provider-safe without resolving Task instances during execution and add configuration-cache coverage.
There was a problem hiding this comment.
fixed, ty! added a PostHogTaskFailureTracker
marandaneto
left a comment
There was a problem hiding this comment.
left one blocker for config cache otherwise lgtm
The onlyIf predicate resolved Task instances at execution time, which the configuration cache cannot serialize. Failures are now tracked by task path in a build service fed by task-completion events, and a functional test pins configuration-cache store and reuse.
…sts observe the hooked path The failed-build test wired its failing task at configuration time, before AGP registers variant tasks, so the build failed during configuration and the assertions passed vacuously; it now wires in afterEvaluate and asserts the merge succeeded and the failure was post-merge. The configuration-cache test ran the explicit debug task, which never attaches the finalizers or the failure-tracker predicate; it now runs the hooked release path with configuration-cache problems failing the build, asserting store, upload success, and reuse.

💡 Motivation and Context
Native crash capture (#659) needs the app's
.sodebug symbols uploaded at build time, the same way the plugin already uploads ProGuard mappings. This registers anuploadPostHogNativeSymbols<Variant>task that pointsposthog-cli symbol-sets upload(>= 0.7.32) at the variant's merged native libs intermediates: the unstripped libraries as built, before AGP strips them for packaging. The CLI scans the directory, uploads every library carrying debug info and a GNU build id, and triages the rest with guidance.Wiring choices:
posthog { uploadNativeSymbols.set(true) }opt-in and skips debuggable variants; the per-variant task remains directly invocable. It scans the complete merged native library directory, covering NDK builds,jniLibs, and libraries from dependencies.onlyIfwhen the variant has no.sofiles at all.PostHogCliExecTask), sotasks.withType<PostHogCliExecTask>configuration applies to it like the mapping upload.💚 How did you test it?
962b0a1) for both AGP versions because Gradle cannot serialize the captured task, and passes onecc097a; a corrected realassembleReleasefailure also skips upload on the current head.assembleDebugskipped automatic upload, whilebundleReleaseranuploadPostHogNativeSymbolsReleaseexactly once. A fresh ELF plus its exact DWARF source tree was uploaded with CLI 0.10.0; the symbol set was stored without failure and marked used by Cymbal.$exceptioncarrying that debug reference. Cymbal resolved the JNI, C++, and inline frames at lines 19, 12, and 8 with bundledlatest-v5source context. Only an actual Play Console upload/distribution remains untested.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file