Quantcast
Viewing all articles
Browse latest Browse all 19

CI Build with TeamCity, Git and GitFlow workflow

Background

Image may be NSFW.
Clik here to view.
Stash Git Repositories
Over the past few months I’ve been slowly revising my approach to version control and continuous integration. Until recently we used Subversion (svn) for our version control but I wanted to use more of a distributed VCS. We are a Windows shop and generally prefer GUI tools to command lines. Perhaps we are unusual in that but nevertheless that’s the way we rock, so some of the Git tools didn’t sit very well with us and our Windows servers and we were initially a bit ‘luke warm’ towards Git. Then we discovered Atlassian’s VCS offerings, Stash and SourceTree. We already use a number of Atlassian tools so these newer offerings were easy for us to adopt. Stash provides features similar to GitHub but behind the firewall and SourceTree is a free Git client. One very useful feature of Stash is the way it integrates tightly with SourceTree, enabling a repository clone from stash with just two mouse clicks. Take that, command line! Another great future with SourceTree is that it can ‘hide’ the Git staging area. This is one aspect of Git that we really didn’t like, having to continually and manually add changes to the staging area before committing. SourceTree has an option ‘Use the Staging Area’ which is on by default, but can be easily turned off to make the client experience much closer to what we are used to with other VCS like Subversion.

Git Workflow

Image may be NSFW.
Clik here to view.
image
Having embraced Git, another decision we faced was how to use all the new branching capabilities in a sensible and consistent way. Once you start using Continuous Integration builds, then pretty soon you also have to start worrying about things like product versioning and managing things like releases and hotfixes. One day while using SourceTree, we noticed an icon in the toolbar called GitFlow. We didn’t know what it did, so we clicked it and it blew our minds! It turns out, these problems are mostly solved and well documented.

GItFlow is really two things.

  1. A method of versioning and branching software projects
  2. A Git add-in that supports the methodology

The approach to branching and versioning is described by its inventor, Vincent Driessen, on his blog. He has a nice graphic illustration that sums up the workflow (click the image to see it full size on Vincent’s blog)Image may be NSFW.
Clik here to view.

This may seem quite complex at first sight, but in fact it is very simple and works incredibly well in practice. The branches are nothing special, they are just ordinary Git branches, but the GitFlow extensions make working with them really simple. The cornerstone of the idea is Semantic Versioning and being very clear about what is supposed to happen on which branches and what triggers a new version.

The master branch is reserved for releases. By definition, each commit (or, rather, merge) to master is a new product release. Therefore, developers should not be pushing to master (and Stash allows easy enforcement of these rules).

Most development is therefore done on the develop branch. Developers can either push directly to develop or can make feature branches off develop. We like to create a branch for each work item. We track work items in Jira/GreenHopper so if we start working on an work item with an ID of JIRA-27 then we’ll create a feature branch called feature/JIRA-27. Once that’s complete, it gets merged back into develop.

Image may be NSFW.
Clik here to view.
image
If, like me, you are relatively new to Git or DVCS, then I encourage you to try out GitFlow. I have found that it has added a much needed layer of structure over the very powerful, but policy agnostic, Git tools. SourceTree is free and has GitFlow support built in and makes it very easy to use. At any moment, you can click on the GitFlow icon to see what your next workflow actions coudl be. The first click offers to initialise your repository for GitFlow. All this does is add a few Python scripts and create the branches required to support the methodology. It doesn’t alter the repository format or do anything irreversible, you retain full control over everything and if later you decide you don’t like it, you can simply stop using it and/or delete the branches. Subsequent clicks of the GitFlow icon will offer you sensible choices based on the state of your repository and your last workflow action.

Semantic Versioning

 

As already mentioned, one of the cornerstones of GitFlow is Semantic Versioning (aka SemVer). There is a tension here between Microsoft’s Major.Minor.Build.Revision format and the SemVer format, which looks more like ‘2.0.0-rc.2’. A SemVer makes a good candidate for the [AssemblyConfigurationAttribute] but clearly isn’t going to work with Microsoft’s versioning scheme, so this problem needs to be solved.

