Lighting up your build process
What we all want is a nice automated build process that runs on checkin and gives an indication when thingsgo awry. Well, it should indicate everything is fine too, obviously.
The system you want to look into is CruiseControl.Net. I hope to get into some of the nice things it can do. For now, suffice it to say it connects to a source control system, pulls changes, spawns a build and informs interested parties about what just happened.
One of the interested parties will very likely be CCTray, a tray icon application that shows a green or – as it sometimes happens – red light in your tray area of the desktop.
Sometimes that is enough, sometimes not. What I want is a clearly visible light, like a lavalamp or something that bubbles away in the corresponding color. If I’m crazy, I am not alone – Ken Nichols and Nigel Thorne offer two excellent examples of seemingly sane people that had the same idea.
Using the publisher pattern of Nigel’s is a really elegant idea. Unfortunately my build server sits in a server room with no windows, and way too far for an extension cord to my lava lamp. Thus, I resorted to Ken’s approach of hacking CCTray with the idea of connecting everything to my development box. Due to a variety of reasons that didn’t work out so well, so I employed an old lap top to listen to build events and control the lava lamp.
Controlling the lava lamp by software and hardware is explained in this post.
Hacking cctray
Thanks to Ken’s post I found a nice entry point to cctray.
-
public MainFormController(ICCTrayMultiConfiguration configuration, ISynchronizeInvoke owner)
-
{
-
[…]
-
new BuildTransitionSoundPlayer(aggregatedProjectMonitor, new AudioPlayer(), configuration.Audio);
-
new BuildTransitionLavaLamp(aggregatedProjectMonitor); // Add this line
-
if (configuration.X10 != null && configuration.X10.Enabled)
-
[…]
-
}
The BuildTransitionLavaLamp class is defined as
-
using System;
-
using System.Collections.Generic;
-
using System.Text;
-
using System.Windows.Forms;
-
using ThoughtWorks.CruiseControl.CCTrayLib.Configuration;
-
using ThoughtWorks.CruiseControl.CCTrayLib.Monitoring;
-
using CardAPI;
-
using System.IO;
-
-
namespace ThoughtWorks.CruiseControl.CCTrayLib.Presentation
-
{
-
class BuildTransitionLavaLamp
-
{
-
private enum ShowResult
-
{
-
MessageBox,
-
Nothing
-
}
-
private List monitoredProjects = new List();
-
public BuildTransitionLavaLamp(IProjectMonitor monitor)
-
{
-
GetMonitoredProjects();
-
-
/*
-
monitoredProjects.Add("lava_fail");
-
monitoredProjects.Add("lava_pass");
-
*/
-
monitor.BuildOccurred += new MonitorBuildOccurredEventHandler(Monitor_BuildOccurred);
-
CreateCard(ShowResult.MessageBox);
-
}
-
-
private void GetMonitoredProjects()
-
{
-
string fileName = Application.ExecutablePath + ".ini";
-
if (!File.Exists(fileName))
-
return;
-
StreamReader configFile = new StreamReader(fileName);
-
string line;
-
while ((line = configFile.ReadLine()) != null)
-
{
-
if (!string.IsNullOrEmpty(line) && !line.StartsWith("#"))
-
{
-
line = line.Trim();
-
monitoredProjects.Add(line);
-
//MessageBox.Show(">>" + line + "<<");
-
}
-
}
-
}
-
-
private Card card;
-
private static int greenLamp = 8;
-
private static int redLamp = 7;
-
-
private byte buildPassRelays = (byte)(1 << (greenLamp – 1));
-
private byte buildFailRelays = (byte)(1 << (redLamp – 1));
-
-
private void Monitor_BuildOccurred(object sender, MonitorBuildOccurredEventArgs e)
-
{
-
string projectname = e.ProjectMonitor.Detail.ProjectName;
-
//MessageBox.Show(">>" + projectname + "<<");
-
-
if (monitoredProjects.Contains(projectname))
-
{
-
//MessageBox.Show("Acting");
-
bool status = e.BuildTransition == BuildTransition.Fixed || e.BuildTransition == BuildTransition.StillSuccessful;
-
-
if (card == null)
-
CreateCard(ShowResult.Nothing);
-
-
if (card == null)
-
return;
-
-
if (status)
-
card.SetRelays(buildPassRelays);
-
else
-
card.SetRelays(buildFailRelays);
-
}
-
}
-
-
private void CreateCard(ShowResult showResult) {
-
try {
-
card = new Card();
-
}
-
catch (Exception e) {
-
if (showResult == ShowResult.MessageBox)
-
MessageBox.Show("Failed to create Card.\n" + e.Message);
-
}
-
}
-
}
-
}
What this code does is simply to
- Load some config from an ini-file
- plug into the listening event of project transitions
- Send commands to the card.
The config file is simply an ini-file with project names.
The sample projects lava_pass and lava_fail are defined as
-
-
cmd
-
/c exit 1
-
-
cmd
-
/c exit 0
Compile everything and move onto the machine hosting the relay card and you’re done.
–Jesper Högström
Love the solution Jesper.
I did a later version of my publisher idea that talked to lightserver that could be run anywhere and kept track of which builds were passing. It means you can use it for several builds.
The problem I have is that if a build fails and then the build machine goes off the light_server has no way of telling so it stays red.
I have heard about people using the new digital photo-frames as cheap monitors for displaying an image for fail and another for passing. I like that idea.
The solution with the relay card is kind of nice in that it keeps the state all the time. I turn off the lava lamps during night time, but leave the relay card running.
I guess it would also be possible to save state in a file, and restore that state when the listener goes on line.
Maybe you could strip down cctray and integrate it with the light_server? That way you will get signals when the build server comes back on line.