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 </p>

        yield

        teamcity_service_message ‘progressFinish’, task

    end </font>

    def teamcity_service_message(message, message_value)

        puts "##teamcity[#{message} ‘#{message_value}’]" </p>

    end

end </font>

class Rake::Task

    include TeamCity </p>

       old_execute = self.instance_method(:execute) </font>

       define_method(:execute) do |args|

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

       end

end</font>

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.

Hola Los Techies