Rust + Diesel + GitLab + CI

The folks over at GitLab give away some free compute power to allow users to have CI builds of their project. It is very straightforward to get Rust projects to build within a CI environment. This post is going to take that build process one small step further, we’re going to build a Rust project that uses the Diesel ORM. This adds a step of complexity since to compile a Diesel project you need to have a postgresql database accessible if you’re using the infer_schema!() macro.

Note: diesel has instructions for using diesel print-schema instead of using infer_schema!() in version 1.3.x. It has been proposed to move away from infer_schema!(), but that issues was closed because infer_schema!() is still useful. A redditor commented that infer_schema!() has been deprecated in diesel 1.3.

The Following assumes you already have rust installed, if not see the awesome project rustup.

First The Very Basics

Create an example project in GitLab then create the rust project using cargo.

cargo new hello_ci

Within that project directory create the CI rule file named .gitlab-ci.yml with the following:

# Use Rust docker image, see: https://hub.docker.com/_/rust/
image: rust:latest

# Defines stages which are to be executed
stages:
  - build

# Run `cargo build` for the project with stable Rust
run-build:
  stage: build
  image: rust:latest
  script:
  - rustc --version && cargo --version
  - cargo build --release --jobs 1

Make sure to add the file to your git project, and push to GitLab.

git add .gitlab-ci.yml
git commit -am "Adding CI file"
git push

Once the project has been pushed, GitLab will kick off a build automatically. See the example screenshots, and note, to see the CI features in GitLab, expand the CI / CD menu on the left panel and select Pipeliles or Jobs. And don’t worry, if something goes wrong, GitLab will kindly email you.

GitLab Figure 1

GitLab Figure 2

Adding in a Little Diesel

Now that the trivial case is completed, let’s take a look at adding in Diesel to the project. Let’s use the diesel getting_started_1 project as an example.

In order to build a project that uses Diesel (and uses infer_schema!()) we need to have a postgresql database accessible. You could use the standard Rust Docker image and then install/configure postgresql then configure postgres to trust local connection, then create the bare DB with psql and then build your project, but that’s, like, kind of a pain. I know this process because I went down that rat-hole first. Fortunately there’s a more elegent solution, GitLab’s services with Postgres.

You just need to configure the GitLab postgres service for your project with the following snippet in your .gitlab-ci.yml file.

services:
  - postgres:latest

variables:
  POSTGRES_DB: diesel_example_db_1
  POSTGRES_USER: runner
  POSTGRES_PASSWORD: ""

The POSTGRES_DB is the name of the database and POSTGRES_USER is the name of the database user … makes sense. runner appears to be the standard POSTGRES_USER, however it can be anything. In general, these can be anything as long as your consistent with your DATABASE_URL variable further down in the build section of your .gitlab-ci.yml file.

You’re also going to need diesel_cli installed into your build container. That can be added to the container using the before_script section of the CI config file.

before_script:
- cargo install diesel_cli

Putting it all together the whole .gitlab-ci.yml

# Use Rust docker image, see: https://hub.docker.com/_/rust/
image: rust:latest

# postgres ci: https://docs.gitlab.com/ee/ci/services/postgres.html
services:
  - postgres:latest

variables:
  POSTGRES_DB: diesel_example_db_1
  POSTGRES_USER: runner
  POSTGRES_PASSWORD: ""


before_script:
- cargo install diesel_cli

# Defines stages which are to be executed
stages:
  - build

# Run `cargo test` for the project with stable Rust
run-build:
  when: manual
  stage: build
  image: rust:latest
  variables:
    DATABASE_URL: "postgres://runner@postgres/diesel_example_db_1"
  script:
      #  - diesel migration run
  - rustc --version && cargo --version
  - cargo build --release --verbose --jobs 1

Note that I have defined when: manual which means you need to manual start the Pipeline in the UI instead of having it kicked off as part of the push. I usually set my CI builds to manual since GitLab is giving this compute time away for free, I don’t want to be an abuser of the service, see GitLab issue 23366. note: a reddit commenter pointed out the linked bug is more about too many artifacts instead of actually compute usage. And that there are limitations to the free CI service where a user is allotted a certain number of CI minutes, 2000 for a free plan.

I got some nice tidbits from the following post.

http://blog.wjlr.org.uk/2016/08/16/fast-rust-gitlab-ci.html

This post will also teach you how to improve build times on GitLab’s CI.

My Example project that is configured to use GitLab’s CI,

https://gitlab.com/noyez_examples/diesel_rs_example_1/

The Reddit thread has some great comments, specifically one about using your own runner