Sunday, May 9, 2010

Hudson Build Times

So we use Hudson at work to build stuff.

I've got it branching all our projects with a branch build using groovy, and because Maven doesn't work properly for us, updating all the project pom versions as well. I'll save that for a another time, the code is fairly in depth.

The branch scripts also create a build in Hudson too, with this little nugget:


package my.package

import groovy.text.GStringTemplateEngine;
import org.apache.commons.httpclient.*
import org.apache.commons.httpclient.auth.*
import org.apache.commons.httpclient.methods.*

import java.net.URLEncoder;

@Grab(group='commons-httpclient', module='commons-httpclient', version='3.1')
class HudsonBuildCreator {
def server = "build"
def hudsonHost = "http://${server}:8080"

def username = "username"
def password = "password"

def createBuild(String projectName, String svnUrl){
println "Creating hudson build..."
def client = new HttpClient()
client.state.setCredentials(
new AuthScope( server, 8080, "realm"),
new UsernamePasswordCredentials( username, password )
)
client.params.authenticationPreemptive = true
projectName = URLEncoder.encode(projectName)
String postString = "${hudsonHost}/createItem?name=${projectName}"
println "creating post method to ${postString}"
def post = new PostMethod( postString )
post.doAuthentication = true
String file = this.getClass().getResource("/my/package/config.template").getFile()
println "loading $file template"
File input = new File(file)
def engine = new GStringTemplateEngine()
def binding = ["svnUrl": "$svnUrl"]
def template = engine.createTemplate(input).make(binding)

def xml = template.toString()
println "sending: \n $xml"
RequestEntity entity = new StringRequestEntity(xml, "application/xml", "UTF-8");
post.setRequestEntity(entity);
try {
int result = client.executeMethod(post)
println "Return code: ${result}"
post.responseHeaders.each{ println it.toString().trim() }
assert result == 200, "$result, failed"
post.responseHeaders.each{ println it.toString().trim() }
println post.getResponseBodyAsString()
} finally {
post.releaseConnection()
}
println "Done creating Hudson build."

}
}


This calls the Hudson API and posts some xml to it to create a new build. I wanted it to be called from outside the Hudson environment too, so that it could be run from one of the dev's local machines without them having to log in to hudson.

The xml sent to Hudson is a copy of the xml you can find in the api, with some GroovyTemplate coolness added to it so I can specify the build svn url (held in the svnUrl variable). You can find the xml in the following location:

http://[my build server}/job/[job name]/config.xml

Just take that, create a resource with it (copy it into the same package as the groovy class) and call it "config.template".

One thing that was left was to space out my newly created builds. Surfing around the Hudson wiki pages I found something that displayed all the build trigger specs (the objects which take a cron tab like string) that let you specify the time the build is triggered. I wanted to update these, spaced out by 15 minutes or so, and came up with this:



import hudson.model.*
import hudson.triggers.*


int hourMax = 23 // could be 8, for 8am, before everyone comes in...
int minMax = 59
int incr = 15
int hr = 0
int m = 0


for(item in Hudson.instance.items) {
item.triggers.each{ descriptor, trigger ->
if(trigger instanceof TimerTrigger) {
println("--- Timer trigger for " + item.name + " ---")
println(trigger.spec + '\n')
if(item.name != "merge-process"){
item.removeTrigger descriptor
item.save()
item.addTrigger(new TimerTrigger("$m $hr * * *"))
item.save()
println "updated to $m $hr * * *"
m += incr
}


if(m > minMax){ hr++; m = 0}
if(hr > hourMax) { hr = 0; }
}
}
}

I run this as a separate build to the branch and merge scripts, triggered from the branch build, as a freestyle project that's sole responsibility is to run a system groovy script.

The output looks like this:

Started by user admin

bug-fix-1
--- Timer trigger for bug-fix-1 ---
@midnight

updated to 0 0 * * *
--- Timer trigger for change-batch-1 ---
@midnight

updated to 15 0 * * *
Finished: SUCCESS


Neat :)

NB you will need the Groovy plugin for Hudson to run the system Groovy script, and also have Groovy installed on your build server.

WHOOPS: This, although it seems to work on the surface, fails to trigger the builds once they have been updated. I will post why this might be soon... possibly a hudson bug or something I haven't done?

1 comments:

  1. "and because Maven doesn't work properly for us, updating all the project pom versions as well."

    Would like to know about it!

    ReplyDelete