Automated Testing for Zig projects with Circle CI

Automated testing is a handy tool for practicing Continuous Integration. There are a lot of different services and platforms available to developers to help automate testing for your projects. In this post, I’ll be exploring using Circle CI to automatically test a Zig based project.

Throughout this post, I will be using two simple projects, both of which are available in a companion repository that also includes the configuration for the CI service. The first of these is the default project that Zig creates when running zig init-lib. The other is a little more involved, making use of SDL to render a window on screen - whilst this project doesn’t have any tests, we want to ensure that it builds.

I will be assuming that you already have your repository configured and connected to the CI service.

I also intend to update this post in the future as I explore some alternative services such as Azure Pipelines, Travis CI and builds.sr.ht.

Circle CI

Circle CI provides a free plan that allows one job to be ran at a time. We’ll take advantage of their Docker support to run our CI. I maintain a Docker image for Zig that publishes tags for both the latest releases - latest - and the latest master releases (which are updated at midnight every night) - master.

We will be testing against both the most recent release and the latest master release.

Testing the simple sample project

There are a couple of steps we need to take to test our simple project:

  • Checkout the code that we want to test.
  • Print the version of Zig that we’re using, which is particularly useful in the case of master releases.
  • Run zig build test to run our tests.

Circle CI uses YAML files to configure workflows. I’ve included the required configuration below to achieve the above steps:

version: 2.1
jobs:
  test_latest:
    docker:
      - image: euantorano/zig:latest
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          name: print zig version
          command: |
            zig version
      - run:
          name: run tests
          command: |
            zig build test

  test_master:
    docker:
      - image: euantorano/zig:master
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          name: print zig version
          command: |
            zig version
      - run:
          name: run tests
          command: |
            zig build test

workflows:
  version: 2
  commit:
    jobs:
      - test_latest
      - test_master

The above configuration defines two jobs - one to test against the latest Zig release (test_latest) and another to test against the latest Zig master release (test_master). We then use these jobs within a workflow that we call commit that doesn’t have any set trigger as the default is to trigger the workflow when pushing to a branch.

Each of these jobs defines a couple of steps to checkout the code, run a command to print the Zig version, then run the tests.

Building the SDL sample project

The SDL project is a little bit more involved - we need to make sure that the SDL development headers and the SDL library itself are installed before we try building the project. The Zig Docker image doesn’t ship with either of these installed, but we know that it’s based on Alpine Linux so we need to install the following two packages:

The easiest way to do this is to create a custom Docker image based off of the standard Zig image that will install these packages. In this case, I’m only going to test against the latest Zig version, but you could test against the master image too with another Docker image. Create a Dockerfile like the following:

FROM euantorano/zig:latest

RUN apk add --no-cache sdl2 sdl2-dev

Now we just need to write our build configuration file. We’ll be adding an extra step compared to the simple project, so our steps will be:

  • Checkout the code that we want to test.
  • Build the Docker image.
  • Print the version of Zig that we’re using, which is particularly useful in the case of master releases.
  • Run zig build to build our project.

The configuration to perform these steps should look something like the following:

version: 2.1
jobs:
  build_sdl_latest:
    machine: true
    working_directory: ~/repo
    steps:
      - checkout
      - run:
          name: build docker image
          command: |
            docker build -t zig-sdl2 .
      - run:
          name: print zig version
          command: |
            docker run zig-sdl2 version
      - run:
          name: build app
          command: |
            docker run -v $PWD:/app zig-sdl2 build

workflows:
  version: 2
  commit:
    jobs:
      - build_sdl_latest

The above configuration uses the machine executor, which has Docker installed and ready to use. It defines a single job build_sdl_latest with several steps that check out the code, build a docker image using the Dockerfile in the root of the repository and tag it as zig-sdl2`, then use the created image to print the Zig version, then finish up by using the Docker image to build the project.