Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .cursor/rules/e2e_tests.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ it. Gradle runs the two in parallel.
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`.
- The oldest version's aggregate also compiles the module's main code against that version, e.g.
`compileJavaBoot3_2_12`, since nothing else checks that the SDK compiles against it.

## Gotchas

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ 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.compile.JavaCompile
import org.gradle.api.tasks.testing.Test
import org.gradle.language.base.plugins.LifecycleBasePlugin

Expand All @@ -28,6 +29,10 @@ abstract class SpringBootMatrixExtension @Inject constructor(private val project
* Registers `allIntegrationTestsBoot<version>` for each of [versions], and
* `allIntegrationTestsBoot<major>` for all of them, which CI runs from the root project for every
* module of the line at once.
*
* The first of [versions] must be the oldest. The module's main code compiles against it in
* `compileJavaBoot<version>`, part of that version's aggregate, because the main compilation only
* proves the code compiles against the catalog's version.
*/
fun versions(versions: List<String>) {
this.versions = versions
Expand All @@ -43,6 +48,22 @@ abstract class SpringBootMatrixExtension @Inject constructor(private val project
group = LifecycleBasePlugin.VERIFICATION_GROUP
}
}

val oldest = versions.first()
val compileClasspath = classpath("main", oldest, compile = true)
val compileOldest =
project.tasks.register("compileJava${suffix(oldest)}", JavaCompile::class.java) {
description = "Compiles the main Java sources against Spring Boot $oldest."
group = LifecycleBasePlugin.VERIFICATION_GROUP
val compileJava = project.tasks.named("compileJava", JavaCompile::class.java).get()
source = sourceSet("main").java
classpath = compileClasspath
destinationDirectory.set(project.layout.buildDirectory.dir("classes/java/main${suffix(oldest)}"))
javaCompiler.set(compileJava.javaCompiler)
options.release.set(compileJava.options.release)
options.annotationProcessorPath = compileJava.options.annotationProcessorPath
}
project.tasks.named(aggregateName(oldest)) { dependsOn(compileOldest) }
}

/**
Expand All @@ -69,28 +90,29 @@ abstract class SpringBootMatrixExtension @Inject constructor(private val project
}

/** [sourceSet]'s output and runtime dependencies, with Spring Boot at [version]. */
fun runtimeClasspath(sourceSet: String, version: String): FileCollection {
fun runtimeClasspath(sourceSet: String, version: String): FileCollection =
sourceSet(sourceSet).output + classpath(sourceSet, version, compile = false)

/** [sourceSet]'s compile or runtime classpath dependencies, with Spring Boot at [version]. */
private fun classpath(sourceSet: String, version: String, compile: Boolean): FileCollection {
val sources = sourceSet(sourceSet)
val name = "$sourceSet${suffix(version)}RuntimeClasspath"
val base =
if (compile) sources.compileClasspathConfigurationName
else sources.runtimeClasspathConfigurationName
val name = "$sourceSet${suffix(version)}${if (compile) "Compile" else "Runtime"}Classpath"
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 configurations.findByName(name)
?: configurations.create(name) {
isCanBeConsumed = false
val baseConfiguration = configurations.getByName(base)
extendsFrom(*baseConfiguration.extendsFrom.toTypedArray())
attributes.addAllLater(baseConfiguration.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) =
Expand Down
Loading