okuma süresi : 15 minutes

Daha önce şu anı yaşadınız: basit bir`./gradlew build`Günde yüz kez çalıştırdığınız şey aniden hiç görülmemiş bir hatayla çökmeye mi başlıyor? Bunu on bir ardışık hatayla çarpın, üzerine bolca Docker Engine 29 uyumsuzluğu ekleyin, on yıldır kullandığımız API’leri ortadan kaldıran Gradle 9’dan da bir tutam serpin. İşte eksiksiz seyir defteri.


Diyagram 1 — Gradle 9’a Geçiş (hata zinciri)

Amaç: uyumsuzluklar dizisini göstermek.

👉 Net, Gradle odaklı.
@startuml
left to right direction
title Geçiş Gradle 9
rectangle "1\nreportsDir" 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

Diyagram 2 — Uyumsuz JHipster Eklentileri

Amaç: bozuk eklentileri izole etmek.

👉 Bağımlılıkların net görünümü.
@startuml
left to right direction
title Düzeltilecek eklentiler
skinparam shadowing false
skinparam roundcorner 18

rectangle "1\nopenApiGenerate" as A #FFF3E0
rectangle "2\ngitProperties\n2.5.7" as B #FFF3E0
rectangle "3\nVersions\ndüzeltilmiş" as C #E3F2FD
rectangle "4\nAssemble Tamam" as D #E8F5E9

A --> B
B --> C
C --> D

@enduml

Diyagram 3 — Docker 29 / Testcontainers

Amaç: çalışma zamanı sorunu.

👉 Runtime’ın net hikayesi.
@startuml
left to right direction
title Docker 29 + Testcontainers

skinparam shadowing false
skinparam roundcorner 18
skinparam defaultFontSize 14

rectangle "1\nAssemble OK" as A #E8F5E9
rectangle "2\nTests Cucumber FAIL" as B #FFEBEE
rectangle "3\nTestcontainers 1.32\nreddedildi" as C #FFF3E0
rectangle "4\nUpgrade\nsürümler" as D #E3F2FD
rectangle "5\nBuild son OK" as E #E8F5E9

A --> B
B --> C
C --> D
D --> E

@enduml

bunu zihinde canlandırmanın başka bir yolu
@startuml

!option handwritten true

skinparam backgroundColor #FEFEFE
skinparam shadowing false
skinparam defaultFontName Inter
skinparam defaultFontSize 14
skinparam ArrowThickness 2
skinparam RoundCorner 18

title Stabilizasyon Yol Haritası — Gradle 9'dan Nihai Derlemeye
left to right direction

rectangle "⚙️ Geçiş Gradle 9" as A #E3F2FD
rectangle "🧩 Eklentiler JHipster" as B #FFF3E0
rectangle "🐳 Docker 29\nTestcontainers" as C #FCE4EC
rectangle "✅ Kararlı Derleme" as D #E8F5E9

note bottom of A
reportsDir
mainClass
sourceCompatibility
Groovy doğrulaması
end note

note bottom of B
openApiGenerate
gitProperties
hizalanmış sürümler
end note

note bottom of C
Cucumber testleri başarısız
Docker görseli reddedildi
kütüphaneleri yükselt
end note

