Moving all our projects to Azure Devops
At the end of 2018, we switched over to Microsoft's Azure Devops product. Here is a breakdown of some of the challenges we faced and the outcome of the project.
Why we did it
We have many different projects, which are implemented in a few different technologies. We had these in an older system but this was starting to show its age, and we weren't enjoying the process of hosting this on a physical server. We also have many libraries which are shared between projects. These libraries were referenced in our Visual Studio projects directly as DLL's. Some of our products were being deployed using continuous integration, but the CI system was crashing. Our automated testing was carried out in a non formal way and we wanted to leverage our unit tests more heavily.
Numerous things needed a CI pipeline; we needed to build and release some of our web applications, and we have nuget packages which we needed to build, package up. We've currently got a few web applications to build and deploy with this system and we needed to keep certain things such as database credentials secure.
In 2018 we had decided that Angular was going to be the main front end technology on all of our products, with future developments using it. Angular has been designed around continuous integration, being based around Webpack and Node - the build server needed to support these things and nobody wanted the task of coaxing our old CI system into doing this. We were also starting to use .net core which was another technology our old system didn't understand.
With our development team growing, the problems started to become more apparent. Updates to our shared libraries became more and more frequent, so it was clear that we needed a Nuget feed to help us synchronise these. We needed a fresh start in for our Continuous Integration system because it wasn't handling newer technologies. Releases were becoming more frequent, and required too much manual work - our future plans involve a service oriented architecture which means a larger number of smaller projects - complicating all of the above.
Build and release pipelines are of course the most complex aspect of this setup. To create a new CI pipeline, you need a good understanding of all the tools that are being used - build agents are hosted on a headless VM so you also need to understand the command line for a tool if you want to customise it. If you've only ever used something through an IDE such as Visual Studio this means more research which may be out of some people's comfort zone.
The UI in Azure Devops build pipelines will try to push you in a YAML direction, but if you are new to the system you stand little chance of knowing the YAML format. The system comes with templates but these are for default setups, any legacy application will have additional steps you'll need to research and implement - although marketing people will always claim their tool is 100% seamless, the reality is you will need to work to move a project over and the older the project, the more work. Generally you can start with one of the templates but you will almost always have to customise your pipeline.
Challenge - Automating Angular in a .net MVC build
Angular was one of the more troublesome things to automate. There are many considerations, including the npm version that the build depends on, and what kind of build to use. Out of the box, Angular wants to be a single page application with an HTML file served directly from the web server in which all the magic happens. In practice, we had to add in custom steps to install specific versions of Angular, and create the right kind of build.
To make angular work, we needed to first add in some of the standard tools - the npm installer and a version switch. Then we were able to add a manual command line to build angular.
The biggest thing we learned from this was that one needs a very good understanding of how a tool works under the hood. Through research, we were able to learn any aspect of MSBuild which we were not familiar with at the start of this project - its quite surprising how much an IDE like Visual Studio hides.
Creating a Nuget feed
For this part of the project, Azure Artifacts is the solution on offer. We could have hosted a service somewhere else, but this seemed the most cost effective. Initially we weren't so familiar with the workings of Nuget, the package manager for .net - so a few things came as a surprise. We were able to figure out how to push build artifacts from our CI pipelines into Azure Artifacts, which was where we ran into the first oddity which had to be resolved.
Challenge: Creating packages which use semantic versioning
Normally in Nuget a package is given a semantic version. Version 1.0.0 is the first ever stable version, and the version usually follows the format <Major Version>.<Minor Version>.<Patch>[possible other stuff] For example, 184.108.40.2062. If your package is a pre-release package, you might append -rc1 or -beta. The reason this is important is that each number has a meaning - for example, minor version changes are expected to maintain backward compatibility.
The Nuget Pack task does have a feature which looks like it should do the job - you can set the version numbers in variables, and on the Nuget Pack command you can choose the pack option "Use the build number".
For our pre-release packages, we set our variables, and set our build number format to Major.Minor.Patch.Revision-beta and for release, we set Major.Minor.Patch.Revision
But unfortunately, in that pipeline step, "build number" means "an internal build number". No matter what we put as our build number format, it would always use Major.Minor.Patch.Revision. We couldn't distinguish between release and pre-release packages and we certainly didn't want to ship a beta package to a live site.
Eventually we found that the best way to approach this problem was to tell the Nuget Pack command to use an environment variable - Build.BuildNumber to be precise.
We realised from this that many of the labels in the UI in Azure Devops should be taken with a pinch of salt. Not everything means what one would initially assume. In the case of "Use the build number" the difference was subtle, but its advisable to test a new setting fully before trying to rely on it.
Another example of this effect is the term "Preview" and "Release" in the listing screen for nuget packages. These are absolutely nothing to do with nuget's definition of a preview or release version. They seem to be there just to help you filter within the UI.
Powerful, but rough around the edges. For anything non-standard it can be fiddly and time consuming. It's still getting some big revisions by Microsoft, so even they know it isn't a finished product. Once set up though it works very well.– Mark Windle
The project took a few weeks in all, and after that there were some further follow-up steps to be taken, but once everything is setup and the nuances of each project have been worked out, everything works brilliantly.