Monkey patching rake for use with TeamCity

We use Ruby’s rake as our build automation tool.  It provides a nice, XML-free way to define logical groupings of steps to perform, with dependency resolution.

We use JetBrain’s TeamCity as our continuous integration server. It has a neat feature that provides real-time reporting of build progress. TeamCity does not have any built-in support for rake*, so we use the simpleRunner build runner which allows you to kick off any process. However, when using the simpleRunner, you do not get any progress updates, since TeamCity doesn’t really know what is going on within your custom process.

Turns out, this is really easy to fix. TeamCity has a very simple API which allows a build script to interact with it. You just have to send specially formatted text messages to standard out (the console). For example, writing the following text to the console from your build script will cause the message “Starting compilation” to appear as the build status (on the project website, or the notification tray):

##teamcity[progressStart 'Starting compilation']

Now, it would be pretty annoying if we had to litter these messages throughout our build script. Fortunately, in ruby, classes are open by default. This means we can add or change behavior of existing classes, even if we do not have control over the source. In this case, I want to change the Execute method on Rake’s Task class so that it sends a message to TeamCity when a task starts and finishes. I use Jay Fields’ example of redefining a method that still needs to call its original implemention (thanks to Steven Harman for the help via twitter). I created a file [TeamCity.rb] with the following contents in the same directory as our RAKEFILE:

module TeamCity

    def teamcity_progress(task)

        teamcity_service_message ‘progressStart’, task

        yield

        teamcity_service_message ‘progressFinish’, task

    end

    def teamcity_service_message(message, message_value)

        puts "##teamcity[#{message} '#{message_value}']"

    end

end

class Rake::Task

    include TeamCity

       old_execute = self.instance_method(:execute)

       define_method(:execute) do |args|

         teamcity_progress("Executing #{name} rake task") { old_execute.bind(self).call(args) }

       end

end

I then conditionally include this patch (so that we do not see all of the annoying TeamCity messages when running the build locally) with the following line in our RAKEFILE:

require ‘TeamCity.rb’ if ENV["teamcity.dotnet.nunitlauncher"]

I just arbitrarily picked an environment variable that I know is defined when run within TeamCity, that is not defined on our local machines. There is probably a more appropriate/universally applicable environment variable which indicates “running in TeamCity”.

We now can see near real-time which rake task is currently running on the CI server. This can be nice when you are trying to fix a broken build, and you want to know if the current build has successfully passed the step that caused the failure. And yeah, it was also a fun way to explore monkey patching with ruby.

* There is actually an EAP for a Rake Build Runner, which I did not find out about until doing some research for this post. From the description, it sounds more geared toward people actually building ruby applications, which does not include us. All of our code is C#/.NET – only our build script is in ruby. However, its probably worth investigating further.

Related Articles:

This entry was posted in ruby, teamcity. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://www.jamesthigpen.com James Thigpen

    That’s pretty schwanky. I think I’ll try that out soon.

    How are you handling unit test reporting? I notice the api is similarly simple. Or are you just calling teamcity’s nunit runner? (assuming you’re using nunit… which may not be the case…)

  • http://joshuaflanagan.lostechies.com Joshua Flanagan

    We were using TeamCity’s nunit runner, but started to have some issues with it when using WatiN and other NUnit addins. We’ve switched to using the TeamCity NUnit addin which allows us to use the normal nunit-console.exe, while still getting all of the test reporting goodness. I don’t think it is well publicized (I stumbled upon it), so I think I may be able to get a blog post out of it.

  • http://www.techfocus2.com Ryan Kelley

    Josh can you post your actual command line text? I am having issues getting this to work on my TeamCity install.

  • http://www.lostechies.com/members/jflanagan/default.aspx Joshua Flanagan

    Ryan – I’d be happy to provide more details, but I’m not sure which command line you are referring to. If you want the Team City build runner command line, ours is:
    c:\ruby\bin\rake.bat
    with command parameter: “cruise” (which is our the name of the rake target we want our CI build to run)

  • http://jetbrains.com Kirill Maximov

    TeamCity 4.0 supports Rake runner out of the box.
    You may see a demo at http://teamcitydev.blogspot.com/2008/11/most-advanced-continuous-integration.html

    Kind regards,
    KIR