fix(build): remove Spring Boot 2 Gradle plugin for Gradle 9 compatibility#5263
fix(build): remove Spring Boot 2 Gradle plugin for Gradle 9 compatibility#5263romtsn merged 36 commits intodeps/scripts/update-gradle.shfrom
Conversation
Semver Impact of This PR🟢 Patch (bug fixes) 📋 Changelog PreviewThis is how your changes will appear in the changelog. This PR will not appear in the changelog. 🤖 This preview updates automatically when you update the PR. |
📲 Install BuildsAndroid
|
…lity The Spring Boot 2.7.x Gradle plugin uses removed Gradle APIs (LenientConfiguration.getFiles()) that are incompatible with Gradle 9. Library modules (sentry-spring, sentry-spring-boot, sentry-spring-boot-starter): - Replace SpringBootPlugin.BOM_COORDINATES with direct BOM reference via version catalog (libs.springboot2.bom) - Remove the 'apply false' plugin declaration entirely Sample apps (spring-boot, webflux, otel, netflix-dgs): - Replace Spring Boot plugin with Shadow plugin for fat JAR creation - Add application plugin for main class configuration - Use platform(libs.springboot2.bom) for dependency version management - Configure shadow JAR to merge Spring metadata files - Replace BootRun task with JavaExec in otel sample
…erge Shadow plugin 9.x defaults to DuplicatesStrategy.EXCLUDE, which drops duplicate META-INF/spring.factories entries before transformers can merge them. Setting INCLUDE allows the AppendingTransformer to see all entries and properly concatenate spring.factories from all JARs. Without this, the shadow JAR only contains spring.factories from a single dependency, causing Spring Boot auto-configuration to fail (e.g. missing RestTemplateBuilder, no embedded web server).
52ced43 to
9dd4f2f
Compare
- Auto-detect shadowJar vs bootJar build task based on build.gradle.kts - Add fallback HTTP readiness check for shadow JAR apps that lack actuator endpoints (actuator web endpoints don't work in flat JARs) - Append spring-autoconfigure-metadata.properties in shadow JAR config
Shadow 9.x enforces duplicatesStrategy before transformers run, so DuplicatesStrategy.FAIL prevents mergeServiceFiles from merging inst/META-INF/services/ files that exist in both the upstream OTel agent JAR and the isolated distro libs. Switching to INCLUDE lets the transformer see all duplicates and merge them correctly.
Shadow 9.x's ServiceFileTransformer strips the `inst/` prefix when using
`include("inst/META-INF/services/*")`, placing merged service files under
`META-INF/services/` instead of `inst/META-INF/services/`. This breaks
the OTel agent's classloader which expects isolated services under `inst/`.
Using `path = "inst/META-INF/services"` preserves the correct output path.
Also add missing `duplicatesStrategy = DuplicatesStrategy.INCLUDE` to
console-otlp, log4j2, and console-opentelemetry-noagent shadow JARs
so that mergeServiceFiles and Log4j2 transformers can see duplicates
before they are deduplicated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion Shadow 9.x only applies package relocations to service files that are claimed by a ServiceFileTransformer. The ContextStorageProvider service file at META-INF/services/ was not being relocated because it wasn't handled by any transformer — only the inst/META-INF/services/ files were. Adding a default mergeServiceFiles() call ensures bootstrap service files (like ContextStorageProvider) go through the transformer and get properly relocated to their shaded paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lity Shadow 9.x enforces DuplicatesStrategy before transformers run, which breaks the `append` transformer for spring.factories and other Spring metadata files. Only the last copy survives instead of being concatenated. Replace `append` calls with a pre-merge task that manually concatenates Spring metadata files (spring.factories, spring.handlers, spring.schemas, spring-autoconfigure-metadata.properties) from the runtime classpath before the shadow JAR is built. The merged files are included first in the shadow JAR so they take precedence over duplicates from dependency JARs. This fixes the PersonSystemTest failure where @SentrySpan AOP and JDBC instrumentation weren't working because SentryAutoConfiguration wasn't properly registered in the merged spring.factories. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Lint 8.13.1 (set via android.experimental.lint.version) expects targetSdk 37 but we target 36. This is a test-only module so suppressing is safe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Performance metrics 🚀
|
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 7a32266 | 305.50 ms | 364.17 ms | 58.67 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 7a32266 | 0 B | 0 B | 0 B |
Previous results on branch: fix/spring-boot2-gradle9
Startup times
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 73551a0 | 319.50 ms | 363.84 ms | 44.34 ms |
| cba6cf7 | 347.26 ms | 404.00 ms | 56.74 ms |
| 3fe59c8 | 314.31 ms | 316.73 ms | 2.41 ms |
| 99a62d0 | 338.20 ms | 392.86 ms | 54.66 ms |
| 3caac95 | 272.74 ms | 354.80 ms | 82.06 ms |
| 6801bad | 311.75 ms | 359.57 ms | 47.82 ms |
| 67e6872 | 315.85 ms | 355.78 ms | 39.92 ms |
| 46ccd28 | 387.92 ms | 463.33 ms | 75.41 ms |
| 7b6e574 | 312.53 ms | 366.39 ms | 53.86 ms |
| 20e3a13 | 354.49 ms | 425.15 ms | 70.66 ms |
App size
| Revision | Plain | With Sentry | Diff |
|---|---|---|---|
| 73551a0 | 0 B | 0 B | 0 B |
| cba6cf7 | 0 B | 0 B | 0 B |
| 3fe59c8 | 0 B | 0 B | 0 B |
| 99a62d0 | 0 B | 0 B | 0 B |
| 3caac95 | 0 B | 0 B | 0 B |
| 6801bad | 0 B | 0 B | 0 B |
| 67e6872 | 0 B | 0 B | 0 B |
| 46ccd28 | 0 B | 0 B | 0 B |
| 7b6e574 | 0 B | 0 B | 0 B |
| 20e3a13 | 0 B | 0 B | 0 B |
…tible Resolve the runtime classpath at configuration time (not inside doLast) so the task doesn't capture Gradle script object references that can't be serialized by the configuration cache. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
f02577c to
8ae3c9c
Compare
… metadata The from() approach with DuplicatesStrategy.INCLUDE doesn't work because dependency JARs' spring.factories overwrites the pre-merged version. Instead, let the shadow JAR build normally, then use a doLast action to replace the Spring metadata files in the built JAR with the properly merged versions using the NIO ZIP filesystem API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
28fff8c to
05c5bac
Compare
The shadow JAR was missing the embedded web server auto-configuration because AutoConfiguration.imports (used by SB 2.7+) had duplicate entries from multiple dependency JARs, with only the last copy surviving. Add AutoConfiguration.imports to the pre-merge file list and use doLast JAR patching via NIO ZIP filesystem to replace metadata files after the shadow JAR is built, avoiding the DuplicatesStrategy issue entirely. Also suppress OldTargetApi lint for uitest-android-benchmark module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This file has duplicate entries across actuator JARs and needs the same pre-merge treatment as AutoConfiguration.imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5a111af to
4a277ae
Compare
…hing The doLast on shadowJar doesn't run when the task is cached/up-to-date. Move JAR patching to a separate `patchSpringMetadata` task that is finalized by shadowJar, ensuring it always runs. Also use recursive walkTopDown to handle nested directories (e.g. META-INF/spring/). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…patching
The separate patchSpringMetadata task approach caused regressions — the
finalizedBy relationship didn't reliably execute the patching in CI.
Revert to doLast directly on shadowJar with outputs.upToDateWhen { false }
to ensure the patching always runs. Also use walkTopDown for recursive
directory traversal (needed for META-INF/spring/ subdirectory).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b7ed928 to
702dfd0
Compare
Replace the separate mergeSpringMetadata task with inline doLast on shadowJar that resolves runtimeClasspath at execution time (not configuration time). This ensures all project dependency JARs are built before their spring.factories entries are read and merged. Verified locally: 20/21 system tests pass. Only PersonSystemTest 'create person works' fails due to @SentrySpan AOP limitation in shadow JARs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b8ec4e1 to
0e33152
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Shadow 9.x upgrade breaks
mergeServiceFilesin three samples- Added
duplicatesStrategy = DuplicatesStrategy.INCLUDEto the console, JUL, and logback sampleshadowJartasks somergeServiceFiles()receives duplicate service entries under Shadow 9.x.
- Added
- ✅ Fixed: Redundant
mergeServiceFiles()is no-op in Spring Boot samples- Removed no-op
mergeServiceFiles()calls from the spring-boot, spring-boot-webflux, and netflix-dgs sampleshadowJartasks to match their actual metadata/service merging viaMergeSpringMetadataAction.
- Removed no-op
Or push these changes by commenting:
@cursor push 29cf9ae479
Preview (29cf9ae479)
diff --git a/sentry-samples/sentry-samples-console/build.gradle.kts b/sentry-samples/sentry-samples-console/build.gradle.kts
--- a/sentry-samples/sentry-samples-console/build.gradle.kts
+++ b/sentry-samples/sentry-samples-console/build.gradle.kts
@@ -51,6 +51,7 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.console.Main" }
archiveClassifier.set("") // Remove the classifier so it replaces the regular JAR
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
mergeServiceFiles()
}
diff --git a/sentry-samples/sentry-samples-jul/build.gradle.kts b/sentry-samples/sentry-samples-jul/build.gradle.kts
--- a/sentry-samples/sentry-samples-jul/build.gradle.kts
+++ b/sentry-samples/sentry-samples-jul/build.gradle.kts
@@ -44,6 +44,7 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.jul.Main" }
archiveClassifier.set("") // Remove the classifier so it replaces the regular JAR
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
mergeServiceFiles()
}
diff --git a/sentry-samples/sentry-samples-logback/build.gradle.kts b/sentry-samples/sentry-samples-logback/build.gradle.kts
--- a/sentry-samples/sentry-samples-logback/build.gradle.kts
+++ b/sentry-samples/sentry-samples-logback/build.gradle.kts
@@ -44,6 +44,7 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.logback.Main" }
archiveClassifier.set("") // Remove the classifier so it replaces the regular JAR
+ duplicatesStrategy = DuplicatesStrategy.INCLUDE
mergeServiceFiles()
}
diff --git a/sentry-samples/sentry-samples-netflix-dgs/build.gradle.kts b/sentry-samples/sentry-samples-netflix-dgs/build.gradle.kts
--- a/sentry-samples/sentry-samples-netflix-dgs/build.gradle.kts
+++ b/sentry-samples/sentry-samples-netflix-dgs/build.gradle.kts
@@ -42,7 +42,6 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.netflix.dgs.NetlixDgsApplication" }
archiveClassifier.set("")
- mergeServiceFiles()
val springMetadataFiles =
listOf(
diff --git a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts
--- a/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts
+++ b/sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts
@@ -52,7 +52,6 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.spring.boot.SentryDemoApplication" }
archiveClassifier.set("")
- mergeServiceFiles()
val springMetadataFiles =
listOf(
diff --git a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
--- a/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
+++ b/sentry-samples/sentry-samples-spring-boot/build.gradle.kts
@@ -79,7 +79,6 @@
tasks.shadowJar {
manifest { attributes["Main-Class"] = "io.sentry.samples.spring.boot.SentryDemoApplication" }
archiveClassifier.set("")
- mergeServiceFiles()
// Shadow 9.x enforces DuplicatesStrategy before transformers run, so `append`
// only sees one copy of each file. We merge Spring metadata from the runtimeThis Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.
Drive the ANR profiling state-machine test synchronously instead of starting the background polling thread. The previous version could read the queue-backed profile store while the polling thread was still appending stack traces, which made the release unit test flaky with NoSuchElementException in QueueFile iteration. Co-Authored-By: Codex <noreply@openai.com>
Move the Spring metadata entry list into MergeSpringMetadataAction so the Spring sample shadowJar tasks use one source of truth. Drop the temporary system-test-runner unit test and keep verification on the existing Spring Boot system test flow. Co-Authored-By: Codex <noreply@openai.com>
Explain that MergeSpringMetadataAction patches shadow JARs by merging Spring metadata with file-specific semantics and by preserving service-provider registrations from the runtime classpath. This keeps the intent of the build logic clear without changing behavior. Co-Authored-By: Codex <noreply@openai.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 327123a. Configure here.
| group = "verification" | ||
| description = "Runs the System tests" | ||
|
|
||
| val test = project.extensions.getByType<SourceSetContainer>()["test"] |
There was a problem hiding this comment.
Missing DuplicatesStrategy.INCLUDE breaks mergeServiceFiles in Shadow 9.x
Medium Severity
The Shadow plugin was upgraded from 8.3.6 to 9.4.1 in libs.versions.toml. Shadow 9.x enforces DuplicatesStrategy before transformers run, and the default is EXCLUDE. These three samples call mergeServiceFiles() without setting DuplicatesStrategy.INCLUDE, meaning duplicate service files are silently discarded before the transformer can merge them. The sibling samples (console-opentelemetry-noagent, console-otlp, log4j2) correctly add DuplicatesStrategy.INCLUDE alongside mergeServiceFiles(), but these three were missed.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 327123a. Configure here.
There was a problem hiding this comment.
I built the current jars for sentry-samples/sentry-samples-console/build.gradle.kts:47, sentry-samples/sentry-samples-jul/build.gradle.kts:40, and sentry-samples/sentry-samples-logback/build.gradle.kts:40 in a
temp worktree, then added duplicatesStrategy = DuplicatesStrategy.INCLUDE to those three shadowJar blocks and rebuilt.
Results:
- jul: jar was byte-identical before/after.
- logback: jar was byte-identical before/after.
- console: jar changed, but only because INCLUDE preserved duplicate non-service resources like LICENSE, META-INF/LICENSE, META-INF/NOTICE, and about.html.
For the actual service files, the outputs were unchanged. In console, all three service files were identical before/after:
- META-INF/services/io.sentry.profiling.JavaContinuousProfilerProvider
- META-INF/services/io.sentry.profiling.JavaProfileConverterProvider
- META-INF/services/javax.cache.spi.CachingProvider
So the general Shadow 9 point is true in principle, but it doesn’t produce a functional difference in these three sample jars. I would leave this comment unresolved as non-actionable.
Set the final agent shadowJar to fail on unexpected duplicate entries while still allowing service descriptors to merge in the bootstrap and inst paths. This keeps duplicate handling strict without breaking the service file transformers Shadow still relies on. Co-Authored-By: Claude <noreply@anthropic.com>
* chore: update scripts/update-gradle.sh to v9.4.1 * Fix build * fix dependsOn * align shadow plugin version * Fix apollo version * Format code * fix(build): remove Spring Boot 2 Gradle plugin for Gradle 9 compatibility (#5263) * fix(build): remove Spring Boot 2 Gradle plugin for Gradle 9 compatibility The Spring Boot 2.7.x Gradle plugin uses removed Gradle APIs (LenientConfiguration.getFiles()) that are incompatible with Gradle 9. Library modules (sentry-spring, sentry-spring-boot, sentry-spring-boot-starter): - Replace SpringBootPlugin.BOM_COORDINATES with direct BOM reference via version catalog (libs.springboot2.bom) - Remove the 'apply false' plugin declaration entirely Sample apps (spring-boot, webflux, otel, netflix-dgs): - Replace Spring Boot plugin with Shadow plugin for fat JAR creation - Add application plugin for main class configuration - Use platform(libs.springboot2.bom) for dependency version management - Configure shadow JAR to merge Spring metadata files - Replace BootRun task with JavaExec in otel sample * fix: set duplicatesStrategy=INCLUDE for shadow JAR spring.factories merge Shadow plugin 9.x defaults to DuplicatesStrategy.EXCLUDE, which drops duplicate META-INF/spring.factories entries before transformers can merge them. Setting INCLUDE allows the AppendingTransformer to see all entries and properly concatenate spring.factories from all JARs. Without this, the shadow JAR only contains spring.factories from a single dependency, causing Spring Boot auto-configuration to fail (e.g. missing RestTemplateBuilder, no embedded web server). * fix: remove duplicate shadow plugin entry in version catalog * Format code * fix: update system test runner for shadow JAR compatibility - Auto-detect shadowJar vs bootJar build task based on build.gradle.kts - Add fallback HTTP readiness check for shadow JAR apps that lack actuator endpoints (actuator web endpoints don't work in flat JARs) - Append spring-autoconfigure-metadata.properties in shadow JAR config * fix(otel): use DuplicatesStrategy.INCLUDE for otel agent shadow JAR Shadow 9.x enforces duplicatesStrategy before transformers run, so DuplicatesStrategy.FAIL prevents mergeServiceFiles from merging inst/META-INF/services/ files that exist in both the upstream OTel agent JAR and the isolated distro libs. Switching to INCLUDE lets the transformer see all duplicates and merge them correctly. * Exclude test-support modules from api validation * Verbose system test output and wire inputs for them properly * align coroutines version to 1.9.0 for system tests * fix(otel): use mergeServiceFiles path instead of include for Shadow 9.x Shadow 9.x's ServiceFileTransformer strips the `inst/` prefix when using `include("inst/META-INF/services/*")`, placing merged service files under `META-INF/services/` instead of `inst/META-INF/services/`. This breaks the OTel agent's classloader which expects isolated services under `inst/`. Using `path = "inst/META-INF/services"` preserves the correct output path. Also add missing `duplicatesStrategy = DuplicatesStrategy.INCLUDE` to console-otlp, log4j2, and console-opentelemetry-noagent shadow JARs so that mergeServiceFiles and Log4j2 transformers can see duplicates before they are deduplicated. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(otel): add default mergeServiceFiles for bootstrap service relocation Shadow 9.x only applies package relocations to service files that are claimed by a ServiceFileTransformer. The ContextStorageProvider service file at META-INF/services/ was not being relocated because it wasn't handled by any transformer — only the inst/META-INF/services/ files were. Adding a default mergeServiceFiles() call ensures bootstrap service files (like ContextStorageProvider) go through the transformer and get properly relocated to their shaded paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(spring-boot2): pre-merge Spring metadata for Shadow 9.x compatibility Shadow 9.x enforces DuplicatesStrategy before transformers run, which breaks the `append` transformer for spring.factories and other Spring metadata files. Only the last copy survives instead of being concatenated. Replace `append` calls with a pre-merge task that manually concatenates Spring metadata files (spring.factories, spring.handlers, spring.schemas, spring-autoconfigure-metadata.properties) from the runtime classpath before the shadow JAR is built. The merged files are included first in the shadow JAR so they take precedence over duplicates from dependency JARs. This fixes the PersonSystemTest failure where @SentrySpan AOP and JDBC instrumentation weren't working because SentryAutoConfiguration wasn't properly registered in the merged spring.factories. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Format code * fix(lint): suppress OldTargetApi for uitest-android module Lint 8.13.1 (set via android.experimental.lint.version) expects targetSdk 37 but we target 36. This is a test-only module so suppressing is safe. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(spring-boot2): make mergeSpringMetadata configuration-cache compatible Resolve the runtime classpath at configuration time (not inside doLast) so the task doesn't capture Gradle script object references that can't be serialized by the configuration cache. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(spring-boot2): replace from() with doLast JAR patching for spring metadata The from() approach with DuplicatesStrategy.INCLUDE doesn't work because dependency JARs' spring.factories overwrites the pre-merged version. Instead, let the shadow JAR build normally, then use a doLast action to replace the Spring metadata files in the built JAR with the properly merged versions using the NIO ZIP filesystem API. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(spring-boot2): merge AutoConfiguration.imports + doLast JAR patching The shadow JAR was missing the embedded web server auto-configuration because AutoConfiguration.imports (used by SB 2.7+) had duplicate entries from multiple dependency JARs, with only the last copy surviving. Add AutoConfiguration.imports to the pre-merge file list and use doLast JAR patching via NIO ZIP filesystem to replace metadata files after the shadow JAR is built, avoiding the DuplicatesStrategy issue entirely. Also suppress OldTargetApi lint for uitest-android-benchmark module. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(spring-boot2): also merge ManagementContextConfiguration.imports This file has duplicate entries across actuator JARs and needs the same pre-merge treatment as AutoConfiguration.imports. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(spring-boot2): use separate patchSpringMetadata task for JAR patching The doLast on shadowJar doesn't run when the task is cached/up-to-date. Move JAR patching to a separate `patchSpringMetadata` task that is finalized by shadowJar, ensuring it always runs. Also use recursive walkTopDown to handle nested directories (e.g. META-INF/spring/). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(spring-boot2): revert to doLast on shadowJar for Spring metadata patching The separate patchSpringMetadata task approach caused regressions — the finalizedBy relationship didn't reliably execute the patching in CI. Revert to doLast directly on shadowJar with outputs.upToDateWhen { false } to ensure the patching always runs. Also use walkTopDown for recursive directory traversal (needed for META-INF/spring/ subdirectory). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(spring-boot2): inline Spring metadata merge into shadowJar doLast Replace the separate mergeSpringMetadata task with inline doLast on shadowJar that resolves runtimeClasspath at execution time (not configuration time). This ensures all project dependency JARs are built before their spring.factories entries are read and merged. Verified locally: 20/21 system tests pass. Only PersonSystemTest 'create person works' fails due to @SentrySpan AOP limitation in shadow JARs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * formatting * fix(build): make Spring sample shadowJar patching config-cache safe * fix(build): merge Spring metadata properties in shadow jars * fix(build): preserve escaped spring metadata keys * refactor(samples): drop no-op spring shadow service merging * test(android): Avoid ANR profiling integration test race Drive the ANR profiling state-machine test synchronously instead of starting the background polling thread. The previous version could read the queue-backed profile store while the polling thread was still appending stack traces, which made the release unit test flaky with NoSuchElementException in QueueFile iteration. Co-Authored-By: Codex <noreply@openai.com> * fix(test): Require actuator health for Spring readiness * ref(build): Share Spring metadata file list Move the Spring metadata entry list into MergeSpringMetadataAction so the Spring sample shadowJar tasks use one source of truth. Drop the temporary system-test-runner unit test and keep verification on the existing Spring Boot system test flow. Co-Authored-By: Codex <noreply@openai.com> * docs(build): Document Spring metadata merge action Explain that MergeSpringMetadataAction patches shadow JARs by merging Spring metadata with file-specific semantics and by preserving service-provider registrations from the runtime classpath. This keeps the intent of the build logic clear without changing behavior. Co-Authored-By: Codex <noreply@openai.com> * build(opentelemetry): Fail agent shadow duplicates by default Set the final agent shadowJar to fail on unexpected duplicate entries while still allowing service descriptors to merge in the bootstrap and inst paths. This keeps duplicate handling strict without breaking the service file transformers Shadow still relies on. Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io> Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Codex <noreply@openai.com> * Apply suggestion from @romtsn --------- Co-authored-by: GitHub <noreply@github.com> Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com> Co-authored-by: Sentry Github Bot <bot+github-bot@sentry.io> Co-authored-by: Alexander Dinauer <adinauer@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Codex <noreply@openai.com>



📜 Description
Shadow 9.x DuplicatesStrategy fix
Shadow 9.x enforces
DuplicatesStrategybefore transformers (likemergeServiceFilesandAppendingTransformer) can process duplicates. This meansFAILorEXCLUDEprevents service files and Spring metadata from being merged. All shadow JAR tasks now useDuplicatesStrategy.INCLUDEto let transformers see all duplicate entries.Spring Boot 2 Gradle plugin removal
Remove the Spring Boot 2 Gradle plugin (
org.springframework.bootv2.7.18) which is incompatible with Gradle 9 due to use of removed APILenientConfiguration.getFiles().Library modules (
sentry-spring,sentry-spring-boot,sentry-spring-boot-starter):apply falseto accessSpringBootPlugin.BOM_COORDINATESlibs.springboot2.bom)Sample apps (
spring-boot,webflux,otel,netflix-dgs):io.spring.dependency-managementwith Shadow plugin (com.gradleup.shadow) for fat JAR creationapplicationplugin for main class configurationplatform(libs.springboot2.bom)for dependency version managementDuplicatesStrategy.INCLUDE+append()to merge Spring metadata files (spring.factories,spring.handlers,spring.schemas,spring-autoconfigure-metadata.properties)BootRuntask in otel sample replaced with standardJavaExecOTel agent shadow JAR fix
sentry-opentelemetry-agentshadowJarswitched fromDuplicatesStrategy.FAILtoINCLUDEsomergeServiceFilescan mergeinst/META-INF/services/from the upstream OTel agent and the distro libsSystem test runner
shadowJarvsbootJarbuild task per moduleNote: Spring Boot 3 and 4 plugins are unaffected — they are already Gradle 9 compatible.
💡 Motivation and Context
Stacked on top of #5063. The Spring Boot 2.7.x Gradle plugin uses
LenientConfiguration.getFiles()which was removed in Gradle 9, causingbootJarandbuildtasks to fail. Shadow 9.x changed duplicate handling behavior requiringDuplicatesStrategy.INCLUDEfor transformer-based merging to work.💚 How did you test it?
./gradlew :sentry-spring:compileJava :sentry-spring-boot:compileJava :sentry-spring-boot-starter:compileJava— passes./gradlew :sentry-spring:test— passesspring.factories(170+ lines)./gradlew :sentry-opentelemetry:sentry-opentelemetry-agent:shadowJar— passes, service files properly merged📝 Checklist
sendDefaultPIIis enabled.🔮 Next steps
Other Gradle 9 compatibility fixes in the parent PR.
#skip-changelog