Wednesday, 18 March 2009

Creating a custom CruiseControl.NET Plugin

What's great about ccnet is that you have the ability to create custom plug-in's within visual studio using reflection. You can then compile these plugins into DLLs and place them into the ..\CruiseControl.NET\server directory.

You may have used plugin's previously, many can be found at http://ccnetplugins.sourceforge.net/, to aid your continuous integration process for managing releases and controlling versioning.

Versioning is a very important aspect of release management and continuous integration as a whole. Versioning should be maintained and traceable across the whole build process.

For me, I have the following components versioned...
- All Libraries (dll files)
- Source Repository Labels (within SourceSafe, CVS, Sub Version etc.)
- CruiseControl.NET labels (within the web interface for each component)
- The Setup Project (this will also add the version info to Add/Remove Programs etc)


So, if I install a component (MSI in this case) on a clients machine, I can trace it back to the build info, the included libraries, and also, the code in the source repository!
The process of building this standard versioning system, however, isn't quite as trivial as one would hope. Alongside our build system (MSBuild, Nant etc) we can utilise CruiseControl.NET plugin's to aid our CI process.

Here is a code snippit I have used while injects version information into a Wix3 Setup Project.

namespace ThoughtWorks.CruiseControl.s34n.VersionWixFile
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    using System.Xml;
    using Exortech.NetReflector;
    using ThoughtWorks.CruiseControl.Core;
 
    [ReflectorType("VersionWixFile")]
    public class VersionWixFile: ITask
    {
        string wixproj;
 
        [ReflectorProperty("WixFile")]
        public string WixFile
        {
            get { return wixproj; }
            set { wixproj = value; }
        }
 
        #region ITask Members
 
        public void Run(IIntegrationResult result)
        {
            // Get Version Info
            string newVersion = "v" + result.Label;
            
            // Inject Into WixProj File
            if (File.Exists(wixproj))
            {
                // Get contents of file
                StreamReader sr = new StreamReader(wixproj);
                string wixProjFile = sr.ReadToEnd();
                sr.Close();
                sr.Dispose();
 
                // Replace version info
                wixProjFile = wixProjFile.Replace("[[VERSION]]", newVersion);
 
                // Write contents of file back out
                StreamWriter sw = new StreamWriter(this.wixproj);
                sw.Write(wixProjFile);
                sw.Close();
                sw.Dispose();
            }
            else
            {
                throw new Exception(string.Format("The specified .wixproj file '{0}' does not exist.", this.wixproj));
            }
        }
 
        #endregion 
    }
}



As you can see, in this example, two references are used..

  using Exortech.NetReflector;
  using ThoughtWorks.CruiseControl.Core;



These DLLs are shipped with CruiseControl.NET, and must be imported into the current solution for your plugin.

We implement the "ITask" interface and must override the "Run" method. The IIntegrationResult parameter provides us with information about the current build, including and not limited to, the version number.
This plugin does a simple replacement, where it looks for a piece of text in the Win project called: [[VERSION]]... it then replaces this with the version of the current build.

Compile the plugin within visual studio and make sure the DLL Library begins with "ccnet." and ends with ".plugin.dll". This is a known issue with CruiseControl.NET, and the plugin wont work correctly otherwise. An example might be: ccnet.s34n.VersionWixFile.plugin.dll

Place this DLL in the "..\CruiseControl.NET\server" directory alongside the ccnet.config file.

The plugin can then be called from the ccnet.config file by using the following code...

<!-- Inject ccnet version into Wix Project File -->
<VersionWixFile>
      <WixFile>[WIX PROJECT FILE PATH]\Product.wxs</WixFile>
</VersionWixFile>


Once this has been injected into the build, we can build the Wix project using MSBuild and produce a versioned MSI Setup Project! (dont forget to restart ccnet!)

No comments: