A simple Continuous Integration and Deployment workflow with ASP.NET MVC, GitHub and Jenkins

This tutorial describes the process of setting up a simple ASP.NET MVC Web Application with Entity Framework and Code First Migrations and a workflow for developing, testing, staging and deploying the application with full automation using GitHub, Jenkins and IIS.

Setting up a development machine

First of all, make sure you have installed the following software:

Setting up GitHub and Git

Create a new repository in GitHub and clone it to your machine. Using your preferred Git client, add three branches – develop, test and production. You might add more, less or other ones depending on your branching strategy, but this will suffice for this example. For example, there may be multiple development, test and production branches, for example if you are maintaining and developing an older version of your software.

The develop branch is the common “snapshot” branch that developers merge their completed features or bug fixes into and it can be built on Jenkins. The test and production branches will be used to build your application for the corresponding environments and optionally publishing them automatically. Push all three branches to the remote GitHub repository.

(Optional) To prevent developers from merging untested code into test and production you can use the fork/pull request GitHub features. In that scenario, Jenkins will pull code from one GitHub account’s repository to which only certain developers and release managers have push access.

The drawback with this setup is that it requires developers to issue pull requests even when merging into develop. You could also create another repository where all developers has push access and pull develop from that repository.

Create and checkout a new branch called setup. Add a .gitignore file in the git root directory with the following rules and the ones in CSharp.gitignore.

# Ignore dot-files, except the .gitignore file.
.*
!.gitignore

# (All rules from CSharp.gitignore)

Add .gitignore to index and commit.

Creating the project

In this part you can create a MVC Web Application, Intranet Application or Web API project. The examples will be a basic Web Application called MyApp. Feel free to commit changes in Git along the way.

Open Visual Studio and create a new MVC Web Application project. Make sure that the EntityFramework NuGet package is added. Open the Package Manager Console and run Enable-PackageRestore or right-click your solution and click Enable NuGet Package Restore.

Create a model class Thing and a DbContext for your EF context. This is described in detail at Entity Framework Code First Migrations.

public class Thing
{
    public long ThingId { get; set; }
    public string Name { get; set; }
}

Open Web.config and locate the <connectionStrings>...</connectionStrings> section. Add the following. Replace SQLEXPRESS if you named your SQL Express instance to something else.


  ...
  

Add a default constructor to your DbContext class. The name in the constructor must be equal to the name="..." attribute in the Web.config connection string. This explicitly tells the context to use the connection string you added in the previous step.

public class MyAppContext : DbContext
{
    public DbSet Things { get; set; }
    public MyAppContext() : base("MyAppContext") { }
    ...
}

In the Package Manager Console, run Enable-Migrations. This will scaffold a basic configuration under Migrations. Set AutomaticMigrationsEnabled = true in the constructor.

internal sealed class Configuration : DbMigrationsConfiguration
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = true;
    }
    protected override void Seed(MyAppContext context)
    {
        ...
    }
}

To use the configuration in your web application, add the following line to Application_Start() in Global.asax.

// In Application_Start()
Database.SetInitializer(new MigrateDatabaseToLatestVersion());

Run Add-Migration in the Package Manager Console, enter a name (e.g. InitialMigration) and a DbMigration class is created. Use this method to create incremental migrations when you change persisted model classes. Read more on customizing migrations at MSDN’s Entity Framework Code First Migrations. Run Update-Database to manually apply the migrations to your SQL Express database.

Publish Profiles

In this step you will create two publish profiles, one for each target environment, which will be used to publish the application from Jenkins once built and tested.

To be able to distinguish the environment in which your application is deployed, add the following to <appSettings>...</appSettings> in your Web.config:

<add key="Environment" value="Development" />

Open Explorer, navigate to the web project’s root directory, create two copies of Web.Release.config and name them Web.Test.config and Web.Production.config. Go back to Visual Studio and toggle Show all files in the Solution Explorer. Select your two new files, right click and click Include In Project. You can now override properties in these new profiles. For example in Web.Test.config:



  
    
  
  
    
  

Right-click your web project and click Publish. Add a new profile and call it Test. Note that the name Test matches the Web.Test.config, which is a convention for transforms and profiles.

In the Connection tab, set Publish method to Web Deploy Package. Set Package location to objPackageMyApp-Test.zip. This path is relative to the web project root and could be anything you like. Site/application can also be anything but must match the site name in IIS which you will setup later. Set it to MyApp.

Check Execute Code First Migrations […]. If you don’t need any specific connection string for the deployment machine, uncheck Use this connection at runtime […]. Also, the connection string can be set directly in IIS, which I think is a more clean solution.

Repeat these steps but call the configuration Production instead of Test.

The test project(s)

By default, when you create a MVC Web Application, you get a test project which uses MSTest. To run these tests you must have Visual Studio (higher than Express) installed. This may work at your development machine but at the build server this can become quite clumsy. Instead, let’s use NUnit!

Add the NUnit and NUnit.Runners NuGet packages to your test project(s). If you have any MSTest cases, change the Attributes from the MSTest specific ones to NUnit Attributes (e.g. [TestFixture], [Test] …).

Creating the MSBuild targets

If you have multiple projects in your solution (code libraries, web projects, test projects), or just want to customize your build, MSBuild is very handy.

NOTE! In this example MSBuild Extensions Pack is used. It’s possible to skip this and use the built in MSBuild tasks, but it can be convenient.

As this might become quite a large file, all details of how this works will not be covered in this example. To sum it up, the build.xml will contain a number of targets which aggregates the individual project targets. The following is the build.xml used to build the example project.



  
  
  
  
  
    MyApp
      MyApp.Tests
    Release
      Test
    .out
    packages\NUnit.Runners.2.6.1\tools\nunit-console.exe
  

  
    
    
    
    
    
  

   
     
     
     
   

      
    
      
  
  
  
  
    
      %(Projects.Identity)
    

    
  
  
  
  
    
      %(TestProjects.Identity)
    
    
    
    
      
        
      
    
    
      /result="$(OutputDir)\$(CurrentProject).Results.xml"
      $(NUnitConsole) $(NUNitOptions) @(TestAssembly)
    
    
    
      
    
  
  
  
    
    
    
    
          
    
    
        
  
  

Using the Developer Command Prompt for VS20xx, navigate to the solution root directory and run:

msbuild build.xml /T:Package /P:PublishProfile=Test

NOTE! This might fail due to an issue with relative paths in NuGet.exe. To resolve this, set the SolutionDir property to the absolute path of your solution.

... /P:PublishProfile=Test;SolutionDir=C:MyProjectsMyApp

Before setting up Jenkins, make sure you have commited all you source files in Git and then merge branch setup into develop. You can go ahead and delete the setup branch when you’re done. Then merge develop into test and push it to GitHub so that Jenkins will be able to pull your new shiny setup.

Setting up Jenkins and GitHub

The build server should be a Windows Server 2008 (or higher) with the following software installed:

NOTE! To let the NuGet command line tool restore packages, set the system environment variable EnableNuGetPackageRestore to true.

Make sure the Jenkins service is run by user with sufficient privilegies. Configure the GitHub Plugin with your GitHub account and repository.

Create a new Job in Jenkins and call it MyApp-Test. In the Source Code Management section, check Git and set Repository URL to your GitHub repository URL. Specify that the branch to build is test. In Build Triggers, check Build when a change is pushed to GitHub. This option is provided by the GitHub Plugin and all required configuration is created automatically by the plugin if you use the Automatic Mode. Make sure no firewall blocks GitHub from accessing Jenkins.

Add a MSBuild build step. MSBuild Build File should be set to build.xml and Command Line Arguments to /T:Package /P:PublishProfile=Test;SolutionDir=C:workspaceMyApp-Test. Change the SolutionDir property to match your Jenkins workspace path. To enable automated deployment to your IIS Server, add a Windows batch command build step. The Command should be something like (replace <URL>, <USER> and <PASS>):

.outPackageTestMyApp-Test.deploy.cmd /y /M:<URL>/msdeployagentservice /U:<USER> /P:<PASS> AllowUntrusted:True`

Add a NUnit test result report post-build action and set Test report XMLs to .out/*.Results.xml which catches all files matching that string and generates a report in Jenkins. Add an Archive the artifacts post-build action and set Files to archive to .out/**/*.* which will catch all files (with an extension) in .out recursively.

Save the configuration and try manually starting a build. This should fail, as you have not set up IIS with Web Deploy yet.

Repeat these steps for Production. If you do not want automatic deployment to your production environment, skip the step with deploy.cmd. Your built deployment packages will be archived by Jenkins and available for download.

You might also repeat these steps for the develop branch and perhaps schedule the builds differently if you are using a separate repository for develop, as discussed in the Setting up Github and Git chapter.

Setting up IIS and MSDeploy on the web server

Follow the tutorial at Configuring a Web Server for Web Deploy Publishing (Remote Agent). Make sure you name your web site as you did in the Publish Profile or vice versa. The user you specified in the Jenkins configuration must be a valid user with permission for web deployment. This could be a AD/Windows user or a IIS Service Account. If you have a specific connection string for your SQL database, add it in the Connection Strings section.

NOTE! If needed, configure your firewall settings so that Jenkins can access the web server.

Trying it out!

Now you should have all your infrastructure up and running. Try running a manual Jenkins build again and verify that it works. The code is pulled from GitHub, the project is built with MSBuild, the packaged web application is both archived and deployed to IIS, the database is updated using your Code First Migrations and the NUnit test results are collected.

Your first automated deployment

This step involves creating a change in the model and the complete flow of deploying that change.

On your development machine, create and checkout a new branch called feature-thing-description and open Visual Studio. Edit Thing.cs and add a Description string property.

Build the solution and run Add-Migration ThingDescription in the Package Manager Console. A new Migration is created for you (and you may customize it). Run Update-Database to check that it is working properly.

Commit your changes in Git, merge feature-thing-description into develop and then develop into test. Push your changes to GitHub.

Open Jenkins in your web browser and verify that the project is building. It might take a moment for Jenkins to receive the notification from GitHub and to start the build. When the build is done, the new package is deployed to your test web site and you can download the artifact in Jenkins. You may now celebrate!

Summary

This is a simple and efficient setup for Continuous Integration and Deployment of a ASP.NET MVC Web Application. In a large-scale project, with many developers, the branching and merging strategy might need to be more complex regarding release strategies and developer permissions. You might also consider a different setup for development, staging and production servers.

However, the basic ideas and techniques still applies and I believe the workflow can be customized to fit most needs.

This Post Has 17 Comments

  1. MartinaHayes

    I am really thankful to the author of this post for making this lovely and informative article live here for us. We really appreciate ur effort. Keep up the good work.

  2. Trev

    Love this… thx.

    If the project was built in Visual Studio 2012 then you need to add the following property to the Package target in build.xml:

    VisualStudioVersion=11.0

    If this isn’t added then the PublishProfile created in Visual Studio will not be used.

    1. Oskar Wickström

      @Trev Thank you for the feedback!

  3. Liam

    Very interesting article. My production environment is an Azure website. Can the process be tweaked to publish the build to production?

  4. MarekLani

    Hi, in first thanks for this guide:). I want to ask how should I and where should I add the build.xml from “Creating the MSBuild targets” part of article.

    Thank you in advance.

    1. Oskar Wickström

      @MarekLani It should reside in the project root – the same folder as the .sln-file, MyApp folder and MyApp.Tests folder.

  5. tomer

    The gitignore link on github is broken.
    Thanks!

    1. Oskar Wickström

      Thanks! It should be fixed now.

  6. Thomas Nelson

    All of the Gist IDs that you have listed in this article do not show the gist, nor can I find them on github. Is this something that you can resolve?

    Thanks!

    1. Oskar Wickström

      Thanks for letting me know! I’ve replaced the Gist references with inline code examples, it should work now.

  7. Ciwan

    Can the above work with Windows Shared Hosting?

  8. Vinicius Mattos

    I would like to know if there is some way to run a Migration command like “Update-Database -Script” from a jenkins build step.

  9. simply

    wonderful. Thanks a lot for sharing it.

  10. Dot Net

    Great post. Simple and easy way to explain the simple Continuous Integration and Deployment workflow with ASP.NET MVC.
    Please share more article to guide us.

  11. Dot Net

    Hi, in first thanks for this guide:) very well written and clean explanation.

Leave a Reply