Basic CircleCi Configuration

I wanted to share a simple configuration for running OCaml projects in CircleCI
that I’ve been using. CircleCI is what I’m using at work, it supports a nice feature of
allowing SSH onto the machine where a build is run. Letting you debug setup issues which is really helpful.

My requirements are simple to build OCaml projects that use OPAM and have simple test requirements (just running unit tests).

First you need to add a file .circleci/config.yml with:

version: 2
jobs:
  build-4.10:
    docker:
      - image: ocaml/opam:ubuntu-18.04-ocaml-4.10
    steps:
      - checkout
      - run:
          name: Build
          command: ./bin/ci

workflows:
  version: 2
  build:
    jobs:
      - build-4.10

This creates a job build-4.10 using docker image ocaml/opam2:4.10 published by the OCaml team. The steps defines the commands to run, we use a built in checkout command provided by CirclCI and then a run command that executes a shell script ./bin/ci.

You could use your own docker container in place of ocaml/opam2:4.10, maybe pre-installing some things or using a different linux distro. How to run the command could also be inlined rather than being its own file. I chose to make it a file for two reasons, when you SSH to debug a script you can just re-run ./bin/ci, and you can re-use the steps between local and CI.

Now to the shell script

#!/bin/sh -eux

WORKING_DIR=$(pwd)

# Install some extras
sudo apt-get install m4 pkg-config -y

# Make sure opam is setup in your environment.
eval `opam config env`
opam update

# Install each package as a dev dependency
find . -type f -name '*.opam' | sort -d | while read P; do
  opam pin add -n "$(basename -s .opam ${P})" . -y --dev
  opam install --deps-only "$(basename -s .opam ${P})"  -y
  eval `opam config env`
done

# Run the builds and
dune build
dune runtest

This configuration is from a project with multiple opam files so we have a find
to locate all those files. One gotcha with this is it’ll sort the file names which may not match the dependency order, if that is the case you will need to explicitly list them.

If you have a single opam file then replace that with the following (replacing project-name with your project name).

opam pin add -n "project-name" . -y --dev
opam install --deps-only "project-name"  -y

Push that into your github main branch, then Set up Project in the circleci UI and you should be off and building. From here the circleci docs can help with setting up different builds based off branches. Adding other OCaml builds is as easy as duplicating the build-4.10 section in YAML, pointing it to another docker container like 4.08 and adding the new build name to workflows under jobs:.

This configuration doesn’t include any explicit caching, adding opam caching would help speed up builds. What’s the right directory to cache for opam? Just ~/.opam I assume.

I also tried using the GitHub - ocaml/ocaml-ci-scripts: Skeletons for CI scripts but they’re too travis-ci specific.

2 Likes

Great writeup Tim! Just a minor correction – you should be using ocaml/opam as the base for the Docker images now, since those are the ones regularly rebuilt.

The migration from the older ocaml/opam2 might seem messy, but unfortunately we are limited in how much we can do using the registries without breaking a bunch of existing users (I wish we had namespace redirects!). I’ll take a look at fixing up our documentation for CI online as soon as I read through the related GitLab CI thread as well.

Fantastic thanks for the correction @avsm I’ll update the instructions.