This article walks through the setup of a real world project in Continua CI. For this walkthrough we will use the open source project Fluent NHibernate, which is located on github. Fluent NHibernate allows you to write database to object mappings in strongly typed C#, however for this exercise we don't need to worry about what the project actually does. Any project can be used to test out Continua as long as the project can build successfully. We are using Fluent NHibernate for this exercise as it is relatively small and will allow us to get a simple build process up and running.
We'll cover:
Issue Connectors
In this example, all issues, bugs and merges for Fluent NHibernate are handled in Github's own issue tracking system. Issue Connectors in Continua allow you to link changesets that are tracked in Continua to their related issue on Github. If you're familiar with Github then you'll know that you can reference an issue in a commit message which becomes automatically linked to the issue.
Continua works in a similar way, although it allows you to specify the commit message pattern. Issue connectors are created in the Administration area, under Builds.
When you create an issue connector you can choose from a set of pre-defined issue trackers. For Github I'll choose 'Custom' and add in the project's issue URL, with an Id placeholder: https://github.com/jagregory/fluent-nhibernate/issues/{id}

We'll also specify a regex pattern to extract the issue Id from the commit message. The default Commit Message Regex #(?<id>\d+) is the same as Github's id pattern (ie. a hash followed by the issue number), so you will not need to change this value.
You can test out your URL and Regex by entering a dummy commit message. If the issue Id can be extracted from your message, you'll see the URL that is produced.
Creating the project
First I'll create a new project using the link on the home page

After filling out the Details page, I'll hit the Complete Wizard button which takes us to the Finished page. From here, we'll create a new configuration. If my project was going to have multiple configurations I'd set up shared repositories, variables etc, but for now that's not needed.
Creating the configuration
Versioning
Because this is an existing product with releases out in the wild, I need to choose a versioning scheme that lines up with the current one. I generally use Major.Minor.Bugfix as the format. I'm going to hard code Major and Minor in the wizard and update them manually whenever I want to do a release. The Bugfix number can be auto-incremented though, so I set the Version Format to 1.2.$Build.BuildNumber$.0. This is the first time we've come across an expression, so lets take a detour.
Expressions
In many circumstances Continua allows you to use place-holder expressions which are evaluated at build time. There are two types of Expressions: Variables & Objects. Variables are referenced using
%varname%, which will be familiar to Finalbuilder users. You can define at set variable values in many places throughout Continua.Objects are referenced using
$objectName$, and contain data relevant to the current context. For example, when you're editing a configuration you'll have access to objects releated to builds of that configuration.Expressions are a powerful way to customise your build. To see if they are available in the field you're editing, type either '$' or '%', if available, you'll see an intellisence window listing the available expressions.
Note that Continua Variables differ slightly from FinalBuilder variables. You no longer need to put an exclamation inside your variable %!variable% to expand the inner variable. You instead create a variable of Expression type. For more info on creating variables see Variables.

For a full list of dynamic values (expressions) you can place in the Version Format, see Version Format String. Note that you can place the Latest Changeset ID of your repository into this field. That will only work if you have setup and assigned the Configuration a Repository, this is explained in the next step.
You may have noticed that I've also set the Version Counter to 36. This is a property of the configuration that is auto-incremented for each build. It can be set to any number you like, so, for example, when I do a new Major or Minor release I set the counter back to 0. I set it to 36 because that's where I'm up to in my pre-Continua build history.
You may have also noticed that I added a trailing ".0" to my version format string. Later down the line I'll be using the calculated version string to set the version number of my executables and the installer, and a 4 part number makes life easier.
Creating a repository
Repositories can be created at the global, project or configuration level. Global repositories can be used by any config, project repositories by any configs in that project, and config repositories only by the config. Because my repository doesn't need to be used anywhere else, I'll create it at the config level. If I need to, I can change its scope later.

Tip: you'll need to have the software for your version control system installed on the Continua Server host
After naming the repository, I choose the issue tracker that I created earlier. This means that the issue connector will look for commit messages from this repository and try and link it up to the Github URL we supplied.
Because I'm hitting Github, I'm going to set the polling frequence to Less Frequent. I don't want to flood them with needless checks for changes in an infequently updated project. For internally hosted repositories we often have the polling interval set to Very Frequent.
Now I select the repository type as Git and add the URL. Because this is a public repository, and we are only pulling changes down, I can use the public read-only URL https://github.com/benrhughes/todotxt.net.git and don't need to supply a username or password.
Git is, obviously, a distributed version control system. Continua makes some extra options available which take advantage of its distributed nature to allow things like feature branch builds. My repository has two branches: dev (where most of the work happens) and master (which is where releases are built from). I want to be able to build from both, so I set Branches to Monitor to All branches. I also set the default branch to master. This is an important thing to get right: when you start a build manually it will always build the default branch.
Property collectors are how Continua discovers what software is installed on the server and agents. The Using field allows you to choose which Git version you'd like this repository to use. In all liklihood you only have one version of git installed, and the Default property collector will find it.
Once all that's done, I hit the Validate button, which will do its best to work out if I've entered reasonable settings.
Stages
This is the meat of Continua, where the actual build process is defined. I'm going to create 3 stages: Build, Test and Installer.

After each stage you'll see a funny arrow thing. This is the stage gate and it allows you to use expressions to stop a build progressing to the next stage. For example, you might only want to progress past your Test stage if more than 80% of your tests passed. For now I'm going to blank out any gate conditions and get on with defining my build.
Build
The first thing I'm going to do is update the Assembly Info class in my project with the current version. I add an AssemblyInfo Updater action, then use an expression to specify the path to the file:
![]()
The workspace
Each build has a workspace: a directory that is sync'd between the Continua server and any agents the build runs on. It contains lots of interesting stuff like a serialized copy of the build's variables, any source files that are changed during the build, any artifacts that are registered, and the build log. When a build is run on an agent, a copy of its source is placed in the workspace. Using a copy means that we can run multiple builds of the same project, at the same time, on the same agent without any conflicts. Cool, huh?
$Source$ references the directory within the build's workspace where its source is copied. Because you can have multiple repositories per configuration, you need to specify which repository you're after (you'll get a list of them in the intellisense window).

After the source path, I've appended the relative path to the assembly info file. I've also set the properties to update, using the $Build.Version$ expression.
Now I'll build the solution using the MSBuild action:
![]()
Again, I use the $Source$ expression to tell MSBuild where the solution file is and set some other MSBuild options. On the Artifacts tab I register the assemblies created by the build. These will be available for download after the stage has run.
![]()
And that's it for the build stage!
Test
All of the tests are in a single assembly, so all I need to do is create a single NUnit action and give it the path to assembly.
![]()
It's worth noting here that stages can run on different agents. So that this works as you'd expect, any changes to the source made in an earlier stage are re-applied. For this reason, I can reference the source location where the assembly was built, regardless of which agent it happened on.
Installer
So far so good, right? This is where things get a bit more involved. I use a Visual Studio Deployment Project to create the installer. It's free and (fairly) simple to use from within VS, but it's a bit of a pain to automate. Luckily there's this cool tool called FinalBuilder that can help out.
![]()
To run this we create a FinalBuilder action and tell it where the FB project is. Note that it's in source control. This is a significant difference if you're coming from FinalBuilder Server, which manages the project for you. Continua is build-tool agnostic, and while we obviously think FinalBuilder is the best choice, it doesn't get any special treatment.
My FinalBuilder project has a variable called version which is used to set the version in the deployment project. To pass the current version string from Continua to FinalBuilder, on the Variables tab of the action I just need to add version=$Build.Version$, again making use of expressions.
![]()
Now that the deployment project has been updated, I can build the solution. I need to use the Visual Studio action here because MSBuild doesn't know about deployment projects. Note that I don't register any artifacts from this action, because I'm not quite done with them yet.
![]()
I like to have the version number in the file name, so I add a Rename action to add the version to the MSI that Visual Studio produced. Now I add a Register Artifact action which finds any .msi files using **.msi. I know there's only one file, so this is fine. If I wanted to be explicit I could use an expression to specifiy the exact location of the file.
![]()
Here's my finished Installer stage:

That's the build setup done.
Triggers
In the usual CI fashion, I like to trigger off the dev branch whenever there is a commit. I could also trigger off master but as I only use it to create release builds, it's something I tend to do manually.
![]()
You can trigger builds from a few different events. I'm interested here in repositories, so that's what I select. Once I've done that I can see the respository I created earlier in the drop down list. Again, because Git is a DVCS I have some additional branch options. I select 'Pattern matched branch' and set the pattern to dev.
Now any commits to the dev branch will tigger a build.
Security
Just because I can, I'm going to make it so that I am the only one who can manually start a bulid. I create a new ACL entry and Deny the Registered Users group the Start Configuration permission. I've left things open enough so that someone could come and give themselves permission, but I could lock it down further if I wanted to.
![]()
Cleanup
Because this is a small side project, I don't need much in the way of historical build information. So, I override the global policy and set the minimum builds to 5 and the maximum to 10. That does what you would expect: keeps at least 5 but no more than 10 builds.

I've chosen to only delete Artifacts and Logs, which frees up disk space while keeping a record in Continua of the build. This means I can still see trends over time etc, but can't drill into logs or download the artifacts of cleaned up builds.
Time to build
That's all the Configuration setup we need to do - it is now ready to be started. But can any agents run it?
Agent compatibility
Different Continua agents will have different properties, based on the software that's been installed on them. Continua will do its best to make sure that stages are only sent to agents that can run them. Many of the Continua actions require external software, and we use Property Collectors to find out what software is installed. When Continua looks for an agent to run a stage on, it checks that the agent has the software that each of the actions needs.
Custom property collectors
Sometimes your build will need a component that's not directly related to an action. For example, todotxt.net requires the MSHTML assembly in order to build. To make sure any agent we build on has this, first I'll create a new property collector (from Administration -> Builds -> Property Collectors):
![]()
This will attempt to find Microsoft.mshtml.dll in c:\Program Files (x86)\Microsoft.NET\Primary Interop Assemblies\. I could provide multiple search paths if (for example) I had 32bit and 64bit agents.
Manual stage requirements
Now that I have a property collector, I can edit my Configuration, go to the Stages page, and double-click on the first stage:
![]()
I add a condition that $Agent.MSHTML.Path$ exists. Now this stage will only be sent to agents where that condition evaluates to true. Note that you can reference any of the Agent properties here. So, for example, if you ran a FinalBuilder project or a batch file that needed Git installed, you could add a $Agent.Git.Default.Path$ exists condition.
Checking compatibility
On the your configuration's page, there is an Agents tab which lists which agents are able to run the configuration. If you hover over the 'X' of an incompatible agent, it will tell you which properties are not available.

Ok, Go!
As long as you have a compatible agent, you should now be ready to start your build.