We have been accustomed to basing our versions on a static MajorVersion and MinorVersion, plus the Subversion revision number, plus the TeamCity auto-incrementing build counter. Now that we use Git, we no longer have a revision number that we can use and the SemVer string isn’t compatible, so what to do?

We have retained our MajorVersion and MinorVersion and added the concept of a PatchVersion, in line with SemVer’s recommendations. Each of these version numbers is incremented manually, within the TeamCity build configuration (more on that later).

We build our SemVer version string from those three static numbers, plus the Git branch being built, plus the TeamCity build counter, as follows:

Major.Minor.Patch-branch.build

The SemVer string forms the Build ID in TeamCity and, coming up, I’ll show how we manage this in our TeamCity configuration.

Putting it All Together in TeamCity

The point of doing all this is the results. I’ve come up with a fairly standard approach to setting up my builds in TeamCity to support GitFlow and SemVer. It has taken a few projects to refine this and get everything just right, so I’m publishing my approach here in the hope that it may save people some time. We are currently using TeamCity 8 EAP, so your screens may look slightly different, but we implemented most of this in TeamCity 7 so it will all work. Before we start, here’s the final result:

Image may be NSFW.
Clik here to view.
image

Project Build Template

TeamCity offers Build Templates that offer a sort of ‘inheritance’ pattern for build configurations. Templates can be tricky to get right and sometimes they seem like more trouble than they are worth, but in this case it can be a very convenient way to support the different builds required for GitFlow. So, here’s my base template. Click on any of the images to see it full size.

General Settings

Image may be NSFW.
Clik here to view.
image

The key setting here is the Build Format Number, which we set to %BuildFormatSpecification%.%build.counter%. The %build.counter% is TeamCity’s auto-incrementing build counter and will be different for every build configuration based on this template, so it can’t form a significant part of the SemVer version string. As you will see later, %BuildFormatSpecification% is a user-defined build parameter that we’ll specify elsewhere. Bear with me for now and it will make sense.

VCS Settings

Image may be NSFW.
Clik here to view.
image
First we need to define a VCS root for the project. We keep each project in its own repository so generally speaking each build project has its own VCS root. Here’s an example from a current project that I am just setting up. The key fields are Default Branch (%DefaultBranch%) and Branch Specification (%BranchSpecification%). Again, we are using build parameters here that can be configured per build configuration, more later.

The rest of the settings on this page (not shown in the screen snipping) are specific to your Git implementation and it should be self evident what you need to put in there.

Tip: One point to note here is that, because we’ve used build parameters that are yet to be defined, TeamCity doesn’t quite have enough information to connect to the repository yet. If you click the ‘Test Connection’ button, it may fail. To get that all-important confidence indication, simply put in a real branch name in the Default Branch field (e.g. master). Then click the Test button and make sure that is successful, once youre happy go back and change the Default Branch field to %DefaultBranch%.’

OK so having created a VCS root, now we need to enter the VCS Settings. You can actually leave all these settings as default, but we like to turn on build labelling for successful builds using the pattern build-%system.build.number%. This tags all of our successful builds in the repository with a label along the lines of ‘build-1.0.0-develop.27’ which as you will see shortly, comes from our SemVer version string. Here’s an example of what this looks like in the SourceTree view of the Git repository:

Image may be NSFW.
Clik here to view.
image

Build Steps

Image may be NSFW.
Clik here to view.
image
Clearly the contents of your build steps will need to vary from project to project, but they should be quite similar across build configurations within the same project. I like to put as much common build logic as possible into the template. My typical build sequence will be:

  1. NuGet package restore
  2. Build Visual Studio Solution (Debug/CodeAnalysis configuration). This performs code contract analysis.
  3. Unit Test & Code Coverage, on the debug build.
  4. Build Solution (Release configuration)
  5. Duplicate detection
  6. ReSharper Code Inspections (a nice feature of TeamCity).

