blog.reis.se

It's all about looks

There is always a time, in configuring and setting up deployment with MSBuild, when you want to start and stop services on a specific machine. You could always run the sc command from an Exec task. But there must be a better way, an of course there is. For most things you need to do in MSBuild there seem to be a task for it in the MSBuild community task library.

So while poking around I find a couple of gems, ServiceQuery and ServiceController. Both are powerful tools in MSBuild deployment and works both locally and remote. The only problem is that the documentation is quite sparse, especially when handling remote services. So I Just figure I write down what I did to get it working in my scripts.

A little background: a web project that should be deployed to different sites, test, stage and production. The stage and production is redundant with clustering so its really stable, but need some extra attention for each node to be updated. You shut down one node, shut down the IIS and state services, clean out a few folders and then update with new web and finish up starting it all up again.

So to the script, add the community tasks to the script, go on with setting up some default properties and some paths and create a default target. After this you are ready to start and stop services:

<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\ MSBuild.Community.Tasks.Targets"/>
 
<PropertyGroup>
  <DeployFiles>$(MSBuildStartupDirectory)\DeployFiles.msbuild</DeployFiles>
  <Machine>localhost</Machine>
</PropertyGroup>

<Target Name="deploy">
  <CallTarget Targets="Shutdown"/>
  <MSBuild Projects="$(DeployTargets)" Properties="Machine=$(Machine)"/>
  <CallTarget Targets="Startup"/>
  <CallTarget Targets="CheckAccess"/>
</Target>

You see the basic structure here, shut down, call a separate MSBuild file that do the actual file copying the start up the services as well, and finally do a smoke test to test that the site is up and running again.

Next we see the actual shutdown part, first we do a ServiceQuery to check if there in fact is a SMTP service, if it reports other than Unknown, the service is in place. This makes it possible to do a conditional shutdown. The MachineName attribute is of course for remote shut down but works on localhost as well, making it possible to have the same script for local and remote handling. Make sure that the account that the TeamCity agent is running under, have access to perform shutdown/startup on the remote machine. Now we can go ahead and shut down the services:

<Target Name="Shutdown">
 <ServiceQuery MachineName="$(Machine)" ServiceName="smtpsvc">
   <Output TaskParameter="Status" PropertyName="SMTPPresent"/>
 </ServiceQuery>
 <ServiceController MachineName="$(Machine)" ServiceName="IISADMIN" Action="Stop"/> 
 <ServiceController MachineName="$(Machine)" ServiceName="W3SVC" Action="Stop"/>
 <ServiceController MachineName="$(Machine)" ServiceName="aspnet_state" Action="Stop"/>
 <ServiceController MachineName="$(Machine)" ServiceName="smtpsvc" Action="Stop" Condition="'$(SMTPPresent)'!='Unknown'"/>
</Target>

The actual deployments of the files are easy, you can just perform a copy task, as in the remote service handling, make sure you have access to the folder on the remote machine you are deploying to. After the deployment we do the Start action on each of the services we want to start up. Last, we do the smoke test. To do this we can use the task WebDownload, we just specify the URL and an output file and this step will fail if you don’t get any access. The other benefit is that you kick the first time JIT compilation here.

<Target Name="CheckAccess">
  <WebDownload FileUri="http://site.to.deply.com" FileName="test.html"/>
</Target>

Now go deploy something!

-----

PS: I have previously run a series on TeamCity and MSBuild in my private blog so if you are interested to learn more go ahead and read:

Artifact handling in TeamCity 4.0
Deploy with MSBuild and custom targets part 1
Deploy with MSBuild and custom targets part 2
Version handling in TeamCity part 1
Version handling in TeamCity part 2


In the first part I wrote about how to get the version number that is available in TeamCity into the assemblies that are built and also how to maintain the dynamic unit testing feedback. I will now continue to explain how to get the complete chain working up until naming the finished package.

Propagating the version number

I left out one bit of MSBuild script that is vital for the next step. How can you transfer a version number from one project to another? Well, it’s actually quite simple, just save it as a text file among your artifacts and you can read it back up in a property in your MSBuild files. This is what it looks like in the MSBuild script:

<WriteLinesToFile File="SoftwareVersion.txt"
Lines="$(BUILD_NUMBER)"
Overwrite="true" />

This gives you a file named SoftwareVersion.txt that you can save as an artifact and use in the following build projects, how simple some solutions are.

File renaming and creating a package

The good thing about about MSBuild is all the extra things around just executing builds, in combination to the WriteLinesToFile there is of course a ReadLinesFromFile, and as you guessed this will read lines from files. These lines can be stored in an item collection (for many lines) or a property (that’s what we are after) like this:

<ReadLinesFromFile File="$(ReleaseDir)\SoftwareVersion.txt">
<Output TaskParameter="Lines"
PropertyName="Version" />
</ReadLinesFromFile>

Now we have what we need to get the version that was built in one project to control the name of a file in another project. The renaming part is easy.Actually there are no rename task but you can of course execute a command directly with the Exec task so renaming will look like this:

<Exec Command="rename $(ReleaseDir)\SoftwareServiceSetup.msi
SoftwareServiceSetup-$(Version).msi" />

Transferring TeamCity versions to Subversion tags

Last is to get the labels created back into subversion (as it is that I’m currently working with, it is what I know well). It is easy to just add the labels in TeamCity. The settings are placed under Version control settings in Build Configuration step two.

However, there might be a mix-up on where you want to save your tags, this is set under the VCS root configuration. The default is set to trunk=>tags but this hasn’t worked for the setup I have. Instead I have to set this to /trunk=>/tags, and remember, don’t checkout the trunk locally it will be a big checkout…

So there you have it. Version numbers that follows every build from development, through testing and ending up on the subversion labels and the package names. Traceability that will make a configuration manager cry with joy.

// Håkan Reis


Last time I posted on MSBuild and TeamCity I showed how you could use it to deploy remotely in Deploy with MSBuild and custom targets Part 1 and Part 2. There are one issue that’s always a part of professional projects - version handling. I will show you a way to let the version get into the assemblies and trickle through all build steps all the way to the production package. In the first part I will concentrate on getting the version number into the assemblies while keeping unit test feedback, where currently running tests are directly visible in TeamCity.

Setting the version in TeamCity

First off is to set the version and build number in TeamCity. This is one of the easiest parts. Just go into the General settings for the project and set up the version number format and the build counter setting. like this:

versionNumber

Now we have something to base our versioning on, the first part 1.0.2. are static and you update that manually as you start a new release, etc. The last {0} will give you a build number, you get the idea.

Getting the version number to the assemblies

Next step is to make sure the version number from TeamCity is used to build the assemblies and added instead of the Assembly, File and Product Version set in the visual studio project properties. But first some background.

When the assemblies are built version values are automatically set from AssemblyInfo.cs files. If nothing is set, it will get the value of 0.0.0.0, file version will get the same version as in Assembly Version and application version will follow the file version. More about this in Assembly version, File version, Product version. The basics is that if we want to control the version we must make sure the AssemblyVersion attribute  is in there. I would suggest setting it to 0.0.0.0 to make it easy to identify if anything fails to update. You could even have safeguards for it in code, aborting execution if the version is zeroed.

Now you have to make sure you change this number after the checkout but before the build has started. This calls for a shift from solution building to using MSBuild. But for the basic solution to build you only need one line of MSBuild script:

<MSBuild Projects="Software.sln"

Targets="Rebuild"

Properties="Configuration=Release;" />

That’s easy enough. Moving on to the version, we first collect the AssemblyInfo.cs files in the solution and then rewrite the version number with the TeamCity number. As it happens, there are a build property available containing the TeamCity number: $(BUILD_NUMBER). To get the build number into the assembly info we can use a task called FileUpdate that is part of a collection of open source MSBuild tasks, it will look like this:

<!— Collect the Assembly info files in the solution –>

<ItemGroup>

<AssemblyInfoFiles

Include="**/Properties/**/AssemblyInfo.cs;" />

</ItemGroup>

 

<!—Identify current AssemblyVersion and replace -–>

<FileUpdate Files="@(AssemblyInfoFiles)"

Regex=’AssemblyVersion\(“.*”\)\]’

