import groovy.util.Node
import groovy.util.XmlParser
import org.gradle.api.GradleException
import java.io.File
/**
* Given an xml of the format
*
*
*
*
*
*
*
*
*
*
* Outputs a changelog in markdown format
*/
object ChangelogGenerator {
class ChangelogException(message: String) : GradleException(message)
private fun fail(message: String): Nothing =
throw ChangelogException(message)
class ChangelogEntry(val version: String, val items: Array)
private fun Node.forEachNode(action: (Node) -> Unit) {
children().forEach {
action(it as Node)
}
}
@JvmStatic
fun read(inputUri: String): List {
val input = File(inputUri)
if (!input.exists()) {
fail("Could not generate changelog from ${input.absolutePath}")
}
val parser = XmlParser().parse(inputUri)
val entries: MutableList = mutableListOf()
var version: String? = null
val items: MutableList = mutableListOf()
fun addEntry() {
version?.also { v ->
entries.add(ChangelogEntry(v, items.toTypedArray()))
items.clear()
}
}
parser.depthFirst().mapNotNull { it as? Node }.forEach { n ->
when (n.name()) {
"version" -> {
addEntry()
version = n.attribute("title")?.toString() ?: ""
}
"item" -> {
n.attribute("text")?.toString()?.takeIf(String::isNotBlank)?.let {
items.add(it)
}
}
}
}
addEntry()
return entries
}
@JvmStatic
fun generate(inputUri: String, outputUri: String): List {
val entries = read(inputUri)
val output = File(outputUri)
if (output.exists()) {
if (output.isDirectory) {
fail("Cannot save changelog at directory ${output.absolutePath}")
}
if (output.isFile && !output.delete()) {
fail("Could not delete changelog at ${output.absolutePath}")
}
} else {
output.parentFile.mkdirs()
}
if (!output.createNewFile()) {
fail("Could not create changelog file at ${output.absolutePath}")
}
val markdown = buildString {
append("# Changelog\n")
entries.forEach { e ->
append("\n## ${e.version}\n")
e.items.forEach {
append("* $it\n")
}
}
}
output.writeText(markdown)
println("Generated changelog at ${output.absolutePath}")
return entries
}
}