ALM Practices Part 8: Automatic Builds & Continuous Integration

Edit this page | 6 minute read

What is it?
An automatic build is a fully automated compilation of a specific version of your source code repository and which runs at regular intervals. More often than not, such a build includes additional steps such as running a static code analysis and code coverage, executing all automatic unit and integration tests, and possibly even a full-blown deployment of the product and the database to a test server. Continuous Integration, or CI, is just a fancy name for saying that a build runs whenever a developer checks-in his changes into the source code repository. This essentially forces all developers to continuously integrate their changes into a working product. However, since these kind of builds are run very frequently, extra steps are usually limited to unit tests and code coverage.

Why would you do it?

  • Because two developers each working in the same area of the code base cannot fully guarantee that their changes will not cause any functional issues.
  • Because the deployment of a new release to a development, test or acceptance server usually involves many manual steps, each highly susceptible to errors.
  • Because deploying your product or system in an automated way requires that it has been built for that. Introducing an automatic daily deployment forces you to deal with possible issues early in the project which would not have appeared until the final release date otherwise.
  • Because it then becomes trivial to redeploy an earlier version of your product or system, for instance when a new release includes some critical bugs and you want to roll back, or to analyze functional changes between versions.
What’s the bare minimum you need to do?
  • Use a continuous integration build that compiles your solutions and runs your unit tests.
What’s the usual thing to do?
  • Deploy the most recent version of your product or system to your central development server.
  • Automatically deploy the database schema changes and test data to your central database.
  • Include a build step that assigns a unique version number to all executables and DLLs so that you can easily see which version is deployed.
