From 033bff72288f65c912f96385198ab4d864c31270 Mon Sep 17 00:00:00 2001 From: Nelson Osacky Date: Mon, 26 Oct 2026 16:47:18 +0100 Subject: [PATCH] test(spring): Run each Spring Boot matrix version as its own Gradle task The Spring Boot matrix workflows tested each version by rewriting the catalog with perl, so a version could only be tested locally by editing the catalog, and every job recompiled the SDK and the suites. The new io.sentry.spring-boot-matrix plugin registers a test task per suite and version, e.g. integrationTestWebfluxBoot3_2_12. It reuses the suite's compiled classes on a runtime classpath resolved for that version, which swaps every org.springframework.boot artifact, the BOM included, as the catalog edit did. The resolved classpaths match the catalog edit's exactly. So the SDK and the suites now compile once, against the catalog's version, and only the runtime varies, which is how users run our jars. Suite code may only use APIs of the oldest version of its line. allIntegrationTestsBoot runs one version's suites in a module, and allIntegrationTestsBoot every version's. Each workflow runs the latter from the root in one job per Boot line, with --continue so a failing version doesn't hide the others. The versions live only in SpringBootVersions in build-logic. Resolves JAVA-802 Co-Authored-By: Claude --- .cursor/rules/e2e_tests.mdc | 13 ++- .github/workflows/spring-boot-2-matrix.yml | 27 +---- .github/workflows/spring-boot-3-matrix.yml | 28 +---- .github/workflows/spring-boot-4-matrix.yml | 29 +---- build-logic/build.gradle.kts | 4 + .../sentry/gradle/SpringBootMatrixPlugin.kt | 102 ++++++++++++++++++ .../io/sentry/gradle/SpringBootVersions.kt | 11 ++ sentry-spring-7/build.gradle.kts | 8 ++ sentry-spring-boot-4/build.gradle.kts | 28 ++++- sentry-spring-boot-jakarta/build.gradle.kts | 27 ++++- sentry-spring-boot/build.gradle.kts | 26 ++++- sentry-spring-jakarta/build.gradle.kts | 8 ++ sentry-spring/build.gradle.kts | 8 ++ 13 files changed, 238 insertions(+), 81 deletions(-) create mode 100644 build-logic/src/main/kotlin/io/sentry/gradle/SpringBootMatrixPlugin.kt create mode 100644 build-logic/src/main/kotlin/io/sentry/gradle/SpringBootVersions.kt diff --git a/.cursor/rules/e2e_tests.mdc b/.cursor/rules/e2e_tests.mdc index 1785e61b5c..fd52adb14d 100644 --- a/.cursor/rules/e2e_tests.mdc +++ b/.cursor/rules/e2e_tests.mdc @@ -64,9 +64,16 @@ it. Gradle runs the two in parallel. ## Running them -- The `spring-boot-{2,3,4}-matrix.yml` workflows run the suites against each supported Spring Boot - version, by setting the version in `gradle/libs.versions.toml`. The suites aren't part of - `check`, so a new suite only runs on CI once it's added to its matrix. +- The `springBootMatrix` block in each Spring module's build file runs its suites on every Spring + Boot version in `SpringBootVersions` (build-logic), with a task per suite and version, e.g. + `integrationTestWebfluxBoot3_2_12`. The suites and the SDK compile once, against the catalog's + version; only the runtime classpath changes. `allIntegrationTestsBoot3_2_12` runs one version's + suites, `allIntegrationTestsBoot3` every version's. +- The `spring-boot-{2,3,4}-matrix.yml` workflows run `./gradlew allIntegrationTestsBoot`, + which picks up every module of the line. The suites aren't part of `check`, so a new suite only + runs on CI once it's added to its module's `springBootMatrix` block. +- Suite code may only use APIs that exist in the oldest version of its line. Compiled against a + newer one, a missing API fails at runtime with `NoSuchMethodError` or `NoClassDefFoundError`. ## Gotchas diff --git a/.github/workflows/spring-boot-2-matrix.yml b/.github/workflows/spring-boot-2-matrix.yml index 3ee4553cf2..e537ca96b9 100644 --- a/.github/workflows/spring-boot-2-matrix.yml +++ b/.github/workflows/spring-boot-2-matrix.yml @@ -18,12 +18,7 @@ jobs: spring-boot-2-matrix: timeout-minutes: 45 runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - springboot-version: [ '2.7.0', '2.7.18' ] - - name: Spring Boot ${{ matrix.springboot-version }} + name: Spring Boot 2.x env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} @@ -51,27 +46,15 @@ jobs: with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - name: Update Spring Boot 2.x version - run: | - springboot_version="${{ matrix.springboot-version }}" - perl -0pi -e 'BEGIN { $v = shift } s/^springboot2[[:space:]]*=[[:space:]]*"\K[^"]*/$v/m or die "::error::springboot2 version entry not found in gradle/libs.versions.toml\n"' "$springboot_version" gradle/libs.versions.toml - echo "Updated Spring Boot 2.x version to $springboot_version" - - name: Test integration suites - run: | - ./gradlew \ - :sentry-spring:integrationTest \ - :sentry-spring-boot:integrationTest \ - :sentry-spring-boot:integrationTestWebflux \ - :sentry-spring-boot:integrationTestOtelNoAgent \ - :sentry-spring-boot:integrationTestOtelAgent \ - :sentry-spring-boot:integrationTestOtelAgentWithoutAutoInit + # Runs every version even after one fails, as separate jobs per version would. + run: ./gradlew allIntegrationTestsBoot2 --continue - name: Upload test results if: always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 with: - name: test-results-springboot-2-${{ matrix.springboot-version }} + name: test-results-springboot-2 path: | **/build/reports/* **/build/test-results/**/*.xml @@ -80,7 +63,7 @@ jobs: uses: phoenix-actions/test-reporting@7317eea6e13c47348dd0bb318669485157c518d6 # pin@v16 if: always() with: - name: JUnit Spring Boot 2.x ${{ matrix.springboot-version }} + name: JUnit Spring Boot 2.x path: | **/build/test-results/**/*.xml reporter: java-junit diff --git a/.github/workflows/spring-boot-3-matrix.yml b/.github/workflows/spring-boot-3-matrix.yml index f698683cdc..9140a0f1fc 100644 --- a/.github/workflows/spring-boot-3-matrix.yml +++ b/.github/workflows/spring-boot-3-matrix.yml @@ -18,12 +18,7 @@ jobs: spring-boot-3-matrix: timeout-minutes: 45 runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - springboot-version: [ '3.2.12', '3.3.13', '3.4.13', '3.5.13' ] - - name: Spring Boot ${{ matrix.springboot-version }} + name: Spring Boot 3.x env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} @@ -51,28 +46,15 @@ jobs: with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - name: Update Spring Boot 3.x version - run: | - springboot_version="${{ matrix.springboot-version }}" - perl -0pi -e 'BEGIN { $v = shift } s/^springboot3[[:space:]]*=[[:space:]]*"\K[^"]*/$v/m or die "::error::springboot3 version entry not found in gradle/libs.versions.toml\n"' "$springboot_version" gradle/libs.versions.toml - echo "Updated Spring Boot 3.x version to $springboot_version" - - name: Test integration suites - run: | - ./gradlew \ - :sentry-spring-jakarta:integrationTest \ - :sentry-spring-boot-jakarta:integrationTest \ - :sentry-spring-boot-jakarta:integrationTestWebflux \ - :sentry-spring-boot-jakarta:integrationTestOtelNoAgent \ - :sentry-spring-boot-jakarta:integrationTestLog4j2 \ - :sentry-spring-boot-jakarta:integrationTestOtelAgent \ - :sentry-spring-boot-jakarta:integrationTestOtelAgentWithoutAutoInit + # Runs every version even after one fails, as separate jobs per version would. + run: ./gradlew allIntegrationTestsBoot3 --continue - name: Upload test results if: always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 with: - name: test-results-springboot-3-${{ matrix.springboot-version }} + name: test-results-springboot-3 path: | **/build/reports/* **/build/test-results/**/*.xml @@ -81,7 +63,7 @@ jobs: uses: phoenix-actions/test-reporting@7317eea6e13c47348dd0bb318669485157c518d6 # pin@v16 if: always() with: - name: JUnit Spring Boot 3.x ${{ matrix.springboot-version }} + name: JUnit Spring Boot 3.x path: | **/build/test-results/**/*.xml reporter: java-junit diff --git a/.github/workflows/spring-boot-4-matrix.yml b/.github/workflows/spring-boot-4-matrix.yml index 8f91289db9..3524a66e3c 100644 --- a/.github/workflows/spring-boot-4-matrix.yml +++ b/.github/workflows/spring-boot-4-matrix.yml @@ -18,12 +18,7 @@ jobs: spring-boot-4-matrix: timeout-minutes: 45 runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - springboot-version: [ '4.0.0', '4.0.5', '4.1.0' ] - - name: Spring Boot ${{ matrix.springboot-version }} + name: Spring Boot 4.x env: GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }} @@ -51,29 +46,15 @@ jobs: with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - - name: Update Spring Boot 4.x version - run: | - springboot_version="${{ matrix.springboot-version }}" - perl -0pi -e 'BEGIN { $v = shift } s/^springboot4[[:space:]]*=[[:space:]]*"\K[^"]*/$v/m or die "::error::springboot4 version entry not found in gradle/libs.versions.toml\n"' "$springboot_version" gradle/libs.versions.toml - echo "Updated Spring Boot 4.x version to $springboot_version" - - name: Test integration suites - run: | - ./gradlew \ - :sentry-spring-7:integrationTest \ - :sentry-spring-boot-4:integrationTest \ - :sentry-spring-boot-4:integrationTestWebflux \ - :sentry-spring-boot-4:integrationTestOtelNoAgent \ - :sentry-spring-boot-4:integrationTestLog4j2 \ - :sentry-spring-boot-4:integrationTestOtelAgent \ - :sentry-spring-boot-4:integrationTestOtelAgentWithoutAutoInit \ - :sentry-spring-boot-4:integrationTestOtlp + # Runs every version even after one fails, as separate jobs per version would. + run: ./gradlew allIntegrationTestsBoot4 --continue - name: Upload test results if: always() uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 with: - name: test-results-springboot-4-${{ matrix.springboot-version }} + name: test-results-springboot-4 path: | **/build/reports/* **/build/test-results/**/*.xml @@ -82,7 +63,7 @@ jobs: uses: phoenix-actions/test-reporting@7317eea6e13c47348dd0bb318669485157c518d6 # pin@v16 if: always() with: - name: JUnit Spring Boot 4.x ${{ matrix.springboot-version }} + name: JUnit Spring Boot 4.x path: | **/build/test-results/**/*.xml reporter: java-junit diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index d02ab58488..408aef2a0a 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -23,5 +23,9 @@ gradlePlugin { id = "io.sentry.animalsniffer.android" implementationClass = "io.sentry.gradle.SentryAnimalSnifferAndroidPlugin" } + register("sentrySpringBootMatrix") { + id = "io.sentry.spring-boot-matrix" + implementationClass = "io.sentry.gradle.SpringBootMatrixPlugin" + } } } diff --git a/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootMatrixPlugin.kt b/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootMatrixPlugin.kt new file mode 100644 index 0000000000..77fad36af4 --- /dev/null +++ b/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootMatrixPlugin.kt @@ -0,0 +1,102 @@ +package io.sentry.gradle + +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.testing.Test +import org.gradle.language.base.plugins.LifecycleBasePlugin + +/** + * Runs integration suites on each Spring Boot version of a line, without recompiling anything. + * + * The suites and the SDK compile once, against the version catalog's Spring Boot version. For each + * version, a suite's test task reuses its compiled classes on a runtime classpath resolved for that + * version, which is how users run our published jars on their Spring Boot version. + */ +class SpringBootMatrixPlugin : Plugin { + override fun apply(project: Project) { + project.extensions.create("springBootMatrix", SpringBootMatrixExtension::class.java) + } +} + +abstract class SpringBootMatrixExtension @Inject constructor(private val project: Project) { + private var versions = emptyList() + + /** + * Registers `allIntegrationTestsBoot` for each of [versions], and + * `allIntegrationTestsBoot` for all of them, which CI runs from the root project for every + * module of the line at once. + */ + fun versions(versions: List) { + this.versions = versions + val major = versions.map { it.substringBefore('.') }.distinct().single() + project.tasks.register("allIntegrationTestsBoot$major") { + description = "Runs the integration suites on Spring Boot ${versions.joinToString()}." + group = LifecycleBasePlugin.VERIFICATION_GROUP + dependsOn(versions.map { aggregateName(it) }) + } + versions.forEach { version -> + project.tasks.register(aggregateName(version)) { + description = "Runs the integration suites on Spring Boot $version." + group = LifecycleBasePlugin.VERIFICATION_GROUP + } + } + } + + /** + * Registers `Boot` for each version, running [suite]'s tests. [name] tells apart + * several tasks of one suite, and [configure] adds what the suite's own task has beyond its + * classes and classpath. + */ + fun test(suite: String, name: String = suite, configure: Test.(version: String) -> Unit = {}) { + check(versions.isNotEmpty()) { "Call versions() before test()" } + versions.forEach { version -> + // Created now rather than with the task, so `dependencies --configuration` finds it. + val runtimeClasspath = runtimeClasspath(suite, version) + val task = + project.tasks.register("$name${suffix(version)}", Test::class.java) { + description = "Runs $name on Spring Boot $version." + group = LifecycleBasePlugin.VERIFICATION_GROUP + testClassesDirs = sourceSet(suite).output.classesDirs + classpath = runtimeClasspath + useJUnit() + configure(version) + } + project.tasks.named(aggregateName(version)) { dependsOn(task) } + } + } + + /** [sourceSet]'s output and runtime dependencies, with Spring Boot at [version]. */ + fun runtimeClasspath(sourceSet: String, version: String): FileCollection { + val sources = sourceSet(sourceSet) + val name = "$sourceSet${suffix(version)}RuntimeClasspath" + val configurations = project.configurations + val dependencies = + configurations.findByName(name) + ?: configurations.create(name) { + isCanBeConsumed = false + extendsFrom( + configurations.getByName(sources.implementationConfigurationName), + configurations.getByName(sources.runtimeOnlyConfigurationName), + ) + attributes.addAllLater( + configurations.getByName(sources.runtimeClasspathConfigurationName).attributes + ) + // What setting the version in the catalog does: every Spring Boot artifact, the BOM + // included, is in this group. Libraries pinned to their own version still win conflicts. + resolutionStrategy.eachDependency { + if (requested.group == "org.springframework.boot") useVersion(version) + } + } + return sources.output + dependencies + } + + private fun sourceSet(name: String) = + project.extensions.getByType(SourceSetContainer::class.java).getByName(name) + + private fun aggregateName(version: String) = "allIntegrationTests${suffix(version)}" + + private fun suffix(version: String) = "Boot" + version.replace('.', '_') +} diff --git a/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootVersions.kt b/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootVersions.kt new file mode 100644 index 0000000000..588d50ce7c --- /dev/null +++ b/build-logic/src/main/kotlin/io/sentry/gradle/SpringBootVersions.kt @@ -0,0 +1,11 @@ +package io.sentry.gradle + +/** + * The Spring Boot versions the Spring integration suites run on, per major line. A WAR suite runs + * on the same versions as its Boot module, because the Boot BOM decides its Spring version. + */ +object SpringBootVersions { + val BOOT_2 = listOf("2.7.0", "2.7.18") + val BOOT_3 = listOf("3.2.12", "3.3.13", "3.4.13", "3.5.13") + val BOOT_4 = listOf("4.0.0", "4.0.5", "4.1.0") +} diff --git a/sentry-spring-7/build.gradle.kts b/sentry-spring-7/build.gradle.kts index b9d418abe4..2eadb3efb6 100644 --- a/sentry-spring-7/build.gradle.kts +++ b/sentry-spring-7/build.gradle.kts @@ -1,3 +1,4 @@ +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.plugin.SpringBootPlugin @@ -5,6 +6,7 @@ import org.springframework.boot.gradle.plugin.SpringBootPlugin plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) @@ -142,3 +144,9 @@ testing { } } } + +// The suites on each Spring Boot 4 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_4) + test("integrationTest") +} diff --git a/sentry-spring-boot-4/build.gradle.kts b/sentry-spring-boot-4/build.gradle.kts index 7f4428551b..0077caf712 100644 --- a/sentry-spring-boot-4/build.gradle.kts +++ b/sentry-spring-boot-4/build.gradle.kts @@ -1,4 +1,5 @@ import io.sentry.gradle.ChildJvmTestArguments +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.plugin.SpringBootPlugin @@ -6,6 +7,7 @@ import org.springframework.boot.gradle.plugin.SpringBootPlugin plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.kotlin.spring) alias(libs.plugins.errorprone) @@ -387,13 +389,17 @@ testing { /** * Has the OpenTelemetry agent tests start agentFixture's app in a child JVM, with the built agent * attached. [autoInit] decides whether the agent initializes Sentry or, as with - * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. + * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. [appClasspath] is the app's classpath for + * another Spring Boot version. */ -fun Test.runAgentFixture(autoInit: Boolean) { +fun Test.runAgentFixture( + autoInit: Boolean, + appClasspath: FileCollection = agentFixture.runtimeClasspath, +) { jvmArgumentProviders.add( objects.newInstance().apply { agentJar.from(otelAgentJar) - appClasspath.from(agentFixture.runtimeClasspath) + this.appClasspath.from(appClasspath) } ) systemProperty("sentry.test.agentAutoInit", autoInit) @@ -423,3 +429,19 @@ listOf( .forEach { tasks.named(it) { compilerOptions.javaParameters = true } } + +// The suites on each Spring Boot 4 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_4) + test("integrationTest") + test("integrationTestLog4j2") + test("integrationTestWebflux") + test("integrationTestOtelNoAgent") + test("integrationTestOtlp") + test("integrationTestOtelAgent") { version -> + runAgentFixture(autoInit = true, appClasspath = runtimeClasspath("agentFixture", version)) + } + test("integrationTestOtelAgent", name = "integrationTestOtelAgentWithoutAutoInit") { version -> + runAgentFixture(autoInit = false, appClasspath = runtimeClasspath("agentFixture", version)) + } +} diff --git a/sentry-spring-boot-jakarta/build.gradle.kts b/sentry-spring-boot-jakarta/build.gradle.kts index 9451cf9511..3b97650119 100644 --- a/sentry-spring-boot-jakarta/build.gradle.kts +++ b/sentry-spring-boot-jakarta/build.gradle.kts @@ -1,4 +1,5 @@ import io.sentry.gradle.ChildJvmTestArguments +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.plugin.SpringBootPlugin @@ -6,6 +7,7 @@ import org.springframework.boot.gradle.plugin.SpringBootPlugin plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) @@ -327,13 +329,17 @@ testing { /** * Has the OpenTelemetry agent tests start agentFixture's app in a child JVM, with the built agent * attached. [autoInit] decides whether the agent initializes Sentry or, as with - * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. + * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. [appClasspath] is the app's classpath for + * another Spring Boot version. */ -fun Test.runAgentFixture(autoInit: Boolean) { +fun Test.runAgentFixture( + autoInit: Boolean, + appClasspath: FileCollection = agentFixture.runtimeClasspath, +) { jvmArgumentProviders.add( objects.newInstance().apply { agentJar.from(otelAgentJar) - appClasspath.from(agentFixture.runtimeClasspath) + this.appClasspath.from(appClasspath) } ) systemProperty("sentry.test.agentAutoInit", autoInit) @@ -362,3 +368,18 @@ listOf( .forEach { tasks.named(it) { compilerOptions.javaParameters = true } } + +// The suites on each Spring Boot 3 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_3) + test("integrationTest") + test("integrationTestLog4j2") + test("integrationTestWebflux") + test("integrationTestOtelNoAgent") + test("integrationTestOtelAgent") { version -> + runAgentFixture(autoInit = true, appClasspath = runtimeClasspath("agentFixture", version)) + } + test("integrationTestOtelAgent", name = "integrationTestOtelAgentWithoutAutoInit") { version -> + runAgentFixture(autoInit = false, appClasspath = runtimeClasspath("agentFixture", version)) + } +} diff --git a/sentry-spring-boot/build.gradle.kts b/sentry-spring-boot/build.gradle.kts index 3f24e73dc9..c20bdc71e9 100644 --- a/sentry-spring-boot/build.gradle.kts +++ b/sentry-spring-boot/build.gradle.kts @@ -1,4 +1,5 @@ import io.sentry.gradle.ChildJvmTestArguments +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -6,6 +7,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) @@ -272,13 +274,17 @@ testing { /** * Has the OpenTelemetry agent tests start agentFixture's app in a child JVM, with the built agent * attached. [autoInit] decides whether the agent initializes Sentry or, as with - * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. + * `SENTRY_AUTO_INIT=false`, the Spring Boot starter. [appClasspath] is the app's classpath for + * another Spring Boot version. */ -fun Test.runAgentFixture(autoInit: Boolean) { +fun Test.runAgentFixture( + autoInit: Boolean, + appClasspath: FileCollection = agentFixture.runtimeClasspath, +) { jvmArgumentProviders.add( objects.newInstance().apply { agentJar.from(otelAgentJar) - appClasspath.from(agentFixture.runtimeClasspath) + this.appClasspath.from(appClasspath) } ) systemProperty("sentry.test.agentAutoInit", autoInit) @@ -308,3 +314,17 @@ listOf( .forEach { tasks.named(it) { compilerOptions.javaParameters = true } } + +// The suites on each Spring Boot 2 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_2) + test("integrationTest") + test("integrationTestWebflux") + test("integrationTestOtelNoAgent") + test("integrationTestOtelAgent") { version -> + runAgentFixture(autoInit = true, appClasspath = runtimeClasspath("agentFixture", version)) + } + test("integrationTestOtelAgent", name = "integrationTestOtelAgentWithoutAutoInit") { version -> + runAgentFixture(autoInit = false, appClasspath = runtimeClasspath("agentFixture", version)) + } +} diff --git a/sentry-spring-jakarta/build.gradle.kts b/sentry-spring-jakarta/build.gradle.kts index 701d5a0420..a3478b383f 100644 --- a/sentry-spring-jakarta/build.gradle.kts +++ b/sentry-spring-jakarta/build.gradle.kts @@ -1,3 +1,4 @@ +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.springframework.boot.gradle.plugin.SpringBootPlugin @@ -5,6 +6,7 @@ import org.springframework.boot.gradle.plugin.SpringBootPlugin plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) @@ -129,3 +131,9 @@ testing { } } } + +// The suites on each Spring Boot 3 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_3) + test("integrationTest") +} diff --git a/sentry-spring/build.gradle.kts b/sentry-spring/build.gradle.kts index f11087c1b0..c9e2620d56 100644 --- a/sentry-spring/build.gradle.kts +++ b/sentry-spring/build.gradle.kts @@ -1,3 +1,4 @@ +import io.sentry.gradle.SpringBootVersions import net.ltgt.gradle.errorprone.errorprone import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -5,6 +6,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { `java-library` id("io.sentry.javadoc") + id("io.sentry.spring-boot-matrix") alias(libs.plugins.kotlin.jvm) alias(libs.plugins.errorprone) alias(libs.plugins.gradle.versions) @@ -117,3 +119,9 @@ testing { } } } + +// The suites on each Spring Boot 2 version the CI matrix tests. +springBootMatrix { + versions(SpringBootVersions.BOOT_2) + test("integrationTest") +}