A -[#42A5F5]-> B : düzeltmeler build.gradle
B -[#FB8C00]-> C : monte et OK
C -[#E53935]-> D : onarılmış testler

@enduml

Sahne: bir JHipster projesi, artık yüklenmeyen bir build

Proje`edster`2024’te oluşturulmuş bir JHipster uygulamasıdır. Kök derleme betiği`build.gradle`aylar boyunca sabit kaldı. Sonra sürümü yükseltmeye karar veriyoruz:

Bileşen Hedef sürüm

Gradle

9.4.1

Java

21.0.11-tem (Eclipse Temurin)

JHipster

8.x (çerçeve`tech.jhipster:jhipster-framework:8.11.0`)

Spring Boot

3.4.5

Kotlin

2.3.0

Docker Engine

29.4.1 (API 1.54)

Java sürüm değişikliği SDKMAN üzerinden yapılır:

sdk use java 21.0.11-tem

İlk başlatma:

$ ./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 script’in yüklenmesini bile geçemiyoruz. Sorun …​ içinde`buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle`.

Hata 1: hayalet değişken `reportsDir

Senaryo`jhipster.cucumber-conventions.gradle`içerir:

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"
    // ...
}

Tanımlanan değişken`cucumberReportsDir`. Kullanılan`reportsDir`-- hiçbir yerde tanımlanmayan.

Düzeltme:`outputs.dir(cucumberReportsDir)`

Hata 2: main = "…​" artık Gradle 9’da yok

Anında hatırlatma:

Could not set unknown property 'main' for task ':consoleLauncherTest' of type org.gradle.api.tasks.JavaExec.

Gradle 9 özelliği kaldırıyor`main`lehine`mainClass`görevler üzerine`JavaExec`.

Düzeltme:`mainClass = "org.junit.platform.console.ConsoleLauncher"`

Hata 3: sourceCompatibility artık bir proje özelliği değil

Yeni hata:

Could not set unknown property 'sourceCompatibility' for root project 'edster' of type org.gradle.api.Project.

Kök betiğin şunları vardı:

sourceCompatibility=17
targetCompatibility=17

Gradle 9 bu özellikleri kök düzeyinde kaldırır. Bunlar bir blok içinde yer almalıdır.java.

Düzeltme :

java {
    sourceCompatibility = JavaVersion.VERSION_21
    targetCompatibility = JavaVersion.VERSION_21
}

Hata 4: üç işlenenli Groovy assertion’ı

Eski script şunları içeriyordu:

assert System.properties["java.specification.version"] == "17" || "21" || "24"

Groovy’de,"21" et "24"`truthy string’lerdir. İfade şuna çözümlenir(…​ == "17") || true || true`, bu her zaman doğrudur — ancak sözdizimi istenen katı doğrulama için geçersizdir.

Düzeltme :

assert System.properties["java.specification.version"] in ["17", "21", "23", "24"]

Hata 5: örtük bağımlılık compileKotlin → `openApiGenerate

Derleme ilerliyor. Kotlin derlemesi başarılı. Sonra:

Task ':compileKotlin' uses this output of task ':openApiGenerate'
without declaring an explicit or implicit dependency.

Gradle 9, görevler arasındaki örtük bağımlılıkları reddeder. Madem ki`compileKotlin`tarafından oluşturulan kaynakları okur`openApiGenerate`, bağlantının bildirilmesi gerekir.

Düzeltmeiçinde`build.gradle` :

afterEvaluate {
    tasks.named("compileKotlin").configure {
        dependsOn(tasks.named("openApiGenerate"))
    }
}

Le `afterEvaluate`gereklidir çünkü`openApiGenerate`bir uzantıdır (OpenAPI Generator eklentisi) ve yapılandırma aşamasında doğrudan erişilebilen bir görev değildir.

Hata 6: generateGitProperties, FilterOutputStream.write() üzerinde çöküyor

Java derlemesi geçer. Kaynaklar işlenir. Sonra:

> Task :generateGitProperties FAILED

No signature of method: java.io.FilterOutputStream.write() is applicable for argument types: (Integer) values: [103]

Eklenti`gradle-git-properties`version 2.5.0, Java 21 / Gradle 9 ile uyumlu değil. Dahili bir hata çağırmaya çalışıyor`write(int)`Groovy aracılığıyla onu kapatan bir akış üzerinde.

Düzeltmeiçinde`gradle.properties` :

gitPropertiesPluginVersion=2.5.7

Sonunda: Testcontainers sahneye çıkıyor

Buraya kadar, bu saf "build script debugging" idi. Her hata, build script’lerinde Gradle 9 veya Java 21 uyumsuzluğuydu. Yukarıdaki altı düzeltmeden sonra,`./gradlew assemble`başarıyla geçer.

Ama`./gradlew build`testleri de çalıştırır ve Cucumber testleri geçici bir PostgreSQL başlatmak için Testcontainers kullanır.

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 ile iletişim kuramıyor. İki farklı strateji (ortam değişkenleri + Unix soketi) aynı hatayla başarısız oluyor.

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false

actor "Testcontainers\n1.20.6" as TC
participant "docker-java\n3.4.1" as DJ
participant "Docker Engine\n29.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

Aşama 1: docker-java yolu

İlk refleks: Testcontainers tarafından kullanılan Java Docker istemcisi API sürümünü gönderir`1.32`HTTP el sıkışmasında, ancak Docker Engine 29.4.1 en az …​ gerektirir`1.40`. Sorun istemci tarafında, daemon tarafında değil.

Testcontainers 1.20.6 tarafından çözümlenen docker-java sürümünün doğrulanması:

$ ./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’dan kalma. docker-java’nın en son herkese açık sürümü 3.5.1. Sürüm yükseltmesini şu yolla zorlamayı deneyelim:`resolutionStrategy`blokta`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")
            }
        }
    }
}

Build yeniden başlatıldı. Aynı hata:`client version 1.32 is too old`.

Gradle önbelleği kontrolü: JAR’lar yeniden indirildi, ancak hata devam ediyor. docker-java 3.5.1 hâlâ gönderiyor`1.32`. Bu sürüm bu nedenle Docker Engine 29.4.1 için YETERLİ DEĞİLDİR.

Arındırma eylemi: Gradle önbelleğini tamamen temizlemek, sadece emin olmak için:

rm -rf ~/.gradle/caches
rm -rf /path/to/project/.gradle

Temizleme sonrası tam yeniden başlatma. Aynı hata.

2. Aşama: web araması ve GitHub gezinti yolu

docker-java 3.5.1 yetersiz. Geriye sadece birinin bu tam sorunla karşılaşıp karşılaşmadığını araştırmak kalıyor.

testcontainers-java ve docker-java sorunlarına yönelik hedefli arama:

site:github.com/testcontainers "client version 1.32 is too old"
site:github.com/docker-java "client version 1.32" Docker Engine 29

İlk anlık sonuçlar:

Gezinti yolu açık: Docker Engine 29.x, minimum API sürümünü şu değerden yükseltti`1.24`(eski kusur) -a`1.40`. Testcontainers 1.20.x, API sürümünü göndererek müzakere eden docker-java 3.4.x’i kullanır`1.32`. Daemon kibarca ama kararlı bir şekilde reddeder.

#11491 numaralı issue’da bir bakımcı şöyle yanıtlıyor:

_ Merhaba, GH runner’da engine 29.1 ile 2.0.3 sürümüyle çalışan bir projem var ve çalışıyor. Hepiniz lütfen bir sürüm çakışması olmadığını kontrol edebilir misiniz? Lütfen, tüm modüllerin şununla ön eklenmiş olduğunu unutmayın`testcontainers-. Yani, 2.x sürümünden başlayarak …​'dan …​'a geçtik`postgresql to testcontainers-postgresql _

Bu yanıt içeririki kritik bilgi:

  1. Testcontainers 2.0.3+ sorunu çözer

  2. Modüller …​ olmuşturtestcontainers-` önekiyle yeniden adlandırılmış

Aşama 3: Yeniden adlandırılmış artefaktların tuzağı

Yeniden adlandırma sert. Testcontainers 2.x’te, eski adlardan hiçbiri artık bu ad altında çalışmıyor:

Eski ad (1.x) Yeni ad (2.x)

org.testcontainers:postgresql

org.testcontainers:testcontainers-postgresql

org.testcontainers:jdbc

org.testcontainers:testcontainers-jdbc

org.testcontainers:junit-jupiter

org.testcontainers:testcontainers-junit-jupiter

org.testcontainers:testcontainers

değişmemiş

İlk deneme: Testcontainers 2.0.5 BOM’unu ve yeni artifact adlarını içine uygulamak`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’un BOM’u yeterli değil.Neden? Çünkü Spring Boot 3.4.5 kendi bağımlılık yönetimi BOM’unu sunar (spring-boot-dependencies) kikazanırgeçişli çözümlemede Testcontainers BOM’u üzerinde. Spring Boot sabitler`testcontainers` à 1.20.6, ve dolayısıyla Spring Boot BOM’unda açıkça yer almayan tüm artifact’lar (yeni olanlar gibi`testcontainers-*) eski adlara çözümlenir`1.20.6-- bu sürümde artık bulunmayan.

İncelerken`dependencies --configuration testRuntimeClasspath`, bulunur:

+--- org.testcontainers:testcontainers -> 1.20.6 (*)
|    \--- org.testcontainers:testcontainers:2.0.5 -> 1.20.6

Le →`yerine koymayı belirtir: Testcontainers 2.0.5 talep ediliyor, ancak Spring Boot zorluyor`1.20.6.

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component "build.gradle\n(consommateur)" as BG #E3F2FD {
    rectangle "demande\n`testcontainers-bom:2.0.5`" as REQ
    rectangle "demande\n`testcontainers-postgresql`" as REQ2
}

component "Maven Central" as MC #FFF3E0

component "Déclarations" as DEC #E8F5E9 {
    rectangle "`testcontainers-bom:2.0.5`\nfixe 2.0.5" as BOM2
    rectangle "`spring-boot-dependencies:3.4.5`\nfixe 1.20.6" as BOM1
}

component "Résolution Gradle" as RES #FFEBEE {
    rectangle "Version gagnante\n`testcontainers:1.20.6`" as WIN #EF9A9A
}

BG --> DEC : "analyse les BOMs"
BOM2 --> RES : "propose 2.0.5"
BOM1 --> RES : "propose 1.20.6"
WIN -> MC : "télécharge\ntestcontainers: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

Aşama 4: çözünürlüğü zorla

Strateji ikili hale gelir:

  1. docker-java’yı 3.7.1 sürümüne zorlayın (Docker Engine 29 ile uyumlu olduğu test edilmiştir)

  2. Spring Boot BOM’unu atlatarak Testcontainers core’u 2.0.5’e zorla

@startuml
skinparam backgroundColor #FEFEFE
skinparam handwritten false
skinparam componentStyle rectangle

left to right direction

component "build.gradle\n(avec resolutionStrategy)" as BG #E8F5E9 {
    rectangle "BOM Testcontainers\n`2.0.5`" as BOM2
    rectangle "`resolutionStrategy\n.eachDependency`" as RS #66BB6A
}

component "Conflit résolu" as RESOLVED #E3F2FD {
    rectangle "docker-java\n`3.7.1` (forcé)" as DJ
    rectangle "testcontainers\n`2.0.5` (forcé)" as TC #81C784
    rectangle "testcontainers-postgresql\n`2.0.5`" as TPG
    rectangle "testcontainers-jdbc\n`2.0.5`" as TJ
    rectangle "testcontainers-junit-jupiter\n`2.0.5`" as TJJ
}

component "BOM Spring Boot\n(implicite, outrepassé)" as BOM1 #FFEBEE

component "Maven Central" as MC #FFF3E0

BG --> RESOLVED : "BOM Testcontainers 2.0.5\npasse directement"
RS -> DJ : "force 3.7.1"
RS -> TC : "force 2.0.5"
RESOLVED --> MC : "télécharge les JARs\ncompatibles Docker 29"
BOM1 -[#gray,dashed]-> RESOLVED : "ignoré sur 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

Son düzeltmeiçinde`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’u aşmanın tek yoludur`spring-boot-dependencies`Spring Boot eklentisi tarafından yönetilir. Eşitlik testi üzerinde`details.requested.name == "testcontainers"`kasıtlı olarak kısıtlayıcıdır: yalnızca core modülünü zorluyoruz. Modüller`testcontainers-*`açıkça bildirdiğimiz Testcontainers 2.0.5 BOM’unu izlerler.

Nihai sonuç

$ ./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 nihayet PostgreSQL konteynerlerini Docker Engine 29.4.1 üzerinden oluşturmayı başarıyor. Nihai iş hatası (Cucumber testindeki HTTP 500), build script’inden tamamen bağımsız bir uygulama sorunudur.

Düzeltmelerin özet tablosu

# Dosya Sorun Düzeltme

1

buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle

Değişken`reportsDir`mevcut olmayan

outputs.dir(cucumberReportsDir)

2

buildSrc/src/main/groovy/jhipster.cucumber-conventions.gradle

`main`kullanımdan kaldırılmış Gradle 9

mainClass

3

build.gradle

`sourceCompatibility`Gradle 9’da top-level yasak

Blok`java { sourceCompatibility = JavaVersion.VERSION_21 }`

4

build.gradle

Sözdizimsel olarak geçersiz Groovy assertion’ı

assert …​ in ["17", "21", "23", "24"]

5

build.gradle

Örtük bağımlılık`compileKotlin`→openApiGenerate

afterEvaluate { tasks.named("compileKotlin").dependsOn("openApiGenerate") }

6

gradle.properties

`gitPropertiesPluginVersion`Java 21 ile uyumsuz

2.5.7

7

Gradle önbelleği

docker-java 3.5.1 test edildi, çözüldü ancak yetersiz

Temizleme + docker-java 3.7.1’e geçiş

8

build.gradle

Testcontainers 1.20.6, Docker Engine 29 ile uyumsuz

Testcontainers 2.0.5 + önek zorlaması`testcontainers-*`+ resolutionStrategy, Spring Boot BOM’a karşı

Sonuç

JHipster/Gradle derlemeniz Gradle 9 + Docker Engine 29 sürümüne yükseltildikten sonra çökerse, hatalar iki kategoriye ayrılır:

Derleme betiği hataları(ilk 6’sı) : Gradle 9, yıllardır çalışan özellikleri ve sözdizimlerini kaldırıyor (sourceCompatibility=, main =, reportsDir). Yeni API’yi öğrendikten sonra bunlar mekanik geçişlerdir.

Docker çalışma zamanı hataları(son 2): Docker Engine 29.4.1, API < 1.40 istemcilerini dışlar. Spring Boot 3.4.5 tarafından zorunlu kılınan Testcontainers 1.20.6, 1.32 gönderen docker-java 3.4.1’i kullanır. Sorunu yalnızca Testcontainers 2.0.5 + docker-java 3.7.1 çözer; artifact’ların yeniden adlandırılması ve sürüm zorlaması ile`resolutionStrategy.eachDependency`Spring Boot’un örtük BOM’unu atlatmanın tek yolu olarak.

Projenin kök dizinindeki Gradle derleme betiği artık temiz.`./gradlew build`Java 21, Gradle 9.4.1, Spring Boot 3.4.5 ve Docker Engine 29.4.1’e geçiyor.

İlgili Makaleler