essay on programming languages, computer science, information techonlogies and all.

Monday, June 28, 2021

Continuous Integration of .NET Application using Azure DevOps

Are you still building exe in your local PC ?

Or maintain a dedicated PC that goes power-cycle at a stormy day or goes dead when HDD break down ? Aren't you tired of maintaining those build PCs ? Here is a story that can put those worries to history - only for .NET application using Azure DevOps now.

Cloud will build like local

Here,I want to make the build process runnable in the local PC as well as in the cloud - Azure DevOps - so that you can verify your change locally and you push the changes and know that the cloud will build your code in same manner. Then, once you fixed something on your local PC, you are just one push away to finish. Now if you sold, be patient and try to follow below.

Create Azure DevOps project

First, go to Azure DevOps and create a new project
Then clone the repository to your local PC. Or just clone template project

Directroy structure

Now you will populate the workspace with below directories. Or just copy and paste template project.

    /build         : contains build batch, project    
    /src   
       /bin        : generated dll, exe, pdb goes here
       /packages   : dependent component such as NUnit goes here
       /Foo        : various VS solutions
       /Bar
       ...

Local build

In local, you can open *.sln file at IDE and code and debug and build. Then you will open a command prompt and run build processs that will goes through all the solutions and make sure nothing break down by your change.

To make the process as easy as double-click an icon, there is Build command-line icon ath the build folder. When double click the icon, msbuild will be executed to build the whole process. Try to follow those arrows at below.


Your aim is to see below 'Build succeded.' like below.

Install packages

NuGet can put referenced dll on your local folder. It needs PackageReference instead of package.config - package.config will install packages at global repository. If correctly setup packages, you can find line something like below at csproj.
<ItemGroup>
<PackageReference Include="NUnit">
<Version>3.13.2</Version>
</PackageReference>
<PackageReference Include="NUnit.ConsoleRunner">
<Version>3.12.0</Version>
</PackageReference>
</ItemGroup>
The folder to hold those packages should be defined at nuget.config at the root folder.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="globalPackagesFolder" value="src\packages" />
</config>
</configuration>
view raw nuget.config hosted with ❤ by GitHub
During build process, those packages should be downloaded and installed - called restore. At local build, it can be defined at the build.proj like below.
<ItemGroup>
<SolutionToBuild Include="..\src\Collider\Collider.sln"/>
</ItemGroup>
<Target Name="NuGetRestore">
<Exec Command="nuget.exe restore @(SolutionToBuild)"/>
</Target>
view raw build.proj hosted with ❤ by GitHub

Build and test

MSBuild task builds all solutions file listed up like below.
<ItemGroup>
<SolutionToBuild Include="..\src\Collider\Collider.sln"/> <!-- list up all the solution files -->
</ItemGroup>
<Target Name="Core">
<MSBuild
Projects="@(SolutionToBuild)"
Properties="Configuration=$(Configuration)"
/> <!-- $(Configuration) is 'Debug' or 'Release' -->
</Target>
view raw build.proj hosted with ❤ by GitHub
And it can test all the unittest like belew.
<PropertyGroup>
<NUnitConsoleRunner>src\packages\NUnit.ConsoleRunner\3.12.0\tools\nunit3-console.exe</NUnitConsoleRunner>
</PropertyGroup>
<ItemGroup>
<TestAssemblies Include="Collider.Unittest.dll"/>
</ItemGroup>
<Target Name="Unittest">
<Exec
Command="..\..\..\$(NUnitConsoleRunner) @(TestAssemblies) --labels=Before"
WorkingDirectory="..\src\bin\$(Configuration)"
CustomErrorRegularExpression = "Failed :"
IgnoreExitCode="true"
/>
</Target>
view raw build.proj hosted with ❤ by GitHub
If you want to debug the code - put a breakpoint - at the unitest, you will need to put '/process=Single'.

zip up binaires

After successful build and test, those dll, exe and relevant files can be zipped up. At below, files at src\bin\debug|release are ziped to src\bin\install\SampleNetApp.{Debug|Release}.zip
<Target Name="Install">
<ItemGroup>
<FilesToDelete Include="..\src\bin\$(Configuration)\*.pdb"/>
<FilesToDelete Include="..\src\bin\$(Configuration)\*.Unittest.dll"/>
<FilesToDelete Include="..\src\bin\$(Configuration)\TestResult.xml"/>
<FilesToDelete Include="..\src\bin\install\**\*"/>
</ItemGroup>
<Delete Files="@(FilesToDelete)" />
<MakeDir Directories="..\src\bin\install"/>
<ZipDirectory
SourceDirectory="..\src\bin\$(Configuration)"
DestinationFile="..\src\bin\install\SampleNetApp.$(Configuration).zip"
/>
</Target>
view raw build.proj hosted with ❤ by GitHub

Do the same thing at cloud

Azure can do what you did on local PC on cloud. It just need azure-pipelines.yml at the root folder.
trigger:
- 0.1 # any push to branch 0.1 will be trigger the pipeline
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release' # Release only
steps:
- task: MSBuild@1 # first nuget restore
timeoutInMinutes: 20
inputs:
solution: build\build.proj
msbuildArchitecture: 'Any'
configuration: '$(buildConfiguration)'
msbuildVersion: 'latest'
msbuildArguments: '-t:NuGetRestore'
- task: MSBuild@1 # main build
timeoutInMinutes: 20
inputs:
solution: build\build.proj
msbuildArchitecture: 'Any'
configuration: '$(buildConfiguration)'
msbuildVersion: 'latest'
- task: CopyFiles@1 # copy zipped file to artifact
inputs:
displayName: 'Copy Install to: $(Build.ArtifactStagingDirectory)'
sourcefolder: src\bin\install
targetFolder: $(Build.ArtifactStagingDirectory)
- task: PublishBuildArtifacts@1
inputs:
pathToPublish: $(Build.ArtifactStagingDirectory)
artifactName: Installer
Then whenever you pushed change on git, then pipeline will start and build. You can look into build log.
With everything in place, you can get the build as zipped file at artifact.
Now as promised, you get the build from cloud in addition to your local PC. This is a simple continous integration of .NET application but it can be a good starting point on your journey.