Continuous Integration and Deployments using AppVeyor
Every development team will benefit from having some form of continuous integration (CI). Razor are no exception. We create high quality software so need to know whether the latest commit is potentially shippable or not. Our team understand the importance of regularly integrating their code back into a shared repository. This enables them to identify and solve problems quickly and efficiently. They also understand the value of automating this process, as manual processes are prone to error. In an ideal world, we want to build, test, and deploy code in just a few clicks. This pursuit of happiness lead us to consider a wide range of options until we finally settled on AppVeyor.
Back in the day, developers of the .NET variety only had a few CI solutions at their disposal: local terminal, CruiseControl.NET and TeamCity. All of the these have various limitations but share one in particular; a previous build might affect a subsequent one because they share the same static Windows environment. One way to reduce this risk is to make each build idempotent, however, builds have different system requirements such as data storage, file storage, message transportation, etc. These need to be installed, configured, and maintained which creates server drift. AppVeyor uses phoenix servers to maintain a standard baseline so there is no trace of a previous build. In other words, you get a fresh Azure Virtual Machine (VM) at the start of each build which comes with a variety of common libraries/tools pre-installed. If you haven’t already, decommission that build server and let someone else worry about the infrastructure - SaaS FTW.
If code is the input for a build, artifacts are the output. For websites and APIs, these tend be ZIP files. Another common artifact is a NuGet package that will be consumed as opposed to being executed. Both, along with an array of others, can be easily deployed using AppVeyor.
Confusingly, AppVeyor refers to a deployment configuration as an Environment. Each one houses all the settings required to deploy the artifact to the specified destination. Whilst it's possible to specify these directly within the
appveyor.yml file, we always create Environments to ensure a clean separation of concerns and provide more control over when deployments take place. This also promotes the use of AppVeyor's roles and permissions to prevent certain users from accessing sensitive information. We mainly use Web Deploy and NuGet Environments. Razor have been using Azure since it’s inception, and AppVeyor offers great support for deploying to this platform via Web Deploy. AppVeyor also has a built-in NuGet repository, so our collection of packages are published to our account NuGet feed meaning we don't have to host our own or pay for a hosted solution.
Environments can be triggered manually via the UI, but it's also possible to automate this after a successful build. This becomes really powerful for NuGet packages where the latest master artifact should be available ASAP. Branching strategies such as GitHub Flow can also take advantage of this feature by deploying every branch to the respective Environment. It's completely feasible to create dynamic environments both in Azure and AppVeyor that can be torn down automatically some point in the future. This is just one of the concepts that we're exploring to speed up our delivery process and prevent contamination across deployments.
AppVeyor is by far the best solution available today for building and deploying .NET code, however, if you aren't satisfied with the standard features, they have a very nice ReST API. We've already experimented with it to create an open source dashboard that is currently displayed on the wall in our studio.
We've been using it in anger for several months and don't really have any bad things to say. A few of our clients have also adopted it following our recommendation and assistance through the transition period.
If you'd like to know more about how AppVeyor can improve your development process, get in touch.