For this post, I’ve been exploring the use of AppVeyor to continuously integrate my ConTabs project. I’ll start by explaining what continuous integration (“CI”) means, continue by introducing AppVeyor, before finally getting stuck into the specifics of my experience (including the gory bits).
What is continuous integration?
First things first: what are we talking about? If you’ve been in software development for any time at all, you’ll probably at least have heard about continuous integration, or “CI”. Put simply, CI is the automatic building and testing of a project whenever new code is committed. By continually integrating changes into the main code-base, problems can be identified quickly, rather than becoming compounded. Or, as the good people at ThoughtWorks put it:
By integrating regularly, you can detect errors quickly, and locate them more easily.
Until I started playing with AppVeyor, I’d always thought of CI as being the preserve of DevOps practioners. It seemed expensive (both in terms of the time spent setting it up and the costs of running a build server) and complicated. I’ve interacted with CI processes in a professional setting, but have never been able to justify spending the time digging into the mechanics of the thing.
Continuous integration with AppVeyor
AppVeyor is a cloud-based CI platform that specialises in building Windows (including .NET) projects. AppVeyor works by maintaining a pool of VMs that watch for changes to repositories, waiting for their moment to shine. There are several things that make AppVeyor an appealing choice:
- It integrates easily with GitHub
- Building .NET applications is painless
- It’s free (as in beer) for open source projects
So, when I was looking for a CI solution for my open source, GitHub-hosted, .NET project, choosing AppVeyor was a no-brainer.
Getting ConTabs to build
Although getting AppVeyor to trigger a build when commits are pushed to GitHub is easy (i.e. no config at all), I did hit a few snags while trying to get my project to build sucessfully. A great thing about AppVeyor is that it retains a log of all builds, which means I can show you exactly what happened, so I’ll go through each obstacle in turn.
The first snag was due to me failing to restore my NuGet packages. I solved this by adding the following to my appveyor.yml
file:
1 2 |
before_build: - cmd: nuget restore |
The second roadblock was caused by the fact I’m using VS 2017. Specifically, the combination of runtimes I’m using means I can’t use the default VS 2015 build image (more in the docs). Changing this was as simple as adding the following to my config file:
1 |
image: Visual Studio 2017 |
With those two issues out of the way, I coaxed out a successful build. Yeah!
My elation didn’t last long though… I managed to break the build pretty quickly after that by including some hard-coded newlines in my tests. Where I had used \r\n
on my Windows laptop, AppVeyor was using \n
(or perhaps the other way round…) on their Linux-based VMs. I was using Environment.Newline
in my actual code, but verbatim string literals in my tests. This one was easy to fix, by replacing all of these hard-coded newlines. Boom! Back to a working build!
Wait… what? It’s broken again? Dang! This time it’s because I’ve not yet implemented any way to specify the format of a date:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
1) Error : ConTabs.Tests.ConformanceTests.BasicTableWithOneLineOfDataShouldLookLikeThis Shouldly.ShouldAssertException : tableString should be "+-----------------------------------------------------------------+ | StringColumn | IntColumn | CurrencyColumn | DateTimeColumn | +-----------------------------------------------------------------+ | AAAA | 999 | 19.95 | 01/01/2017 00:00:00 | +-----------------------------------------------------------------+" but was "+------------------------------------------------------------------+ | StringColumn | IntColumn | CurrencyColumn | DateTimeColumn | +------------------------------------------------------------------+ | AAAA | 999 | 19.95 | 1/1/2017 12:00:00 AM | +------------------------------------------------------------------+" |
So, although the date is the same, the representation differs: the date portion has no leading zeros and the time is in 12-hour format. Essentially, the tests passed on my machine because my local culture settings specify a date with leading zeros and a time in 24-hour format (like a good European), whereas our Canadian friends at AppVeyor don’t.
The fix this time is to hide the troublesome column until I implement the functionality to fix it properly. Hoorah! Back to a working build (again)!
Summary
Getting started with AppVeyor is a walk in the park. Even when I hit some roadblocks, they were pretty simple to overcome. The first two were solved by tweaking my build environment using the appveyor.yml
file. (Incidentally, whilst I was poking around in the docs solving these issues, I realised that this config file can be really powerful.) The second two were issues with my code. Although it “worked on my machine”, these failing tests were a great reminder of the importance not to make assumptions about the environments in which your code will be called.
I’m expecting the real benefits of CI and AppVeyor to become apparent as ConTabs matures. As the project attracts contributors and becomes more complicated, the central value of CI will come into its own. As I start to want to do more with the code (e.g. analysing test coverage, deploying to NuGet), AppVeyor’s build scripting will allow me to easily trigger these other actions. It’s arguably a bit early to have invested in the CI infrastructure, but it’s been a really interesting project.
In short, AppVeyor provides a powerful platform for automatically building .NET projects in the cloud. I’ll be exploring some of the possibilities that AppVeyor opens up in some of the future posts in the ConTabs series.