Skip to content

fix(gradle): Fix compatibility with react-native-legal#5253

Merged
antonis merged 7 commits intomainfrom
rz/fix/react-native-legal-compat
Oct 13, 2025
Merged

fix(gradle): Fix compatibility with react-native-legal#5253
antonis merged 7 commits intomainfrom
rz/fix/react-native-legal-compat

Conversation

@romtsn
Copy link
Member

@romtsn romtsn commented Oct 9, 2025

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

  • This moves the entire script logic from afterEvaluate (which is too late to register tasks) into androidComponents.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 a releases map anymore. The tasks are configured per-variant, omitting the debug variant.
  • Register our tasks lazily to avoid early resolution via register instead of create

I tested it locally both debug and release builds and the react-native-legal json 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

  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPII is enabled
  • I updated the docs if needed.
  • I updated the wizard if needed.
  • All tests passing
  • No breaking changes

🔮 Next steps

@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 441.30 ms 438.43 ms -2.87 ms
Size 17.75 MiB 19.70 MiB 1.95 MiB

Baseline results on branch: main

Startup times

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

Previous results on branch: rz/fix/react-native-legal-compat

Startup times

Revision Plain With Sentry Diff
c5a3d21+dirty 462.63 ms 475.18 ms 12.55 ms

App size

Revision Plain With Sentry Diff
c5a3d21+dirty 17.75 MiB 19.70 MiB 1.95 MiB

@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 341.60 ms 391.98 ms 50.38 ms
Size 7.15 MiB 8.43 MiB 1.28 MiB

Baseline results on branch: main

Startup times

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

@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

iOS (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1238.26 ms 1237.27 ms -0.99 ms
Size 3.19 MiB 4.56 MiB 1.37 MiB

Baseline results on branch: main

Startup times

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

Previous results on branch: rz/fix/react-native-legal-compat

Startup times

Revision Plain With Sentry Diff
c5a3d21+dirty 1219.57 ms 1233.40 ms 13.83 ms

App size

Revision Plain With Sentry Diff
c5a3d21+dirty 3.19 MiB 4.56 MiB 1.37 MiB

@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

iOS (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 1229.21 ms 1228.21 ms -1.00 ms
Size 2.63 MiB 3.99 MiB 1.36 MiB

Baseline results on branch: main

Startup times

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

Previous results on branch: rz/fix/react-native-legal-compat

Startup times

Revision Plain With Sentry Diff
c5a3d21+dirty 1222.29 ms 1215.84 ms -6.45 ms

App size

Revision Plain With Sentry Diff
c5a3d21+dirty 2.63 MiB 3.99 MiB 1.36 MiB

Copy link
Contributor

@alwx alwx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have just checked it — it works and solves the issue. Thanks a lot!

plugins.withId('com.android.application') {
def ac = extensions.getByName("androidComponents")

ac.onVariants(androidComponents.selector().all()) { v ->
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: v is the release?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

v is variant, but since we have one down there already, I just named it v

@lucas-zimerman
Copy link
Collaborator

Thank you for the PR! is it ready for review?

@romtsn
Copy link
Member Author

romtsn commented Oct 9, 2025

@lucas-zimerman not yet, I will mark it as ready soonish, still sorting out some stuff

@romtsn romtsn force-pushed the rz/fix/react-native-legal-compat branch from 14b894b to 6decb5a Compare October 10, 2025 11:30
@romtsn romtsn marked this pull request as ready for review October 10, 2025 11:32
@romtsn romtsn requested a review from antonis as a code owner October 10, 2025 11:32
@romtsn
Copy link
Member Author

romtsn commented Oct 10, 2025

@lucas-zimerman this is ready for review now.

Copy link
Contributor

@antonis antonis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@antonis antonis enabled auto-merge (squash) October 13, 2025 13:16
@antonis antonis merged commit 161947d into main Oct 13, 2025
64 of 65 checks passed
@antonis antonis deleted the rz/fix/react-native-legal-compat branch October 13, 2025 13:19
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Conflict with react-native-legal on Android

4 participants