La polygot maven extension permet de produit des pom en Yaml (j'avais fait un tuto ici en son temps) et maintenant en Kotlin. Vous savez que j'adore Kotlin et pourtant je vois dans cette opportunité une immondice sans nom pour ne pas dire la plus grande des saloperies !
Pourquoi ?
Parce que le DSL Kotlin permet de coder dans le pom dont le but est qu'il soit pourtant 100 % déclaratif. Car du code dans un pom n'aura jamais de TU, a un probabilité très haute de ne pas être portable et surtout, chaque évolution du code ne profitera ni à la communauté (car pas de plugin) et deviendra un risque pour l'entreprise qui va accentuer l'effet "copier-coller".
Non vraiment, si c'était pour faire de Maven la même horreur de maintenance que l'est Gradle, mais les performances en moins, je pense qu'il valait mieux s'abstenir.
Voici l'exemple qui m'a fait vomir deux fois (vous saurez apprécier le code à copier-coller dans chaque projet d'entreprise en début de pom n'est-ce pas ?) :
import java.io.File
// define a comparable data class to simplify handling versions
data class Version(val major: Int, val minor: Int, val patch: Int) : Comparable<Version> {
override fun compareTo(other: Version): Int =
compareValuesBy(this, other, Version::major, Version::minor, Version::patch)
override fun toString(): String = "$major.$minor.$patch"
}
// define a function to execute the git command and return its standard output
fun git(vararg args: String): String {
// use the basedir of the project as the command's working dir if it contains a '.git' subdir
// otherwise use the current working directory of this script if it contains a '.git' subdir
// if both conditions are false the result will be null; the git command will probably fail
val workingDir = basedir.takeIf { it.resolve(".git").exists() }
?: File(".").takeIf { it.resolve(".git").exists() }
// run the git command with the provided arguments
val process = ProcessBuilder()
.directory(workingDir)
.redirectErrorStream(true)
.command(listOf("git") + args)
.start()
// read the standard output completely as a String
val output = process.inputStream.bufferedReader().readText().trim()
// return the output if the exit value is 0 or throw an exception otherwise
if (process.waitFor() == 0) return output
else throw IllegalStateException(output)
}
val gitVersions by lazy {
// run the `git tag` command
git("tag")
// the returned list of tags is separated by newlines
.split("\n")
// filter out only tags that are versions (such as 1.231.15)
.filter { it.matches(Regex("[0-9]+\\.[0-9]+\\.[0-9]")) }
// the separate parts of each version are separated by dots,
// also parse each part as an int
.map { it.split('.').map { it.toInt() } }
// map each triple of numbers to an instance of the `Version` class
.map { (major, minor, patch) -> Version(major, minor, patch) }
// sort the list of versions
.sorted()
}
// the last release is always the tag with the highest version number
val lastRelease by lazy {
gitVersions.max()
}
// the next version is determined based on the git commit log
val nextVersion by lazy {
// use the lsat released version as the base
val baseVersion = lastRelease
// if there are no releases yet, we use the version 0.0.1
if (baseVersion == null) Version(0, 0, 1)
else {
// split the base version in each separate part using destructuring
val (major, minor, patch) = baseVersion
// create a separator to split each log message on (log messages are multiline)
val separator = "-".repeat(5) + "commit" + "-".repeat(5)
// get all log messages from the last release tag until the current HEAD
// for each commit the separator is printed + the full commit message
val logMessages = git("log", "--pretty=format:$separator%n%B", "$baseVersion..HEAD")
// split the output on each separator generated earlier
.split(separator)
// trim each message, removing excess newlines
.map { it.trim() }
// only keep non-empty messages
.filter { it.isNotEmpty() }
when {
// increment the major and reset the minor + patch if any
// message contains the words 'BREAKING CHANGE'
logMessages.any { it.contains("BREAKING CHANGE") } -> Version(major + 1, 0, 0)
// increment the minor and reset the patch if any message starts with 'feat'
logMessages.any { it.startsWith("feat") } -> Version(major, minor + 1, 0)
// increment the patch in all other cases
else -> Version(major, minor, patch + 1)
}
}
}
project {
// use the next version calculated above when defining our project id
id("nl.craftsmen.blog.kotlin:kotlin-rest-service:${nextVersion}")
dependencies {
compile("org.glassfish.jersey.inject:jersey-hk2:2.29")
compile("org.glassfish.jersey.containers:jersey-container-netty-http:2.29")
compile("org.glassfish.jersey.media:jersey-media-json-jackson:2.29")
runtime("ch.qos.logback:logback-classic:1.2.3")
}
properties {
"project.build.sourceEncoding" to "UTF-8"
"maven.compiler.source" to "11"
"maven.compiler.target" to "11"
}
distributionManagement {
repository("local") {
url(basedir.resolve("repo").toURI().toASCIIString())
}
}
build {
execute(id = "release", phase = "deploy") {
// create a new tag using the next version calculated above
git("tag", "-am", "Release $nextVersion", "$nextVersion")
// print some output
println("Tagged current HEAD as $nextVersion")
}
}
}