Passing information to and from different plugins in CruiseControl.net

by Michael Henstock 8. January 2013 11:56

Passing a value from a custom trigger plugin to another plugin inside CruiseControl.net is quite simple.

From the ITrigger.Fire() method, CruiseControl.net expects an IntegrationRequest to be returned. In this object we can add the information that we wish to pass to our next plugin. To do this, we add our information to the BuildValues Dictionary<string, string> like this:
var request = new IntegrationRequest(buildCondition, "New integration request", null);
request.BuildValues.Add("my_info", "test info");
When a build is successfully started in CruiseControl.net, these values are parsed into the Parameters NameValuePair list on the IIntegrationResult object, that is either passed to your objects through the standard interface. This can be accessed like this:
var labelParameter = integrationResult.Parameters.SingleOrDefault(x => x.Name == "my_info");
if (labelParameter != null)
    return labelParameter.Value;

Tags:

C# | CruiseControl.net

Extending CruiseControl.net with a sample trigger that fires when a file exists

by Michael Henstock 21. October 2012 11:03

This is a simple example of a CruiseControl.net Trigger server plugin which is based on the sample given in the source code. It's probably best to familiarise yourself with that and the plugin examples at http://www.cruisecontrolnet.org/projects/ccnet (of which this example is heavily based off, if you really wanted you could just go and play with the sample code provided by cruisecontrol.net and skip this article, it's okay I won't be offended).

The first caught me out (despite it being well documented here), was the need to name your plugin dlls in the format of ccnet.*.plugin.dll. That's the only requirement for your plugins as once cruise control has that it just uses reflection to find everything it needs from your dll.

To start, create your class, have it implement the ITrigger interface, and assign the ReflectorType attribute to the class with the name of your trigger as a parameter. This name is the name that will be used in the XML project configuration and can be completely different from the class name if you wish.

[ReflectorType("mySampleTrigger")]
public class SampleTrigger : ITrigger
{
    #region ITrigger Members

    public IntegrationRequest Fire()
    {
        throw new NotImplementedException();
    }

    public void IntegrationCompleted()
    {
        throw new NotImplementedException();
    }

    public DateTime NextBuild
    {
        get { throw new NotImplementedException(); }
    }

    #endregion
}

The Fire() method is the method that is called every time that CruiseControl has scheduled the next integration to occur. If you have this scheduled to occur once every minute, it will call the Fire() method every minute. This is where you want to declare your logic to determine if a build should occur.

The IntegrationCompleted() method is logic that will be called after a build has finished running that has this trigger.

The NextBuild property is used by CruiseControl to determine when the next call to the Fire method of the trigger should occur.

Now that we understand the basics of the interface, lets look at how we can create a simple trigger that fires every 15 seconds.

To start, we need access to the CruiseControl.net DateTimeProvider class. To do this, lets create a constructor to accept the provider and assign it to a field.

DateTimeProvider _dateTimeProvider;

public SampleTrigger()
    : this(new DateTimeProvider())
{
}

public SampleTrigger(DateTimeProvider dateTimeProvider)
{
    _dateTimeProvider = dateTimeProvider;
}

Next, we'll implement the NextBuild property in the interface:

public DateTime NextBuild { get; private set; }

and assign Now + 15 seconds to it in the constructor of the class:

public SampleTrigger(DateTimeProvider dtProvider)
{
    _dateTimeProvider = dtProvider;
    NextBuild = _dateTimeProvider.Now.AddSeconds(15);
}

Now we need to implement the Fire method. We just need to check the current time against the most recent time stored in the NextBuild property. If it is less than the 15 seconds stated above, we just return null, otherwise we return a new IntegrationRequest object to tell CruiseControl that we want a build to occur.

public IntegrationRequest Fire()
{
    if (_dateTimeProvider.Now < NextBuild)
        return null;

    return new IntegrationRequest(BuildCondition.ForceBuild, "Sample Trigger says GO!", null);
}

The BuildCondition parameter in the IntegrationRequest constructor can give CruiseControl a bit more information, such as only causing a build if there are modifications found within source control. As we want a build to occur every 15 seconds no matter what, we're just going to return a ForceBuild.

Finally, we'll implement the integration request to handle any post build processing, in this case, we'll just increment the NextBuild so that the next one will occur 15 seconds after the end of the current build.

public void IntegrationCompleted()
{
    NextBuild = _dateTimeProvider.Now.AddSeconds(15);
}

So finally our trigger should look something like this:

[ReflectorType("uiTestTrigger")]
public class SampleTrigger : ITrigger
{
    DateTimeProvider _dateTimeProvider;

    public SampleTrigger()
        : this(new DateTimeProvider())
    {
    }

    public SampleTrigger(DateTimeProvider dtProvider)
    {
        _dateTimeProvider = dtProvider;
        NextBuild = _dateTimeProvider.Now.AddSeconds(15);
    }

    #region ITrigger Members

    public IntegrationRequest Fire()
    {
        if (_dateTimeProvider.Now < NextBuild)
            return null;

        return new IntegrationRequest(BuildCondition.ForceBuild, "Sample Trigger says GO!", null);
    }

    public void IntegrationCompleted()
    {
        NextBuild = _dateTimeProvider.Now.AddSeconds(15);
    }

    public DateTime NextBuild { get; private set; }

    #endregion
}

Now, having a build occur every 15 seconds isn't really useful (and the interval trigger already does that for us if that's what you need). However, if we wanted to extend this to say, fire a build when a file exists on disk, it's a simple process of just adding that to this class with a few lines of code.

So to start, we alter the Fire method to check if a file exists:

public IntegrationRequest Fire()
{
    if (_dateTimeProvider.Now < NextBuild)
        return null;

    NextBuild = _dateTimeProvider.Now.AddSeconds(15);

    if (!File.Exists(@"C:\Temp\test.txt"))
        return null;

    return new IntegrationRequest(BuildCondition.ForceBuild, "Sample Trigger says GO!", null);
}

The only other thing we have added here is to increment the NextBuild property 15 seconds after we have passed the existing 15 second counter to force this file check to only occur every 15 seconds. And that's it, we're now checking if a file exists every 15 seconds, and if it does we're telling CruiseControl to force a build to start.

Where to from here? You can then start extending this such that the 15 second increment value and the file on disk are properties of the trigger that can be configured in the build configuration file to truly extend this trigger.

Tags:

C# | CruiseControl.net