JBake ve JGit ile yapılandırılabilir Kotlin Gradle eklentisinin TDD'si
Yayınlandı: 09 July 2025
Bu gönderi, Kotlin DSL’de yazılmış bir Gradle eklentisinin tasarım ve test sürecinin tamamı (TDD)ni sunar. Bu eklenti, JBake ile statik sitelerin üretimini ve JGit üzerinden Git dağıtımını otomatikleştirir, bir YAML yapılandırma dosyasından.
Hedef
Bir Gradle plugin oluştur:
-
Kotlin DSL’de yapılandırılabilir ;
-
yapabilecek :
-
JBake’ten statik bir site oluştur ;
-
Uzak bir Git deposuna JGit kullanarak dağıt;
-
CI/CD iş akışı içinde entegre edilebilir ;
-
TDD yaklaşımıyla test edildi.
Bildirim tabanlı yapılandırma
İşte DSL’ümüzde modellenmesi gereken bir YAML yapılandırma örneği:
bake:
srcPath: "./site/jbake"
destDirPath: "bake"
pushPage:
from: "bake"
to: "cvs"
repo:
name: "trainings"
repository: "https://github.com/pages-content/trainings.git"
credentials:
username: "USERNAME"
password: "SECRET_TOKEN"
branch: "main"
message: "cheroliv.com"
Bu yapılandırma, Kotlin DSL’de bildirilebilen bir Gradle uzantısı aracılığıyla gösterilir.
Eklenti mimarisi
@startuml
package "motor eklentisi" {
class WorkspaceEngineExtension
class GenerateSiteTask
interface GitAdapter
class JGitAdapter
WorkspaceEnginePlugin --> WorkspaceEngineExtension
WorkspaceEnginePlugin --> GenerateSiteTask
GenerateSiteTask --> GitAdapter
JGitAdapter ..|> GitAdapter
}
@enduml
DSL uzantısı tanımı
open class WorkspaceEngineExtension {
var outputDir: String = "build/site"
var template: String = "freemarker"
var repoUrl: String = ""
var branch: String = "main"
var message: String = "Generated by engine"
}
Özel görev tanımı
abstract class GenerateSiteTask : DefaultTask() {
lateinit var gitAdapter: GitAdapter
@get:Input abstract val repoUrl: Property<String>
@get:InputDirectory abstract val outputDir: DirectoryProperty
@get:Input abstract val branch: Property<String>
@get:Input abstract val message: Property<String>
@TaskAction
fun run() {
val repo = gitAdapter.cloneRepository(repoUrl.get(), outputDir.get().asFile)
gitAdapter.commitAndPush(repo, branch.get(), message.get())
}
}
GitAdapter Arabirimi
interface GitAdapter {
fun cloneRepository(uri: String, directory: File): Repository
fun commitAndPush(repo: Repository, branch: String, message: String)
}
JGit ile somut uygulama
class JGitAdapter : GitAdapter {
override fun cloneRepository(uri: String, directory: File): Repository {
return Git.cloneRepository()
.setURI(uri)
.setDirectory(directory)
.call()
.repository
}
override fun commitAndPush(repo: Repository, branch: String, message: String) {
Git(repo).use {
it.add().addFilepattern(".").call()
it.commit().setMessage(message).call()
it.push().call()
}
}
}
Kotlin DSL yapılandırması
workspaceEngine {
outputDir = "build/out"
template = "freemarker"
repoUrl = "https://github.com/cheroliv/trainings.git"
branch = "main"
message = "deploy from plugin"
}
Mockito ile birim testleri
Bağımlılıklar
testImplementation("org.mockito:mockito-core:5.12.0")
testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
Git davranış testi
class GenerateSiteTaskMockitoTest {
@TempDir
lateinit var tempDir: File
@Test
fun `task should call clone and commitAndPush`() {
val project = ProjectBuilder.builder().build()
val task = project.tasks.create("generateSite", GenerateSiteTask::class.java)
val mockGitAdapter = mock<GitAdapter>()
val mockRepo = mock<Repository>()
whenever(mockGitAdapter.cloneRepository(any(), any())).thenReturn(mockRepo)
task.gitAdapter = mockGitAdapter
task.repoUrl.set("https://github.com/cheroliv/test.git")
task.outputDir.set(project.layout.projectDirectory.dir(tempDir.name))
task.branch.set("main")
task.message.set("test commit")
task.run()
verify(mockGitAdapter).cloneRepository(eq("https://github.com/cheroliv/test.git"), any())
verify(mockGitAdapter).commitAndPush(eq(mockRepo), eq("main"), eq("test commit"))
}
}
@startuml actor "Geliştirici" as Dev participant "Gradle Proje Oluşturucu" as Project participant "GenerateSiteTask" as Task participant "GitAdapter (Mock)" as Adapter participant "JGitAdapter / Mockito" as Impl Dev -> Project : construit projet Gradle Dev -> Task : configure les propriétés Task -> Adapter : cloneRepository() Adapter -> Impl : simulé par Mockito Task -> Adapter : commitAndPush() @enduml
Sonuç
Sorumluluk ayrımıyla`GitAdapter`Gradle eklenti geliştirmesinde TDD’yi tamamen uygulamasına olanak tanır:
-
Git çağrıları soyut ve test edilebilir ;
-
DSL yapılandırması temiz ve net;
-
eklenti gerçek uygulamadan agnostik kalır.
Bu yaklaşım yüksek test edilebilirlik ve genişletilebilirlik sağlar (GitHub API, GitLab vb. için).
Bir sonraki adımlar
-
JBake üretimi bir olarak entegrate etmek`BakeAdapter`
-
Entegrasyon testleri eklemek`GradleRunner`
-
Dosyaların otomatik olarak yüklenmesini desteklemek`site.yml`SnakeYAML aracılığıyla