Cuando Gradle 9, Docker 29 y Testcontainers se enfadan mucho: depuración de una build JHipster
Publié le 25 April 2026
- La escena: un proyecto JHipster, una compilación que ya no carga
- Error 1 : la variable fantasma `reportsDir
- Error 2:
main = \"…\"ya no existe en Gradle 9 - Error 3:
sourceCompatibilityya no es una propiedad del proyecto - Error 4 : la afirmación de Groovy a tres operandos
- Error 5 : la dependencia implícita
compileKotlin→ `openApiGenerate - Error 6 :
generateGitPropertiesfalla en `FilterOutputStream.write() - Finalmente: Testcontainers entra en escena
- Fase 1: la pista docker-java
- Fase 2 : búsqueda web y camino de migas de pan GitHub
- Fase 3 : la trampa de los artifacts renombrados
- Fase 4 : forzar la resolución
- Resultado final
- Resumen de correcciones
- Conclusión
Ya has conocido ese momento en el que un simple`./gradlew build`que lanzas cien veces al día se bloquea repentinamente ante un error nunca visto ? Multiplícalo por once errores sucesivos, agrega una buena dosis de incompatibilidad de Docker Engine 29, espolvorea con Gradle 9 que hace desaparecer las API que utilizábamos desde hace diez años. Aquí tienes el cuaderno de bitácora completo.
Objetivo: mostrar la serie de incompatibilidades.
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 5) ] @startuml left to right direction title Migración Gradle 9 rectangle "1\nreportsDir" as A rectangle "2 ^^^^^ Syntax Error? (Assumed diagram type: component) @startuml left to right direction title Migración Gradle 9 rectangle "1\nreportsDir" as A rectangle "2 clase principal" as B rectangle "3\nsourceCompatibility" as C rectangle "4\nGroovy assert" as D A --> B B --> C C --> D @enduml
Objetivo: aislar los plugins rotos.
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 7) ] @startuml left to right direction title Plugins a corregir skinparam shadowing false skinparam roundcorner 18 rectangle "1 ^^^^^ Syntax Error? (Assumed diagram type: class) @startuml left to right direction title Plugins a corregir skinparam shadowing false skinparam roundcorner 18 rectangle "1 openApiGenerate" as A #FFF3E0 rectangle "2 gitProperties 2.5.7" as B #FFF3E0 rectangle "3 Versiones corregidas" as C #E3F2FD rectangle "4\nEnsamblar OK" as D #E8F5E9 A --> B B --> C C --> D @enduml
Objetivo: problema de tiempo de ejecución.
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 12) ] @startuml left to right direction title Docker 29 + Testcontainers skinparam shadowing false skinparam roundcorner 18 skinparam defaultFontSize 14 rectangle "1\nEnsambla OK" as A #E8F5E9 rectangle "2\nTests Cucumber FAIL" as B #FFEBEE rectangle "3\nTestcontainers 1.32\nrechazado" as C #FFF3E0 rectangle "4 ^^^^^ Syntax Error? (Assumed diagram type: component) @startuml left to right direction title Docker 29 + Testcontainers skinparam shadowing false skinparam roundcorner 18 skinparam defaultFontSize 14 rectangle "1\nEnsambla OK" as A #E8F5E9 rectangle "2\nTests Cucumber FAIL" as B #FFEBEE rectangle "3\nTestcontainers 1.32\nrechazado" as C #FFF3E0 rectangle "4 Actualización versiones es es es versiones" as D #E3F2FD rectangle "5\nCompilación final OK" as E #E8F5E9 A --> B B --> C C --> D D --> E @enduml
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 17) ] @startuml skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam defaultFontName Inter skinparam defaultFontSize 14 skinparam ArrowThickness 2 skinparam RoundCorner 18 title Roadmap de Estabilización — Gradle 9 hacia Build Final left to right direction rectangle "�⚙️ Migración Gradle 9" as A #E3F2FD rectangle "Plugins JHipster" as B #FFF3E0 rectangle "🐳 Docker 29 ^^^^^ Syntax Error? (Assumed diagram type: component) @startuml !option handwritten true skinparam backgroundColor #FEFEFE skinparam shadowing false skinparam defaultFontName Inter skinparam defaultFontSize 14 skinparam ArrowThickness 2 skinparam RoundCorner 18 title Roadmap de Estabilización — Gradle 9 hacia Build Final left to right direction rectangle "�⚙️ Migración Gradle 9" as A #E3F2FD rectangle "Plugins JHipster" as B #FFF3E0 rectangle "🐳 Docker 29 Testcontainers" as C #FCE4EC rectangle "�✅ Build Estable" 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
La escena: un proyecto JHipster, una compilación que ya no carga
El proyecto`edster`es una aplicación JHipster generada en 2024. El script de build raíz`build.gradle`estuvo estable durante meses. Entonces deciden subir a una versión:
Componente |
Versión objetivo |
Gradle |
9.4.1 |
Java |
21.0.11-tem (Eclipse Temurin) |
JHipster |
8.x (framework`tech.jhipster:jhipster-framework:8.11.0`) |
Spring Boot |
3.4.5 |
Kotlin |
2.3.0 |
Docker Engine |
29.4.1 (API 1.54) |
El cambio de versión de Java se realiza mediante SDKMAN:
sdk use java 21.0.11-tem
Primer lanzamiento:
$ ./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'
No podemos ni siquiera iniciar la carga del script de compilación. El problema está en`buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle`.
Error 1 : la variable fantasma `reportsDir
El script`jhipster.cucumber-conventions.gradle`contiene:
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"
// ...
}
La variable definida es`cucumberReportsDir`. La utilizada es`reportsDir`-- que no está definida en ninguna parte.
Corrección:`outputs.dir(cucumberReportsDir)`
Error 2: main = \"…\" ya no existe en Gradle 9
Reinicio inmediato:
Could not set unknown property 'main' for task ':consoleLauncherTest' of type org.gradle.api.tasks.JavaExec.
Gradle 9 elimina la propiedad`main`en beneficio de`mainClass`sobre las tareas`JavaExec`.
Corrección : mainClass = "org.junit.platform.console.ConsoleLauncher"
Error 3: sourceCompatibility ya no es una propiedad del proyecto
Nuevo error:
Could not set unknown property 'sourceCompatibility' for root project 'edster' of type org.gradle.api.Project.
El script raíz tenía:
sourceCompatibility=17
targetCompatibility=17
Gradle 9 elimina estas propiedades a nivel raíz. Deben vivir en un bloque.java.
Corrección:
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
Error 4 : la afirmación de Groovy a tres operandos
El antiguo script contenía :
assert System.properties["java.specification.version"] == "17" || "21" || "24"
En Groovy,"21" et "24"`son strings truthy. La expresión se resuelve en(… == "17") || true || true`, lo cual es siempre cierto — pero la sintaxis no es válida para la afirmación estricta deseada.
Corrección:
assert System.properties["java.specification.version"] in ["17", "21", "23", "24"]
Error 5 : la dependencia implícita compileKotlin → `openApiGenerate
El build progresa. Compilación de Kotlin exitosa. Luego:
Task ':compileKotlin' uses this output of task ':openApiGenerate'
without declaring an explicit or implicit dependency.
Gradle 9 rechaza las dependencias implícitas entre tareas. Desde`compileKotlin`lee las fuentes generadas por`openApiGenerate`, hay que declarar el enlace
Correcciónen`build.gradle`:
afterEvaluate {
tasks.named("compileKotlin").configure {
dependsOn(tasks.named("openApiGenerate"))
}
}
Le `afterEvaluate`es necesario porque`openApiGenerate`es una extensión (plugin OpenAPI Generator) y no una tarea directamente accesible durante la fase de configuración.
Error 6 : generateGitProperties falla en `FilterOutputStream.write()
La compilación Java pasa. Los recursos son procesados. Entonces:
> Task :generateGitProperties FAILED
No signature of method: java.io.FilterOutputStream.write() is applicable for argument types: (Integer) values: [103]
El plugin`gradle-git-properties`la versión 2.5.0 es incompatible con Java 21 / Gradle 9. Un error interno intenta llamar`write(int)`a través de Groovy en un flujo que lo cerró.
Correcciónen`gradle.properties`:
gitPropertiesPluginVersion=2.5.7
Finalmente: Testcontainers entra en escena
Hasta ahora, era "build script debugging" puro. Cada error era una incompatibilidad de Gradle 9 o Java 21 en los scripts de construcción. Después de las seis correcciones anteriores, el`./gradlew assemble`pasa con éxito.
Pero`./gradlew build`ejecuta también las pruebas, y las pruebas de Cucumber utilizan Testcontainers para iniciar un PostgreSQL efímero.
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 es incapaz de comunicarse con Docker. Dos estrategias diferentes (variables de entorno + socket Unix) fallan con el mismo error.
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 6) ] @startuml skinparam backgroundColor #FEFEFE skinparam handwritten false actor "Testcontainers\n1.20.6" as TC participant "docker-java ^^^^^ Syntax Error? (Assumed diagram type: sequence) @startuml skinparam backgroundColor #FEFEFE skinparam handwritten false actor "Testcontainers\n1.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
Fase 1: la pista docker-java
Primer reflejo : el cliente Docker Java utilizado por Testcontainers envía la versión API`1.32`en su handshake HTTP, pero Docker Engine 29.4.1 requiere un mínimo de`1.40`. El problema está a nivel del cliente, no del daemon.
Verificación de la versión de docker-java resuelta por 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, de Testcontainers 1.20.6. La última versión pública de docker-java es 3.5.1. Intentemos forzar la actualización de la versión mediante`resolutionStrategy`en el bloque`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")
}
}
}
}
Reinicio del build. Mismo error:`client version 1.32 is too old`.
Comprobación del caché de Gradle : los JARs han sido re-descargados correctamente, pero el error persiste. docker-java 3.5.1 sigue enviando`1.32`. Esta versión por lo tanto NO es suficiente para Docker Engine 29.4.1.
Acción de purificación: vaciar completamente el caché de Gradle, solo para estar seguro :
rm -rf ~/.gradle/caches
rm -rf /path/to/project/.gradle
Reinicio completo después de la purga. Mismo error.
Fase 2 : búsqueda web y camino de migas de pan GitHub
docker-java 3.5.1 es insuficiente. Solo queda buscar si alguien ha encontrado este problema exacto.
Búsqueda específica sobre los problemas testcontainers-java y 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
Primeros resultados inmediatos :
-
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.*
El rastro de migas es claro: Docker Engine 29.x ha elevado su versión mínima de API de`1.24`(antiguo defecto) a`1.40`. Testcontainers 1.20.x utiliza docker-java 3.4.x que negocia la versión de la API al enviar`1.32`. El daemon rechaza cortés pero firmemente.
En el issue #11491, un mantenedor responde:
__ Hola, tengo un proyecto en ejecución con la versión 2.0.3 en un runner de GH con motor 29.1 y funciona. ¿Podrían todos comprobar por favor que no haya ningún conflicto de versiones? Por favor, recuerda que todos los módulos estaban prefijados con`testcontainers-. Así que, empezando con la versión 2.x pasamos de`postgresql to testcontainers-postgresql (blank)
Esta respuesta contienedos informaciones críticas(empty)
-
Testcontainers 2.0.3+ resuelve el problema
-
Los módulos han sidorenombrados con el prefijo `testcontainers-
Fase 3 : la trampa de los artifacts renombrados
El renombramiento es brutal. En Testcontainers 2.x, ninguno de los antiguos nombres funciona bajo ese nombre :
Antiguo nombre (1.x) |
Nuevo nombre (2.x) |
|
|
|
|
|
|
|
sin cambios |
Primer intento: aplicar la BOM Testcontainers 2.0.5 y los nuevos nombres de artefactos en`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.
El BOM de testcontainers-bom no es suficiente.¿Por qué? Porque Spring Boot 3.4.5 expone su propio BOM de gestión de dependencias`spring-boot-dependencies`) quienganaen el BOM de Testcontainers en la resolución transitiva. Spring Boot establece`testcontainers` à 1.20.6, y por lo tanto todos los artefactos que no están explícitamente en el BOM de Spring Boot (como los nuevos`testcontainers-*) resuelven a los antiguos nombres`1.20.6-- que ya no existen en esta versión.
Al inspeccionar el`dependencies --configuration testRuntimeClasspath`, se encuentra :
+--- org.testcontainers:testcontainers -> 1.20.6 (*)
| \--- org.testcontainers:testcontainers:2.0.5 -> 1.20.6
Le →`indica la sustitución: Testcontainers 2.0.5 es solicitado, pero Spring Boot fuerza`1.20.6.
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 17) ]
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle\n(consumidor)" as BG #E3F2FD {
rectangle "solicitud\n`testcontainers-bom:2.0.5" as REQ
rectangle "solicitud\n`testcontainers-postgresql" as REQ2
}
component "Maven Central" as MC #FFF3E0
component "Declaraciones" as DEC #E8F5E9 {
rectangle "testcontainers-bom:2.0.5`\ncorrige 2.0.5" as BOM2
rectangle "spring-boot-dependencies:3.4.5`
^^^^^
Syntax Error? (Assumed diagram type: component)
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle\n(consumidor)" as BG #E3F2FD {
rectangle "solicitud\n`testcontainers-bom:2.0.5" as REQ
rectangle "solicitud\n`testcontainers-postgresql" as REQ2
}
component "Maven Central" as MC #FFF3E0
component "Declaraciones" as DEC #E8F5E9 {
rectangle "testcontainers-bom:2.0.5`\ncorrige 2.0.5" as BOM2
rectangle "spring-boot-dependencies:3.4.5`
fijo 1.20.6" as BOM1
}
component "Resolución Gradle" as RES #FFEBEE {
rectangle "Versión ganadora\n`testcontainers:1.20.6" as WIN #EF9A9A
}
BG --> DEC : "analiza los BOMs"
BOM2 --> RES : "propone 2.0.5"
BOM1 --> RES : "propone 1.20.6"
WIN -> MC : "descarga
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
Fase 4 : forzar la resolución
La estrategia se vuelve doble:
-
Forzar docker-java a su versión 3.7.1 (probada como compatible con Docker Engine 29)
-
Forzar Testcontainers core a 2.0.5 evitando el BOM de Spring Boot
Failed to generate image: PlantUML preprocessing failed: [From <input> (line 8) ]
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle
^^^^^
Syntax Error? (Assumed diagram type: class)
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle
(con resolutionStrategy)" as BG #E8F5E9 {
rectangle "BOM Testcontainers
`2.0.5" as BOM2
rectangle "resolutionStrategy\n.eachDependency" as RS #66BB6A
}
component "Conflicto resuelto" as RESOLVED #E3F2FD {
rectangle "docker-java\n`3.7.1` (forzado)" as DJ
rectangle "testcontainers
`2.0.5` (forzado)" as TC #81C784
rectangle "testcontainers-postgresql
`2.0.5" as TPG
rectangle "testcontainers-jdbc\n`2.0.5" as TJ
rectangle "testcontainers-junit-jupiter
`2.0.5" as TJJ
}
component "BOM Spring Boot\n(implícito, sobrescrito)" as BOM1 #FFEBEE
component "Maven Central" as MC #FFF3E0
BG --> RESOLVED : "BOM Testcontainers 2.0.5
pasa directamente"
RS -> DJ : "fuerza 3.7.1"
RS -> TC : "fuerza 2.0.5"
RESOLVED --> MC : "descarga los JARs
compatibles Docker 29"
BOM1 -[#gray,dashed]-> RESOLVED : "ignorado en testcontainers\net 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
Corrección finalen`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`es el único modo de sobrepasar el BOM`spring-boot-dependencies`gestionado por el plugin Spring Boot. La prueba de igualdad sobre`details.requested.name == "testcontainers"`es voluntariamente restrictivo: solo forzamos el módulo core. Los módulos`testcontainers-*`siguen el BOM de Testcontainers 2.0.5 que hemos declarado explícitamente.
Resultado final
$ ./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 finalmente logran crear sus contenedores PostgreSQL mediante Docker Engine 29.4.1. El error de negocio final (HTTP 500 en la prueba Cucumber) es un problema de aplicación completamente independiente del script de compilación.
Resumen de correcciones
| # | Archivo | Problema | Corrección |
|---|---|---|---|
1 |
|
Variable`reportsDir`inexistente |
|
2 |
|
`main`obsoleto Gradle 9 |
|
3 |
|
`sourceCompatibility`top-level prohibido Gradle 9 |
bloque`java { sourceCompatibility = JavaVersion.VERSION_21 }` |
4 |
|
Aserción Groovy sintácticamente inválida |
|
5 |
|
Dependencia implícita`compileKotlin` → |
|
6 |
|
`gitPropertiesPluginVersion`incompatible Java 21 |
|
7 |
Caché Gradle |
docker-java 3.5.1 probado, resuelto pero insuficiente |
Purga + paso docker-java 3.7.1 |
8 |
|
Testcontainers 1.20.6 incompatible con Docker Engine 29 |
Forzamiento Testcontainers 2.0.5 + prefijo`testcontainers-*`+ resoluciónStrategy frente a Spring Boot BOM |
Conclusión
Si tu build JHipster/Gradle falla después de una actualización a Gradle 9 + Docker Engine 29, los errores se dividen en dos categorías :
Errores de script de construcción(las 6 primeras): Gradle 9 elimina propiedades y sintaxis que funcionaban desde hace años (sourceCompatibility=, main =, reportsDir). Son migraciones mecánicas una vez que se conoce la nueva API.
Errores de tiempo de ejecución Docker(las 2 últimas): Docker Engine 29.4.1 descarta los clientes API < 1.40. Testcontainers 1.20.6 (impuesto por Spring Boot 3.4.5) utiliza docker-java 3.4.1 que envía 1.32. Solo Testcontainers 2.0.5 + docker-java 3.7.1 resuelve el problema, con el renombrado de los artefactos y el forzado de la versión via`resolutionStrategy.eachDependency`como única forma de evitar el BOM implícito de Spring Boot.
El script de build de Gradle en la raíz del proyecto ahora está limpio.`./gradlew build`pasa a Java 21, Gradle 9.4.1, Spring Boot 3.4.5 y Docker Engine 29.4.1.