Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET Application Continuous Integration ( CI ) using Jenkins

0.00/5 (No votes)
19 Aug 2015 1  
Continuous Integration of .NET project using Jenkins & Gradle Script

Introduction

In this article, we will cover how to configure a .NET application into Jenkins server. We will configure the Jenkins server from scratch to give you a clear idea of setup and execution. This Jenkins server will first connect to code repository server and download the latest code and then execute all the Unit and Integration. 

.NET code which I have used has been developed using VS2013. For scripting Jenkin's task, I will be using Gradle script. I am assuming that you do have a good understanding of .NET code.

I would like to get feedback on this article. Please feel free to share your comments or you can also email me at shettyashwin@outlook.com. If you like this article, please don't forget to rate it.

Background

Recently, we planned to setup Jenkins in our local environment. The idea behind this activity was to setup a gate which continuous monitors the quality of code and triggers an alarm if coding standards are not being followed. This gate will also keep a check on dirty checked-in where some wrong piece of code was checked-in or developer did miss to checkin some files. 

Installing Jenkins 

Download the latest version of Jenkins setup from here. Jenkins setup for Windows is available in .zip file. You will have to extract this to a folder and double click on Jenkins.msi for installing Jenkins. By default, Jenkins is configured on your C:\ drive (or primary drive on which OS is installed). Make sure you have enough space on your C: drive because once you connect your code repository; the latest code will be fetched on the same drive. I would recommend selecting a different drive to avoid space related issues in future. For this article, I am using a Virtual machine which unfortunately has a single drive, so I will have to continue using C: drive.  

If you have already configured your Jenkins on C drive, you can change the workspace location from Jenkins Configuration available at home/root --> Manage Jenkins --> Configure System and then click on advance button under Home Directory. You should see Workspace Directory. I have not changed this path and verified. But share your feedback if you were able to configure this correctly.

Installing Plugins

Once you have installed your Jenkins application successfully, you should be able to see its UI athttp://localhost:8080. Jenkins server does have pre-installed plugins, but we will require some additional plugins for setting up .NET project. For installing plugins, navigate to Home / Root -->Manage Jenkins --> Manage Plugins. All the installed plugins should be available under Installed Tab. If internet access to your Jenkins sever is provided VIA proxy server, you have to do some additional configuration for installing plugins using Jenkins UI. Alternately, you can download the plugins and install it manually using Advance tab. I would prefer applying the below configuration since this option simplifies taking update in future.

For configuring proxy server, you will have to update Jenkins.xml which is available inside root folder where Jenkins application has been installed. If you have installed it on C drive, it should be able under C:\Program Files (x86)\Jenkins. Open this file and in service\argument section, add –“Dhttp.proxyHost=[Proxy server IP] -Dhttp.proxyPort=[Proxy server Port] -Dhttp.nonProxyHosts=[Exclude IP if Any]”. You will have to restart Jenkins service for applying this setting.

Once you restart your server, we should be able to install plugins using Jenkins UI. Navigate to Available tab under Manage Plugins. Use the search textbox to install the following plugins.

  1. MsBuild

Setting Up Jenkins

We will be using MSbuild for building code, FXCop for doing static code analysis, Gradle for creating task and executing it. You will have to download this and install the same on the Jenkins server. Configuration for the same is available below. Please make a note of the name you are specifying against each setting. This will be used in individual Job configuration.

Also for sending Email notification, we will have to configure SMTP details.

Setting Up New Jenkins Job

Let’s create a new job in which we will configure .NET application. Navigate back to Jenkins home page. Since this is Fresh installation, you should see “Create New Job” option under welcome message. You can also create new job by clicking on “New Item” in left menu. Once you click on “Create New Job”, you will see four other options. This option has description below it, so I will not go in much detail for this. Enter the Project Name in Item and select “Freestyle project”. 

Click on OK to Continue.

Now to configure your source code which will build and executed under this project, go to section “Source Code Management”. In Repository URL textbox, mention your Repository path. I am using SVN for the same project. 

If your repository is set in secure mode, the screen will prompt you to enter credential. Based upon security setting configuration, select your appropriate options available on screen.

For code review, we are using FishEye. Same can also be used to view the code changes via Jenkins. You can use many other options available under Repository browser.

This should configure the SVN with Jenkins. Next step will be to trigger the build after regular interval or when developer makes a check-in. I personally prefer build on every check-in. For configuring the same, we need make changes in Build Triggers section. Select option “Poll SCM” for setting up the time duration.  You should be able to see the next scheduled build time below the text box.

