Kada Gradle 9, Docker 29 i Testcontainers se rate uzvuku u čorbu : debugovanje gradnje JHipster
Објављено 25 April 2026
- Scena: JHipster projekat, build koji više ne učitava
- Greška 1 : fantomska promenljiva `reportsDir
- Greška 2 :
main = "…"više ne postoji u Gradle 9 - Greška 3 :
sourceCompatibilityviše nije svojstvo projekta - Greška 4: Groovy tvrdenje sa tri operanda
- Greška 5: implicitna zavisnost
compileKotlin→ `openApiGenerate - Грешка 6 :
generateGitPropertiesсе срушава на `FilterOutputStream.write() - Konačno: Testcontainers ulazi na scenu
- Faza 1 : docker-java staza
- Faza 2: web pretraga i GitHub krumplin
- Faza 3 : zamka preimenovanih artefakata
- Faza 4: prisiljavanje resolucije
- Konačni rezultat
- Табела сажетак исправка
- Zaključak
Već ste doživili ovaj trenutak kad je jednostavno`./gradlew build`Шта то покрећеш сто пута на дан, неожињено почиње да пада на грешку коју није раније видела? Помножи то на једанаест последоватих грешака, додај добру дозу несусадности Docker Engine 29, порошљи Gradle 9 који брише API-je које smo koristili deset года. Ово је повни журнал.
Дијаграм 1 — Миграција Gradle 9 (niz грешака)
Cilj: pokazati seriju nekompatibilnosti.
Jasno, usmeren na Gradle.
@startuml left to right direction title Migracija Gradle 9 rectangle "1 reportsDir" as A rectangle "2\nmainClass" as B rectangle "3\nsourceCompatibility" as C rectangle "4\nGroovy assert" as D A --> B B --> C C --> D @enduml
Cilj: izolirati pokvareni plugins.
@startuml left to right direction title Плагинови који треба исправити skinparam shadowing false skinparam roundcorner 18 rectangle "1 openApiGenerate" as A #FFF3E0 rectangle "2 gitProperties 2.5.7" as B #FFF3E0 rectangle "3 Verzije ispravljene" as C #E3F2FD rectangle "4\nSklati OK" as D #E8F5E9 A --> B B --> C C --> D @enduml
Cilj : problem runtime
@startuml left to right direction title Docker 29 + Testcontainers skinparam shadowing false skinparam roundcorner 18 skinparam defaultFontSize 14 rectangle "1 Sastavi OK" as A #E8F5E9 rectangle "2 Testovi Cucumber NEUSPEŠNO" as B #FFEBEE rectangle "3 Testcontainers 1.32 odbacen" as C #FFF3E0 rectangle "4 Upgrade verzije" as D #E3F2FD rectangle "5 Izgradi konačno OK" as E #E8F5E9 A --> B B --> C C --> D D --> E @enduml
@startuml !option handwritten true skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam defaultFontName Inter skinparam defaultFontSize 14 skinparam ArrowThickness 2 skinparam RoundCorner 18 title Roadmap stabilizacije — Gradle 9 ka finalnom buildu left to right direction rectangle "⚙️ Migracija Gradle 9" as A #E3F2FD rectangle "Додатци JHipster" as B #FFF3E0 rectangle "🐳 Docker 29 Testcontainers" as C #FCE4EC rectangle "✅ stabilna izgradnja" as D #E8F5E9 note bottom of A reportsDir mainClass sourceCompatibility Groovy assert end note note bottom of B openApiGenerate gitProperties versions alignées end note note bottom of C tests Cucumber KO image Docker rejetée upgrade libs end note A -[#42A5F5]-> B : corrections build.gradle B -[#FB8C00]-> C : assemble OK C -[#E53935]-> D : tests réparés @enduml
Scena: JHipster projekat, build koji više ne učitava
Projekat`edster`Ovo je JHipster aplikacija generisana 2024. godine. Glavni skript za gradnju`build.gradle`bio je ostao stabilan tokom meseci. Zatim odlučujemo da nadogradimo verziju :
komponenta |
Ciljna verzija |
Gradle |
9.4.1 |
Јава |
21.0.11-tem (Eclipse Temurin) |
JHipster |
8.x (okvir`tech.jhipster:jhipster-framework:8.11.0`) |
Spring Boot |
3.4.5 |
Kotlin |
2.3.0 |
Docker Engine |
29.4.1 (API 1.54) |
Promena verzije Jave se vrši putem SDKMAN-a:
sdk use java 21.0.11-tem
Prvi pokretanje:
$ ./gradlew build --no-daemon
FAILURE: Build failed with an exception.
* Where:
Build file '/home/.../edster/build.gradle' line: 18
* What went wrong:
An exception occurred applying plugin request [id: 'jhipster.cucumber-conventions']
> Failed to apply plugin 'jhipster.cucumber-conventions'.
> Could not create task ':cucumberTest'.
> Could not create task ':consoleLauncherTest'.
> Could not set unknown property 'reportsDir' for task ':consoleLauncherTest'
Nikad čak i nismo pokrenuli učitavanje skripte za gradnju. Problem je u`buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle`.
Greška 1 : fantomska promenljiva `reportsDir
Скрипта`jhipster.cucumber-conventions.gradle`sadrži :
tasks.register('consoleLauncherTest', JavaExec) {
dependsOn(testClasses)
String cucumberReportsDir = file("$buildDir/reports/tests")
outputs.dir(reportsDir) // <-- reportsDir n'existe PAS
classpath = sourceSets["test"].runtimeClasspath
main = "org.junit.platform.console.ConsoleLauncher"
// ...
}
Definisana promenljiva je`cucumberReportsDir`. Korišćena je`reportsDir`— која није дефинисана нигде.
Корекција : outputs.dir(cucumberReportsDir)
Greška 2 : main = "…" više ne postoji u Gradle 9
Hitno ponovno pokretanje :
Could not set unknown property 'main' for task ':consoleLauncherTest' of type org.gradle.api.tasks.JavaExec.
Gradle 9 uklanja svojstvo`main`u korist`mainClass`о задацима`JavaExec`.
Корекција:`mainClass = "org.junit.platform.console.ConsoleLauncher"`
Greška 3 : sourceCompatibility više nije svojstvo projekta
Nova greška:
Could not set unknown property 'sourceCompatibility' for root project 'edster' of type org.gradle.api.Project.
Коренијски скрипт је imao :
sourceCompatibility=17
targetCompatibility=17
Gradle 9 uklanja ove svojstva na korenom nivou. Oni moraju da budu unutar bloka.java.
Корекција:
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
Greška 4: Groovy tvrdenje sa tri operanda
Stari skript je sadržavao:
assert System.properties["java.specification.version"] == "17" || "21" || "24"
У Грови,"21" et "24"`су truthy низове. Izraz se rešava u(… == "17") || true || true`, što je uvek tačno — ali sintaksa nije validna za zahtevanu strogu tvrdnju.
Ispravka :
assert System.properties["java.specification.version"] in ["17", "21", "23", "24"]
Greška 5: implicitna zavisnost compileKotlin → `openApiGenerate
Build napreduje. Kompajliranje Kotlin-a je uspelo. Zatim:
Task ':compileKotlin' uses this output of task ':openApiGenerate'
without declaring an explicit or implicit dependency.
Gradle 9 odbija implicitne zavisnosti između zadataka. Jer`compileKotlin`чита генерисане изворове од`openApiGenerate`, Треба јавити везу.
Исправкаu`build.gradle`:
afterEvaluate {
tasks.named("compileKotlin").configure {
dependsOn(tasks.named("openApiGenerate"))
}
}
Le `afterEvaluate`jer je potrebno`openApiGenerate`је додатак (OpenAPI Generator plugin) и није задатак који је директно доступан током фазе конфигурације.
Грешка 6 : generateGitProperties се срушава на `FilterOutputStream.write()
Kompajliranje Jave prolazi. Resursi se obrađuju. Zatim :
> Task :generateGitProperties FAILED
No signature of method: java.io.FilterOutputStream.write() is applicable for argument types: (Integer) values: [103]
Плагин`gradle-git-properties`version 2.5.0 nije kompatibilna sa Java 21 / Gradle 9. Unutrašnji greška pokušava da pozove`write(int)`preko Groovy na toku koji ga je zatvorio
Ispravkau`gradle.properties`:
gitPropertiesPluginVersion=2.5.7
Konačno: Testcontainers ulazi na scenu
Do sada, ovo je bilo čisto 'build script debugging'. Svaka greška je bila nekompatibilnost Gradle 9 ili Java 21 u skriptovima za gradnju. Posle šest ispravki iznad, le`./gradlew assemble`успешно прође
Ali`./gradlew build`Он такође извршава тестове, а Cucumber тестови користе Testcontainers да покрену привремено PostgreSQL.
Could not find a valid Docker environment.
EnvironmentAndSystemPropertyClientProviderStrategy: failed with exception BadRequestException
(Status 400: {"message":"client version 1.32 is too old. Minimum supported API version is 1.40"}
UnixSocketClientProviderStrategy: failed with exception BadRequestException
(Status 400: {"message":"client version 1.32 is too old. Minimum supported API version is 1.40"}
Testcontainers ne može da komunicira sa Docker-om. Dve različite strategije (promenljive okoline + Unix soket) ne uspevaju sa istom greškom.
@startuml skinparam backgroundColor #FEFEFE skinparam handwritten false actor "Testcontainers 1.20.6" as TC participant "docker-java 3.4.1" as DJ participant "Docker Engine 29.4.1" as DE TC -> DJ : new DockerClient() DJ -> DE : GET /_ping\nUser-Agent: docker-java/3.4.1\nAPI-Version: 1.32 DE --> DJ : HTTP 400 Bad Request\nclient version 1.32 is too old.\nMinimum supported API version is 1.40 DJ --> TC : BadRequestException TC --> TC : Could not find a valid\nDocker environment note right of DE Docker Engine 29.x impose API minimum = 1.40 (défaut ancien : 1.24) end note note left of DJ docker-java 3.4.1 envoie hardcoded API version 1.32 dans l'en-tête HTTP end note @enduml
Faza 1 : docker-java staza
Prvi refleks : Docker Java klijent korišćen od Testcontainers šalje verziju API`1.32`u njegovom HTTP handshake-u, ali Docker Engine 29.4.1 zahteva najmanje`1.40`. Проблем је на нивоу клијента, не демона.
Провера верзије docker-java решена од стране Testcontainers 1.20.6:
$ ./gradlew dependencies --configuration testRuntimeClasspath | grep docker-java
+--- com.github.docker-java:docker-java-api:3.4.1
+--- com.github.docker-java:docker-java-transport:3.4.1
docker-java 3.4.1, datira iz Testcontainers 1.20.6. Najnovija javna verzija docker-jave je 3.5.1. Pokušamo da prisilimo na nadogradnju verzije kroz`resolutionStrategy`у блоку`configurations` de build.gradle:
configurations {
all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == "com.github.docker-java"
&& details.requested.name.startsWith("docker-java")) {
details.useVersion("3.5.1")
}
}
}
}
Ponovno pokretanje izgradnje. Ista greška:`client version 1.32 is too old`.
Провера Gradle кеша: JAR-ови су успешно поново преузети, али грешка остаје. docker-java 3.5.1 увек шаље`1.32`. Ova verzija nije dovoljna za Docker Engine 29.4.1.
Акција чишћењаOčisti potpuno keš Gradle, radi slučaja
rm -rf ~/.gradle/caches
rm -rf /path/to/project/.gradle
Ponovni pokret nakon čišćenja. Ista greška.
Faza 2: web pretraga i GitHub krumplin
docker-java 3.5.1 je nedovoljan. Ostaje samo da se proveri da li je neko susrešao ovaj tačan problem.
Ciljana pretraga na problemima testcontainers-java i docker-java :
site:github.com/testcontainers "client version 1.32 is too old"
site:github.com/docker-java "client version 1.32" Docker Engine 29
Prvi neposredni rezultati :
-
testcontainers-java#11210—
[Bug]: client version 1.32 is too old. Minimum supported API version is 1.44 -
testcontainers-java#11212—
[Bug]: Docker 29.0.0 could not find a valid Docker environment -
testcontainers-java#11491—
[Bug]: Incompatibility in Ubuntu Github runners with Docker Engine 29.1.*
Нита Ариадне је јасна: Docker Engine 29.x је повео свој минималан API на`1.24`(стари дефект) у`1.40`. Testcontainers 1.20.x koristi docker-java 3.4.x koji pregovara verziju API-ja slanjem`1.32`. Daemon ljubazno ali tvrdohladno odbija.
У проблему #11491, одржавач одговара:
_ Zdravo, imam projekat koji radi na verziji 2.0.3 na GH runneru sa engine-om 29.1 i radi. Молим вас, проверите да ли нема конфликта верzije? Molimo, sećajte se da su svi moduli prefiksirani sa`testcontainers-. Zato, počevši od verzije 2.x, prešli smo od`postgresql to testcontainers-postgresql _
Ovaj odgovor sadržidve kritične informacije:
-
Testcontainers 2.0.3+ rešava problem
-
Moduli su bilipreimenovani sa prefiksom `testcontainers-
Faza 3 : zamka preimenovanih artefakata
Preimenuvanje je brutalno. U Testcontainers 2.x, ni jedno od starih imena ne radi pod tim imenom:
Старо име (1.x) |
Novo ime (2.x) |
|
|
|
|
|
|
|
непромењен |
Prvi pokušaj : primeniti BOM Testcontainers 2.0.5 i nova imena artefakata u`build.gradle`.
dependencies {
testImplementation platform("org.testcontainers:testcontainers-bom:2.0.5")
testImplementation "org.testcontainers:testcontainers-postgresql"
testImplementation "org.testcontainers:testcontainers-jdbc"
testImplementation "org.testcontainers:testcontainers-junit-jupiter"
testImplementation "org.testcontainers:testcontainers"
}
./gradlew build
FAILURE: Could not find org.testcontainers:jdbc:2.0.5.
Could not find org.testcontainers:junit-jupiter:2.0.5.
BOM testcontainers-bom nije dovoljan.Зашто? Пошто Spring Boot 3.4.5 излага свој BOM за управљање зависиснца (spring-boot-dependencies) копобедина BOM-u Testcontainers у transitivnoj rezoluciji. Spring Boot исправља.testcontainers à 1.20.6, i zato svi artefakti koji nisu eksplicitno u BOM-u Spring Boot ( kao noviji`testcontainers-*) razrešavaju se ka starim imenima`1.20.6-- koji više ne postoje u ovoj verziji.
Prilikom inspekce`dependencies --configuration testRuntimeClasspath`, nalazi se :
+--- org.testcontainers:testcontainers -> 1.20.6 (*)
| \--- org.testcontainers:testcontainers:2.0.5 -> 1.20.6
Le →`označuje zamenu: Testcontainers 2.0.5 je zahtevan, ali Spring Boot prisiljuje`1.20.6.
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle\n(potrošač)" as BG #E3F2FD {
rectangle "zahtev\n`testcontainers-bom:2.0.5" as REQ
rectangle "захтев
`testcontainers-postgresql" as REQ2
}
component "Maven Central" as MC #FFF3E0
component "Izjave" as DEC #E8F5E9 {
rectangle "testcontainers-bom:2.0.5`\nfiksirano 2.0.5" as BOM2
rectangle "spring-boot-dependencies:3.4.5`\nисправљено 1.20.6" as BOM1
}
component "Решење Gradle" as RES #FFEBEE {
rectangle "Pobednička verzija
`testcontainers:1.20.6" as WIN #EF9A9A
}
BG --> DEC : "analiziraj BOM-ove"
BOM2 --> RES : "предлаже 2.0.5"
BOM1 --> RES : "предлага 1.20.6"
WIN -> MC : "preuzmi
testcontainers:1.20.6"
note bottom of WIN
Spring Boot BOM prime car
le plugin spring-boot applique
ses dépendances de gestion
après la résolution du consommateur
end note
@enduml
Faza 4: prisiljavanje resolucije
Strategija postaje dvostruka:
-
Postaviti docker-java na verziju 3.7.1 (testirana kao kompatibilna sa Docker Engine 29)
-
Prisiliti Testcontainers core na 2.0.5 obilazeći Spring Boot BOM
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle
(uz resolutionStrategy)" as BG #E8F5E9 {
rectangle "BOM Testcontainers
`2.0.5" as BOM2
rectangle "resolutionStrategy\n.eachDependency" as RS #66BB6A
}
component "Конфликт решен" as RESOLVED #E3F2FD {
rectangle "docker-java
`3.7.1` (prisilno)" as DJ
rectangle "testcontainers
`2.0.5` (prisiljeno)" as TC #81C784
rectangle "testcontainers-postgresql
`2.0.5" as TPG
rectangle "testcontainers-jdbc
`2.0.5" as TJ
rectangle "testcontainers-junit-jupiter\n`2.0.5" as TJJ
}
component "BOM Spring Boot\n(implicitno, prekrženo)" as BOM1 #FFEBEE
component "Maven Centar" as MC #FFF3E0
BG --> RESOLVED : "BOM Testcontainers 2.0.5
prosto prolazi"
RS -> DJ : "sila 3.7.1"
RS -> TC : "sila 2.0.5"
RESOLVED --> MC : "preuzmi JAR-ove\nkompatibilne sa Docker 29"
BOM1 -[#gray,dashed]-> RESOLVED : "пропуштено на testcontainers\nи docker-java"
note bottom of RS
Le `eachDependency` intercepte
la résolution Gradle AVANT
que le BOM Spring Boot ne gagne.
C'est une intervention chirurgicale.
end note
@enduml
Коначна исправкау`build.gradle`:
configurations {
all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == "com.github.docker-java"
&& details.requested.name.startsWith("docker-java")) {
details.useVersion("3.7.1")
}
if (details.requested.group == "org.testcontainers"
&& details.requested.name == "testcontainers") {
details.useVersion("2.0.5")
}
}
}
}
dependencies {
testImplementation platform("org.testcontainers:testcontainers-bom:2.0.5")
testImplementation "org.testcontainers:testcontainers-jdbc"
testImplementation "org.testcontainers:testcontainers-junit-jupiter"
testImplementation "org.testcontainers:testcontainers-postgresql"
testImplementation "org.testcontainers:testcontainers"
// ... autres dépendances Spring Boot
}
Le `resolutionStrategy.eachDependency`једин начин за обилазак BOM`spring-boot-dependencies`upravljeno pluginom Spring Boot. Test jednakosti na`details.requested.name == "testcontainers"`Namerno je ograničeno: prisiljujemo samo core modul. Module`testcontainers-*`Sledeju BOM Testcontainers 2.0.5 koji smo eksplicitno naveli.
Konačni rezultat
$ ./gradlew build --no-daemon
> Task :consoleLauncherTest
[2 containers found]
[2 containers started]
[2 containers successful]
BUILD SUCCESSFUL in 1m 16s
Testcontainers 2.0.5 + docker-java 3.7.1 konačno uspevaju da kreiraju svoje PostgreSQL kontejnere putem Docker Engine 29.4.1. Konačna poslovna greška (HTTP 500 na Cucumber testu) je problem u aplikaciji potpuno nezavisno od skripte za grade.
Табела сажетак исправка
| # | datoteka | Проблем | Исправка |
|---|---|---|---|
1 |
|
promenljiva`reportsDir`nepostojeca |
|
2 |
|
`main`zastareo Gradle 9 |
|
3 |
|
`sourceCompatibility`top-level zabranjeno Gradle 9 |
блок`java { sourceCompatibility = JavaVersion.VERSION_21 }` |
4 |
|
Sintaktički neispravna Groovy assertion |
|
5 |
|
Implicitna zavisnost`compileKotlin`</think> (empty) |
|
6 |
|
`gitPropertiesPluginVersion`nekompatibilan Java 21 |
|
7 |
Keš Gradle |
docker-java 3.5.1 testiran, rešen ali nedostavan |
Обриши + прелазак docker-java 3.7.1 |
8 |
|
Testcontainers 1.20.6 je nekompatibilan sa Docker Engine 29 |
Prisiljenje Testcontainers 2.0.5 + prefiks`testcontainers-*`+ resolutionStrategy vs Spring Boot BOM |
Zaključak
Ако ваш JHipster/Gradle build падне nakon подијања верзије на Gradle 9 + Docker Engine 29, грешке се подељују на две категорије :
Greške u skripti za gradnju(prvih 6) : Gradle 9 uklanja svojstva i sintakse koje su radile veoma godinama (sourceCompatibility=, main =, reportsDir). Ovo su mehaničke migracije jednom kada upoznamo novi API.
Greške u izvršavanju Docker(последње две) : Docker Engine 29.4.1 odbacuje klijente API < 1.40. Testcontainers 1.20.6 (impozovan od strane Spring Boot 3.4.5) koristi docker-java 3.4.1 koji šalje 1.32. Jedino Testcontainers 2.0.5 + docker-java 3.7.1 rešava problem, sa preimenovanjem artefakata i prisilivanjem verzije via`resolutionStrategy.eachDependency`kao jedini način da se obiđe implicitni BOM u Spring Boot-u.
Gradle build skript u korenu projekta je sada čist.`./gradlew build`користи Java 21, Gradle 9.4.1, Spring Boot 3.4.5 и Docker Engine 29.4.1.