How do you do that?
Setup a Continuous Integration build in Team Foundation Server 2008 or 2010 using the following steps:
  • Create a dedicated Build Definition in Team Build 2008 or Team Build 2010 for every solution related to your product or system and configure it so that is triggered by every check-in.
  • Don’t forget to configure your definition to build the release version of your solution.
  • Optionally, configure the build to enable Code Analysis using the project-specific settings. Read this post on how to configure this using the Default Template in Team Build 2010.
  • Use the <TestContainer> element to configure which test assemblies to inspect for unit tests, or use a wildcard, for instance *\**\*.specs.dll to run all test in assemblies post-fixed with .specs.dll.
  • If your tests need a specific .testrunconfig, open up the underlying build.proj file and add a <RunConfigFile> element as explained by this post.
  • Notice that in Team Build 2010, you can skip the previous two steps and use the Build Definition dialog box instead. See this post for more info.
  • Make sure you keep the CI build fast, for instance by only running the unit tests that are independent of any slow resources such as I/O, databases, etc.
  • You can speed up the build by enabling the incremental build and disabling the step that copies the results to a dedicated network drop folder (configured through the <SkipDropBuild> property.
Setup a Daily Build in Team Build 2008 that deploys the latest version to a dedicated test server using the following steps.
  • Use separate solution configurations to have project settings specific to the deployment environment. So in addition to the default Release and Debug configuration, you could introduce Test, Acceptance and Production configurations.
  • Introduce alternative .config files associated with specific solution configurations, such as for instance web.production.config or enterpriselibrary.production.config and configure Team Build 2008 to deploy the correct version using this block in your web application project:
<Target Name="AfterBuild" Condition="('$(Configuration)' != 'Debug') and ('$(Configuration)' != 'Release')">
<!-- If a configuration-specific web.config exists, replace the default web.config with it –> <Copy Condition="Exists('$(DeploymentFolder)\web.$(Configuration).config')" SourceFiles="$(DeploymentFolder)\web.$(Configuration).config" DestinationFiles="$(DeploymentFolder)\web.config" />
<Delete Files="$(DeploymentFolder)\web.$(Configuration).config" />
<!-- If a configuration-specific enterpriselibrary.config exists, replace the default enterpriselibrary.config with it –> <Copy Condition="Exists('$(DeploymentFolder)\enterpriselibrary.$(Configuration).config')" SourceFiles="$(DeploymentFolder)\enterpriselibrary.$(Configuration).config" DestinationFiles="$(DeploymentFolder)\enterpriselibrary.config" />
<Delete Files="$(DeploymentFolder)\enterpriselibrary.$(Configuration).config" />
</Target>
  • If you’re using Visual Studio 2010, you can reduce the amount of overlap between those alternative .config files using Config Transformation Files.
  • To deploy a web site using Team Build 2008 without the Web Deployment project, customize the .csproj of your Web Application project to automatically deploy the entire website to a dedicated server using the following example config. Notice that you need to set the $(DeploymentFolder) property to a valid UNC path.
<Target Name="AfterBuild" Condition="('$(Configuration)' != 'Debug') and ('$(Configuration)' != 'Release')">
<Message Text="Deploying Web Site to '$(DeploymentFolder)'." Importance="high" />
<!-- Create the _PublishedWebsites\app\bin folder –> <MakeDir Directories="$(DeploymentFolder)\bin" />
<!-- Copy build outputs to $(DeploymentFolder)\bin folder. –> <Copy SourceFiles="@(IntermediateAssembly)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(AddModules)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="$(IntermediateOutputPath)$(_SGenDllName)" DestinationFolder="$(DeploymentFolder)\%(Content.SubFolder)%(Content.RecursiveDir)" SkipUnchangedFiles="true" Condition="'$(_SGenDllCreated)'=='true'" />
<Copy SourceFiles="$(IntermediateOutputPath)$(TargetName).pdb" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" Condition="'$(_DebugSymbolsProduced)'=='true'" />
<Copy SourceFiles="@(DocFileItem)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" Condition="'$(_DocumentationFileProduced)'=='true'" />
<Copy SourceFiles="@(IntermediateSatelliteAssembliesWithTargetPath)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" />
<Copy SourceFiles="@(ReferenceComWrappersToCopyLocal); @(ResolvedIsolatedComModules); @(_DeploymentLooseManifestFile); @(NativeReferenceFile)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" />
<!-- copy any referenced assemblies to $(DeploymentFolder)\bin folder –> <Copy SourceFiles="@(ReferenceCopyLocalPaths)" DestinationFolder="$(DeploymentFolder)\bin" SkipUnchangedFiles="true" />
<!-- Copy content files recursively to $(DeploymentFolder)\bin folder –> <Copy SourceFiles="@(Content)" DestinationFolder="$(DeploymentFolder)\%(Content.RelativeDir)" />
</Target>
  • To deploy a web site using the Web Deployment project type in Visual Studio 2010 project type, read this post.
  • To deploy a web site using Team Build 2010 and MSDeploy, read this post.
  • If you want to automatically add an incremented version number to all assemblies using Team Build 2008, read this post. If you want to do this using Team Build 2010 with an Upgrade Template, read this. And if you want to use the Default Template, read this.
If you also want to deploy the database automatically, include the following additional steps.
  • Install Visual Studio Team System 2008 Database Edition and GDR R2. You don’t need any additional licenses for that.
  • Import the schema and configuration of your database using a Database Project.
  • Configure your database project so that a specific database is linked to a specific solution configuration. For instance, a local build should deploy your database to your local development PC, while the Daily Build should deploy it to a central database server.
  • Add a dedicated AfterDropBuild target to the build.proj of your Build Definition to build and deploy the database project to the configured database:
<Target Name="AfterDropBuild">
<MSBuild Projects="$(SolutionRoot)\Main\Source\ConstructIT\Database\Database.dbproj" Properties=" OutDir=$(OutDir)" Targets="Deploy"/>
</Target>
  • For more information on database deployment using Visual Studio 2008, read the Gert Drapers’ blog.
  • This also works with the Team Build 2010’s Upgrade Template, but if you want to do this with the Default Template, follow the steps in this post.
Other tips and suggestions
  • Make an arrangement with your team that the first on entering the office in the morning will be responsible for checking the build status, and if one of them failed, fixing the problem(s).
  • Write down any important details around the build (such as common problems, details on every build, etc) on your project or department site, and make sure there is more than one team member who knows how to customize or adapt the build.
  • Use the Build Notification tool part of the TFS Power Tools to keep an eye on the build through its corresponding icon on the Windows Task Bar.
  • You can tweak a Team Build 2008 Build Definition (or a 2010 Upgrade Template) using the many properties mentioned on this site.
  • Most of the guidance is intended for Team Build 2008, but will work just as well on a Team Build 2010 Upgrade Template. However, if you want to find out how to get the most out of the workflow-based Default Template, check out this series of posts written by MVP Ewald Hofman.

Leave a Comment