Ok, now we have configured our build to trigger on regular interval. Now, we need to define what all setups need to be executed once the build is been triggered. The first and most important thing is that if you are using Nugets packages for referencing the DLL, you will have to restore it back. For restoring the same, you will have to use Nudget.exe. You can download this from online. Once you have successfully installed Nugets, click on “Add Build step” button available at Job Configuration page. From menu, select “Execute Windows Batch command”. This should add a new section in Build. Configure the textbox with below detail.

For building our code using MSBuild, you will have to add new section by selecting “Build a Visual Studio project or solution using MSBuild”. This will add a new section, configure the same using below section. Make sure you are setting build arguments correctly in Command line Arguments.

Nugets will restore all the DLLs from the repository and MSBuild will rebuild your code. Now, we will be using Gradle scripts to run our unit, integration and Static code analysis. For adding these steps, we need to again click on “Add new steps” and then select “Invoke Gradle script”. This will add a new section on the page. You will have to select Gradle script name which you have entered while making Gradle configuration in Jenkin Configuration page. Do not forget to add task name in Tasks list. Task will be executed in the same order in which you have added them in Task textbox.

For your reference, I am adding my gradle script code. This Gradle script also has the source code for generating code coverage report using opencover.

task untiTestCaseWithCodeCoverage << {
    String contents = ""
    FileTree tree = fileTree(dir: 'Test', 
    includes: ['**/bin/Debug/**/[Project]*UnitTest.dll'], exclude:['**/bin/Debug/**/*[Project].dll'])
    int i = 0
    def reportDir = "${buildDir}\\report\\nunit"
    def executedDll = []
    file(reportDir).mkdirs()
    tree.each { path ->
        if(!executedDll.contains(path.name)){
            def stdout = new ByteArrayOutputStream()
            def s = "External\\Tools\\NUnit\\nunit-console.exe 
            \"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
            println s
            contents = contents + s + "\r\n"
            println "Output:\n$stdout"
        }
        else{
            println "Excluded already executed dll $path.name"
        }
    }   

    new File( 'C:/Program Files (x86)/Jenkins/workspace/
    [Project]/unitTestCase.bat' ).write( contents, 'UTF-8' )
    exec {
                commandLine 'cmd', '/c', 
                "C:\\JenkinAddOnFramework\\OpenCover\\OpenCover.Console.exe 
                -register:user -target:unitTestCase.bat -filter:+[Project*]* 
                -output:codeCoverageResult.xml"
        }
    exec {
            commandLine 'cmd', '/c', 
            "C:\\JenkinAddOnFramework\\ReportGenerator\\ReportGenerator.exe 
            -reports:codeCoverageResult.xml -targetdir:coverage"
    }
}
task runNUnitIntTest << {
    FileTree tree = fileTree(dir: 'Test', 
    includes: ['**/bin/Debug/**/[ProjectName]*IntTest.dll', 
    '[Project]/bin/Debug/[Project].dll'], exclude:['**/bin/Debug/**/*Web.SecurityUnitTest.dll'])
    int i = 0
    def reportDir = "${buildDir}\\report\\nunit"
    def executedDll = []
    file(reportDir).mkdirs()
    tree.each { path ->
        if(!executedDll.contains(path.name)){
            def stdout = new ByteArrayOutputStream()
            def s = "External\\Tools\\NUnit\\nunit-console.exe 
            \"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
            println s
            exec {
                commandLine 'cmd', '/c', s
                standardOutput = stdout
            }
            executedDll.add(path.name)
            println "Output:\n$stdout"
        }
        else{
            println "Excluded already executed dll $path.name"
        }
    }
}
task runNUnit << {
    FileTree tree = fileTree(dir: 'Test', 
    includes: ['**/bin/Debug/**/[ProjectName]*UnitTest.dll', 
    '**/bin/Debug/**/[Project]*IntTest.dll', 
    'Framework/[Project]/bin/Debug/[Project].dll'], exclude:['**/bin/Debug/**/*[Project].dll'])
    int i = 0
    def reportDir = "${buildDir}\\report\\nunit"
    def executedDll = []
    file(reportDir).mkdirs()
    tree.each { path ->
        if(!executedDll.contains(path.name)){
            def stdout = new ByteArrayOutputStream()
            def s = "External\\Tools\\NUnit\\nunit-console.exe 
            \"$path\" /noshadow /xml:\"$reportDir\\${path.name}.xml\" /framework:net-4.0"
            println s
            exec {
                commandLine 'cmd', '/c', s
                standardOutput = stdout
            }
            executedDll.add(path.name)
            println "Output:\n$stdout"
        }
        else{
            println "Excluded already executed dll $path.name"
        }
    }

For publishing the reports and sending out email in the event of failure, you have to set up the following configuration.

Click on Save / Apply and we are done. Below are snapshots of some the reports which we have generated using Jenkins.

Unit Test Case Execution Report

Code Analysis Report

Code Coverage Report

References

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here