Введение

Целевая аудитория: промежуточные разработчики Gradle, желающие управлять сложными конфигурациями.

В разработке плагинов Gradle управление сложными конфигурациями является обычной задачей. Вместо того чтобы перегружать DSL сотнями свойств, часто чище и поддерживаемее определять конфигурацию во внешнем файле, например, в YAML. Эта статья проведет вас через интеграцию мощной библиотеки Jackson для парсинга файлов YAML в объекты Kotlin, применяя строгий подход TDD, чтобы обеспечить надежность нашего плагина.site-baker.

1. Контекст: Наш плагин `site-baker

Наш плагин`site-baker`предназначен для автоматизации генерации и развертывания статического сайта. Ему необходимо читать файл конфигурации YAML (managed-jbake-context.yml) для получения информации, такой как пути исходных файлов, места развертывания и учетные данные Git.

YAML конфигурация, которую мы хотим разобрать, выглядит так:

bake:
  srcPath: "./site/jbake"
  destDirPath: "bake"
  cname: "cheroliv.com"
pushPage:
  from: "bake"
  to: "cvs"
  repo:
    name: "trainings"
    repository: "https://github.com/pages-content/pages-content.github.io.git"
    credentials:
      username: "USERNAME"
      password: "SECRET_TOKEN"
  branch: "main"
  message: "cheroliv.com"
# ... autres configurations (pushMaquette, supabase, etc.)

2. Моделирование конфигурации в Kotlin

Перед парсингом нам нужно определить структуру наших данных на Kotlin. Jackson будет использовать эти классы для автоматического сопоставления содержимого YAML.

// plugin/src/main/kotlin/com/cheroliv/site/baker/data/SiteConfiguration.kt
package com.cheroliv.site.baker.data

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.registerKotlinModule

fun parseSiteConfiguration(yaml: String): SiteConfiguration {
    val mapper = ObjectMapper(YAMLFactory()).registerKotlinModule()
    return mapper.readValue(yaml)
}

data class GitPushConfiguration(
    val from: String = "",
    val to: String = "",
    val repo: RepositoryConfiguration = RepositoryConfiguration(),
    val branch: String = "",
    val message: String = "",
)

data class RepositoryConfiguration(
    val name: String = "",
    val repository: String = "",
    val credentials: RepositoryCredentials = RepositoryCredentials(),
) {
    companion object {
        const val ORIGIN = "origin"
        const val CNAME = "CNAME"
        const val REMOTE = "remote"
    }
}

data class RepositoryCredentials(val username: String = "", val password: String = "")

data class SiteConfiguration(
    val bake: BakeConfiguration = BakeConfiguration(),
    val pushPage: GitPushConfiguration = GitPushConfiguration(),
    val pushMaquette: GitPushConfiguration = GitPushConfiguration(),
    val pushSource: GitPushConfiguration? = null,
    val pushTemplate: GitPushConfiguration? = null,
    val supabase: SupabaseContactFormConfig? = null
)

data class BakeConfiguration(
    val srcPath: String = "",
    val destDirPath: String = "",
    val cname: String? = null,
)

// ... (autres data classes pour Supabase, si nécessaire)

Функция`parseSiteConfiguration`является нашей точкой входа для десериализации. Она использует`ObjectMapper`Джексона, настроенного с`YAMLFactory`для формата YAML и`registerKotlinModule()`для поддержки особенностей Kotlin (таких как значения свойств по умолчанию).

3. Интеграция Jackson в `build.gradle.kts

Для использования Jackson нам необходимо добавить необходимые зависимости в`build.gradle.kts`нашего модуля`plugin`Пожалуйста, введите текст для перевода.

// plugin/build.gradle.kts
dependencies {
    // Jackson for YAML parsing
    implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.3")
    implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.18.3")

    // ... autres dépendances
}

Мы используем`jackson-module-kotlin`для поддержки Kotlin и`jackson-dataformat-yaml`pour управления форматом YAML.

4. Подход TDD : Тестирование парсинга YAML

Теперь применим TDD для проверки нашей логики парсинга.

4.1. Тест юнита: Преобразовать YAML в объект Kotlin

