About Peter Daum

Peter is senior Java developer in the telecommunication industry. He works in the area of business and operations support systems and is always happy to share his knowledge and experience. He is interested in everything related to Java and software craftsmanship.

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 featuresbugfixes 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

  1. the readTags method
  2. the readTagMessage method
  3. 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

 def readTagMessage(String tag) {
    def message = []
    def proc = "git cat-file tag $tag".execute()
    def startCollection = false
    proc.in.eachLine { line ->
        if (line.isEmpty()) {
            startCollection = true
        }
        if (startCollection) {
            message += line
        }
    }
    proc.err.eachLine { line -> println line }
    message
}

 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.

 

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


2 − = one



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close