Saturday 21 June 2014

Integrating StyleCop with TeamCity

Having used CruiseControl.Net for some time I thought it was time to try something new: TeamCity from JetBrains. I’m a bit fussy about code quality so one thing I like my integration builds to do is run StyleCop and fail the build if violations are found.

Create an MSBuild file

After some research I tracked down some basic guidance on StackOverflow and adapted it to my needs [1].

I created an MSBuild file that could be referenced from a umber of TeamCity build configurations. This build file invokes StyleCop, counts the violations and fails the build if StyleCop violations are encountered. I saved the build file to a shared location where it could be used from different builds. Here’s the basic script:

 

<Project DefaultTargets="RunStyleCop" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
	<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
    <Import Project="$(ProgramFiles)\MSBuild\StyleCop\v4.7\StyleCop.targets" />
	<UsingTask TaskName="XmlRead" AssemblyFile="C:\MSBuild\lib\MSBuild.Community.Tasks.dll" />

	<Target Name="RunStyleCop">
        <CreateItem Include="$(teamcity_build_checkoutDir)\**\*.cs">
            <Output TaskParameter="Include" ItemName="StyleCopFiles" />
        </CreateItem>

        <StyleCopTask ProjectFullPath="$(MSBuildProjectFile)"
                      SourceFiles="@(StyleCopFiles)"
                      ForceFullAnalysis="true"
                      TreatErrorsAsWarnings="true"
                      OutputFile="StyleCopReport.xml"
                      CacheResults="true"
                      AdditionalAddinPaths="$(ProgramFiles)\StyleCop 4.7\Andy.French.StyleCop.Rules.dll"
                      OverrideSettingsFile="$(teamcity_build_checkoutDir)\Settings.StyleCop" />
                      
        <XmlRead XPath="count(//Violation)" XmlFileName="StyleCopReport.xml">
            <Output TaskParameter="Value" PropertyName="StyleCopViolations" />
        </XmlRead>

        <TeamCitySetStatus Status="$(AllPassed)" Text="StyleCop violations: $(StyleCopViolations)" />

        <Error Condition="$(StyleCopViolations) > 0" Text="There were $(StyleCopViolations) StyleCop violations." />
	</Target>
</Project>

 

On line 2 we import the StyleCop.targets from the StyleCop installation directory. This makes the StyleCopTask available on line 10. If you examine this file you’ll find it references the StyleCop.dll in the StyleCop installation directory. The StyleCopTask is actually in that DLL.

On line 3 we import the MSBuild.Community.Tasks.dll. This is an open source project that adds some useful MSBuild tasks including the XmlRead task on line 19 (see [2] below).

You may have to hop on over to the project GitHub site to grab a release [3]. I downloaded the Zip file and extracted the DLLs that I wanted, putting them in a shared location (C:\MSBuild\lib\ in this case).

The RunStyleCop target does all the work. On line 6 we grab all the C# files in the solution. Note that we are using a TeamCity variable here: teamcity.build.checkoutDir. NB: Don’t forget you have to replace all instances of “.” with “_” if you are using MSBuild.

 

“Make sure to replace "." with "_" when using properties in MSBuild scripts; e.g. use teamcity_dotnet_nunitlauncher_msbuild_task instead of teamcity.dotnet.nunitlauncher.msbuild.task” [4]

 

Now I have some custom StyleCop rules and I like to disable a couple of the default rules. To activate my custom StyleCop rules DLL I had to specify the path to it using the AdditionalAddinPaths attribute on line 16. I also include a Settings.StyleCop file with overridden settings with each solution so on line 17 I set the OverrideSettingsFile attribute to point to that file.

The XmlRead task on line 19 reads the output from StyleCop and makes the result available in a property called StyleCopViolations. This is used on line 23 to report the number of violations to TeamCity using the TeamCitySetStatus task on line 23. Then, on line 25, we throw an error – failing the build – if there are any errors.

TeamCity configuration

It’s quite straight forward then in TeamCity. You need to add a Build Step to your Build Configuration. I set the path to the shared build file created above and specified the target:

 

image

 

Note I have set the path to the shared build file and included the target to run. Here’s an example of a failed build, first in the Projects overview where our status message is displayed:

 

image

 

And in the build results page where the Error message can be seen:

 

image

 

There’s more work to do, for example getting the violations to display better but for now I can get at them via the build log tab.

References