Мы начинаем с юнит-теста в`SiteBakerPluginTest.kt`чтобы проверить, что функция`parseSiteConfiguration`может правильно преобразовать строку YAML в объект`SiteConfiguration`.

// plugin/src/test/kotlin/com/cheroliv/site/baker/SiteBakerPluginTest.kt
@Test
fun `can map configuration text to SiteConfiguration object`() {
    val yamlString = "../../managed-jbake-context.yml"
        .run(::File)
        .readText()
        .trimIndent()

    val config: SiteConfiguration = parseSiteConfiguration(yamlString)

    assertEquals("cheroliv.com", config.bake.cname)
    assertEquals("main", config.pushPage.branch)
    assertEquals("https://github.com/pages-content/pages-content.github.io.git", config.pushPage.repo.repository)
}

Этот тест читает содержимое файла`managed-jbake-context.yml`(которое должно существовать для того, чтобы тест был корректен) и выполняет ассерции над объектом`SiteConfiguration`Полученное. Он подтверждает, что ключевые значения YAML правильно сопоставлены.

4.2. Тест функциональный: Проверить чтение файла конфигурации

Чтобы убедиться, что плагин может читать файл конфигурации через свой DSL и что парсинг работает в реальной среде Gradle, мы добавляем функциональный тест в`SiteBakerPluginFunctionalTest.kt`.

Этот тест важен, поскольку он имитирует выполнение плагина в реальном проекте. Он должен бытьгерметичный, то есть он должен сам создать файл`managed-jbake-context.yml`с контролируемым содержимым, для обеспечения воспроизводимости и изоляции теста

// plugin/src/functionalTest/kotlin/com/cheroliv/site/baker/SiteBakerPluginFunctionalTest.kt
@Test
fun `config file contains good data`(){
    val configContent = configFile.readText(UTF_8)
    // Vérifications comme dans vos commentaires
    assertTrue(configContent.contains("bake"))
    assertTrue(configContent.contains("pushPage"))
    assertTrue(configContent.contains("repository"))
}

Этот функциональный тест проверяет, что скопированный файл конфигурации во временной директории теста содержит ожидаемые данные. Хотя этот тест не парсит YAML напрямую в объект Kotlin, он проверяет наличие файла и его содержимого, что является необходимым предварительным шагом перед парсингом плагином.

5. Визуализация потока парсинга

Следующая диаграмма иллюстрирует поток данных и взаимодействия при разборе конфигурации YAML:

@startuml
actor "Utilisateur Gradle" as User
participant "build.gradle.kts" as BuildScript
participant "SiteBakerPlugin" as Plugin
participant "SiteExtension" as Extension
participant "managed-jbake-context.yml" as ConfigFile
participant "parseSiteConfiguration()" as Parser
participant "SiteConfiguration (Objet Kotlin)" as KotlinObject

User -> BuildScript : Applique le plugin et configure le DSL
BuildScript -> Plugin : apply()
Plugin -> Extension : Crée et enregistre l'extension 'site'
User -> BuildScript : site { configPath = "..." }
BuildScript -> Extension : Définit configPath

Plugin -> ConfigFile : Lit le fichier (via configPath)
ConfigFile --> Plugin : Contenu YAML
Plugin -> Parser : Appelle parseSiteConfiguration(yamlString)
Parser -> KotlinObject : Mappe le YAML en objet SiteConfiguration
KotlinObject --> Plugin : Retourne l'objet configuré

@enduml

Заключение

Следуя подходу TDD, мы успешно интегрировали библиотеку Jackson для парсинга файлов конфигурации YAML в объекты Kotlin внутри нашего Gradle‑плагина. Этот метод позволил нам:

  • Четко моделироватьнаша конфигурация с data classes Kotlin.

  • Подтвердить логику парсингас целевыми юнит-тестами.

  • Обеспечить интеграциюв реальной среде Gradle благодаря герметичными функциональными тестами

Эта надежная основа дает нам большую уверенность в расширении нашего плагина функционалом, основанным на этой конфигурации, обеспечивая при этом поддерживаемость и надежность кода. Парсинг YAML теперь является надежной и протестированной функцией нашего плагина.site-baker.

Похожие статьи