Skip to content

Commit 4bef2eb

Browse files
romtsnclaude
andcommitted
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>
1 parent 05c5bac commit 4bef2eb

File tree

6 files changed

+69
-56
lines changed

6 files changed

+69
-56
lines changed

sentry-android-integration-tests/sentry-uitest-android-benchmark/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ android {
8181
lint {
8282
warningsAsErrors = true
8383
checkDependencies = true
84+
// Suppress OldTargetApi: lint 8.13.1 expects API 37 but we target 36
85+
disable += "OldTargetApi"
8486

8587
// We run a full lint analysis as build part in CI, so skip vital checks for assemble tasks.
8688
checkReleaseBuilds = false

sentry-samples/sentry-samples-netflix-dgs/build.gradle.kts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,26 @@ dependencies {
4646
// from the runtime classpath and include the merged result in the shadow JAR.
4747
val mergeSpringMetadata by
4848
tasks.registering {
49-
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
49+
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata")
5050
val classpathJars = configurations.runtimeClasspath.get().filter { it.name.endsWith(".jar") }
5151
val filesToMerge =
5252
listOf(
53-
"spring.factories",
54-
"spring.handlers",
55-
"spring.schemas",
56-
"spring-autoconfigure-metadata.properties",
53+
"META-INF/spring.factories",
54+
"META-INF/spring.handlers",
55+
"META-INF/spring.schemas",
56+
"META-INF/spring-autoconfigure-metadata.properties",
57+
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
5758
)
5859
outputs.dir(outputDir)
5960
inputs.files(classpathJars)
6061
doLast {
6162
val out = outputDir.get().asFile
62-
out.mkdirs()
63-
filesToMerge.forEach { fileName ->
63+
filesToMerge.forEach { entryPath ->
6464
val merged = StringBuilder()
6565
classpathJars.forEach { jar ->
6666
try {
6767
val zip = ZipFile(jar)
68-
val entry = zip.getEntry("META-INF/$fileName")
68+
val entry = zip.getEntry(entryPath)
6969
if (entry != null) {
7070
merged.append(zip.getInputStream(entry).bufferedReader().readText())
7171
if (!merged.endsWith("\n")) merged.append("\n")
@@ -76,7 +76,9 @@ val mergeSpringMetadata by
7676
}
7777
}
7878
if (merged.isNotEmpty()) {
79-
File(out, fileName).writeText(merged.toString())
79+
val outFile = File(out, entryPath)
80+
outFile.parentFile.mkdirs()
81+
outFile.writeText(merged.toString())
8082
}
8183
}
8284
}
@@ -88,7 +90,7 @@ tasks.shadowJar {
8890
manifest { attributes["Main-Class"] = "io.sentry.samples.netflix.dgs.NetlixDgsApplication" }
8991
archiveClassifier.set("")
9092
mergeServiceFiles()
91-
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
93+
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata")
9294
doLast {
9395
val jarFile = archiveFile.get().asFile
9496
val metaDir = metadataDir.get().asFile

sentry-samples/sentry-samples-spring-boot-opentelemetry-noagent/build.gradle.kts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,26 @@ dependencies {
8787
// from the runtime classpath and include the merged result in the shadow JAR.
8888
val mergeSpringMetadata by
8989
tasks.registering {
90-
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
90+
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata")
9191
val classpathJars = configurations.runtimeClasspath.get().filter { it.name.endsWith(".jar") }
9292
val filesToMerge =
9393
listOf(
94-
"spring.factories",
95-
"spring.handlers",
96-
"spring.schemas",
97-
"spring-autoconfigure-metadata.properties",
94+
"META-INF/spring.factories",
95+
"META-INF/spring.handlers",
96+
"META-INF/spring.schemas",
97+
"META-INF/spring-autoconfigure-metadata.properties",
98+
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
9899
)
99100
outputs.dir(outputDir)
100101
inputs.files(classpathJars)
101102
doLast {
102103
val out = outputDir.get().asFile
103-
out.mkdirs()
104-
filesToMerge.forEach { fileName ->
104+
filesToMerge.forEach { entryPath ->
105105
val merged = StringBuilder()
106106
classpathJars.forEach { jar ->
107107
try {
108108
val zip = ZipFile(jar)
109-
val entry = zip.getEntry("META-INF/$fileName")
109+
val entry = zip.getEntry(entryPath)
110110
if (entry != null) {
111111
merged.append(zip.getInputStream(entry).bufferedReader().readText())
112112
if (!merged.endsWith("\n")) merged.append("\n")
@@ -117,7 +117,9 @@ val mergeSpringMetadata by
117117
}
118118
}
119119
if (merged.isNotEmpty()) {
120-
File(out, fileName).writeText(merged.toString())
120+
val outFile = File(out, entryPath)
121+
outFile.parentFile.mkdirs()
122+
outFile.writeText(merged.toString())
121123
}
122124
}
123125
}
@@ -129,7 +131,7 @@ tasks.shadowJar {
129131
manifest { attributes["Main-Class"] = "io.sentry.samples.spring.boot.SentryDemoApplication" }
130132
archiveClassifier.set("")
131133
mergeServiceFiles()
132-
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
134+
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata")
133135
doLast {
134136
val jarFile = archiveFile.get().asFile
135137
val metaDir = metadataDir.get().asFile

sentry-samples/sentry-samples-spring-boot-opentelemetry/build.gradle.kts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,26 +83,26 @@ dependencies {
8383
// from the runtime classpath and include the merged result in the shadow JAR.
8484
val mergeSpringMetadata by
8585
tasks.registering {
86-
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
86+
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata")
8787
val classpathJars = configurations.runtimeClasspath.get().filter { it.name.endsWith(".jar") }
8888
val filesToMerge =
8989
listOf(
90-
"spring.factories",
91-
"spring.handlers",
92-
"spring.schemas",
93-
"spring-autoconfigure-metadata.properties",
90+
"META-INF/spring.factories",
91+
"META-INF/spring.handlers",
92+
"META-INF/spring.schemas",
93+
"META-INF/spring-autoconfigure-metadata.properties",
94+
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
9495
)
9596
outputs.dir(outputDir)
9697
inputs.files(classpathJars)
9798
doLast {
9899
val out = outputDir.get().asFile
99-
out.mkdirs()
100-
filesToMerge.forEach { fileName ->
100+
filesToMerge.forEach { entryPath ->
101101
val merged = StringBuilder()
102102
classpathJars.forEach { jar ->
103103
try {
104104
val zip = ZipFile(jar)
105-
val entry = zip.getEntry("META-INF/$fileName")
105+
val entry = zip.getEntry(entryPath)
106106
if (entry != null) {
107107
merged.append(zip.getInputStream(entry).bufferedReader().readText())
108108
if (!merged.endsWith("\n")) merged.append("\n")
@@ -113,7 +113,9 @@ val mergeSpringMetadata by
113113
}
114114
}
115115
if (merged.isNotEmpty()) {
116-
File(out, fileName).writeText(merged.toString())
116+
val outFile = File(out, entryPath)
117+
outFile.parentFile.mkdirs()
118+
outFile.writeText(merged.toString())
117119
}
118120
}
119121
}
@@ -125,7 +127,7 @@ tasks.shadowJar {
125127
manifest { attributes["Main-Class"] = "io.sentry.samples.spring.boot.SentryDemoApplication" }
126128
archiveClassifier.set("")
127129
mergeServiceFiles()
128-
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
130+
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata")
129131
doLast {
130132
val jarFile = archiveFile.get().asFile
131133
val metaDir = metadataDir.get().asFile

sentry-samples/sentry-samples-spring-boot-webflux/build.gradle.kts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,26 @@ dependencies {
5656
// from the runtime classpath and include the merged result in the shadow JAR.
5757
val mergeSpringMetadata by
5858
tasks.registering {
59-
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
59+
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata")
6060
val classpathJars = configurations.runtimeClasspath.get().filter { it.name.endsWith(".jar") }
6161
val filesToMerge =
6262
listOf(
63-
"spring.factories",
64-
"spring.handlers",
65-
"spring.schemas",
66-
"spring-autoconfigure-metadata.properties",
63+
"META-INF/spring.factories",
64+
"META-INF/spring.handlers",
65+
"META-INF/spring.schemas",
66+
"META-INF/spring-autoconfigure-metadata.properties",
67+
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
6768
)
6869
outputs.dir(outputDir)
6970
inputs.files(classpathJars)
7071
doLast {
7172
val out = outputDir.get().asFile
72-
out.mkdirs()
73-
filesToMerge.forEach { fileName ->
73+
filesToMerge.forEach { entryPath ->
7474
val merged = StringBuilder()
7575
classpathJars.forEach { jar ->
7676
try {
7777
val zip = ZipFile(jar)
78-
val entry = zip.getEntry("META-INF/$fileName")
78+
val entry = zip.getEntry(entryPath)
7979
if (entry != null) {
8080
merged.append(zip.getInputStream(entry).bufferedReader().readText())
8181
if (!merged.endsWith("\n")) merged.append("\n")
@@ -86,7 +86,9 @@ val mergeSpringMetadata by
8686
}
8787
}
8888
if (merged.isNotEmpty()) {
89-
File(out, fileName).writeText(merged.toString())
89+
val outFile = File(out, entryPath)
90+
outFile.parentFile.mkdirs()
91+
outFile.writeText(merged.toString())
9092
}
9193
}
9294
}
@@ -98,7 +100,7 @@ tasks.shadowJar {
98100
manifest { attributes["Main-Class"] = "io.sentry.samples.spring.boot.SentryDemoApplication" }
99101
archiveClassifier.set("")
100102
mergeServiceFiles()
101-
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
103+
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata")
102104
doLast {
103105
val jarFile = archiveFile.get().asFile
104106
val metaDir = metadataDir.get().asFile

sentry-samples/sentry-samples-spring-boot/build.gradle.kts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,28 +83,28 @@ dependencies {
8383
// from the runtime classpath and include the merged result in the shadow JAR.
8484
val mergeSpringMetadata by
8585
tasks.registering {
86-
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
86+
val outputDir = project.layout.buildDirectory.dir("merged-spring-metadata")
8787
val classpathJars = configurations.runtimeClasspath.get().filter { it.name.endsWith(".jar") }
8888
val filesToMerge =
8989
listOf(
90-
"spring.factories",
91-
"spring.handlers",
92-
"spring.schemas",
93-
"spring-autoconfigure-metadata.properties",
90+
"META-INF/spring.factories",
91+
"META-INF/spring.handlers",
92+
"META-INF/spring.schemas",
93+
"META-INF/spring-autoconfigure-metadata.properties",
94+
"META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports",
9495
)
9596

9697
outputs.dir(outputDir)
9798
inputs.files(classpathJars)
9899

99100
doLast {
100101
val out = outputDir.get().asFile
101-
out.mkdirs()
102-
filesToMerge.forEach { fileName ->
102+
filesToMerge.forEach { entryPath ->
103103
val merged = StringBuilder()
104104
classpathJars.forEach { jar ->
105105
try {
106106
val zip = ZipFile(jar)
107-
val entry = zip.getEntry("META-INF/$fileName")
107+
val entry = zip.getEntry(entryPath)
108108
if (entry != null) {
109109
merged.append(zip.getInputStream(entry).bufferedReader().readText())
110110
if (!merged.endsWith("\n")) merged.append("\n")
@@ -115,7 +115,9 @@ val mergeSpringMetadata by
115115
}
116116
}
117117
if (merged.isNotEmpty()) {
118-
File(out, fileName).writeText(merged.toString())
118+
val outFile = File(out, entryPath)
119+
outFile.parentFile.mkdirs()
120+
outFile.writeText(merged.toString())
119121
}
120122
}
121123
}
@@ -131,16 +133,17 @@ tasks.shadowJar {
131133
// After the shadow JAR is built, replace Spring metadata files with pre-merged versions.
132134
// Shadow 9.x's `append` transformer doesn't properly merge these files because
133135
// DuplicatesStrategy is enforced before transformers run.
134-
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata/META-INF")
136+
val metadataDir = project.layout.buildDirectory.dir("merged-spring-metadata")
135137
doLast {
136138
val jarFile = archiveFile.get().asFile
137-
val metaDir = metadataDir.get().asFile
138-
if (!metaDir.exists()) return@doLast
139+
val baseDir = metadataDir.get().asFile
140+
if (!baseDir.exists()) return@doLast
139141
val uri = URI.create("jar:${jarFile.toURI()}")
140-
val env = mapOf("create" to "false")
141-
FileSystems.newFileSystem(uri, env).use { fs ->
142-
metaDir.listFiles()?.forEach { merged ->
143-
val target = fs.getPath("META-INF/${merged.name}")
142+
FileSystems.newFileSystem(uri, mapOf("create" to "false")).use { fs ->
143+
baseDir.walkTopDown().filter { it.isFile }.forEach { merged ->
144+
val relative = merged.relativeTo(baseDir).path
145+
val target = fs.getPath(relative)
146+
if (target.parent != null) Files.createDirectories(target.parent)
144147
Files.copy(merged.toPath(), target, StandardCopyOption.REPLACE_EXISTING)
145148
}
146149
}

0 commit comments

Comments
 (0)