Skip to content

fix(android): Generate modules.json into build folder instead of source tree - #6753

Merged
antonis merged 14 commits into
mainfrom
al/fix-gradle-modules-generated-assets
Sep 23, 2026
Merged

antonis merged 14 commits into
mainfrom
al/fix-gradle-modules-generated-assets

Conversation

@antonis

@antonis antonis commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Note

Stacked on #6751 (base branch al/fix-gradle-sentry-options-generated-assets). Please merge #6751 first; this PR's diff will collapse to modules-only once it does.

📢 Type of change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring

📜 Description

This replaces the Exec + Delete cleanup tasks with a typed CollectModulesTask that:

  • Writes into build/generated/sentry/modules/<variant>/ — never into src/main/assets.
  • Declares the source map, the collect-modules script, the module search paths, and the opt-out as task inputs, and the output dir as an output, so it participates correctly in up-to-date checks and the build cache.
  • Is registered as a generated assets source via the AGP Variant API (variant.sources.assets.addGeneratedSourceDirectory, reached reflectively since a script plugin can't depend on AGP types), so AGP wires it into merge<Variant>Assets with correct ordering. A classic sourceSets["main"].assets.srcDir + scoped merge<Variant>Assets dependsOn fallback covers older AGP.
  • Runs the node collector through injected ExecOperations, so it stays Configuration Cache compatible.

💡 Motivation and Context

Follow-up to #6751. The Android Gradle plugin generated modules.json into the version-controlled android/app/src/main/assets directory on release builds (via an Exec task), then removed it afterward with a cleanup task. That Exec task declared no Gradle inputs/outputs, so it broke up-to-date checks and build caching for the asset-merge tasks, and a failed build could leave the file behind in the source tree — the same anti-pattern reported in #6750, of which sentry.options.json (#6751) was the first instance.

💚 How did you test it?

Manually, on samples/react-native (AGP 9.x / Gradle 9.4.1, JDK 17), since there is no Gradle test harness for the script plugin:

📝 Checklist

🔮 Next steps

antonis and others added 6 commits September 18, 2026 09:36
…of source tree

The Android Gradle plugin copied `sentry.options.json` into the version-controlled
`src/main/assets` during builds via a task with no declared inputs/outputs. That broke
Gradle's up-to-date checks and build caching for the asset-merge tasks and required a
cleanup task to remove the file afterward, which could leave the file behind on a failed
build.

Replace it with a typed `generateSentryOptions` task that writes into
`build/generated/sentry/options` with declared inputs/outputs (source file plus the
SENTRY_ENVIRONMENT/RELEASE/DIST overrides), registered as a generated assets source via
the AGP Variant API, with a classic sourceSets fallback for older AGP. Nothing is written
into the source tree anymore, and asset merging is now correctly cached.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…out output

The lint model/analysis tasks read the generated assets dir without a declared
dependency on generateSentryOptions, which Gradle 9 fails as an implicit
dependency error. Declare it explicitly.

Also make the SENTRY_COPY_OPTIONS_FILE opt-out an @input instead of onlyIf, so
disabling it re-runs the task and clears the output dir rather than leaving a
stale generated file to be packaged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Older plugin versions copied the file into src/main/assets; a crashed build
could leave it behind, where it now shadows or conflicts with the generated
copy. Emit a configuration-time warning pointing the user to remove it. Never
delete it automatically — the file may be intentional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ce tree

The Android Gradle plugin collected `modules.json` into the version-controlled
`src/main/assets` directory on release builds via an `Exec` task with no declared
inputs/outputs, then removed it afterward with a cleanup task. That broke
up-to-date checks and build caching for asset merging, and a failed build could
leave the file behind in the source tree.

Replace it with a typed `CollectModulesTask` that writes into
`build/generated/sentry/modules/<variant>`, registered as a generated assets
source via the AGP Variant API (with a classic source-set fallback), so AGP
merges and orders it into `merge<Variant>Assets` with correct up-to-date/caching
behavior. Lint tasks depend on it explicitly (Gradle 9 rejects the previously
implicit dependency), and the source-map cleanup now runs after it.

Mirrors the `sentry.options.json` fix (#6751); closes the same anti-pattern for
`modules.json` from #6750.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Semver Impact of This PR

None (no version bump detected)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


  • fix(android): Generate modules.json into build folder instead of source tree by antonis in #6753
  • fix(android): Generate sentry.options.json into build folder instead of source tree by antonis in #6751
  • feat(core): Expose native network breadcrumb options by antonis in #6764
  • chore(deps): update JavaScript SDK to v10.75.2 by github-actions in #6767
  • ci: Unify derivedData path resolution with realpath by antonis in #6761
  • fix(e2e): Fix TestFlight upload after enabling Mac Catalyst on the sample by antonis in #6762
  • chore(deps): update JavaScript SDK to v10.75.1 by github-actions in #6763
  • fix(ios): Fix Mac Catalyst linking the wrong Sentry.xcframework slice by antonis in #6758
  • chore(deps): bump the codeql-action group across 1 directory with 3 updates by dependabot in #6756
  • chore(deps): bump ruby/setup-ruby from 1.321.0 to 1.324.0 by dependabot in #6757
  • fix(core): Declare optional peer dependencies to resolve phantom imports under strict package managers by antonis in #6729
  • Add missing exports and update public API report by antonis in #6731
  • test(ios): Reenable Session Replay E2E test by antonis in #6737
  • fix(ios): Honor shutdownTimeout on iOS by antonis in #6749

🤖 This preview updates automatically when you update the PR.

Comment thread packages/core/sentry.gradle.kts
@sentry

sentry Bot commented Sep 18, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
Sentry RN io.sentry.reactnative.sample 8.27.0 (107) Release

⚙️ sentry-react-native Build Distribution Settings

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/sentry.gradle.kts
Comment thread packages/core/sentry.gradle.kts Outdated
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Android (legacy) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 424.56 ms 448.54 ms 23.98 ms
Size 50.56 MiB 56.50 MiB 5.93 MiB

Baseline results on branch: al/fix-gradle-sentry-options-generated-assets

Startup times

Revision Plain With Sentry Diff
6dc72d4+dirty 468.91 ms 547.58 ms 78.67 ms
a164657+dirty 422.72 ms 448.76 ms 26.03 ms

App size

Revision Plain With Sentry Diff
6dc72d4+dirty 50.56 MiB 56.49 MiB 5.93 MiB
a164657+dirty 50.56 MiB 56.49 MiB 5.93 MiB

Previous results on branch: al/fix-gradle-modules-generated-assets

Startup times

Revision Plain With Sentry Diff
7a2d816+dirty 470.94 ms 515.62 ms 44.68 ms
38ab0b8+dirty 426.03 ms 446.65 ms 20.62 ms
3bacb46+dirty 423.54 ms 428.93 ms 5.39 ms
1e3886c+dirty 421.49 ms 464.16 ms 42.67 ms

App size

Revision Plain With Sentry Diff
7a2d816+dirty 50.56 MiB 56.50 MiB 5.93 MiB
38ab0b8+dirty 50.56 MiB 56.50 MiB 5.93 MiB
3bacb46+dirty 50.56 MiB 56.50 MiB 5.93 MiB
1e3886c+dirty 50.56 MiB 56.50 MiB 5.93 MiB

…arn on stale copy

Addresses review feedback on the modules.json generated-assets change:

- Source-set fallback (old AGP < 7.3) now registers each variant's generated
  modules dir into that variant's own source set instead of the shared "main".
  Adding per-variant dirs to "main" leaked modules.json into debug and caused a
  duplicate-asset merge conflict between multiple non-debug variants. Because each
  variant is now isolated, no dedup guard is required (unlike the shared-dir
  options fallback). The primary AGP 7.3+ variant-API path was already scoped and
  is unchanged.
- Warn on a stale modules.json left in src/main/assets by older plugin versions
  (mirrors the sentry.options.json warning), since a leftover copy would clash
  with the generated one during asset merge on upgrade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

Android (new) Performance metrics 🚀

  Plain With Sentry Diff
Startup time 429.00 ms 457.34 ms 28.34 ms
Size 50.56 MiB 56.50 MiB 5.93 MiB

Baseline results on branch: al/fix-gradle-sentry-options-generated-assets

Startup times

Revision Plain With Sentry Diff
6dc72d4+dirty 471.48 ms 552.45 ms 80.97 ms
a164657+dirty 428.37 ms 440.90 ms 12.52 ms

App size

Revision Plain With Sentry Diff
6dc72d4+dirty 50.56 MiB 56.49 MiB 5.93 MiB
a164657+dirty 50.56 MiB 56.49 MiB 5.93 MiB

Previous results on branch: al/fix-gradle-modules-generated-assets

Startup times

Revision Plain With Sentry Diff
7a2d816+dirty 433.18 ms 458.43 ms 25.25 ms
38ab0b8+dirty 417.88 ms 431.80 ms 13.92 ms
3bacb46+dirty 428.24 ms 459.82 ms 31.58 ms
1e3886c+dirty 434.40 ms 470.29 ms 35.89 ms

App size

Revision Plain With Sentry Diff
7a2d816+dirty 50.56 MiB 56.50 MiB 5.93 MiB
38ab0b8+dirty 50.56 MiB 56.50 MiB 5.93 MiB
3bacb46+dirty 50.56 MiB 56.50 MiB 5.93 MiB
1e3886c+dirty 50.56 MiB 56.50 MiB 5.93 MiB

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/sentry.gradle.kts Outdated
Comment thread packages/core/sentry.gradle.kts
…fingerprint the collect-modules script

Addresses two review findings on the modules.json generation change:

- The lint-task dependency used a loose `it.name.contains(variantCapitalized)`
  substring match, so a longer build type whose name ends in a shorter one
  (e.g. `qaRelease` vs `release`) would have its lint tasks pull in the wrong
  variant's modules task. Match the variant as a full task-name segment via the
  known AGP lint verbs / `generate<Variant>Lint…` prefix instead, mirroring the
  exact match already used for the `merge<Variant>Assets` fallback wiring.
- `collectModulesScript` was an `@Input` on the path string only, which does not
  fingerprint the script's content — editing it in place (e.g. an SDK upgrade at
  the same path) would not re-run the task. Make it `@Internal` and add
  `collectModulesScriptFiles` (`@InputFiles`, RELATIVE path sensitivity) to
  fingerprint content, tolerating a missing script as an empty input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antonis added a commit that referenced this pull request Sep 21, 2026
Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/sentry.gradle.kts
antonis added a commit that referenced this pull request Sep 21, 2026
Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread packages/core/sentry.gradle.kts
…stic

Match a variant's whole AGP lint family by the `Lint` camelCase word boundary
(name starts with the lowercase `lint` verb, or embeds a capital-L `Lint`
segment) scoped to that variant, instead of an allowlist of lint verbs plus a
generator-prefix regex.

This covers `updateLintBaseline*`, `*UnitTest` lint models, and any future AGP
lint task without relying on those tasks transitively depending on an
allowlisted `lintAnalyze*` task. The word boundary — rather than a
case-insensitive `lint` substring — excludes unrelated `ktlint*` tasks (e.g.
`ktlintReleaseCheck`), which must not be made to depend on the modules task and
pull in the JS bundler. Cross-variant matches (e.g. `release` inside
`qaRelease`/`releaseStaging`) are excluded precisely using the set of processed
variant names, which is complete by the time lint tasks are realized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@antonis
antonis force-pushed the al/fix-gradle-modules-generated-assets branch from 9d22855 to a2fcc04 Compare September 21, 2026 14:03
antonis added a commit that referenced this pull request Sep 21, 2026
Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a2fcc04. Configure here.

Comment thread packages/core/sentry.gradle.kts
Comment thread packages/core/sentry.gradle.kts
… races

The CollectModulesTask was decoupled from the source-map upload chain so it
runs via mergeAssets, which introduced two races with the upload flow:

- The upload rewrites the source map in place (copy-debugid) in its doFirst,
  while the modules task reads that same file. With nothing ordering them, a
  parallel / configuration-cache build could read a half-written map and fail
  JSON parsing. Order the upload strictly after the modules task.

- The "clean up extra sourcemap" task deletes the source map after uploading.
  Fingerprinting that (deleted) map as the task input invalidated the modules
  task on every rebuild and could package an empty modules.json when an
  up-to-date bundle task didn't regenerate the map. Fingerprint the stable
  bundle instead (it changes iff the JS/module list changes) and read the map
  at execution as an @internal input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antonis added a commit that referenced this pull request Sep 21, 2026
Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread packages/core/sentry.gradle.kts Outdated
`resolveSentryReactNativeSDKPath` spawns a blocking `node --print
require.resolve(...)` subprocess. It was hoisted into the plain body of
`processVariant`, which the AGP `onVariants` callback runs at configuration
time for every non-debug variant on every Gradle invocation (including debug
builds and `./gradlew tasks`). Its result also feeds only the fallback branch,
so it was wasted whenever `collectModulesScript` is configured.

Move the resolution into the CollectModulesTask config block and into the `?:`
fallback only, so the node subprocess runs lazily (when the task is realized)
and conditionally (when no explicit script path is set) — restoring the
pre-refactor behavior where it lived inside the Exec task's config block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antonis added a commit that referenced this pull request Sep 22, 2026
Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@lucas-zimerman lucas-zimerman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM!

Base automatically changed from al/fix-gradle-sentry-options-generated-assets to main September 23, 2026 07:02
…-generated-assets

# Conflicts:
#	CHANGELOG.md
#	packages/core/sentry.gradle.kts
@antonis
antonis merged commit cee91a7 into main Sep 23, 2026
71 of 80 checks passed
@antonis
antonis deleted the al/fix-gradle-modules-generated-assets branch September 23, 2026 07:27
antonis added a commit that referenced this pull request Sep 23, 2026
…sks (#6754)

* fix(android): Generate sentry.options.json into build folder instead of source tree

The Android Gradle plugin copied `sentry.options.json` into the version-controlled
`src/main/assets` during builds via a task with no declared inputs/outputs. That broke
Gradle's up-to-date checks and build caching for the asset-merge tasks and required a
cleanup task to remove the file afterward, which could leave the file behind on a failed
build.

Replace it with a typed `generateSentryOptions` task that writes into
`build/generated/sentry/options` with declared inputs/outputs (source file plus the
SENTRY_ENVIRONMENT/RELEASE/DIST overrides), registered as a generated assets source via
the AGP Variant API, with a classic sourceSets fallback for older AGP. Nothing is written
into the source tree anymore, and asset merging is now correctly cached.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(changelog): Reference PR #6751

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Wire lint tasks to generateSentryOptions and clear opt-out output

The lint model/analysis tasks read the generated assets dir without a declared
dependency on generateSentryOptions, which Gradle 9 fails as an implicit
dependency error. Declare it explicitly.

Also make the SENTRY_COPY_OPTIONS_FILE opt-out an @input instead of onlyIf, so
disabling it re-runs the task and clears the output dir rather than leaving a
stale generated file to be packaged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Warn on stale sentry.options.json left in src/main/assets

Older plugin versions copied the file into src/main/assets; a crashed build
could leave it behind, where it now shadows or conflicts with the generated
copy. Emit a configuration-time warning pointing the user to remove it. Never
delete it automatically — the file may be intentional.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Generate modules.json into build folder instead of source tree

The Android Gradle plugin collected `modules.json` into the version-controlled
`src/main/assets` directory on release builds via an `Exec` task with no declared
inputs/outputs, then removed it afterward with a cleanup task. That broke
up-to-date checks and build caching for asset merging, and a failed build could
leave the file behind in the source tree.

Replace it with a typed `CollectModulesTask` that writes into
`build/generated/sentry/modules/<variant>`, registered as a generated assets
source via the AGP Variant API (with a classic source-set fallback), so AGP
merges and orders it into `merge<Variant>Assets` with correct up-to-date/caching
behavior. Lint tasks depend on it explicitly (Gradle 9 rejects the previously
implicit dependency), and the source-map cleanup now runs after it.

Mirrors the `sentry.options.json` fix (#6751); closes the same anti-pattern for
`modules.json` from #6750.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(changelog): Reference PR #6753

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Scope modules.json fallback to variant source set and warn on stale copy

Addresses review feedback on the modules.json generated-assets change:

- Source-set fallback (old AGP < 7.3) now registers each variant's generated
  modules dir into that variant's own source set instead of the shared "main".
  Adding per-variant dirs to "main" leaked modules.json into debug and caused a
  duplicate-asset merge conflict between multiple non-debug variants. Because each
  variant is now isolated, no dedup guard is required (unlike the shared-dir
  options fallback). The primary AGP 7.3+ variant-API path was already scoped and
  is unchanged.
- Warn on a stale modules.json left in src/main/assets by older plugin versions
  (mirrors the sentry.options.json warning), since a leftover copy would clash
  with the generated one during asset merge on upgrade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Scope modules lint dependency to the exact variant and fingerprint the collect-modules script

Addresses two review findings on the modules.json generation change:

- The lint-task dependency used a loose `it.name.contains(variantCapitalized)`
  substring match, so a longer build type whose name ends in a shorter one
  (e.g. `qaRelease` vs `release`) would have its lint tasks pull in the wrong
  variant's modules task. Match the variant as a full task-name segment via the
  known AGP lint verbs / `generate<Variant>Lint…` prefix instead, mirroring the
  exact match already used for the `merge<Variant>Assets` fallback wiring.
- `collectModulesScript` was an `@Input` on the path string only, which does not
  fingerprint the script's content — editing it in place (e.g. an SDK upgrade at
  the same path) would not re-run the task. Make it `@Internal` and add
  `collectModulesScriptFiles` (`@InputFiles`, RELATIVE path sensitivity) to
  fingerprint content, tolerating a missing script as an empty input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(android): Make the modules lint-dependency matcher verb-agnostic

Match a variant's whole AGP lint family by the `Lint` camelCase word boundary
(name starts with the lowercase `lint` verb, or embeds a capital-L `Lint`
segment) scoped to that variant, instead of an allowlist of lint verbs plus a
generator-prefix regex.

This covers `updateLintBaseline*`, `*UnitTest` lint models, and any future AGP
lint task without relying on those tasks transitively depending on an
allowlisted `lintAnalyze*` task. The word boundary — rather than a
case-insensitive `lint` substring — excludes unrelated `ktlint*` tasks (e.g.
`ktlintReleaseCheck`), which must not be made to depend on the modules task and
pull in the JS bundler. Cross-variant matches (e.g. `release` inside
`qaRelease`/`releaseStaging`) are excluded precisely using the set of processed
variant names, which is complete by the time lint tasks are realized.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(android): Order and re-key modules collection to avoid source-map races

The CollectModulesTask was decoupled from the source-map upload chain so it
runs via mergeAssets, which introduced two races with the upload flow:

- The upload rewrites the source map in place (copy-debugid) in its doFirst,
  while the modules task reads that same file. With nothing ordering them, a
  parallel / configuration-cache build could read a half-written map and fail
  JSON parsing. Order the upload strictly after the modules task.

- The "clean up extra sourcemap" task deletes the source map after uploading.
  Fingerprinting that (deleted) map as the task input invalidated the modules
  task on every rebuild and could package an empty modules.json when an
  up-to-date bundle task didn't regenerate the map. Fingerprint the stable
  bundle instead (it changes iff the JS/module list changes) and read the map
  at execution as an @internal input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* perf(android): Resolve collect-modules script path lazily

`resolveSentryReactNativeSDKPath` spawns a blocking `node --print
require.resolve(...)` subprocess. It was hoisted into the plain body of
`processVariant`, which the AGP `onVariants` callback runs at configuration
time for every non-debug variant on every Gradle invocation (including debug
builds and `./gradlew tasks`). Its result also feeds only the fallback branch,
so it was wasted whenever `collectModulesScript` is configured.

Move the resolution into the CollectModulesTask config block and into the `?:`
fallback only, so the node subprocess runs lazily (when the task is realized)
and conditionally (when no explicit script path is set) — restoring the
pre-refactor behavior where it lived inside the Exec task's config block.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Add GradleTestKit tests for sentry.gradle.kts asset tasks

Cover the generated-asset tasks introduced for issue #6750 (PRs #6751/#6753)
with functional tests that apply the real sentry.gradle.kts script plugin to a
minimal AGP fixture and drive the tasks through GradleTestKit:

- SentryOptionsTaskTest: plain copy, SENTRY_RELEASE override, opt-out via
  SENTRY_COPY_OPTIONS_FILE=false, and missing-source fallback.
- SentryModulesTaskTest: modules.json generated into build/ (never src/main/
  assets), UP-TO-DATE on rebuild, skipCollectModules opt-out, and missing
  source map fallback.

Runs on the existing RNSentryAndroidTester testDebugUnitTest job, so no new CI
infrastructure is required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Add regression test for variant-scoped modules lint dependency

Two-build-type fixture (`release` + `qaRelease`) proving `lintQaRelease` does
not depend on the `release` variant's modules task. Verified red-capable: it
fails against the old `it.name.contains(variantCapitalized)` substring match and
passes with the token-boundary scoping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Scrub host SENTRY_* env and tighten lint-scope regression

Pass a SENTRY_*-scrubbed environment to GradleRunner in both asset-task test
classes so the assertions depend only on the fixture and each test's explicit
env, not on SENTRY_RELEASE/ENVIRONMENT/DIST/COPY_OPTIONS_FILE a developer may
have exported locally.

Also extend the variant-scoping regression to assert the non-`lint*`-prefixed
`updateLintBaselineQaRelease` is likewise not wired to the release modules task,
and document that `lintRelease` is a non-vacuity control (in this fixture lint
depends on the modules task only through the explicit wiring).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Assert ktlint tasks are not wired to the modules task

Guards the lint-family matcher against over-binding: a `ktlint*` task (from the
ktlint Gradle plugin) carries a lowercase `lint` mid-name and the variant name
but is not an AGP lint task, so it must not depend on CollectModulesTask and
pull in the JS bundler. Fails against a case-insensitive `lint` substring match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Cover modules.json survival across source-map cleanup

Add a GradleTestKit regression test asserting the modules task stays
UP-TO-DATE — and its modules.json is preserved, not emptied — when the
source map it reads is deleted (as the upload cleanup does) and the bundle is
unchanged. The bundle stub now also writes the --bundle-output file (the
task's stable fingerprint) and can declare it as an output so the bundle task
goes up-to-date on rerun, reproducing the real bundle task that does not track
the forced source map as an output.

Red against a source-map-fingerprinted task (re-runs, finds no map, packages
an empty assets dir); green once the fingerprint is the bundle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Cover invalid-JSON fallback in generateSentryOptions

GenerateSentryOptionsTask.generate() parses the source as JSON before applying
SENTRY_ENVIRONMENT/RELEASE/DIST overrides, and on a parse failure logs a warning
and copies the source verbatim rather than failing the build. That catch branch
was untested (Warden flagged the coverage gap on #6751).

Add a case with an override set and a malformed source, asserting the task
succeeds and the output matches the source byte-for-byte — verbatim equality
proves the fallback ran, since a successful parse would re-serialize the JSON.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(android): Track host AGP/compileSdk and share fixture scaffolding

Address review feedback on the GradleTestKit fixtures:

- Stop hardcoding AGP 8.3.2 and compileSdk 34 in the fixtures. The host build
  now injects its own AGP version (com.android.Version) and compileSdk as system
  properties, and the fixtures read them, so bumping the host no longer leaves
  the fixtures silently testing a stale toolchain. This also fixes the fixture
  pinning compileSdk 34 while the host is on 33.
- Extract the duplicated scaffolding (settings.gradle, local.properties, stub
  manifest, path escaping, SENTRY_* env scrubbing, system-property reads) from
  both test classes into a shared BaseSentryGradleTest base class.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Triggers the full CI test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants