Monday, November 17, 2008

Hudson and Grails

I debated on whether or not to blog about my very positive experience with the Hudson CI server. I think the buzz pretty much speaks for itself, so I'll stick to how I implemented build number tagging with my Grails build.

My goal for my Hudson builds was to 'tag' the Hudson build number directly into the footer of my Grails GSP layouts. Since the applications that I'm applying this strategy to are internal facing, it suits us really well to be able to see the build number directly in the user interface.

Here's how I went about accomplishing it:

1) I added a filterset token in my layout ($PROJECT_HOME/grails-app/views/layouts/main.gsp):

<html>
<body>
...
<div class="footer">Build Version: @HUDSON_BUILD@</div>
</body>
</html>

2) I hooked into Grails' WAR target by defining a closure in $PROJECT_HOME/scripts/Events.groovy
eventWarStart = {
def hudsonBuild = Ant.antProject.properties."env.BUILD_NUMBER" ?: "Custom Build"
String relLayoutPath = "grails-app/views/layouts"
Ant.copy(todir: "$stagingDir/WEB-INF/$relLayoutPath", overwrite: true) {
fileset(dir: "${basedir}/$relLayoutPath", includes: "*.gsp")
filterset() {
filter(token: "HUDSON_BUILD", value: hudsonBuild)
}
}
}

The reason this works is because Hudson exposes a number of environment variables at build time. As you can see from the script above, I use the 'BUILD_NUMBER' variable as the token's replacement value.

Kudos to Jeff Brown and the Grails folks for releasing the Hudson plugin which enables Hudson builds using Gant.

4 comments:

iamsteveholmes said...

Hey Brock!
It's funny you should post about Hudson and Grails and on the same day ask me what I was doing on my blog:

http://capitalcodemonkey.blogspot.com/

It turns out what I was doing was exposing services (Gets and Posts) in the Grails application that I used by calling Groovy scripts from Hudson Jobs using HTTPClient. This was a basic CMDB that is now open source:
http://code.google.com/p/configuration-data-repository/

Brock Heinz said...

@iamsteveholmes

Ha - it's a small world :)

Tom Jenkins said...

Thanks for this. However in grails 1.1 the event has changed slightly. In your Events.groovy file change to

eventCreateWarStart = { warName, stagingDir ->
...
}

Everything else should be the same

Kevin J Slater said...

Tom Jenkins - thanks for posting that solution - it was driving me batty!