Some stuff you need to know :
Tasks and publishers are the same in ccnet, the only difference is the handling of errors. If a class defined in the
tasks section throws an error, the execution of the entire
tasks section in the ccnet.config file is stopped. If an error occurs in a class defined in the publisher section, the current publisher will stop, but the next publisher of the
publishers section will still be called.
When you open the solution
c:\source\ccnet\project\ccnet.sln you see that there are (for the moment) 10 projects :
CCTray : The CCTray application
CCTrayLib : Library for CCTray, here resides all its logic
Console : The CCtray Console server application
Core : Here resides the majority of the functionality
Objection : Code responsible for creating objects (heavy code)
Remote : Communication layer server
(communication between cctray/dashboard and a CCNet
Service : The CCNet service counterpart of the CCNet console application
UnitTests : All the unit tests of CCNet
Validator : A winform application that validates a ccnet.config file
WebDashboard : The Dashboard application
Now, the publishers are part of the Core, so if you expand the core project,
you will see a publishers folder. This holds all the publishers.
For example, we'll be creating a very simple publisher : FilePublisher.
This will create a txt file with the results of a build.
For configuration : it will take the path of the file.
Now, creating this publisher :
° Create a class FilePublisher in the publishers folder
° change
.publishers into
.Publishers in the namespace
° add
using Exortech.NetReflector;° make the class public
You have now the following :
using System.Collections; using System.IO; using System.Xml.Serialization; using Exortech.NetReflector;
namespace ThoughtWorks.CruiseControl.Core.Publishers { public class FilePublisher : ITask { public void Run(IIntegrationResult result) { throw new System.NotImplementedException(); } } }
|
For letting the CCNet-system know that this is a publisher/task, the class must
implement the interface
ITask. This interface foresees a
Run method with an arguement of
IIntegrationResult; The argument holds all the information of the current build.
So in fact, writing this publisher is nothing more than :
° opening the defined target file
° write the wanted properties of IIntegrationResult
° close the file
you see, not that hard.
Ok, back to the code. First add the code for the file argument. This is done by
adding a public property, lets say
ResultFile. If you want this property to be definable in ccnet.config, you must decorate it with a
Reflector attribute, coming from the
Exortech.NetReflector namespace.
Also, the class must have such an attribute, to property is passed to the correct class.
using System.Collections; using System.IO; using System.Xml.Serialization; using Exortech.NetReflector;
namespace ThoughtWorks.CruiseControl.Core.Publishers { [ReflectorType("filePublisher")] public class FilePublisher : ITask { private string resultFile = "Result.txt";
[ReflectorProperty("resultFile")] public string ResultFile { get { return resultFile; } set { resultFile = value; } }
public void Run(IIntegrationResult result) { throw new System.NotImplementedException(); } } }
|
All that is left, is the code that actually writes the wanted results to the file.
This results for example in :
using System.Collections; using System.IO; using System.Xml.Serialization; using Exortech.NetReflector;
namespace ThoughtWorks.CruiseControl.Core.Publishers { [ReflectorType("filePublisher")] public class FilePublisher : ITask { private string resultFile = "Result.txt";
[ReflectorProperty("resultFile")] public string ResultFile { get { return resultFile; } set { resultFile = value; } }
public void Run(IIntegrationResult result) { PublishIt(ResultFile, result); }
private void PublishIt(string targetFile, IIntegrationResult result) { StreamWriter Result = new StreamWriter(targetFile, false); System.Text.StringBuilder Info = new System.Text.StringBuilder();
Info.AppendFormat("Project {0} has status {1}", result.ProjectName, result.Status); Info.AppendLine(); Info.AppendFormat("Modifications :"); Info.AppendLine(); foreach (Modification mod in result.Modifications) { Info.AppendFormat(mod.ToString()); Info.AppendLine(); } Result.WriteLine(Info.ToString()); } } }
|
In ccnet.config, you define it as follows in the
publishers section :
<publishers> <filePublisher resultFile="c:\logsresult.txt" /> </publishers>
|