AsciiDoc : Découvrir et maîtriser la syntaxe pour une documentation efficace
Publié le 09 July 2025
Ce billet présente la démarche complète de conception et de tests (TDD) d’un plugin Gradle écrit en Kotlin DSL. Ce plugin automatise la génération de sites statiques avec JBake et le déploiement Git via JGit, le tout à partir d’un fichier YAML de configuration.
1. Objectif
Créer un plugin Gradle :
-
configurable en Kotlin DSL ;
-
capable de :
-
générer un site statique à partir de JBake ;
-
déployer sur un dépôt Git distant via JGit ;
-
-
intégrable dans un workflow CI/CD ;
-
testé selon une démarche TDD.
2. Configuration déclarative
Voici un exemple de configuration YAML à modéliser dans notre DSL :
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"
Cette configuration est représentée via une extension Gradle déclarable en Kotlin DSL.
3. Architecture du plugin

4. Définition de l’extension DSL
open class WorkspaceEngineExtension {
var outputDir: String = "build/site"
var template: String = "freemarker"
var repoUrl: String = ""
var branch: String = "main"
var message: String = "Generated by workspace-engine"
}
5. Définition de la tâche personnalisée
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())
}
}
6. Interface GitAdapter
interface GitAdapter {
fun cloneRepository(uri: String, directory: File): Repository
fun commitAndPush(repo: Repository, branch: String, message: String)
}
7. Implémentation concrète avec JGit
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()
}
}
}
8. Configuration DSL Kotlin
workspaceEngine {
outputDir = "build/out"
template = "freemarker"
repoUrl = "https://github.com/cheroliv/trainings.git"
branch = "main"
message = "deploy from plugin"
}
9. Tests unitaires avec Mockito
9.1. Dépendances
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")
9.2. Test du comportement Git
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"))
}
}

10. Conclusion
La séparation des responsabilités via GitAdapter
permet d’appliquer pleinement le TDD dans le développement de plugin Gradle :
-
les appels Git sont abstraits et testables ;
-
la configuration DSL est propre et claire ;
-
le plugin reste agnostique de l’implémentation réelle.
Cette approche assure une haute testabilité et extensibilité (vers GitHub API, GitLab, etc.).
11. Prochaines étapes
-
Intégrer la génération JBake comme un
BakeAdapter
-
Ajouter des tests d’intégration avec
GradleRunner
-
Supporter le chargement automatique de fichiers
site.yml
via SnakeYAML