Creating beautiful release notes with git, gradle and markdown
During the last days I asked myself how to generated releases notes from information that are available in commit / tag messages from git.
The decisions
My first approach was to create the list of changes directly from the commit messages, but this approach has multiple drawbacks.
- The commit messages must be written very disciplined
- An marker is required to collect messages that belongs to a release (A usual tag would be sufficient for that)
- The content of the commit messages must follow conventions, so that features, bugfixes and changes could be collected and displayed in one block
So I go for annotated tags, which must provide a message with a tag. Creating them is fairly easy with git
git tag -a v.1.0.0 -m "This is an annotated tag"
Sidenote: This kind of tags has a nice addition, it allows to check which commits belongs a specific tag.
The next decision was about the markup language that should be used for the messages. Because I’m a fan of markdown, I decided to go for that. This will lead to human readable messages and a well known and working parsing.
Important hint
Git interprets # usually as the start of a comment. The annotated tags must be created with the option –cleanup=verbatim to suppress this feature if you would like to use more than the first and second header.
With this decisions I started to implement a small example in gradle, which creates the release notes during the build process. I the next section I show you the required code
Implementation
Prerequisites
Because I want to have also a beautiful version of the release notes I decided to convert the markdown directly to a html page. For doing this I use PegDown as Markdown processor and twitter bootstrap with bootswatch themes for styling the output.
Initial build script
buildscript { repositories { mavenCentral() mavenLocal() } dependencies { classpath 'org.pegdown:pegdown:1.4.1' } } import org.pegdown.PegDownProcessor import groovy.text.SimpleTemplateEngine
Implementing the logic
We start with the task the controls the process of generating the release notes
task releaseNotes() { def releaseNotes = new File('releaseNotes.md') releaseNotes.delete() def versions = "" def tags = readTags() tags.each {tag -> versions += "- [$tag](#$tag)\n" } tags.each {tag -> releaseNotes << "# ${tag}\n" def message = readTagMessage(tag) message.each{releaseNotes << "$it\n"} releaseNotes << "\n" } def writer = new StringWriter() def pdp = new PegDownProcessor() def engine = new SimpleTemplateEngine() def template = engine.createTemplate(new File("releaseNotes.tpl")) def daten = [releaseNotes:pdp.markdownToHtml(new File("releaseNotes.md").text), application: project.name, versions:pdp.markdownToHtml(versions)] def ergebnis = template.make(daten) new File('releaseNotes.html').withWriter { w -> w.write(ergebnis) } }
The process is really simple.
- First we clean up old artifacts of a markdown file (line 2 and 3). After this we created the section Versions and load the available tags (lines 5 till 10) and put everything into a separated list.
- Then we create information about the Release Notes and add a header for each tag followed by the message belonging to this tag (lines 12 till 18).
- As a last step we parse the markdown file and create a html page by passing by the parsed values to a template.
As you see three more components are required
- the readTags method
- the readTagMessage method
- a template for the html page
readTags
This method calls:
git tag -l
and returns a reversed ordered list of tags
def readTags() { def tags = [] def proc = "git tag -l".execute() proc.in.eachLine { line -> tags += line} tags.sort {} Collections.reverse( tags ) tags }
readTagMessage
This method uses
releaseNotes.tpl
This template contains three properties application, releaseNotes and versions. Application is substituted with your applications name and used in the title. releaseNotes contains the converted release notes. And finally versions represents:
<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xml:lang='de' xmlns='http://www.w3.org/1999/xhtml' lang='de'> <head> <title>Release Notes of $application</title> <link rel="stylesheet" href="http://bootswatch.com/yeti/bootstrap.min.css"> <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css"> <style type="text/css"> .releasenotes h1 { font-size: 1.5em; } .releasenotes h2 { font-size: 1.2em; } </style> </head> <body> <div class="row"> <div class="navbar navbar-default"> <div class="col-md-4"></div> <div class="col-md-4"> <h1>Releasenotes</h1> </div> <div class="col-md-4"></div> </div> </div> <div class="row releasenotes"> <div class="col-md-4"></div> <div class="col-md-4">$releaseNotes</div> <div class="col-md-4"><h1>Versions</h1>$versions</div> </div> <script src="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script> </body> </html>
As already stated in the prerequisites twitter bootstrap and a theme from bootswatch is applied to beautify the code.
Final
After putting everything together we can create a annoated tag and call simply
gradle releaseNotes
and gradle will read all the messages and tags and create two files
- releaseNotes.md and
- releaseNotes.html (Example – Thanks to a colleague of mine for his thoughts on layout and design for this version)
Conclusion
With this simple build script and a little bit conventions we can create release notes from our repository information. Additionally we don’t have to maintain multiple places of release notes, because everything is directly stored in the repository.
By using Markdown we have a simple markup and human readable markup language that allows us to create beautiful release notes for our web applications. It’s possible to parse the markdown information directly to a html page, as I did in this example, but you can also provide the simple markdown file and parse it on the client side or at delivery time on your server.
- As usual the complete code is available on github https://github.com/coders-kitchen/tut-releaseNotesFromTags
PegDown is depricated (look at description: https://github.com/sirthias/pegdown), FlexMark is the choose now.