Extending CC.Net to write your own trigger
posted by Bryan on
The default triggers provided by CruiseControl.NET just didn't fit my unique scenario. I needed a trigger that could check a database table, and if rows exist - to force a build. Check the low down...
We are working on a dashboard that will queue up NUnit categories for a suite run. The categories will be determined dynamically - and so I couldn't rely on some hard-coded NUnit configurations as we've done in the past while trying to keep things simple.
To work around this, I add the build details to a SQL Server database as a queuing spot. In order to leverage some of CruiseControl.NET's goodness, I wanted a trigger to kick off a build if rows existed. I tried to load the datatable onto a page of the Dashboard - and update the HTTP Header Last Modified Date to work with the UrlTrigger, but that didn't work quite right.
So I created my own.
- Create new solution.
- Add references to NetReflector.dll, ThoughtWorks.CruiseControl.Core.dll, ThoughtWorks.CruiseControl.Remote.dll.
- Create a new class, such as MyTrigger
- Implement the ITrigger interface.
- Implement each of the interface's members.
- Note: Be sure to add the attribute declaration at the class level : [ReflectorType("myTrigger")]
- Build
- Copy .dll to CC.Net's main folder (C:Program FilesCruiseControl.NETserver).
- Rename dll to "ccnet.MyTrigger.plugin.dll"
- In your ccnet.config, reference the trigger.
using System; using Exortech.NetReflector; using ThoughtWorks.CruiseControl.Core.Util; using ThoughtWorks.CruiseControl.Remote; namespace DatabaseTrigger { [ReflectorType("myTrigger")] public class MyTrigger : ITrigger { //member variables here public MyTrigger() { } [ReflectorProperty("buildCondition", Required = false)] public BuildCondition BuildCondition = BuildCondition.IfModificationExists; public void IntegrationCompleted() { IncrementNextBuildTime(); } public IntegrationRequest Fire() { BuildCondition buildCondition = ShouldRunIntegration(); if (buildCondition == BuildCondition.NoBuild) return null; return new IntegrationRequest(buildCondition, Name, null); } private BuildCondition ShouldRunIntegration() { if (databaseRowExists()) return BuildCondition.ForceBuild; return BuildCondition; } private bool databaseRowExists() { return parser.CheckForRowsInQueue(); } public DateTime NextBuild { get { return nextBuildTime; } } /// <summary> /// The name of the trigger. This name is passed to external tools as a means to identify the trigger that requested the build. /// </summary> /// <version>1.1</version> /// <default>IntervalTrigger</default> [ReflectorProperty("name", Required = false)] public virtual string Name { get { if (name == null) name = GetType().Name; return name; } set { name = value; } } /// <summary> /// The number of seconds after an integration cycle completes before triggering the next integration cycle. /// </summary> /// <version>1.0</version> /// <default>60</default> [ReflectorProperty("seconds", Required = false)] public double IntervalSeconds { get { return intervalSeconds; } set { intervalSeconds = value; IncrementNextBuildTime(); } } protected DateTime IncrementNextBuildTime() { double delaySeconds; if (isInitialInterval) delaySeconds = InitialIntervalSeconds; else delaySeconds = IntervalSeconds; return nextBuildTime = dateTimeProvider.Now.AddSeconds(delaySeconds); } /// <summary> /// The delay (in seconds) from CCNet startup to the first check for modifications. /// </summary> /// <version>1.4</version> /// <default>Defaults to the IntervalSettings value.</default> [ReflectorProperty("initialSeconds", Required = false)] public double InitialIntervalSeconds { get { if (initialIntervalSeconds == -1) return IntervalSeconds; // If no setting for this, use IntervalSeconds instead. else return initialIntervalSeconds; } set { initialIntervalSeconds = value; IncrementNextBuildTime(); } } } }
In the ccnet.config file :
<triggers> <mytrigger name="dashboard" seconds="30" buildcondidtion="ForceBuild" initialseconds="30"> </mytrigger></triggers>