Perhaps the most interesting part of the Build Steps screen is under Additional Build Features. Here I use the AssemblyInfo Patcher feature to version-stamp my built assemblies, as shown. I set all of the available versions to the string %MajorVersion%.%MinorVersion%.%PatchVersion%.%build.counter% which gives us our Microsoft compatible version.  If if my build label was build-1.0.0-develop.27, then my SemVer string is 1.0.0-develop.27 and my AssemblyVersion becomes 1.0.0.27. Ideally I’d like to set the AssemblyConfiguration attribute to the SemVer strings, but TeamCity doesn’t currently support that (please vote for this issue).

It may be better to come back later and fill in the build steps once the rest of the template is configured. In particular, you’ll probably want to have step 7 (Build Parameters) complete. So just enter a dummy build step for now and press on…

Build Triggers

I like to add NuGet Dependency Triggers here for any of my own NuGet packages I’ve used. Other than that, I don;t add any build triggers in the template, preferring to add them directly to the build configurations for more control. So we can skip over this screen.

Dependencies

This will depend in your project, I don;t have any build dependencies so skip forward again.

Build Parameters

Image may be NSFW.
Clik here to view.
image
This screen is definitely of interest. This is where we set values for some of the parameters we’ve been defining and set any default values that will be inherited by the build configurations. Here is where we set the static version numbers and pull them all together into the build ID aka Build Format Specification. Settings are as follows:

  • BranchSpecification– left blank, this will be set per build configuration
  • BuildFormatSpecification– set to: %MajorVersion%.%MinorVersion%.%PatchVersion%-%teamcity.build.branch%
  • DefaultBranch – set to develop, overridable in each build configuration.
  • MajorVersion, MinorVersion, PatchVersion– set as appropriate. This sets the version scheme for the entire project.

Tip: Consider what happens when a user clicks on the ‘Run’ button in TeamCity. A manually started build allows the user to set build parameters. You may wish to hide some of these parameters from the user (for example, the version numbers) or you may wish to force the user to supply a value for some parameters. TeamCity lets you do this by editing the Edit button within the parameter Spec. For example, to hide teh MajorVersion parameter, configure the parameter spec as follows:

Image may be NSFW.
Clik here to view.
image

I prefer to hide all of the version number parameters because I want those set by policy not by the users. The one value that users definitely need to be able to set is the DefaultBranch parameter.

Agent Requirements

The final template is where you set any agent requirements for your project and these will be specific to your needs. Note that there should be an implicit requirement as follows:

Image may be NSFW.
Clik here to view.
image

We haven’t yet defined a branch specification. That will be done in each build configuration as each configuration will need to operate on different branches.

Build Configurations

Ok now we have a template, we can create the build configurations from it. I like to create the following three configurations as a minimum, you will probably need others depending on your project needs, but these should get you started:

  • Integration Builds– builds commits on the develop and feature/* branches
  • Hotfix Builds– builds commits on the hotfix branch
  • Release Builds– build commits on the release-hardening branches and on master.

Image may be NSFW.
Clik here to view.
image
Note that each build configuration can have additional steps and can also disable any of the default steps provided by the template. There is a lot of flexibility here. I use this to add deploy steps that deploy web sites to different servers, for example.

Integration Builds

Lets look at the Integration Builds configuration in detail. Upon creating this new build configuration from the template, you’ll see a dialog asking for the build parameters. Here is where you’ll enter your branch specification that is appropriate for the GitFlow branch(es) youre building.’

In the case of the integration build configuration, I like to build the following branches:

+:refs/heads/(develop)
+:refs/heads/feature/(*)
+:refs/pull-requests/(*/merge)

Its worth just taking a moment to explain this branch specification. If you’ve built from Git before in TeamCity then you’ll know that it handles branches somewhat different from Subversion where typically there would be a different build configuration for each branch built. With Git, TeamCity has direct support for building from multiple branches within the same build configuration. This branch specification specifies commits on the ‘develop’ branch, any sub-branches of the ‘feature’ branch and the final part will automatically build any pull requests submitted as if they had been merged into the target branch. This last feature is quite interesting, it may not be something you need but it is a useful trick to know about. I typically include it as standard. It’s very useful for knowing if a pull request will adversely affect the build prior to merging it.

