Jenkins – Build set up for .Net project

Continuous integration and continuous deployment is the holy grail of the modern DevOps.

Jenkins is the most popular continuous integration tool out there, but it requires some other knowledge to set it up, because Jenkins does not provide full out-of-the-box features for .Net projects.

Installing Jenkins and set up to use source code control system such as Git is relatively easy.

Tip – Set up to use BitBucket

When using Github, it’s OK to use id/password as normal.
However, when it comes to Bitbucket, repository URL needs to be like https://bitbucket.org/rocker8942/marketwatchconsole.git without an username at the start of URL. I spent some time pulling out my hair for this.

Setting up build process could be a bit challenging and require lots of external tools. So, just skipping up to setting up source code set up, I’ll focus on build and release / deployment automation process in Jenkins.

It could be easier if you use TeamCity and Octopus Deploy, but if you have to go with Jenkins due to cost of whatever, the rest of the process will be helpful.

External tools as below will be used to set up Jenkins Build steps.

  • Octopack – nuget package to make the project into nuget package so that it’s easy to handle.
  • Nuget.exe
  • MSBuild.exe
  • MSTest.exe
  • Powershell Script
  • Octo.exe – Octopus Deploy console app (if you use Octopus Deploy)

It looks a lot when naming it, but most of it should be familiar to .Net developer even though it may not being used often as a console application.

There are a few steps to set this up.

 

#1 Nuget restore

Nuget packages don’t need to be included in source control system, nuget packages can be restored just before build process.

Just use nuget command line command.

e.g.

"C:\Program Files (x86)\NuGet\nuget.exe" restore yourSolutionName.sln

 

#2 Build

#2-1 Just build in Jenkins

Build is done basically by MSBuild using the argument as below.

/t:Rebuild /p:Configuration=Release;RunOctoPack=true /p:OctoPackPackageVersion=1.0.${BUILD_NUMBER}

By running Octopack, the compiled files are packaged as a nuget pacakge, which is basically a zip file.

Even though we build an app with configuration pamareter ‘Release’, which create compiled library using ‘Release’ configuration. Web.config is not transformed to Release. So, we will transform it to release version later using Powershell.

#2-2 Build and publish to Octopus Deploy

or, we can publish the complied package into Octopus Deploy server if you used Octopus deploy by using the argument as below. After this, Octopus Deploy will release the package to servers using this package.

/t:Rebuild /p:Configuration=Release;RunOctoPack=true;OctoPackPublishApiKey=UseYourOwnAPIKey;OctoPackPublishPackageToHttp=http://localhost:8201/nuget/packages
/p:OctoPackPackageVersion=1.0.${BUILD_NUMBER}
/p:VisualStudioVersion=14.0

It’s similar with the previous argument. Just added OctoPackPublishApiKey and OctoPackPublishPackageToHttp

 

#3 Run Octopus to create release

By using Octopus console tool, we can even run Octopus Deploy from Jenkins (or custom build automation script) to create release

"C:\Octopus\Tools\Octo.exe" create-release --project MarketWatchConsole --version 1.1.%BUILD_NUMBER% --packageversion 1.1.%BUILD_NUMBER% --server http://localhost:8110/ --apiKey %OctopusApiKey% --releaseNotes "Jenkins build [%BUILD_NUMBER%](http://localhost:8054/job/MarketWatchConsole/%BUILD_NUMBER%)/"

 

#4 Run Test

Unit test can be done by calling MSTest.exe within Jenkins.

"C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe" /resultsfile:"%WORKSPACE%\AutomationTestAssistantResults.%BUILD_NUMBER%.trx" /testcontainer:"%WORKSPACE%\TestProject\TestProjectTests\bin\Release\TestProjectTests.dll" /nologo

 

#5 Release

To get the benefit of powerful Powershell, release script can be written by Powershell and loaded into Jeknins as a build step.

 #
 # Demo.ps1
 #

$destination = "D:\DemoProject"

function XmlDocTransform($xml, $xdt)
 {
 if (!$xml -or !(Test-Path -path $xml -PathType Leaf)) {
 throw "File not found. $xml";
 }
 if (!$xdt -or !(Test-Path -path $xdt -PathType Leaf)) {
 throw "File not found. $xdt";
 }

$scriptPath = "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v14.0\Web"
 Add-Type -LiteralPath "$scriptPath\Microsoft.Web.XmlTransform.dll"

$xmldoc = New-Object Microsoft.Web.XmlTransform.XmlTransformableDocument;
 $xmldoc.PreserveWhitespace = $true
 $xmldoc.Load($xml);

$transf = New-Object Microsoft.Web.XmlTransform.XmlTransformation($xdt);
 if ($transf.Apply($xmldoc) -eq $false)
 {
 throw "Transformation failed."
 }
 $xmldoc.Save($xml);
 }

# Clean up - Pre

remove-item $destination\bin\*
 remove-item $destination\Content\*
 remove-item $destination\fonts\*
 remove-item $destination\Scripts\*
 remove-item $destination\"Service References"\*
 remove-item $destination\Views\*

# unzip package 

& 'C:\Program Files (x86)\NuGet\nuget.exe' install DemoProject -Source $ENV:WORKSPACE\DemoProject\bin -OutputDirectory "D:\" -ExcludeVersion

# transform configs

XmlDocTransform $destination\Web.config $destination\Web.Release.config

# Clean up - Post

 remove-item $destination\*.$ENV:BUILD_NUMBER.zip
 remove-item $destination\Web.Debug.config
 remove-item $destination\Web.Release.config
 remove-item $destination\*.xml
 remove-item $destination\*.nuspec
 remove-item $destination\bin\*.pdb
 remove-item $destination\DemoProject.nupkg

As most of automation is done by calling console application, Jenkins itself doesn’t do much here, but  hosting and managing all these steps in a central place will be its role.

A good thing about setting up automation in Jenkins using various external console application tools is that we can set this automation up without Jenkins, because build / test / release process can be achieve by creating custom script.