Skip to content

Commit 7270bfd

Browse files
bric3devflow.devflow-routing-intake
andauthored
Convert Spring Boot 3 / quarkus / armeria / vertx to smoke-test plugin (#11421)
chore(build-logic): add smoke-test plugin for nested Gradle builds Adds a new included build `build-logic/` hosting a single subproject `smoke-test` that exposes the `dd-trace-java.smoke-test-app` plugin. The plugin contributes: - `NestedGradleBuild` task type that runs a nested Gradle build via the Gradle Tooling API. It pins the nested Gradle version (no committed per-application wrappers), uses the configured Java toolchain for the nested daemon, forwards artifact paths from the root build as `-P<name>=<path>`, and redirects the nested `buildDir` via `-PappBuildDir=<path>` so outputs land under the outer project's build directory. - `smokeTestApp` project extension with an `application { ... }` block that registers the `NestedGradleBuild` task, wires it into every `Test` task via `dependsOn` + a `jvmArgumentProvider` for the produced artifact's system property. Consumers can also register `NestedGradleBuild` directly when they need more control; the plugin is a no-op until `application` or a manual registration is done. - `projectJar(name, project)` helper that forwards a sibling project's jar to the nested build through a resolvable `Configuration` (avoids `evaluationDependsOn` and the cross-project access ordering issues). The plugin is verified with JUnit 5 unit tests (`ProjectBuilder`) and end-to-end tests that drive the Tooling API path through the Gradle Test Kit with a temporary Kotlin-DSL test project. `build-logic/settings.gradle.kts` references the existing `gradle/libs.versions.toml` catalog (mirroring `buildSrc/`) so the plugin can use the same library coordinates as the rest of the repo. The Gradle libs Maven repository (`https://repo.gradle.org/gradle/libs-releases`, scoped to `org.gradle:`) is added to the root build's `pluginManagement` and to `gradle/repositories.gradle` so the Tooling API jar resolves. Smoke-test modules with Spring Boot plugin versions incompatible with Gradle 9 will use this plugin in follow-up PRs instead of a committed Gradle 8 wrapper. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> chore(build-logic): default smokeTestApp to JDK 21 daemon + Gradle 8.14.5 Set conventions on `smokeTestApp`: - `gradleVersion` defaults to `"8.14.5"` (Gradle 8 last release; pinned because Spring Boot plugin pre-3.5 calls `Configuration.getUploadTaskName()`, removed in Gradle 9). - `javaLauncher` defaults to a JDK 21 toolchain (the version the root build requires for its own Gradle 9 migration; standardising the nested daemon on the same JDK avoids requiring an extra toolchain on dev machines and CI runners). Consumers that need a different JDK or Gradle version still override explicitly. The inner build script is responsible for pinning the produced bytecode level (`java { sourceCompatibility = JavaVersion.VERSION_1_8 }` or similar) — Gradle adds `--release N` automatically when source/target differs from the daemon JVM. `JavaToolchainService` is now injected into the extension; this works in any project where a `java*` (or related) plugin is applied. Smoke-test modules already apply `gradle/java.gradle`, which applies `java`, so the convention resolves on first read. Public defaults exposed as `DEFAULT_NESTED_GRADLE_VERSION` and `DEFAULT_NESTED_JAVA_VERSION` constants so the values are discoverable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> chore(build-logic): use org.gradle.kotlin.dsl idioms in smoke-test plugin Switch the plugin sources and unit tests over to the typed `org.gradle.kotlin.dsl` extension functions where they replace `::class.java` boilerplate: - `tasks.register(name, Type::class.java) { … }` → `tasks.register<Type>(name) { … }` - `tasks.withType(Type::class.java).configureEach { … }` → `tasks.withType<Type>().configureEach { … }` - `extensions.create("name", Type::class.java)` → `extensions.create<Type>("name")` - `extensions.getByType(Type::class.java)` → `extensions.getByType<Type>()` - `extensions.findByName("name")` (followed by `isInstanceOf`) → `extensions.findByType<Type>()` - `project.plugins.apply(Plugin::class.java)` → `project.apply<Plugin>()` (PluginAware) - `objects.newInstance(Type::class.java)` → `objects.newInstance<Type>()` Also drop the six `captured*` local variables in `SmokeTestAppExtension.application` — inside `tasks.register<NestedGradleBuild>(taskName) { … }` the outer extension's properties are now reached via `this@SmokeTestAppExtension.<prop>` directly. No behavioural change; the 9 plugin tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> chore(smoke-tests): convert SB3 / quarkus / armeria / vertx to smoke-test plugin Drop-in conversion of ten more nested-build smoke tests to the `dd-trace-java.smoke-test-app` plugin (added in #11405). All ten previously used an `Exec` task that shelled out to a `gradlew` — either the root build's (now Gradle 9, incompatible with their plugin chain) or a committed inner wrapper. Spring Boot 3 / Gradle nested build, root-gradlew Exec: - `spring-boot-3.0-webflux` - `spring-boot-3.0-webmvc` - `spring-boot-3.3-webmvc` - `kafka-3` Non-Spring Gradle nested build, root-gradlew Exec: - `quarkus` (Quarkus 1.9.2 platform, Java 8) - `armeria-grpc` Inner Gradle wrapper (committed) → Tooling API: - `vertx-3.4`, `vertx-3.9`, `vertx-3.9-resteasy`, `vertx-4.2` The committed inner `gradlew` / `gradlew.bat` / `gradle/wrapper/` files are removed — the Tooling API pins the same Gradle 8.14.5 distribution the wrappers used. Each module's outer `build.gradle` collapses to a small `smokeTestApp { application { taskName / nestedTasks / artifactPath / sysProperty } projectJar(...) }` block. The daemon Gradle version + JDK 21 toolchain come from the plugin's conventions. Each inner `application/build.gradle` pins `sourceCompatibility` explicitly so JDK 21's javac cross-compiles to the right bytecode level (`--release`): Java 8 for `quarkus` and the vertx modules, Java 17 for the rest. `kafka-3` swaps its `toolchain.languageVersion` for `sourceCompatibility` to avoid pulling a separate JDK 17 toolchain on top of the daemon's JDK 21. Verified locally on Gradle 9.5.1: all ten nested builds produce their expected artifact; representative smoke tests (`spring-boot-3.0-webmvc`, `kafka-3`, `vertx-3.9`) pass with `-PskipFlakyTests=true`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> docs(smoke-tests): document nested-build settings.gradle config choices Match the review feedback applied on #11408: expand the terse comments on the nested settings.gradle files to explain the CI Maven mirror proxies and the CI-only shared build cache (f6ec1f5 / #982, b34ccbc), plus the Quarkus plugin version property forwarding. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Merge branch 'master' into bdu/smoke-test-pattern-f-drop-in Remove unnecessary Quarkus smoke test launcher fix(smoke-tests): pin Quarkus 1.9.2 launcher back to JDK 11 The previous commit removed the explicit `javaLauncher` thinking it was unnecessary, but Quarkus 1.9.2 (Sep 2020) cannot run on JDK 21. With the launcher removed, the smoke-test plugin defaulted to JDK 21 and the Quarkus app process exited before opening its port, causing 8 initialization errors in QuarkusJBossLoggingSmokeTest / QuarkusSlf4jSmokeTest. Restore the launcher pinned to JDK 11 — within Quarkus 1.x's supported JDK range (8 / 11). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 8fb1c6a commit 7270bfd

38 files changed

Lines changed: 248 additions & 1487 deletions

File tree

dd-smoke-tests/armeria-grpc/application/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ if (hasProperty('appBuildDir')) {
1717

1818
version = ""
1919

20+
// Pin bytecode target: the nested daemon now runs on JDK 21, but the smoke test launches
21+
// the produced jar on Java 17 (per testJvmConstraints).
22+
java {
23+
sourceCompatibility = JavaVersion.VERSION_17
24+
}
25+
2026
protobuf {
2127
// Configure the protoc executable.
2228
protoc {

dd-smoke-tests/armeria-grpc/application/settings.gradle

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// The `gradlePluginProxy` / `mavenRepositoryProxy` properties point to internal Maven
2+
// mirrors used by CI when the public repos are unreachable; they are forwarded from the
3+
// outer build via the smoke-test plugin. `allowInsecureProtocol` is required because the
4+
// mirrors are reached over plain HTTP inside the CI network.
15
pluginManagement {
26
repositories {
37
mavenLocal()
@@ -20,12 +24,16 @@ pluginManagement {
2024

2125
def isCI = providers.environmentVariable("CI").isPresent()
2226

23-
// Don't pollute the dependency cache with the build cache
27+
// On CI, point the local Gradle build cache to the shared workspace directory under the
28+
// repository root, so cache entries are reused across the many smoke-test nested builds
29+
// (and across CI jobs that mount the same workspace). See f6ec1f5cc8 / #982 for the
30+
// root-level cache, and b34ccbc048 for the `isCI` gating — locally we keep the default
31+
// per-user cache to avoid leaking entries into the repo tree.
32+
// The directory must line up with the outer project's settings.gradle.
2433
if (isCI) {
2534
def sharedRootDir = "$rootDir/../../../"
2635
buildCache {
2736
local {
28-
// This needs to line up with the code in the outer project settings.gradle
2937
directory = "$sharedRootDir/workspace/build-cache"
3038
}
3139
}

dd-smoke-tests/armeria-grpc/build.gradle

Lines changed: 14 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins {
2+
id 'dd-trace-java.smoke-test-app'
23
id 'com.google.protobuf' version '0.10.0'
34
}
45

@@ -8,6 +9,8 @@ testJvmConstraints {
89
minJavaVersion = JavaVersion.VERSION_17
910
}
1011

12+
description = 'Armeria gRPC Smoke Tests.'
13+
1114
sourceSets {
1215
main {
1316
proto {
@@ -39,6 +42,16 @@ protobuf {
3942
}
4043
}
4144

45+
smokeTestApp {
46+
application {
47+
taskName = 'armeriaBuild'
48+
nestedTasks = ['build']
49+
artifactPath = 'libs/armeria-smoketest-all.jar'
50+
sysProperty = 'datadog.smoketest.armeria.uberJar.path'
51+
}
52+
projectJar('apiJar', project(':dd-trace-api'))
53+
}
54+
4255
dependencies {
4356
testImplementation project(':dd-smoke-tests')
4457

@@ -49,49 +62,13 @@ dependencies {
4962
testImplementation(testFixtures(project(":dd-smoke-tests:iast-util")))
5063
}
5164

52-
def appDir = "$projectDir/application"
53-
def appBuildDir = "$buildDir/application"
54-
def isWindows = System.getProperty("os.name").toLowerCase().contains("win")
55-
def gradlewCommand = isWindows ? 'gradlew.bat' : 'gradlew'
56-
57-
// define the task that builds the armeria project
58-
tasks.register('armeriaBuild', Exec) {
59-
workingDir "$appDir"
60-
environment += [
61-
"GRADLE_OPTS": "-Dorg.gradle.jvmargs='-Xmx512M'",
62-
"JAVA_HOME": getLazyJavaHomeFor(17)
63-
]
64-
commandLine "${rootDir}/${gradlewCommand}", "build", "--no-daemon", "--max-workers=4", "-PappBuildDir=$appBuildDir", "-PapiJar=${project(':dd-trace-api').tasks.jar.archiveFile.get()}"
65-
66-
outputs.cacheIf { true }
67-
68-
outputs.dir(appBuildDir)
69-
.withPropertyName("applicationJar")
70-
71-
inputs.files(fileTree(appDir) {
72-
include '**/*'
73-
exclude '.gradle/**'
74-
})
75-
.withPropertyName("application")
76-
.withPathSensitivity(PathSensitivity.RELATIVE)
77-
}
78-
79-
evaluationDependsOn ':dd-trace-api'
80-
armeriaBuild {
81-
dependsOn project(':dd-trace-api').tasks.named("jar")
82-
}
83-
8465
tasks.named("compileTestGroovy", GroovyCompile) {
8566
dependsOn 'armeriaBuild'
8667
outputs.upToDateWhen {
87-
!armeriaBuild.didWork
68+
!tasks.named('armeriaBuild').get().didWork
8869
}
8970
}
9071

91-
tasks.withType(Test).configureEach {
92-
jvmArgs "-Ddatadog.smoketest.armeria.uberJar.path=$appBuildDir/libs/armeria-smoketest-all.jar"
93-
}
94-
9572
spotless {
9673
java {
9774
target "**/*.java"

dd-smoke-tests/kafka-3/application/build.gradle

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ plugins {
44
id 'io.spring.dependency-management' version '1.1.4'
55
}
66

7+
// Pin bytecode target: the nested daemon runs on JDK 21, but the smoke test launches the
8+
// produced jar on Java 17. Using sourceCompatibility (vs. toolchain.languageVersion) lets
9+
// Gradle cross-compile via `--release 17` without pulling a separate JDK 17 toolchain.
710
java {
8-
toolchain {
9-
languageVersion.set(JavaLanguageVersion.of(17))
10-
}
11+
sourceCompatibility = JavaVersion.VERSION_17
1112
}
1213

1314
def sharedRootDir = "$rootDir/../../../"

dd-smoke-tests/kafka-3/application/settings.gradle

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// The `gradlePluginProxy` / `mavenRepositoryProxy` properties point to internal Maven
2+
// mirrors used by CI when the public repos are unreachable; they are forwarded from the
3+
// outer build via the smoke-test plugin. `allowInsecureProtocol` is required because the
4+
// mirrors are reached over plain HTTP inside the CI network.
15
pluginManagement {
26
repositories {
37
mavenLocal()
@@ -20,12 +24,16 @@ pluginManagement {
2024

2125
def isCI = providers.environmentVariable("CI").isPresent()
2226

23-
// Don't pollute the dependency cache with the build cache
27+
// On CI, point the local Gradle build cache to the shared workspace directory under the
28+
// repository root, so cache entries are reused across the many smoke-test nested builds
29+
// (and across CI jobs that mount the same workspace). See f6ec1f5cc8 / #982 for the
30+
// root-level cache, and b34ccbc048 for the `isCI` gating — locally we keep the default
31+
// per-user cache to avoid leaking entries into the repo tree.
32+
// The directory must line up with the outer project's settings.gradle.
2433
if (isCI) {
2534
def sharedRootDir = "$rootDir/../../../"
2635
buildCache {
2736
local {
28-
// This needs to line up with the code in the outer project settings.gradle
2937
directory = "$sharedRootDir/workspace/build-cache"
3038
}
3139
}

dd-smoke-tests/kafka-3/build.gradle

Lines changed: 14 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1+
plugins {
2+
id 'dd-trace-java.smoke-test-app'
3+
id 'java-test-fixtures'
4+
}
5+
16
apply from: "$rootDir/gradle/java.gradle"
2-
apply plugin: 'java-test-fixtures'
37

48
testJvmConstraints {
59
minJavaVersion = JavaVersion.VERSION_17
610
}
711

812
description = 'Kafka 3.x Smoke Tests.'
913

14+
smokeTestApp {
15+
application {
16+
taskName = 'bootJar'
17+
artifactPath = 'libs/kafka-3-smoketest.jar'
18+
sysProperty = 'datadog.smoketest.springboot.shadowJar.path'
19+
}
20+
}
21+
1022
dependencies {
1123
testImplementation('org.springframework.kafka:spring-kafka-test:2.9.13')
1224

@@ -15,45 +27,13 @@ dependencies {
1527
testImplementation(testFixtures(project(":dd-smoke-tests:iast-util")))
1628
}
1729

18-
final appDir = "$projectDir/application"
19-
final appBuildDir = "$buildDir/application"
20-
final isWindows = System.getProperty('os.name').toLowerCase().contains('win')
21-
final gradlewCommand = isWindows ? 'gradlew.bat' : 'gradlew'
22-
23-
tasks.register('bootJar', Exec) {
24-
workingDir appDir
25-
environment += [
26-
"GRADLE_OPTS": "-Dorg.gradle.jvmargs='-Xmx512M'",
27-
"JAVA_HOME": getLazyJavaHomeFor(17)
28-
]
29-
commandLine "$rootDir/${gradlewCommand}", "bootJar", "--no-daemon", "--max-workers=4", "-PappBuildDir=$appBuildDir"
30-
31-
outputs.cacheIf { true }
32-
33-
outputs.dir(appBuildDir)
34-
.withPropertyName("applicationJar")
35-
36-
inputs.files(fileTree(appDir) {
37-
include '**/*'
38-
exclude '.gradle/**'
39-
})
40-
.withPropertyName("application")
41-
.withPathSensitivity(PathSensitivity.RELATIVE)
42-
43-
group('build')
44-
}
45-
4630
tasks.named('compileTestGroovy') {
4731
dependsOn 'bootJar'
4832
outputs.upToDateWhen {
49-
!bootJar.didWork
33+
!tasks.named('bootJar').get().didWork
5034
}
5135
}
5236

53-
tasks.withType(Test).configureEach {
54-
jvmArgs "-Ddatadog.smoketest.springboot.shadowJar.path=${appBuildDir}/libs/kafka-3-smoketest.jar"
55-
}
56-
5737
spotless {
5838
java {
5939
target "**/*.java"

dd-smoke-tests/quarkus/application/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ if (hasProperty('appBuildDir')) {
1515

1616
version = ""
1717

18+
// Pin bytecode target: the nested daemon now runs on JDK 21, but Quarkus 1.9 supports Java 8.
19+
java {
20+
sourceCompatibility = JavaVersion.VERSION_1_8
21+
}
22+
1823
dependencies {
1924
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
2025
implementation 'io.quarkus:quarkus-resteasy'

dd-smoke-tests/quarkus/application/settings.gradle

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
// The `gradlePluginProxy` / `mavenRepositoryProxy` properties point to internal Maven
2+
// mirrors used by CI when the public repos are unreachable; they are forwarded from the
3+
// outer build via the smoke-test plugin. `allowInsecureProtocol` is required because the
4+
// mirrors are reached over plain HTTP inside the CI network.
5+
// The inner `plugins { id 'io.quarkus' ... }` block resolves the Quarkus version from a
6+
// `quarkusPluginVersion` Gradle property forwarded by the outer build.
17
pluginManagement {
28
repositories {
39
mavenLocal()
@@ -23,12 +29,16 @@ pluginManagement {
2329

2430
def isCI = providers.environmentVariable("CI").isPresent()
2531

26-
// Don't pollute the dependency cache with the build cache
32+
// On CI, point the local Gradle build cache to the shared workspace directory under the
33+
// repository root, so cache entries are reused across the many smoke-test nested builds
34+
// (and across CI jobs that mount the same workspace). See f6ec1f5cc8 / #982 for the
35+
// root-level cache, and b34ccbc048 for the `isCI` gating — locally we keep the default
36+
// per-user cache to avoid leaking entries into the repo tree.
37+
// The directory must line up with the outer project's settings.gradle.
2738
if (isCI) {
2839
def sharedRootDir = "$rootDir/../../../"
2940
buildCache {
3041
local {
31-
// This needs to line up with the code in the outer project settings.gradle
3242
directory = "$sharedRootDir/workspace/build-cache"
3343
}
3444
}

dd-smoke-tests/quarkus/build.gradle

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,43 @@
1+
plugins {
2+
id 'dd-trace-java.smoke-test-app'
3+
}
4+
15
apply from: "$rootDir/gradle/java.gradle"
26

37
testJvmConstraints {
48
// Quarkus is only supported up to Java 21: https://github.com/quarkusio/quarkus
59
maxJavaVersion = JavaVersion.VERSION_21
610
}
711

8-
dependencies {
9-
testImplementation project(':dd-smoke-tests')
10-
}
11-
12-
def appDir = "$projectDir/application"
13-
def appBuildDir = "$buildDir/application"
14-
def isWindows = System.getProperty("os.name").toLowerCase().contains("win")
15-
def gradlewCommand = isWindows ? 'gradlew.bat' : 'gradlew'
16-
17-
// define the task that builds the quarkus project
18-
tasks.register('quarkusBuild', Exec) {
19-
workingDir "$appDir"
20-
environment += [
21-
"GRADLE_OPTS": "-Dorg.gradle.jvmargs='-Xmx512M'",
22-
"JAVA_HOME": getLazyJavaHomeFor(8)
23-
]
24-
commandLine "${rootDir}/${gradlewCommand}", "build", "--no-daemon", "--max-workers=4", "-PappBuildDir=$appBuildDir", "-PapiJar=${project(':dd-trace-api').tasks.jar.archiveFile.get()}"
25-
26-
outputs.cacheIf { true }
12+
description = 'Quarkus Smoke Tests.'
2713

28-
outputs.dir(appBuildDir)
29-
.withPropertyName("applicationJar")
30-
31-
inputs.files(fileTree(appDir) {
32-
include '**/*'
33-
exclude '.gradle/**'
34-
})
35-
.withPropertyName("application")
36-
.withPathSensitivity(PathSensitivity.RELATIVE)
14+
smokeTestApp {
15+
// Quarkus 1.9.2 (Sep 2020) cannot run on JDK 21; use Java 11 launcher
16+
javaLauncher = javaToolchains.launcherFor {
17+
languageVersion = JavaLanguageVersion.of(11)
18+
}
19+
application {
20+
taskName = 'quarkusBuild'
21+
nestedTasks = ['build']
22+
// Quarkus' uber-jar is named `<projectName>-runner.jar`; here the inner project has no
23+
// version, so the artifact name is `quarkus-smoketest--runner.jar`.
24+
artifactPath = 'quarkus-smoketest--runner.jar'
25+
sysProperty = 'datadog.smoketest.quarkus.uberJar.path'
26+
}
27+
projectJar('apiJar', project(':dd-trace-api'))
3728
}
3829

39-
evaluationDependsOn ':dd-trace-api'
40-
41-
tasks.named("quarkusBuild", Exec) {
42-
dependsOn project(':dd-trace-api').tasks.named("jar")
30+
dependencies {
31+
testImplementation project(':dd-smoke-tests')
4332
}
4433

4534
tasks.named("compileTestGroovy", GroovyCompile) {
4635
dependsOn 'quarkusBuild'
4736
outputs.upToDateWhen {
48-
!quarkusBuild.didWork
37+
!tasks.named('quarkusBuild').get().didWork
4938
}
5039
}
5140

52-
tasks.withType(Test).configureEach {
53-
jvmArgs "-Ddatadog.smoketest.quarkus.uberJar.path=$appBuildDir/quarkus-smoketest--runner.jar"
54-
}
55-
5641
spotless {
5742
java {
5843
target "**/*.java"

dd-smoke-tests/spring-boot-3.0-webflux/application/build.gradle

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ if (hasProperty('appBuildDir')) {
1616

1717
version = ""
1818

19+
// Pin bytecode target: the nested daemon now runs on JDK 21, but the smoke test launches
20+
// the produced jar on Java 17 (per testJvmConstraints).
21+
java {
22+
sourceCompatibility = JavaVersion.VERSION_17
23+
}
24+
1925
dependencies {
2026
implementation 'org.springframework.boot:spring-boot-starter-webflux'
2127
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

0 commit comments

Comments
 (0)