Wednesday 10 October 2012

Using CruiseControl.Net to build branched code from Subversion

WARNING: You will want to treat this post with caution. Subsequent investigation has indicated that the original solution provided is inaccurate. I’ve left the post in place as a record of my investigation into the Subversion Source Control Block in CCNet.  There’s an Update at the bottom of the page that describes how CCNet implements autoGetSource  and cleanCopy for Subversion and the probable cause of our issue.
We recently branched some code in Subversion ready to kick off development for the next phase of a project. We employ continuous integration (CI) and use CruiseControl.Net (CCNet) so we thought it would be a simple matter of cloning the existing CCNet projects and modifying the Subversion repository path to point to the new branch rather than the trunk. However, we discovered that the branch builds were actually getting the source from the trunk even though the repository URL was pointing to the new branch. The solution to this problem turned out to be simple but took a bit of head scratching.
We are using CCNet version 1.6.7981.1.
Firstly, our Subversion repository a structure similar to the following:

Repo
So, we have a repository root under which are a set of projects. Each project has a branches folder and a tags folder. Branches and tags can contain as many branches and tags as necessary. Each project also has a single trunk for the current working copy.
To build the trunk we had CCNet source control configuration like the following (note that the trunkUrl is pointing to the trunk in our repository):
<sourcecontrol type="filtered">
  <sourceControlProvider type="svn">
    <trunkUrl>http://<server name here>/repository/ProjectName/Trunk</trunkUrl>
    <workingDirectory>C:\SomePathToWorkingDirectory</workingDirectory>
    <executable>c:\svn\bin\svn.exe</executable>
    <username>someusername</username>
    <password>somepassword</password>
  </sourceControlProvider>
  <exclusionFilters>
    <pathFilter>
      <pattern>/SomePathToFilter/*.*</pattern>
    </pathFilter>
  </exclusionFilters>
</sourcecontrol>

To build a branch we had CCNet source control configuration like the following (note that the trunkUrl is now pointing to a branch in our repository):
<sourcecontrol type="filtered">
  <sourceControlProvider type="svn">
    <trunkUrl>http://<server name here>/repository/ProjectName/Branches/v2.0.0.0</trunkUrl>
    <workingDirectory>C:\SomePathToWorkingDirectory</workingDirectory>
    <executable>c:\svn\bin\svn.exe</executable>
    <username>someusername</username>
    <password>somepassword</password>
  </sourceControlProvider>
  <exclusionFilters>
    <pathFilter>
      <pattern>/SomePathToFilter/*.*</pattern>
    </pathFilter>
  </exclusionFilters>
</sourcecontrol>

But this configuration failed as described above; we ended up building the trunk code, not the branch.
The solution turned out to be to use the autoGetSource configuration element of the Subversion Source Control Block [1]. There is limited documentation about this element but what we are told is it indicates “whether to retrieve the updates from Subversion for a particular build”.
<sourcecontrol type="filtered">
  <sourceControlProvider type="svn">
    <autoGetSource>true</autoGetSource>
    <trunkUrl>http://<server name here>/repository/ProjectName/Branches/v2.0.0.0</trunkUrl>
    <workingDirectory>C:\SomePathToWorkingDirectory</workingDirectory>
    <executable>c:\svn\bin\svn.exe</executable>
    <username>someusername</username>
    <password>somepassword</password>
  </sourceControlProvider>
  <exclusionFilters>
    <pathFilter>
      <pattern>/SomePathToFilter/*.*</pattern>
    </pathFilter>
  </exclusionFilters>
 </sourcecontrol>
This seems to have solved the problem and the branch builds are now working correctly. However, I’m not all together sure why this works because the documentation for our version of CCNet indicates that autoGetSource is optional but defaults to ‘true’.

Update

Having been confused by this behaviour I’ve had a look at the CCNet source code for the Subversion Source Control Block (ThoughtWorks.CruiseControl.Core.Sourcecontrol.Svn class) for the version we are using (1.6.7981.1).
Firstly, the AutoGetSource property is set the ‘true’ in the class constructor and – as far as I can see – it is only referenced in the GetSource(IIntegrationResult result) method.  So, it should be ‘true’ if you don’t set it in your CCNet config file.
public override void GetSource(IIntegrationResult result)
{
    result.BuildProgressInformation.SignalStartRunTask("Getting source from SVN");

    if (!AutoGetSource) return;

    if (DoesSvnDirectoryExist(result) && !CleanCopy)
    {
        UpdateSource(result);
    }
    else
    {
        if (CleanCopy)
        {
            if (WorkingDirectory == null)
            {
                DeleteSource(result.WorkingDirectory);
            }
            else
            {
                DeleteSource(WorkingDirectory);
            }
        }
        CheckoutSource(result);
    }
}
Looking at the code above if you set autoGetSource to ‘false’ CCNet won’t get try to checkout/update the source from Subversion at all.
Next, if the Subversion directory exists and you haven’t set cleanCopy to ‘true’ in CCNet config, CCNet will do a Subversion update on the existing code. Otherwise it will end up doing a Subversion checkout, deleting the working directory first if cleanCopy was set to ‘true’.
It now seems very unlikely that explicitly setting autoGetSource to ‘true’ would have had the effect of fixing our problem. It seems much more likely that the first time the build ran it did a checkout against the the trunk and not the branch (perhaps because the CCNet config trunkUrl was incorrect at that time). Subsequent builds were therefore doing an update against the trunk. As part of trying to resolve the issue we deleted the working directory (and the SVN directory within it) which would have forced a fresh checkout and we can assume that the trunkUrl was then correctly pointing to the branch.

References

[1] CruiseControl.NET : Subversion Source Control Block