ReplacementText=’AssemblyVersion(“$(BUILD_NUMBER)”)]’ />

This will get the desired result, with all assemblies carrying the correct version number. However, it will also give as a new problem; we now loose the unit test feedback in TeamCity, but we can solve that in the next step.

Unit testing with feedback

When you move from solution based execution to MSBuild based, the first issue you come across is that the unit tests are missing. The natural step is to make the MSBuild script execute them. Normally you would call nunit-console or maybe the NUnit task. But that will only give you a file to display as an artifact. Not the direct and continuous feedback you get from TeamCity. Fortunately TeamCity will provide a unit test runner task that can be used instead. The following lines need to be in the MSBuild script:

<!-- Integreate with TC's nunit runner –>

<UsingTask TaskName="NUnit"

AssemblyFile="$(teamcity_dotnet_nunitlauncher_msbuild_task)" />

 

<!-- Select the assemblies to test –>

<ItemGroup>

<TestAssemblies Include="**/release/*UnitTest*.dll;"

Exclude="**\obj\**;" />

</ItemGroup>

 

<!-- Execute the tests –>

<NUnitTeamCity Assemblies="@(TestAssemblies)" />

Read more about the NUnitTeamCity task on the TeamCity wiki, where you find information on how to execute it in 32bit, categories, on specific versions of NUnit, etc. Now we have all the parts to handle versions and still get the feedback. This also opens up some more opportunities as we now execute the build from MSBuild. We can do some pre- and post-processing around the build like zipping up files or exchange files or data after checkout but before build.

Next step is to get the version numbers across to the next project and use it for packaging as well as labeling our builds in subversion.

// Håkan Reis


dw_seminars Well, I just finished my session in Stockholm and as promised here are a set of links and the build scripts used in the presentation:

And you can also download the presentation as well as the msbuild scripts and msbuild tasks. And for some more information on remote deploy on servers, you can lock at a couple of earlier blog post - Deploy with MSBuild and custom targets part 1 and part 2. Have fun and don’t hesitate to contact me if you want more information

// Håkan Reis


This is a bit frustrating but planning can not help all the way. I have to tell you all that the session Continuation of continuous integration in Malmoe on Wednesday, April 1, have been moved to the day before. Tuesday, March 31. Nope no April fools day joke, I swear it’s the truth!

// Håkan Reis


Seminar in Gothenburg cancelled

Unfortunately I have to announce that the session Continuation of continuous integration has been cancelled for Gothenburg. I will still be in Malmoe, Linkoping and Stockholm, so now changes there.

I would love to give it in Gothenburg as well, but it’s out of my control. But who knows, I might show up in another context… :)

// Håkan Reis


TC-Logo_3I will be speaking  in Malmö, Göteborg, Linköping, and Stockholm in March/April. The subject will be build automation, some MSBuild and all done in TeamCity. The following abstract is in Swedish as the session will be in Swedish:

Continuation of continuous integration

Vad är nästa steg för dina automatiska byggen?

2009 års seminarier inleds med en närmare titt på hur man kan vidareutveckla automatiska bygge (Continuous Integration). I dag är de flesta medvetna om hur viktigt ett versionshanteringssystem som Subversion eller Team Foundation Server är. Oftast har man också insett hur viktigt det är att alltid ha byggbar kod, så man har gått vidare med periodiska, eller ännu hellre kontinuerliga byggen. Men i förlängningen vill man ständigt ha en leverans redo.

Vi tittar på hur man kan hantera kodanalys, integrationstester, release och automatisk installation. Fokus kommer att ligga på att utföra detta i en lösning från JetBrains - TeamCity. Men vi kommer inte ensidigt titta på TeamCity utan även på verktygen runt detta samt hur man kan utnyttja MSBuild för mer än att bara kompilera koden.

Bland annat kommer vi gå in på:

  • Vad kommer efter kontinuerliga byggen
  • Artifakthantering och beroende mellan byggen
  • Tricks MSBuild och custom tasks

If there are any other topics that are of interest I can still make room for changes in my material so feel free to contact me.

You can also register for any of the events, and don’t forget to mention where and date.

// Håkan Reis