وقتی Gradle 9، Docker 29 و Testcontainers خشمگین میشوند: اشکالزدایی یک ساخت JHipster
منتشر شده در 25 April 2026
- صحنه: یک پروژه JHipster، یک build که بارگذاری نمیشود
- خطا 1 : متغیر وهمی `reportsDir
- خطا 2:
main = "…"در Gradle 9 موجود نیست. - خطا ۳:
sourceCompatibilityدیگر یک ویژگی پروژه نیست - خطای ۴: ادعای سهعمله Groovy
- خطا 5 : وابستگی ضمنی
compileKotlin→ `openApiGenerate - خطای 6 :
generateGitPropertiesدرFilterOutputStream.write()شکست میخورد - در نهایت: Testcontainers به صحنه میآید
- مرحله 1 : مسیر docker-java
- فاز 2: جستجوی وب و مسیرbreadcrumb GitHub
- مرحله ۳: جالبه artefactهای باز نامگذاری
- مرحله ۴ : اجبار به حل
- نتیجه نهایی
- جدول خلاصه تصحیحها
- نتیجه
شما قبلا این لحظه را تجربه کردهاید که یک ساده`./gradlew build`که صد بار در روز اجرا میکنید ناگهان به دلیل خطایی که تا به حال نبوده، قطع میشود؟ آن را به ۱۱ خطای متوالی ضرب کنید، یک مقدار مناسب ناسازگاری Docker Engine ۲۹ اضافه کنید، Gradle ۹ را بریزید که APIهایی را که از ده سال قبل استفاده میکردیم محو میکند. این لاگ کامل است.
هدف : نمایش سری ناسازگاریها
@startuml left to right direction title مهاجرت Gradle 9 rectangle "1 دایرکتوری گزارشات" as A rectangle "2 mainClass" as B rectangle "3 سازگاری منبع" as C rectangle "4\nتأیید Groovy" as D A --> B B --> C C --> D @enduml
هدف: افزونههای خراب را جدا کنید.
@startuml left to right direction title افزونههای مورد اصلاح skinparam shadowing false skinparam roundcorner 18 rectangle "1\nopenApiGenerate" as A #FFF3E0 rectangle "2\ngitProperties\n2.5.7" as B #FFF3E0 rectangle "3 نسخهها اصلاحشده" as C #E3F2FD rectangle "4 تجميع OK" as D #E8F5E9 A --> B B --> C C --> D @enduml
هدف : مشکل زمان اجرا.
@startuml left to right direction title Docker 29 + Testcontainers skinparam shadowing false skinparam roundcorner 18 skinparam defaultFontSize 14 rectangle "1 ترکیب کن OK" as A #E8F5E9 rectangle "2 تستهای Cucumber ناموفق" as B #FFEBEE rectangle "3\nTestcontainers 1.32\nرد شده" as C #FFF3E0 rectangle "4 ارتقا نسخها" as D #E3F2FD rectangle "5 ساخت نهایی موفق" 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 نقشه مسیر تثبیت — Gradle 9 به ساخت نهایی left to right direction rectangle "⚙️ Migration Gradle ۹" as A #E3F2FD rectangle "🧩 پلاگینهای JHipster" as B #FFF3E0 rectangle "🐳 Docker 29\nTestcontainers" as C #FCE4EC rectangle "✅ ساخت پایدار" 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
صحنه: یک پروژه JHipster، یک build که بارگذاری نمیشود
پروژه`edster`این یک برنامه JHipster است که در سال 2024 تولید شده است. script ساخت ریشه`build.gradle`ماهها ثابت ماند. سپس تصمیم میگیریم تا به نسخه جدید ارتقا دهیم:
بخش |
نسخه هدف |
Gradle |
9.4.1 |
جاوا |
21.0.11-tem (Eclipse Temurin) |
JHipster |
8.x (چارچوب`tech.jhipster:jhipster-framework:8.11.0`) |
اسپرینگ بوت |
3.4.5 |
کاتلین |
2.3.0 |
موتور داکر |
29.4.1 (API 1.54) |
تغییر نسخه Java از طریق SDKMAN :
sdk use java 21.0.11-tem
اولین راهاندازی :
$ ./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'
ما حتی از بارگذاری اسکریپت build هم خارج نمیشویم. مشکل در`buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle`.
خطا 1 : متغیر وهمی `reportsDir
اسکریپت`jhipster.cucumber-conventions.gradle`شامل :
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"
// ...
}
متغیر تعریفشده است`cucumberReportsDir`. کی که به کار رفته است`reportsDir`-- که در هیچ جای تعریف نشده است.
تصحیح(Empty)outputs.dir(cucumberReportsDir)
خطا 2: main = "…" در Gradle 9 موجود نیست.
بازتاب فوری
Could not set unknown property 'main' for task ':consoleLauncherTest' of type org.gradle.api.tasks.JavaExec.
Gradle 9 ویژگی را حذف میکند`main`به نفع`mainClass`روی وظایف`JavaExec`.
تصحیح:`mainClass = "org.junit.platform.console.ConsoleLauncher"`
خطا ۳: sourceCompatibility دیگر یک ویژگی پروژه نیست
خطای جدید :
Could not set unknown property 'sourceCompatibility' for root project 'edster' of type org.gradle.api.Project.
اسکریپت ریشه داشت :
sourceCompatibility=17
targetCompatibility=17
Gradle 9 این ویژگیها را در سطح ریشه حذف میکند. باید در یک بلوک باشند.java.
تصحیح :
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
خطای ۴: ادعای سهعمله Groovy
اسکریپت قدیمی شامل بود:
assert System.properties["java.specification.version"] == "17" || "21" || "24"
در گرووی,"21" et "24"`همراه با رشتههای truthy هستند. عبارت به(… == "17") || true || true`، که همیشه درست است — اما سینتکس برای ادعای دقیق مورد نظر معتبر نیست.
تصحیح :
assert System.properties["java.specification.version"] in ["17", "21", "23", "24"]
خطا 5 : وابستگی ضمنی compileKotlin → `openApiGenerate
ساخت پیش میرود. کامپایل کاتلین موفق بود. سپس :
Task ':compileKotlin' uses this output of task ':openApiGenerate'
without declaring an explicit or implicit dependency.
Gradle 9 وابستگیهای ضمنی بین کارها را رد میکند. چون`compileKotlin`منابع تولید شده توسط را میخواند`openApiGenerate`، باید لینک را اعلام کرد.
تصحیحدر`build.gradle`:
afterEvaluate {
tasks.named("compileKotlin").configure {
dependsOn(tasks.named("openApiGenerate"))
}
}
Le `afterEvaluate`ضروری است زیرا`openApiGenerate`یک افزونه (پلاگین OpenAPI Generator) است و نه کاری که مستقیماً در مرحله پیکربندی قابل دسترسی است.
خطای 6 : generateGitProperties در FilterOutputStream.write() شکست میخورد
کامپایل جاوا موفق میشود. منابع پردازش میشوند. سپس :
> Task :generateGitProperties FAILED
No signature of method: java.io.FilterOutputStream.write() is applicable for argument types: (Integer) values: [103]
افزونه`gradle-git-properties`نسخه 2.5.0 با Java 21 / Gradle 9 ناسازگار است. یک bug داخلی سعی میگیرد صدا بزند`write(int)`از طریق Groovy روی یک جریان که آن را بسته است
تصحیحدر`gradle.properties`:
gitPropertiesPluginVersion=2.5.7
در نهایت: Testcontainers به صحنه میآید
تا به حال، صرفاً "build script debugging" بود. هر خطا یک ناسازگاری Gradle 9 یا Java 21 در اسکریپتهای ساخت بود. پس از شش اصلاح فوق،`./gradlew assemble`موفقانه میگذرد.
اما`./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 نمیتواند با Docker ارتباط برقرار کند. دو استراتژی متفاوت (متغیرهای محیط + سوکت یونیکس) هر دو به یک خطای مشابه برمیخورند.
@startuml skinparam backgroundColor #FEFEFE skinparam handwritten false actor "Testcontainers 1.20.6" as TC participant "docker-java\n3.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
مرحله 1 : مسیر docker-java
اولین واکنش: کلاینت Docker Java استفاده شده توسط Testcontainers نسخه API را ارسال میکند`1.32`در handshake HTTP آن، اما Docker Engine 29.4.1 حداقل را میطلبد`1.40`.مشکل در سمت کلاینت است، نه در daemon.
بررسی نسخه 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، از Testcontainers 1.20.6. آخرین نسخه عمومی docker-java 3.5.1 است. سعی میکنیم تا ارتقای نسخه را از طریق`resolutionStrategy`در بلوک`configurations` de build.gradle(Note: No output)
configurations {
all {
resolutionStrategy.eachDependency { details ->
if (details.requested.group == "com.github.docker-java"
&& details.requested.name.startsWith("docker-java")) {
details.useVersion("3.5.1")
}
}
}
}
دوباره ساخت. همان خطا:`client version 1.32 is too old`.
بررسی کش Gradle: JARها به درستی دوباره دانلود شدند، اما خطا ادامه دارد. docker-java 3.5.1 همچنان ارسال میکند`1.32`. این نسخه بنابراین برای Docker Engine 29.4.1 کافی نیست.
عمل تطهیر: خالی کردن کامل کش Gradle، فقط برای اطمینان :
rm -rf ~/.gradle/caches
rm -rf /path/to/project/.gradle
بارگیری کامل پس از پاکسازی. همان خطا
فاز 2: جستجوی وب و مسیرbreadcrumb GitHub
docker-java 3.5.1 کافی نیست. کاری که مانده فقط جستجو است تا ببینیم آیا کسی این مشکل دقیق را تجربه کرده است.
جستجوی هدفمند بر روی issues testcontainers-java و 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
نتایج اولیه فوری:
-
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 از docker-java 3.4.x استفاده میکند که با ارسال negotiated API version`1.32`. daemon به صورت مهذب اما قطعی رد میکند.
در Issue #11491، یک نگهدارنده پاسخ میدهد:
_ سلام، من پروژهای دارم که با نسخه 2.0.3 روی یک رانر GH با موتور 29.1 در حال اجرا است و کار میکند. لطفاً همگی بررسی کنید که هیچ نسخهای در تعارض نباشد. لطفاً به یاد داشته باشید که تمام ماژولها با … پیشوند گذاشته شدهاند`testcontainers-. بنابراین، از نسخهٔ ۲.x شروع کردیم که ما از`postgresql to testcontainers-postgresql _
این پاسخ شاملdo etelaat hayati :
-
Testcontainers 2.0.3+ مشکل را حل میکند
-
ماژولها بودندتغییر نام شده با پیشوند `testcontainers-
مرحله ۳: جالبه artefactهای باز نامگذاری
تغییر نام سخت است. در Testcontainers 2.x، هیچیک از نامهای قدیمی تحت این نام کار نمیکنند:
نام قدیمی (1.x) |
نام جدید (2.x) |
|
|
|
|
|
|
|
بدون تغییر |
تلاش اول: اعمال BOM Testcontainers 2.0.5 و نامهایartifact جدید در`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.
بوم testcontainers-bom کافی نیست.چرا؟ چون Spring Boot 3.4.5 BOM خود مدیریت وابستگیها را نشان میدهد (spring-boot-dependencies) کهاو برنده میشوددر BOM Testcontainers در حل transitive. Spring Boot رفع میکند`testcontainers` à 1.20.6, و بنابراین تمام آرتیفکتهای که بهصورت صریح در BOM Spring Boot نیستند (مثل جدید`testcontainers-*) به سمت نامهای قدیمی حل میشوند`1.20.6-- که در این نسخه وجود ندارند.
در بررسی`dependencies --configuration testRuntimeClasspath`, به شرح زیر:
+--- org.testcontainers:testcontainers -> 1.20.6 (*)
| \--- org.testcontainers:testcontainers:2.0.5 -> 1.20.6
Le →`جایگزینی را نشان میدهد: Testcontainers 2.0.5 مطلوب است، اما Spring Boot اجبار میکند`1.20.6.
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle
(مصرفکننده)" as BG #E3F2FD {
rectangle "درخواست
`testcontainers-bom:2.0.5" as REQ
rectangle "درخواست
`testcontainers-postgresql" as REQ2
}
component "مخزن مرکزی Maven" as MC #FFF3E0
component "بیانیهها" as DEC #E8F5E9 {
rectangle "`testcontainers-bom:2.0.5`\nfixe 2.0.5" as BOM2
rectangle "spring-boot-dependencies:3.4.5`
ثابت 1.20.6" as BOM1
}
component "حل Gradle" as RES #FFEBEE {
rectangle "نسخهٔ برنده
`testcontainers:1.20.6" as WIN #EF9A9A
}
BG --> DEC : "BOMها را تحلیل میکند"
BOM2 --> RES : "پیشنهاد 2.0.5"
BOM1 --> RES : "پیشنهاد 1.20.6"
WIN -> MC : "دانلود
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
مرحله ۴ : اجبار به حل
استراتژی دوگانه میشود :
-
دocker-java را به نسخه ۳.۷٫۱ اجبار کنید (که بهعنوان سازگار با Docker Engine ۲۹ آزمایش شده)
-
Testcontainers core را به 2.0.5 اجبار کنید با دور زدن BOM Spring Boot
@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle
left to right direction
component "build.gradle
(با resolutionStrategy)" as BG #E8F5E9 {
rectangle "بوم Testcontainers
`2.0.5" as BOM2
rectangle "resolutionStrategy\n.eachDependency" as RS #66BB6A
}
component "اختلاف حل شد" as RESOLVED #E3F2FD {
rectangle "docker-java
`3.7.1` (اجباری)" as DJ
rectangle "testcontainers\n`2.0.5` (مجبور)" as TC #81C784
rectangle "testcontainers-postgresql\n`2.0.5" as TPG
rectangle "testcontainers-jdbc
`2.0.5" as TJ
rectangle "testcontainers-junit-jupiter
`2.0.5" as TJJ
}
component "BOM Spring Boot
(ضمنی, بازنویسی)" as BOM1 #FFEBEE
component "موان سنترال" as MC #FFF3E0
BG --> RESOLVED : "BOM Testcontainers 2.0.5
مستقیماً می‑گذراند"
RS -> DJ : "نیرو 3.7.1"
RS -> TC : "نیروی 2.0.5"
RESOLVED --> MC : "دانلود کن فایلهای JAR\nسازگار با Docker 29"
BOM1 -[#gray,dashed]-> RESOLVED : "نادیده گرفته شده در 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
تصحیح نهاییدر`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`مدیریتشده توسط پلاگین Spring Boot. تست مساوی بودن بر`details.requested.name == "testcontainers"`به صورت عمدی محدود است: ما فقط ماژول core را اجبار میکنیم. ماژولها`testcontainers-*`که بوم Testcontainers 2.0.5 را که صریحاً اعلام کردهایم، دنبال میکنند.
نتیجه نهایی
$ ./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 در نهایت موفق به ساخت کانترینرهای PostgreSQL خود از طریق Docker Engine 29.4.1 میشوند. خطای کاری نهایی (HTTP 500 در تست Cucumber) یک مشکل برنامهای است که بهطور کامل مستقل از اسکریپت ساخت است.
جدول خلاصه تصحیحها
| # | فایل | مشکل | تصحیح |
|---|---|---|---|
1 |
|
متغیر`reportsDir`موجود نیست |
|
2 |
|
`main`Gradle 9 منسوخ |
|
3 |
|
`sourceCompatibility`top-level ممنوع Gradle ۹ |
بلوک`java { sourceCompatibility = JavaVersion.VERSION_21 }` |
4 |
|
سنتکس assertion Groovy نامعتبر |
|
5 |
|
وابستگی ضمنی`compileKotlin`→ |
|
6 |
|
`gitPropertiesPluginVersion`جاوا ۲۱ ناصازگار |
|
7 |
کش Gradle |
docker-java 3.5.1 آزمایش شده، حلشده اما ناکافی |
حذف + عبور docker-java 3.7.1 |
8 |
|
Testcontainers 1.20.6 ناسازگار با موتور Docker 29 |
اجبار Testcontainers 2.0.5 + پیشوند`testcontainers-*`+ resolutionStrategy مقابل Spring Boot BOM |
نتیجه
اگر build JHipster/Gradle شما پس از ارتقا به Gradle 9 + Docker Engine 29 خراب شود، خطاها به دو دسته تقسیم میشوند:
خطاهای اسکریپت ساخت(۶ اول) : Gradle 9 ویژگیها و سینتکسهایی را حذف میکند که سالها کار میکردند (sourceCompatibility=, main =, reportsDir). اینها مهاجرات مکانیکی هستند پس از آشنایی با API جدید.
خطاهای زمان اجرا Docker(دو آخرین) : Docker Engine 29.4.1 عملای API < 1.40 را رد میکند. Testcontainers 1.20.6 (بهاجبار Spring Boot 3.4.5) از docker-java 3.4.1 استفاده میکند که 1.32 را ارسال میکند. تنها Testcontainers 2.0.5 + docker-java 3.7.1 مشکل را حل میکند، با تغییر نام artifactها و اجبار نسخه از طریق`resolutionStrategy.eachDependency`به عنوان تنها راه برای دور زدن BOM ضمنی Spring Boot.
اسکریپت ساخت Gradle در ریشه پروژه اکنون پاک است.`./gradlew build`بر Java 21، Gradle 9.4.1، Spring Boot 3.4.5 و Docker Engine 29.4.1 میگذرد