Continuous Integration for Nim using Circle CI

Continuous Integration is an important and widely used practice when it comes to software development. Over recent years, several projects have cropped up to help provide automated testing and delivery as part of your Continuous Integration system.

Since reading BlaXpirit’s excellent blog post regarding using Travis CI with Nim, I’ve been making use of Travis to build and test several of my open source projects. In that blog post, another Continuous Integration service is mentioned: Circle CI. Circle CI are currently beta testing version 2.0 of their platform, which allows you to run your tests inside Docker Containers. Given that there are offical Nim Docker images which target the stable Nim channel (eg: the latest releases) on both Ubuntu Linux (using GNU libc) and on Alpine Linux (using musl libc), I thought that it might be worth taking a look at their offering. After a little playing around, I managed to get tests running against both Ubuntu and Alpine sucessfully.

The first step is to add a .circleci/config.yml file to your source repository. This file describes how to build your project for Cicle CI. There are a lot of options that you can include in this file to specify build steps, how to publish artifacts form the build and more. These are all explained in the official documentation, though beware that the 2.0 platform and its documentation is still a work in progress.

My setup for the build is fairly simple, and makes use of the Workflow system in Circle CI to run multiple build steps:

version: 2
jobs:
  build:
    working_directory: /usr/src/dotenv
    docker:
      - image: nimlang/nim
    branches:
      only:
        - master
    steps:
      - checkout
      - run:
          name: test
          command: nim c -r tests/main.nim
  build_alpine:
    working_directory: /usr/src/dotenv
    docker:
      - image: nimlang/nim:alpine
    branches:
      only:
        - master
    steps:
      - checkout
      - run:
          name: test
          command: nim c -r tests/main.nim
workflows:
  version: 2
  build_and_test:
    jobs:
      - build
      - build_alpine

The complete configuration file can be found here for the project in question.

The configuration file is written in the YAML format, and specifies the version of the build service to use, several jobs and the workflows. The most important section is the jobs section, in which we describe the Docker image to use to run each job (such as nimlang/nim or nimlang/nim:alpine), the branches to build (in the above configuration I only want to build the master branch), and the steps to take to perform the job. In this case, the steps to take consist of checking out the repository to the working_directory (this is also specified per job, and in the above case is specified to be /usr/src/dotenv - I recommend using a directory under /usr/src/ that corresponds to your project’s name), and running a command to build and run the main unit test file.

Once you publish this configuration file and add your project within the Circle CI dashboard, each time you push your project to its repository, it will be built automatically in three different configurations of Nim. If the build fails, you’ll get an email and you’ll be able to look at the build output to diagnose the problem. Each and every build will have a detailed output page such as the one that can be found here for the most recent build of my dotenv project.

As the build are ran inside Docker containers, the build process is very quick - much quicker than that of Travis CI even when it uses caching in my experience. Each of the three builds I specified in the above configuration are ran at the same time, with each taking around 20 seconds after the initial build (initial builds take time to pull down the required Docker images from the Docker registry, which can take a little while - subsequent builds check whether the existing image is up to date and uses it if so).

There are many other features that you can use with Circle CI, such as publishing HTML test reports (which is not currently a feature of the unittest module in the Nim standard library) and publishing generated binaries or documentation from your projects. So far I’ve not had a chance to experiment with these advanced options, but so far I like what I see.