fix(gradle): Fix compatibility with react-native-legal#5253
Merged
Conversation
Contributor
Android (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 8a4ce6f | 422.88 ms | 408.33 ms | -14.55 ms |
| 95aaf8a | 437.89 ms | 419.45 ms | -18.44 ms |
| 1bea095+dirty | 401.42 ms | 426.93 ms | 25.52 ms |
| c08359e | 421.87 ms | 445.37 ms | 23.50 ms |
| ba75c7c | 367.72 ms | 369.16 ms | 1.44 ms |
| 0b64753+dirty | 448.67 ms | 474.61 ms | 25.94 ms |
| bfe454a+dirty | 573.44 ms | 579.46 ms | 6.02 ms |
| 49ef936+dirty | 405.96 ms | 417.22 ms | 11.26 ms |
| a0b15d6 | 423.06 ms | 437.77 ms | 14.71 ms |
| c9e95bd | 428.04 ms | 458.70 ms | 30.65 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 8a4ce6f | 17.75 MiB | 19.68 MiB | 1.94 MiB |
| 95aaf8a | 17.75 MiB | 19.68 MiB | 1.93 MiB |
| 1bea095+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| c08359e | 17.75 MiB | 20.15 MiB | 2.41 MiB |
| ba75c7c | 17.75 MiB | 20.15 MiB | 2.41 MiB |
| 0b64753+dirty | 17.75 MiB | 19.70 MiB | 1.95 MiB |
| bfe454a+dirty | 17.75 MiB | 19.69 MiB | 1.94 MiB |
| 49ef936+dirty | 17.75 MiB | 19.69 MiB | 1.94 MiB |
| a0b15d6 | 17.75 MiB | 20.15 MiB | 2.41 MiB |
| c9e95bd | 17.75 MiB | 19.68 MiB | 1.93 MiB |
Contributor
Android (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 49ef936+dirty | 333.72 ms | 387.51 ms | 53.79 ms |
| 5526494+dirty | 380.79 ms | 432.70 ms | 51.91 ms |
| 3bd3f0d+dirty | 334.38 ms | 402.19 ms | 67.81 ms |
| 1853710+dirty | 360.67 ms | 396.28 ms | 35.61 ms |
| 8d89cc9+dirty | 357.69 ms | 415.79 ms | 58.10 ms |
| 000da7a+dirty | 347.89 ms | 381.98 ms | 34.09 ms |
| 6fee48d+dirty | 370.23 ms | 427.86 ms | 57.63 ms |
| c4e097a+dirty | 382.43 ms | 443.77 ms | 61.34 ms |
| c9e95bd+dirty | 339.32 ms | 401.24 ms | 61.92 ms |
| 6479fd5+dirty | 393.06 ms | 434.04 ms | 40.98 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 49ef936+dirty | 7.15 MiB | 8.42 MiB | 1.26 MiB |
| 5526494+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 3bd3f0d+dirty | 7.15 MiB | 8.43 MiB | 1.28 MiB |
| 1853710+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 8d89cc9+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 000da7a+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 6fee48d+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| c4e097a+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| c9e95bd+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
| 6479fd5+dirty | 7.15 MiB | 8.41 MiB | 1.26 MiB |
Contributor
iOS (new) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d751a5d+dirty | 1212.22 ms | 1217.94 ms | 5.71 ms |
| 9f211e3+dirty | 1215.38 ms | 1218.15 ms | 2.77 ms |
| 785ffb1+dirty | 1213.71 ms | 1213.37 ms | -0.35 ms |
| 95aaf8a+dirty | 1206.83 ms | 1213.65 ms | 6.81 ms |
| c94a927+dirty | 1211.33 ms | 1223.31 ms | 11.97 ms |
| 98f632c+dirty | 1221.38 ms | 1229.26 ms | 7.88 ms |
| 459a438+dirty | 1218.39 ms | 1226.14 ms | 7.75 ms |
| 46bd012+dirty | 1231.78 ms | 1212.30 ms | -19.47 ms |
| 93137d1+dirty | 1232.69 ms | 1245.18 ms | 12.49 ms |
| 3e0a5f9+dirty | 1233.65 ms | 1239.10 ms | 5.45 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d751a5d+dirty | 3.19 MiB | 4.54 MiB | 1.36 MiB |
| 9f211e3+dirty | 3.19 MiB | 4.48 MiB | 1.29 MiB |
| 785ffb1+dirty | 3.19 MiB | 4.38 MiB | 1.19 MiB |
| 95aaf8a+dirty | 3.19 MiB | 4.44 MiB | 1.25 MiB |
| c94a927+dirty | 3.19 MiB | 4.56 MiB | 1.37 MiB |
| 98f632c+dirty | 3.19 MiB | 4.38 MiB | 1.19 MiB |
| 459a438+dirty | 3.19 MiB | 4.55 MiB | 1.36 MiB |
| 46bd012+dirty | 3.19 MiB | 4.38 MiB | 1.19 MiB |
| 93137d1+dirty | 3.19 MiB | 4.55 MiB | 1.37 MiB |
| 3e0a5f9+dirty | 3.19 MiB | 4.38 MiB | 1.19 MiB |
Contributor
iOS (legacy) Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d751a5d+dirty | 1215.57 ms | 1220.56 ms | 4.99 ms |
| 9f211e3+dirty | 1218.80 ms | 1233.88 ms | 15.08 ms |
| 785ffb1+dirty | 1237.63 ms | 1240.50 ms | 2.87 ms |
| 95aaf8a+dirty | 1234.78 ms | 1241.94 ms | 7.16 ms |
| c94a927+dirty | 1227.14 ms | 1239.64 ms | 12.50 ms |
| 98f632c+dirty | 1236.40 ms | 1241.62 ms | 5.22 ms |
| 459a438+dirty | 1222.12 ms | 1214.60 ms | -7.53 ms |
| 46bd012+dirty | 1220.49 ms | 1226.89 ms | 6.40 ms |
| 93137d1+dirty | 1230.73 ms | 1230.98 ms | 0.25 ms |
| 3e0a5f9+dirty | 1226.94 ms | 1230.02 ms | 3.08 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| d751a5d+dirty | 2.63 MiB | 3.98 MiB | 1.34 MiB |
| 9f211e3+dirty | 2.63 MiB | 3.91 MiB | 1.28 MiB |
| 785ffb1+dirty | 2.63 MiB | 3.81 MiB | 1.18 MiB |
| 95aaf8a+dirty | 2.63 MiB | 3.87 MiB | 1.24 MiB |
| c94a927+dirty | 2.63 MiB | 3.99 MiB | 1.36 MiB |
| 98f632c+dirty | 2.63 MiB | 3.81 MiB | 1.18 MiB |
| 459a438+dirty | 2.63 MiB | 3.98 MiB | 1.35 MiB |
| 46bd012+dirty | 2.63 MiB | 3.81 MiB | 1.18 MiB |
| 93137d1+dirty | 2.63 MiB | 3.99 MiB | 1.35 MiB |
| 3e0a5f9+dirty | 2.63 MiB | 3.81 MiB | 1.18 MiB |
alwx
approved these changes
Oct 9, 2025
Contributor
alwx
left a comment
There was a problem hiding this comment.
Have just checked it — it works and solves the issue. Thanks a lot!
packages/core/sentry.gradle
Outdated
| plugins.withId('com.android.application') { | ||
| def ac = extensions.getByName("androidComponents") | ||
|
|
||
| ac.onVariants(androidComponents.selector().all()) { v -> |
Collaborator
There was a problem hiding this comment.
Q: v is the release?
Member
Author
There was a problem hiding this comment.
v is variant, but since we have one down there already, I just named it v
Collaborator
|
Thank you for the PR! is it ready for review? |
Member
Author
|
@lucas-zimerman not yet, I will mark it as ready soonish, still sorting out some stuff |
14b894b to
6decb5a
Compare
Member
Author
|
@lucas-zimerman this is ready for review now. |
10 tasks
antonis
added a commit
that referenced
this pull request
Feb 24, 2026
…ifacts API conflict tasks.findAll iterates the entire task container, realizing every lazily-registered task as a side effect. This broke two distinct scenarios: - react-native-legal (issue #5236): AboutLibraries registers tasks lazily via tasks.register(); eager realization during onVariants caused a build crash. - Fullstory / AGP Artifacts API (issue #5698): AGP Artifacts API transforms (e.g. variant.artifacts.use().wiredWithDirectories().toTransformMany()) must be registered before the artifact chain is finalized. Realizing AGP's internal tasks inside onVariants locks the APK artifact prematurely, causing the APK to land in build/intermediates/ instead of build/outputs/. Fix: predict the two known RN bundle task names from the variant name (createBundle${Variant}JsAndAssets / bundle${Variant}JsAndAssets), check existence with tasks.names.contains() (no realization), then wire lazily via tasks.named(). A warn() is emitted when neither task is found so the skip is observable. Additional changes: - Add || currentVariants.isEmpty() guard to prevent orphan upload-task registration - Remove redundant bundleTask.configure { finalizedBy } nesting (already inside configure) Fixes #5698 Related: #5236, #5253 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
antonis
added a commit
that referenced
this pull request
Feb 26, 2026
…ifacts API conflict (#5714) * fix(android): replace tasks.findAll with tasks.named() to fix AGP Artifacts API conflict tasks.findAll iterates the entire task container, realizing every lazily-registered task as a side effect. This broke two distinct scenarios: - react-native-legal (issue #5236): AboutLibraries registers tasks lazily via tasks.register(); eager realization during onVariants caused a build crash. - Fullstory / AGP Artifacts API (issue #5698): AGP Artifacts API transforms (e.g. variant.artifacts.use().wiredWithDirectories().toTransformMany()) must be registered before the artifact chain is finalized. Realizing AGP's internal tasks inside onVariants locks the APK artifact prematurely, causing the APK to land in build/intermediates/ instead of build/outputs/. Fix: predict the two known RN bundle task names from the variant name (createBundle${Variant}JsAndAssets / bundle${Variant}JsAndAssets), check existence with tasks.names.contains() (no realization), then wire lazily via tasks.named(). A warn() is emitted when neither task is found so the skip is observable. Additional changes: - Add || currentVariants.isEmpty() guard to prevent orphan upload-task registration - Remove redundant bundleTask.configure { finalizedBy } nesting (already inside configure) Fixes #5698 Related: #5236, #5253 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Adds changelog * fix(android): fix variant capitalization and task timing in sentry.gradle - Replace v.name.capitalize() with substring(0,1).toUpperCase()+substring(1) so that flavored variants like freeRelease produce FreeRelease (not Freerelease), matching React Native's bundle task naming convention. - Replace tasks.named() with tasks.configureEach + name-set filter to handle bundle tasks registered after sentry's onVariants callback fires (e.g. when sentry.gradle is applied before the React Native plugin). configureEach does not iterate or realize the task container so the Fullstory AGP Artifacts API fix (#5698) and react-native-legal fix (#5236) are preserved. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): restore missing bundle task warning via taskGraph.whenReady Re-adds the diagnostic warn() that was lost when switching from tasks.named() to tasks.configureEach. The check is deferred to gradle.taskGraph.whenReady so all plugins' onVariants callbacks (including the RN plugin's) have completed and tasks.names reflects the full set of registered tasks before we decide to emit the warning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): move bundle task lookup to afterEvaluate, restore warning Follow the SAGP pattern (sentry-android-gradle-plugin/util/tasks.kt): register project.afterEvaluate{} inside onVariants{} so that task lookup is deferred until after all plugins have registered their tasks. onVariants fires during project evaluation — before the task container is complete — so tasks.configureEach registered there could miss late-registered bundle tasks. afterEvaluate runs after all onVariants callbacks (including the React Native plugin's) have completed, making tasks.names reliable. Replaces tasks.configureEach + gradle.taskGraph.whenReady with: - project.afterEvaluate for timing - tasks.names.contains() guard with inline warn() for missing tasks - tasks.named() for a targeted lazy reference (no container iteration) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): resolve AGP variant data inside onVariants, not afterEvaluate AGP Variant objects (outputs, applicationId, versionCode/versionName providers) are only valid inside the onVariants callback. Using them inside project.afterEvaluate{} can trigger late variant API access errors. Pre-extract all AGP-dependent data as plain values before registering the afterEvaluate block: - variantName (String) from v.name - variantApplicationId (String) from v.applicationId.get() - variantOutputsData (List<Map>) from v.outputs with all providers resolved Update extractCurrentVariants() to accept these plain values instead of the AGP Variant object so no AGP API is called outside onVariants. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): remove afterEvaluate wrapper, fix indentation in sentry.gradle project.afterEvaluate{} is not needed: bundle tasks are already registered by the time onVariants fires, matching the timing of the original tasks.findAll. Moving the tasks.names.contains() check and tasks.named().configure{} directly into onVariants keeps the fix simple and avoids the regression risk that afterEvaluate introduced in the earlier PR #5690. Also fixes the indentation of the ~240-line configure{} closure body so it is visually distinct from the enclosing onVariants block. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): pre-register sentry tasks in onVariants to fix Gradle 8.x Gradle 8.x forbids tasks.register() inside a task configuration action (the closure passed to tasks.named().configure {}). The previous change wrapped the entire sentry task setup in tasks.named(bundleTaskName).configure {}, which triggered the restriction when the RN bundle task was being created: DefaultTaskContainer#register(String, Action) on task set cannot be executed in the current context. Fix: pre-register all sentry task stubs (cliTask, modulesTask, cleanup tasks) directly in onVariants where task registration is always allowed. The tasks.named().configure {} block now only calls .configure {} on already-registered tasks and wires finalizedBy/dependsOn — both of which are allowed inside configuration actions. extractCurrentVariants() is now called in onVariants using the bundle task name as a proxy (the helper only reads bundleTask.name), so currentVariants is available before tasks.named().configure {} is reached. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): use ctx map to avoid TaskProvider.configure inside configure action Gradle 8.14.3 also forbids TaskProvider.configure(Action) inside a task configuration action, not just tasks.register(). The previous fix moved tasks.register() out but left tasks.named(other).configure {} calls inside bundleTask.configure {}, which triggered: DefaultTaskContainer#NamedDomainObjectProvider.configure(Action) on task set cannot be executed in the current context. Fix: introduce a shared mutable context map (ctx) that task action closures (doFirst/doLast/onlyIf/delete) capture by reference. The tasks are fully registered and wired in onVariants — including their complete doFirst/doLast logic referencing ctx. bundleTask.configure {} now does exactly two things: populate ctx from the bundle task's properties, and call bundleTask.finalizedBy. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(android): add Gradle test-repro for task-realization regressions Adds a self-contained Android project under packages/core/test-repro/ that verifies two canary regressions: - CANARY 1 (#5236, react-native-legal): sentry.gradle must not realize lazily-registered tasks by iterating the task container (tasks.findAll). - CANARY 2 (#5698, Fullstory): sentry.gradle must not configure the fullstoryTransformRelease task before AGP's onVariants wires it via toTransformMany(), otherwise the APK lands in build/intermediates/ instead of build/outputs/. Includes stubs for multiple approaches under test: - sentry-main.gradle → tasks.findAll in onVariants (❌ both canaries fail) - sentry-noop.gradle → baseline no-op (✅ both canaries pass) - sentry-named.gradle → tasks.names.contains + tasks.named (✅ our fix) - sentry-configureEach.gradle → tasks.configureEach alternative (✅) - sentry-afterEvaluate.gradle → afterEvaluate + tasks.named (✅) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Revert "test(android): add Gradle test-repro for task-realization regressions" This reverts commit 513a738. * fix(android): replace tasks.findAll with tasks.named to fix AGP Artifacts API conflict tasks.findAll iterates the entire task container, realizing every lazily- registered task regardless of whether it matches the predicate. This caused two distinct issues: - react-native-legal (#5236): AboutLibraries tasks were realized as a side-effect of container iteration. - Fullstory / AGP Artifacts API (#5698): fullstoryTransformRelease was configured before AGP's toTransformMany() wired its artifact paths, causing the APK to land in build/intermediates/ instead of build/outputs/. Fix: predict the bundle task name from the variant name and use tasks.names.contains() (no realization) to check existence, then tasks.named().get() to obtain only that specific task. The rest of the task registration logic is unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(android): simplify variant capitalization using Groovy capitalize() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📢 Type of change
📜 Description
afterEvaluate(which is too late to register tasks) intoandroidComponents.onVariants. Since our tasks only run when there's an android app being build, it's a good place to hook our logic into. It also gives us direct access to the variant, so we don't have to search for it and build areleasesmap anymore. The tasks are configured per-variant, omitting thedebugvariant.registerinstead ofcreateI tested it locally both
debugandreleasebuilds and thereact-native-legaljson file is correctly generated in both cases. Also, our tasks run correctly for the release build, you can see here I uploaded sourcemaps: https://sentry-sdks.sentry.io/settings/projects/sentry-react-native/source-maps/23317267-0d24-5f07-9852-28630b8cf98a/The only thing that probably needs more testing is modules collection, because it generated an empty json locally for me, but perhaps I was doing something unexpected, because I was running gradle builds directly as opposed calling
yarn/npx. If someone could verify it works, that'd be great.💡 Motivation and Context
Closes #5236
💚 How did you test it?
manually
📝 Checklist
sendDefaultPIIis enabled🔮 Next steps