The brackets in the branch specification determine how TeamCity displays and labels builds on that branch. So refs/heads/(develop) labels the branch as ‘develop’ rather than ‘refs/heads/develop’ which is a bit unwieldy.

Image may be NSFW.
Clik here to view.
image
Leave all the other parameters as-is and they will be inherited from the template. In this way, the build version for the entire project can be configured by editing only the template and all of the build configurations will automatically update. According to GitFlow, the version number gets bumped with the creation of either a release/* branch, or a hotfix branch, so when creating either of those types of branch you’ll need to remember to edit the TeamCity build to adjust the version number. Hotfixes cause the Patch Version to increment; Releases cause either the MajorVersion or MinorVersion to increment, depending on whether the release has breaking changes.

General Settings

On the General Settings page, set your build description. For my Integration build I set it to “Builds commits from the develop and feature/* branches and any pull requests or personal builds. Note that the Build Number Format field is greyed out, this is inherited from the template.

Version Control Settings

There are no settings to make on this page, it is all inherited from the template.

Build Steps

Here, you can add any additional build steps needed that are specific to this configuration. An example would be deployment to a test server. I don;t deploy from my integration builds but your needs will vary. You can also disable and of the build steps that were inherited from the template, if you need to, but you can’t delete them.

Build Triggers

Image may be NSFW.
Clik here to view.
image
This is where I’ll add a VCS trigger that is tailored specifically for the branches I’m building. I generally trigger a build on each check-in but allow merging successive commits from the same developer. I leave VCS Trigger Rules and Branch Filter set to the default values, which will trigger on any commit on the branches specified in the BranchSpecification parameter, which is normally what you want.

Image may be NSFW.
Clik here to view.
image
One more thing to add here (in the Integration Build configuration) is a Branch Remote Run trigger. This lets developers run private ‘personal builds’ on a designated branch so that they can try out their changes in private without disrupting the normal build process. To get this feature, add a new Branch Remote Run build trigger. TeamCity offers the default remote branch specification as “refs/heads/remote-run/*” which you can accept as-is or edit to match your preferred branch pattern. I prefer to use ‘personal’ instead of ‘remote-run’. The effect of this is that any commits on a personal/* branch will trigger a personal build against that last committer on that branch. The build results will then only be visible to the committer and there will be no VCS labelling, etc. Note that for personal builds to work, the ‘personal’ branch must not appear in the normal BranchSpecification parameter. If it does, then builds will be run as ‘public’ builds instead.

Build Parameters

This screen should already have been populated from the dialog when you created the build configuration. You can adjust the settings or add anything additional that you need here.  If you’ve followed my instructions exactly, then the only parameter that differs from the template will be the BranchSpecification parameter, which should already be set.

OK, that’s it. Commit something on develop or a feature branch and the build should light up.

Image may be NSFW.
Clik here to view.
image

One more trick to consider in your Build Steps configuration. If you are using Atlassian Stash for your Git version control, then there’s a TeamCity plugin that will report build status back to Stash, so you can see whether any given commit has a successful build. The plugin is available at http://code.mendhak.com/teamcity-stash/ and it is very simple to configure:

Image may be NSFW.
Clik here to view.
image
 Image may be NSFW.
Clik here to view.
image

Other build configurations can be set up similarly. The standard builds I normally set up (and their differences from the integration build) are as follows.

Pre-Release Builds

Build Triggers – no ‘remote run’ trigger.

Build Parameters: Branch Specifications:
+:refs/heads/hotfix/(*)
+:refs/heads/release/(*)

DefaultBranch: <empty> (forces user to specify a branch for custom builds).

Release Builds

Build Triggers – no ‘remote run’ trigger.

Build Parameters: Branch Specifications:
+:refs/heads/(master)

DefaultBranch: <empty> (Don’t really want users running custom builds here).


Viewing all articles
Browse latest Browse all 19

Trending Articles