views:

694

answers:

2

I have two projects set up in CC.NET: CI build and nightly build.

Both of them execute the same nant script, but with different parameters.

The CCNET label (currently generated by the DefaultLabeler) is embedded into AssemblyInfo as the build part of the version (e.g *MajorVersion.MinorVersion.CCNET_Label.SVN_Revision*).

For more consistent versioning I would like both projects to share the same CCNET label value.

I have investigated the labelers that are available as part of the CC.NET installation but could not find one that does what I want.

How do I share a label value between multiple CC.NET builds?
If there is a better way to do this, I would like to know.

EDIT: Found a way. See my answer below.

+4  A: 

I could not find an existing solution that to do what I needed, so I ended up writing a custom CruiseControl.NET labeller.

Here's how it is done:

  1. Create a new project. This will be used as a plugin library by CC.NET

  2. The name of the output DLL needs to match *ccnet.\*.CruiseControl.plugin*. Go to project properties and change "Assembly name" to *ccnet.<insert name here>.CruiseControl.plugin*

  3. In your project, add references to the three assemblies found in the CC.NET server installation directory (default is: C:\Program Files\CruiseControl.NET\server):
    • NetReflector.dll
    • ThoughtWorks.CruiseControl.Core.dll
    • ThoughtWorks.CruiseControl.Remote.dll

  4. Create a new public class such as this:
    using ThoughtWorks.CruiseControl.Core;
    using ThoughtWorks.CruiseControl.Remote;
    
    // this is the labeller name that will be used in  ccnet.config
    [ReflectorType("customLabeller")]
    public class CustomLabeller : ILabeller
    {
     [ReflectorProperty("syncronisationFilePath", Required = true)]
     public string SyncronisationFilePath { get; set; }
    
     #region ILabeller Members
    
     public string Generate(IIntegrationResult previousResult)
     {
      if (ShouldIncrementLabel(previousResult))
       return IncrementLabel();
    
      if (previousResult.Status == IntegrationStatus.Unknown)
       return "0";
    
      return previousResult.Label;
     }
    
     public void Run(IIntegrationResult result)
     {
      result.Label = Generate(result);
     }
    
     #endregion
    
     private string IncrementLabel()
     {
      if(!File.Exists(SyncronisationFilePath))
       return "0";
    
      using (FileStream fileStream = File.Open(SyncronisationFilePath,
           FileMode.OpenOrCreate,
           FileAccess.ReadWrite,
           FileShare.None))
       {
        // read last build number from file
        var bytes = new byte[fileStream.Length];
        fileStream.Read(bytes, 0, bytes.Length);
    
        string rawBuildNumber = Encoding.ASCII.GetString(bytes);
    
        // parse last build number
        int previousBuildNumber = int.Parse(rawBuildNumber);
        int newBuildNumber = previousBuildNumber + 1;
    
        // increment build number and write back to file
        bytes = Encoding.ASCII.GetBytes(newBuildNumber.ToString());
    
        fileStream.Seek(0, SeekOrigin.Begin);
        fileStream.Write(bytes, 0, bytes.Length);
    
        return newBuildNumber.ToString();
       }
     }
    
     private static bool ShouldIncrementLabel(IIntegrationResult previousResult)
     {
      return (previousResult.Status == IntegrationStatus.Success ||
        previousResult.Status == IntegrationStatus.Unknown)
     }
    }
    


  5. Compile your project and copy the DLL to CC.NET server installation directory (default is: C:\Program Files\CruiseControl.NET\server)

  6. Restart CC.NET Windows service

  7. Create a text file to store the current build number

  8. Add the new labeler to you project definition in ccnet.config file:
        <labeller type="sharedLabeller">
            <syncronisationFilePath>C:\Program Files\CruiseControl.NET\server\shared\buildnumber.txt</syncronisationFilePath>
    <incrementOnFailure>false</incrementOnFailure>
        </labeller>
    
    


Arnold Zokas
A: 

I ran into the same issue, but found that using the <stateFileLabeller> in conjunction with the <assemblyVersionLabeller> proved to be a much simpler solution.

The only gotcha about using the stateFileLabeller is that you can't specify a directory for your state files in a project, becuase CCNET won't find it. I left it in the default directory and it works great.

ProKiner