Showing posts with label ccnet. Show all posts
Showing posts with label ccnet. Show all posts

27 October 2012

ccnet config in source control

Two posts ago I had a comment from Ignace in which he suggests the idea of having the configuration file of ccnet in the cvs (perforce in his and my case, but the idea applies to other systems as well). This is indeed a brilliant idea, because if you do that, the state of the code on the cvs will match the state of the config file (or build script, what it actually is). This is something you really need because more often than not the code and the way you build it are thightly coupled. It made me put on the thinking cap.

You cannot put the config file next to the code base, because
  • There are multiple projects in one config file, so in what project would you include it then?
  • There are multiple branches of a project, so what branch should define the build process for all other branches? What with merging branches then?

Another problem is that the server who uses the build script is a server. It cannot open up p4v and get the latest version. So if this needs to be done manually you can just as well just edit the script on the server an be done with it. The only reason to have the script on the cvs would be for logging purposes and backtracking. Not a good motivator, people would easily forget to update the script on the cvs, thus having the config out of sync which defeats the whole purpose then.

Googling a bit on this topic brought me to this post, which is actually the perfect solution for above issues. The config in the post did not work, at least not on the 1.8.2 server and with perforce, so here is my version:

<project name="Config File Monitor">
    <workingDirectory>%depot-root%\BuildServers\ServerName\</workingDirectory>
    <triggers>
        <intervalTrigger seconds="60"/>
    </triggers>
    <sourcecontrol type="p4">
        <view>//depot/BuildServers/ServerName/ccnet.config</view>
        ... and the other params you need ...
    </sourcecontrol>
    <tasks>
        <nullTask />
    </tasks>
    <publishers>
        <xmllogger />
    </publishers>
</project>

Add this project to your config file, place the file at the location specified in it (use a separate folder for each server) and change the path in the ccservice.config file. The project will check every minute if there is a new version of the config file and update it if needed. Since the server is monitoring that file it will automatically reload the configuration. I have not noticed any performance issues on the server of checking perforce every minute. Also note, I really needed to add the nulltask, otherwise it wouldn't work - no idea why, never bothered to check why.

I wanted to test this out for a while before posting about it, but now I know I'm very happy with this solution. We don't have to give special access anymore to some path on the server for users to be able to change the build config, they can just use perforce. The versions are in sync with the builds, and since you need to add a new project for every new branch it's good that the config file resides in its own folder - no messing with branches or anything. People can now also always review how a build is made themselves and don't have to ask the person responsible for the build server how the the build is made. A fine idea by Ignace and a fine solution I've found.

Have fun with this!

05 September 2012

ccnet 1.8.* and powershell

Two weeks ago I finally got my Windows Server 2008 R2! Hurray! I can now start on my buildserver for all the projects that I'm working on.

It cannot be over-emphasized how important it is to have a buildserver for your project. In fact, it is even an item in the c++ coding standards book: "Use an automated build system"
Some people ask me what the use is of a buildserver, and I answer them: "Do you want to take the responsibility to release a product that has not been build by a buildserver? Do you want to release a build that has been put together manually by somebody of the team, in the hope he remembers to take all the right steps, that he is completely in sync with the cvs, without locally changed files? Or even worse, say you want to release a second version, this time another person must do the build (the first one is on holliday), are you sure he knows all the steps, that he is in sync and that he has the correct compiler installed?"

This is only one reason why you'd use a buildserver, I can think of many more. Even for a small project, that only you yourself work on, it is good to let the buildserver build it. One reason (there are more): small one-person projects tend to become bigger projects and attract more developers and before you know it there is a small team working on it, or it is used in a bigger project. I've seen this happen more often than not.

But that's not the topic that I want to write about. For years I've been using ccnet, an open source, .Net based build server. Extremely easy to use, very versatile.
Recently the latest version 1.8.0 was released so I installed it on my new server. I haven't had the time yet to review the new features because I need my projects up and running fast.

One of the important features I always use is the assembly version labeller, which for me looks like:

<labeller type="assemblyVersionLabeller">
<major>1</major>
<minor>2</minor>
<incrementOnFailure>false</incrementOnFailure>
</labeller>

In combination with perforce this gives me a version number as an environment variable that looks like 1.2.{buildnumber}.{versionnumber}. From that number I know what build it was, and with what version on perforce it was build. With this number it is key to incorporate it as mush as possible in the application; in the installer, in the exe, in the assemblies, etc. Because with this information, if your application is in the field and you start receiving bugs and feedback, you always know exactly about which version people are talking, you know exactly with what files that exe was build that had a specific error, you know when they fucked up and when you did. It's something I've learned from dire experience at Larian. There were a lot of versions of Divinity II made and the first ones did not have a good version number, which made it impossible to track bugs and user's versions.

So for C# exe's and assemblies I use this powershell script:

$version = [environment]::GetEnvironmentVariable("CCNetLabel","Process")
foreach($arg in $args)
{
$file = [System.IO.Path]::Combine($arg, "Properties\AssemblyInfo.cs")
$old = "`"[0-9]+.\s?[0-9]+.\s?[0-9]+.\s?[0-9]+`""
$new = "`"" + $version + "`""
(Get-Content $file) | % {$_ -Replace $old , $new} | Set-Content -Path $file
}

As argument you give it the folder of your assembly, and it will change the version numbers to the ccnet label version number. This needs to be done before the devenv task and the result is an assembly or exe with correct version numbers if you look at the details of the file. This is extremely convenient to ask a user what version of a certain file they have, or to check yourself what version you're using.

And then hell started for me, somehow the script that I've used on several other ccnet servers, didn't work on the new one. I had the executing policy correct (even for 64bit Windows) which is the common pitfall, but no luck.
To make a long story short: the 1.8.0 and 1.8.1 version of ccnet place the arguments for your script before your script instead of after in the command line, a typo I guess. Luckily it is open source so you can easily create a quick fix until it is fixed in a new version (it really is easy, the source compiles right out of the zip).

But what I forgot was that the "scriptsDirectory" parameter must be absolute or the task takes the default one (which does not exist for the LocalSystem user). You'd expect that you can give a relative path as in all other ccnet tasks, but not for the powershell task. Apparently this is a bug that's always been in there, but I forgot. I looked in the source to fix it but it appears te be a little more tricky than I imagined.

In any case, you're warned now, and you can follow this thread on the ccnet user group if you'd like to know if it gets resolved. There's also some more info there.