Anders G. Nordby

Lead Consultant at Itera

Creating deployment zip package with MSBuild and Team City

I had already set up continous integration with MSBuild and Team City, when the network guys suddenly decided that I could no longer have direct access to the test servers. In other words, I had to rewrite my deployment routine into building a zip package that I can manually deploy by coping and installing it on the test servers. I also decided to split the application files from the configuration files. Currently I’m only deploying to one server, but this will soon change, and I thought it a good idea to just build the application files once, and then have a separate package of config files for each environment.

The first thing I did, was create two local directories on the Team City server to deploy to. Then I changed my MSBuild file from deploying to the test server (which I had no longer access to) and simply deploy to the local directories instead. This is what my MSBuild file now looks like:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
	<UsingTask TaskName="TransformXml"
             AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
  <PropertyGroup>
    <WorkingFolder>$(MSBuildProjectDirectory)\</WorkingFolder>    
    <Configuration>Test</Configuration>
    <SolutionFile>..\MySolution.sln</SolutionFile>
    <DeployAppPath>C:\TempDeploy\AppFiles</DeployAppPath>
    <DeployConfigPath>C:\TempDeploy\ConfigFiles</DeployConfigPath>
  </PropertyGroup>  
  
  <Target Name="BuildAllAndDeploy" DependsOnTargets="Compile;transform-configuration-files;Deploy;" />

  <Target Name="Compile">
    <Message Text="=== COMPILING $(Configuration) configuration ===" />
    <MSBuild Projects="$(SolutionFile)"
             Properties="Configuration=$(Configuration)" />
  </Target>
  
  <Target Name="transform-configuration-files">
    <Message Text="=== DEPLOY CONFIG FILES to $(DeployConfigPath) ===" />
    
    <Message Text="=== XML transformation from $(WorkingFolder) to $(DeployConfigPath) ===" />
	  <TransformXml Source="$(WorkingFolder)web.config" Transform="$(WorkingFolder)web.$(Configuration).config" Destination="$(DeployConfigPath)\web.config"/>
	  <TransformXml Source="$(WorkingFolder)episerver.config" Transform="$(WorkingFolder)episerver.$(Configuration).config" Destination="$(DeployConfigPath)\episerver.config"/>
	  <TransformXml Source="$(WorkingFolder)episerverFramework.config" Transform="$(WorkingFolder)episerverFramework.$(Configuration).config" Destination="$(DeployConfigPath)\episerverFramework.config"/>
	  <TransformXml Source="$(WorkingFolder)episerverLog.config" Transform="$(WorkingFolder)episerverLog.$(Configuration).config" Destination="$(DeployConfigPath)\episerverLog.config"/>
	  <TransformXml Source="$(WorkingFolder)connectionStrings.config" Transform="$(WorkingFolder)connectionStrings.$(Configuration).config" Destination="$(DeployConfigPath)\connectionStrings.config"/>
    <TransformXml Source="$(WorkingFolder)IS-ESP.config" Transform="$(WorkingFolder)IS-ESP.$(Configuration).config" Destination="$(DeployConfigPath)\IS-ESP.config"/>
    <Copy SourceFiles="$(WorkingFolder)FileSummary.config" DestinationFiles="$(DeployConfigPath)\FileSummary.config" />

    <Message Text="=== DEPLOYED CONFIG FILES to $(DeployConfigPath) ===" />
  </Target>
    
  <Target Name="Deploy">
     <Message Text="=== DEPLOY LATEST BUILD to $(DeployAppPath) ===" />

    <ItemGroup>
       <DeployWebFiles Include="$(WorkingFolder)**\*.*" Exclude="$(WorkingFolder)**\*.cs;$(WorkingFolder)**\*.bat;$(WorkingFolder)**\*.rb;$(WorkingFolder)**\*.csproj;$(WorkingFolder)**\*.user;$(WorkingFolder)**\obj\**;$(WorkingFolder)**\*.pdb;$(WorkingFolder)**\*.config;$(WorkingFolder)**\*.vspscc;$(WorkingFolder)**\*.build;$(WorkingFolder)**\*.build.*;$(WorkingFolder)**\lib\**;$(WorkingFolder)**\Templates\Styles\sass\**;" />
     </ItemGroup>
    
    <Message Text="=== TheFiles: @(DeployWebFiles) ===" />

    <Copy SourceFiles="@(DeployWebFiles)" 
          DestinationFiles="@(DeployWebFiles->'$(DeployAppPath)\%(RecursiveDir)%(Filename)%(Extension)')" />

    <Message Text="=== DEPLOYED LATEST BUILD to $(DeployAppPath) ===" />
  </Target>
</Project>

I then added two artefact paths to my Team City configuraion:

c:\TempDeploy\AppFiles => MySolution-AppFiles-build-%build.number%-tfs-%build.vcs.number_tfs_h4%.zip
c:\TempDeploy\ConfigFiles => MySolution-ConfigFiles-build-%build.number%-tfs-%build.vcs.number_tfs_h4%.zip

The Team City configuration now looks like this:

This works perfectly! I can now download the artifacts like this:

The solution is easy to expand with more environments (which I know will be coming pretty soon), and I can also use the tagging functionality in Team City to know which artifacts I have deployed to what servers.

By the way, out of curiosity, I searched up the location for the artifacts on the Team City server:

C:\ProgramData\JetBrains\TeamCity\system\artifacts
Advertisements

2 responses to “Creating deployment zip package with MSBuild and Team City

  1. Thomas 2014-06-18 at 13:08

    Stupid network guys… 😛 You should force them to manually install new testversions using notepad and command prompt

  2. Alberto 2015-07-15 at 00:29

    Thanks for finally talking about >Creating deployment
    zip package with MSBuild and Team City | Anders
    G. Nordby <Loved it!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: