/ CI/CD

Debugging Gitlab CI pipelines locally

Continuous Integration pipelines are a fantastic tools to leverage in application testing and fortunately Gitlab comes with built-in Continuous Integration support. Unfortunately, tests don’t always run the same locally as they do in CI builds, and a failed test in a CI pipeline can be difficult to debug when you’re seeing different results locally. Instead of making incremental changes to your code and praying the next CI build passes, you can leverage Gitlab runners to run CI builds locally and speed up the debugging process.


Assumptions

This article assumes you already have Gitlab CI set up for your project. If not, take a look at the getting started guide, or look at this guide for more information on what Gitlab CI is and how it works.

Gitlab Runners

To execute CI jobs, Gitlab uses Runners. You can think of Gitlab Runners as individual computers that execute the jobs in your .gitlab-ci.yml file. It’s assumed in this guide that you already have a dedicated Runner set up and connected with your project. Instead we’ll be installing a Gitlab Runner on your local machine in order to debug CI problems locally. I’ve highlighted the steps for installing a Runner on a Mac or Linux machine below, but feel free to refer back to the official guide which contains some additional details that aren’t necessary for our setup.

Installing a Gitlab Runner on MacOS

$ sudo curl --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-darwin-amd64
$ sudo chmod +x /usr/local/bin/gitlab-runner

Installing a Gitlab Runner on Linux

$curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
$sudo apt-get install gitlab-runner

Installing a Gitlab Runner on Windows

The setup process is a bit more detailed for Windows users, but can be found here.

Make sure Gitlab Runner is working

$ gitlab-runner

or

$ gitlab-ci-multi-runner

depending if the runner was installed on a Linux or Mac/Windows system.

You should see output like this:

NAME:
gitlab-ci-multi-runner - a GitLab Runner
USAGE:
gitlab-ci-multi-runner [global options] command [command options] [arguments...]
VERSION:
9.5.0 (413da38)
AUTHOR(S):
GitLab Inc. <support@gitlab.com>
COMMANDS:
exec   execute a build locally
...
GLOBAL OPTIONS:
--debug  debug mode [$DEBUG]
...

Using gitlab-runner to run a job locally

Assuming you already have a .gitlab-ci.yml file that looks something like this:

image: node:latest
 
Build:
  stage: build
  script:
    - docker build .

Unit-Tests:
  stage: test
  script:
    - npm install
    - npm test

You can run the Unit-Tests job with:

$ cd path/to/project 
$ gitlab-runner exec docker Unit-Tests

Things to know

  • Your local changes must be committed to git or they won’t be available to gitlab-runner. Because of this, I find it useful to use $ git reset — soft HEAD~1 which will unstage the last commit(s) made when debugging CI issues until the issue is resolved.
  • You may need to use shell instead of docker if running on MacOS or Windows. In that case, our command would be gitlab-runner exec shell Unit-Tests for this example.
  • You can run any of the jobs defined in your .gitlab-ci.yml file locally but not all at one, you’ll have to specify which job to run each time. For example, you can run either the Build or Unit-Tests stage but the gitlab-runner won’t run them both in one command.
  • The cache and artifacts in your .gitlab-ci.yml won’t work with the gitlab-runner exec docker command.
  • I recommend running gitlab-runner locally any time you’re setting up a new .gitlab-ci.yml or making any modifications to it. It can save hours in debugging time when it comes to catching errors in your shell scripts and finding discrepancies between your local testing environment and the one on the Gitlab Runner.