Automated builds and deployments with TeamCity and MSBuild

We've been using TeamCity as a build server for a while now. It's quick to install, point at Team Foundation Server (TFS) source control and a solution file, tell it where the test dll's are and bob's your uncle. We usually have a couple of different builds set up, one as a continuous integration build that polls TFS and builds on check in and one that runs overnight and produces a nightly build.

The nightly builds work well but the project I am working on at the moment involves multiple web applications (two websites and plus five wcf host sites) that need publishing. That's a whole lot of clicking every morning even once team city has got the latest version and built/tested it for you. I have heard about buildscripts but never written one so set about figuring out this MSBuild thing.

Writing an MSBuild Script

I knew I had four things that I wanted the script to achieve:

  1. Compile
  2. Test
  3. Package (Publish the websites)
  4. Deploy it to the test box for me!

MSBuild scripts are just xml, the same xml that Visual Studio project and solution files are. To begin with you need a project tag with the msbuild xml namespace and a default target (more about those in a minute).

<Project DefaultTargets="Run" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

The three main things you need to know about when working with msbuild are Properties, Targets and Tasks.

Properties

Properties are essentially variables. You can group them via PropertyGroups then refer to them via the $(PROPERTY_NAME) syntax.

<PropertyGroup>
    <!-- The NUnit-ToolPath is the directory where the nunit bin folder is located  -->
    <NUnit-ToolPath>Lib\nunit 2.5.3 bin</NUnit-ToolPath>

    <!-- The folder the packaged build will be copied to-->
    <PackageFolder>C:\NightlyBuilds\ExampleNightlyBuild </PackageFolder>

    <!-- The folder the build will be deployed to, this can be a share on a test server -->
    <DeployFolder>\\10.0.1.1\Example\</DeployFolder>
    <ClassLibraryOutputDirectory>bin\Release</ClassLibraryOutputDirectory>
  </PropertyGroup>

You could put lots more of the script into properties to make it re-usable, I just added the stuff that I am likely to change for this project like where it is packaged to on the local machine and the remote folder I want it deployed to.

Targets

Targets are actions, similar to methods I suppose. The four things I wanted the script to do above are the four targets I have in my script. Targets can call other targets, and targets can be dependent on other targets.

<Target Name="Run">
    <CallTarget Targets="Compile"></CallTarget>
    <CallTarget Targets="Test"></CallTarget>
    <CallTarget Targets="Package"></CallTarget>
    <CallTarget Targets="Deploy"></CallTarget>
  </Target>

The Run target is specified as the default target in the project. As this is my nightly build script the run target calls each of my four other targets sequentially.

Including the DependsOnTargets attribute in a target tag will mean that it will not run unless the specified targets have successfully completed. Test for example, cannot run until the compile task has successfully completed. If the compile fails, the test target will not be executed.

<Target Name="Test" DependsOnTargets="Compile">

Tasks

Targets use tasks to do things. Tasks are essentially compiled code that is executed with the given parameters.

<Target Name="Compile">
    <MSBuild Projects="..\Src\Example.sln" Properties="Configuration=Release"></MSBuild>
  </Target>

The compile target for example, calls the MSBuild task with the relative solution path and the configuration=release switch parameters, in the same way you might call the MSBuild executable from the command line.

The MsBuild Community Tasks project provides a bunch of useful tasks that extend the functionality of MSBuild, most notably (IMO) the NUnit task. The Community Tasks are free to download and can be used with one line in your build script to include the project.

<!-- The test target runs all test dll's that end in *.tests.dll within the tests folder using nunit -->
  <Target Name="Test" DependsOnTargets="Compile">
    <CreateItem Include="..\Src\Tests\*\$(ClassLibraryOutputDirectory)\*.Tests.dll">
      <Output TaskParameter="Include" ItemName="TestAssembly" />
    </CreateItem>
    <NUnit Assemblies="@(TestAssembly)"
           ToolPath="$(NUnit-ToolPath)"
           DisableShadowCopy="true" ContinueOnError="false"/>
  </Target>

This is the full test target. It calls the CreateItem task using the * wildcard to find all dll's within the ..\Src\Tests folder that end in .Tests.dll and adds them to a Task Parameter named TestAssembly. We can then reference this within the NUnit task with the @(TestAssembly) syntax. This means that all new test projects we add in the Tests folder will be run automatically. This is much easier than manually telling TeamCity about each test dll!

The package task is similar to the compile task in that it just calls MSBuild, but with some different parameters to specify a web application publish, not just a build.

The deploy target uses the CreateItem task again to include all files from the package folder except configuration files (as these are environment specific). It then uses the copy task to copy all of these items to the DeloyFolder, using the %(RecursiveDir) wildcard to maintain the folder structure (otherwise all items get copied to the root directory!). This directory is a windows shared folder on our test server that an IIS website is pointing at. As this build is overnight at 11pm when everybody is at home, the files get overwritten without any worries about locks.

  <!-- The deploy task will copy the packaged files excluding the configs to the DeployFolder directory-->
  <Target Name="Deploy" DependsOnTargets="Package">
    <CreateItem Include="$(PackageFolder)$(buildDate)\**\*.*" Exclude="$(PackageFolder)$(buildDate)\**\*.config">
      <Output TaskParameter="Include" ItemName="DeployedWebsiteFiles" />
    </CreateItem>
    <Copy SourceFiles="@(DeployedWebsiteFiles)"
         DestinationFiles="@(DeployedWebsiteFiles->'$(DeployFolder)%(RecursiveDir)%(Filename)%(Extension)')" />
  </Target>

Configuring TeamCity

The final step is to configure our TeamCity nightly build project to use the MSBuild runner and point to the build file path, rather than the solution file.

You can download the full build script to use as a reference here MsBuildExample.xml (3.00 kb). Let me know via comment if you found this useful, or if you have any build script tips.

 

Tags: , , , , , ,

Comments

trackback
DotNetKicks.com
5/2/2010 4:06:02 AM Permalink

Automated builds and deployments with TeamCity and MSBuild

You've been kicked (a good thing) - Trackback from DotNetKicks.com

pingback
topsy.com
5/2/2010 4:10:23 AM Permalink

Pingback from topsy.com

Twitter Trackbacks for
        
        instantiate | Automated builds and deployments with TeamCity and MSBuild
        [instantiate.co.nz]
        on Topsy.com

David Ridgway
David Ridgway United States
5/2/2010 1:19:29 PM Permalink

If anyones interested I have access to 60 day extended evaluation licenses for the Enterprise (not free version) of Team City and also 10% Discount Coupons. Also have similar access to DotTrace and Resharper too. See here for more details:

http://web2asp.net/2010/04/resharper-5.html

or email me at web2asp@live.com and I will get the license issued and send over the coupon details

pingback
149.unlockiphone30.net
5/31/2010 8:28:26 PM Permalink

Pingback from 149.unlockiphone30.net

1984 - 2009 @ Nerve Impulse Definition, Impulse Marriland - 149.unlockiphone30.net

trackback
instantiate
10/4/2010 2:44:49 AM Permalink

Regular expressions with MSBuild. Reading version numbers from AssemblyInfo.cs

Regular expressions with MSBuild. Reading version numbers from AssemblyInfo.cs

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading