Mark Ku's Blog

Background

Most systems at my company can be containerized, but there are some legacy websites—the oldest, largest, and most profitable—that we can't upgrade technologically for the time being. With many people involved in manual deployments, we often run into issues where someone forgets to commit a binary. Therefore, I wanted to automate the build part of the process to optimize our deployment workflow and reduce the impact of errors.

Because it's a legacy system using the old .NET MVC framework, it can't be containerized. A fully automated deployment would be a bit risky, so the deployment part will remain a manual process.

Goals

  • When someone pushes any code to the master branch, Jenkins will automatically trigger a build and then commit and push the resulting binaries to GitHub.
  • In the production environment, we'll write a script to pull the latest binaries ready for deployment via Git. Then, we'll use Beyond Compare to check the differences in the binaries, and finally, perform the deployment manually.

Environment Details

Since it's a legacy system, Jenkins is installed on a Windows Server.

First, Find MSBuild

We all know that .NET MVC projects are built using the MSBuild tool. Based on our version of Visual Studio 2019, the corresponding MSBuild version is 16. However, on Microsoft's website, the download links for older versions of the MSBuild tools are hard to find. I eventually found the download link for the older Visual Studio 2019 from my MSDN subscription account. MSDN

During installation, remember to check "Desktop development with C++". Installation

Configure the MSBuild Path in Jenkins

The MSBuild installation path is: The path for MSBuild.exe installed with Visual Studio 2019 is:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin

Download nuget.exe

Download nuget.exe to:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin 

Download and Install the .NET Framework 4.8 Developer Pack

First, Configure a Webhook in the GitHub Repo to Notify Jenkins on Commits for Automatic Builds

GitHub webhook setting
GitHub webhook setting
http://jks.xxx.com/github-webhook/

Next, Configure the Automatic Build Pipeline in Jenkins

Create a Job > Select "New Item" > "Pipeline", and configure the GitHub Project.

GitHub project setting
GitHub project setting
properties([pipelineTriggers([githubPush()])])

pipeline {
	agent any

	environment {		
	}
	
	options {
		buildDiscarder logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '3', daysToKeepStr: '', numToKeepStr: '3')
	}

	stages {
	    stage('Cleanup Directories') {
            steps {
                script {
                    // Delete the 'Api' and 'Artifact' directories
                    dir('Api') {
                        deleteDir()
                    }
                    dir('Artifact') {
                        deleteDir()
                    }
                    
                    dir('Publish') {
                        deleteDir()
                    }
                    echo "Deleted Api and Artifact directories."
                }
            }
        }
	
	    stage("GitHub Pull - Net mvc api") {
            steps {
                dir('Api') {
                    git(
                        branch: 'master',
                        credentialsId: 'xxxx-xxxx-xxxx',
                        url: '[email protected]:iRobot/MVC.Api.git'
                    )
                }
            }
        }
		
		 stage("GitHub Pull - Artifact") {
            steps {
                dir('Artifact') {
                    git(
                        branch: 'main',
                        credentialsId: 'xxxx-xxxx-xxxx',
                        url: '[email protected]:iRobot/Artifact.git'
                    )
                }
            }
        }
        
    stage('Restore nuget') {
        steps {
        script {
            bat 'echo Current Directory: %CD%'
            bat 'echo Workspace Directory: %WORKSPACE%'
            bat '"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\MSBuild\\Current\\Bin\\nuget.exe" restore ".\\Api\\IRobot.WebCore\\IBuyPower.WebCore.sln"'
        }
        }
    }
    
    stage('Build') {
            steps {
                script {
                    // Define the MSBuild path and solution file
                    def msBuildPath = '"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe"'
                    def solutionPath = '".\\api\\IRobot.WebCore\\IRobot.WebCore.sln"'

                    // Build command with all necessary parameters
                    def buildCommand = "${msBuildPath} ${solutionPath} /t:Clean,Build /p:Configuration=enUSLive /p:DeployOnBuild=True /p:DeployDefaultTarget=WebPublish /p:WebPublishMethod=FileSystem /p:DeleteExistingFiles=True /p:OutDir=\"%WORKSPACE%\\Publish\" /p:ExcludeGeneratedDebugSymbol=True -verbosity:minimal"

                    // Execute the build command
                    bat script: buildCommand, returnStatus: true
                }
            }
    }
    
     stage("Copy to Artifact") {
            steps {
                script {
                    
                    def buildCommand =  "robocopy \"%WORKSPACE%\\Publish\\_PublishedWebsites\\Core.Web\" \"%WORKSPACE%\\Artifact\" /E /COPY:DAT /MT:100"

                    // Execute the build command
                    bat script: buildCommand, returnStatus: true
                }
            }
        
    }
    
    stage('Check for Changes and Commit') {
    steps {
        dir('Artifact') {
            script {
                // Set local git configuration for user identity
                bat "git config user.email \"[email protected]\""
                bat "git config user.name \"Jenkins CI\""

                // Stage all changes
                bat "git add ."

                // Commit with a timestamp
                bat "git commit -m \"Automated commit at ${new Date().format('HH:mm:ss')} by Jenkins\""

                // Attempt to push changes to remote repository
                def pushResult = bat(script: "git push --set-upstream origin main", returnStdout: true, returnStatus: true)
                if (pushResult != 0) {
                    echo "Failed to push changes. Error Code: ${pushResult}"
                }
            }
        }
    }
}	
}
}

Manual Deployment

Because it's a legacy system using the old .NET MVC framework, it can't be containerized. A fully automated deployment would be a bit risky, so the deployment part will remain a manual process.

Then, Install Beyond Compare

Write the PowerShell script deploy.ps1

git pull 

$LOCAL = "C:\Workspace\USApiArtifact\bin"
$REMOTE = "C:\inetpub\Core.Web\bin"

$env:Path = "C:\Program Files\Beyond Compare 4\" 
$filters = "/filters=-web.config"
BCompare.exe /excludefilter="web.config"  "$LOCAL" "$REMOTE" $filters

Finally, after running the script, you can use Beyond Compare to verify that the DLLs for this deployment are as expected. Then, deploy the new version of the application by right-clicking and selecting "Copy to Right".

Final
Final

Author

Mark Ku

擁有 10+ 年經驗的資深軟體工程師,現為 AI 應用 Builder,專注於大型平台架構與簡化複雜系統設計,從電商系統到訂閱與收費平台,結合 AI Agent、AI 整合與自動化開發,打造高效率且可持續演進的產品技術基礎。Read More

Found this useful?

The author's free tools, daily podcasts and newsletter are all here.

Mark Ku · This article is licensed under CC BY 4.0. Credit the author and link back to the original when reusing it.

Comments

Subscribe to Newsletter

Subscribe to get new posts delivered instantly — never miss a tech share.

By submitting, you agree to receive emails. You can anytime.

Popular Posts

View all
Mark Ku
··602

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution

Oracle Cloud Always Free Tier: Linux Host and Static IP for a $0 Cloud Solution
Mark Ku
··490

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.

Say Goodbye to Postman's Fee Trap! A Hands-on Guide to Bruno, the Open-Source Git-Native API Testing Powerhouse.
Mark Ku
··333

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki

A Free, Open-Source, Notion-like Knowledge Base — A Complete Guide to Deploying and Backing Up Outline Wiki
Mark Ku
··264

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning

Training Your Own AI Voice: Hardware Requirements, Open-Source Model Comparison, and LoRA Fine-Tuning
Mark Ku
··221

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1

Building an Efficient API Management Platform: Deploying Kong Gateway from Scratch - Part 1
Mark Ku
··215

Setting Up Samba on Ubuntu to Share Folders with Windows 11

Setting Up Samba on Ubuntu to Share Folders with Windows 11