222

If a GitLab project is configured on GitLab CI, is there a way to run the build locally?

I don't want to turn my laptop into a build "runner", I just want to take advantage of Docker and .gitlab-ci.yml to run tests locally (i.e. it's all pre-configured). Another advantage of that is that I'm sure that I'm using the same environment locally and on CI.

Here is an example of how to run Travis builds locally using Docker, I'm looking for something similar with GitLab.

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
  • 3
    should be available in latest devel, see [gitlab-ci-multi-runner#312](https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/issues/312) – jangorecki Dec 01 '15 at 02:58

11 Answers11

215

Since a few months ago this is possible using gitlab-runner:

gitlab-runner exec docker my-job-name

Note that you need both docker and gitlab-runner installed on your computer to get this working.

You also need the image key defined in your .gitlab-ci.yml file. Otherwise won't work.

Here's the line I currently use for testing locally using gitlab-runner:

gitlab-runner exec docker test --docker-volumes "/home/elboletaire/.ssh/id_rsa:/root/.ssh/id_rsa:ro"

Note: You can avoid adding a --docker-volumes with your key setting it by default in /etc/gitlab-runner/config.toml. See the official documentation for more details. Also, use gitlab-runner exec docker --help to see all docker-based runner options (like variables, volumes, networks, etc.).

Due to the confusion in the comments, I paste here the gitlab-runner --help result, so you can see that gitlab-runner can make builds locally:

   gitlab-runner --help
NAME:
   gitlab-runner - a GitLab Runner

USAGE:
   gitlab-runner [global options] command [command options] [arguments...]
   
VERSION:
   1.1.0~beta.135.g24365ee (24365ee)
   
AUTHOR(S):
   Kamil Trzciński <ayufan@ayufan.eu> 
   
COMMANDS:
   exec         execute a build locally
   [...]
   
GLOBAL OPTIONS:
   --debug          debug mode [$DEBUG]
   [...]

As you can see, the exec command is to execute a build locally.

Even though there was an issue to deprecate the current gitlab-runner exec behavior, it ended up being reconsidered and a new version with greater features will replace the current exec functionality.

Note that this process is to use your own machine to run the tests using docker containers. This is not to define custom runners. To do so, just go to your repo's CI/CD settings and read the documentation there. If you wanna ensure your runner is executed instead of one from gitlab.com, add a custom and unique tag to your runner, ensure it only runs tagged jobs and tag all the jobs you want your runner to be responsible of.

elboletaire
  • 5,180
  • 2
  • 36
  • 46
  • @elboletaire How about running the whole build, not a single stage? – Nowaker Aug 30 '16 at 03:55
  • I'm not sure nowaker but I think there's no easy way. Probably you'll need to specify one by one. – elboletaire Aug 30 '16 at 09:59
  • @Nowaker there's currently a PR adding pipeline support https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/issues/2226 – elboletaire May 31 '17 at 19:35
  • 14
    [`gitlab-runner exec` is being deprecated after GitLab 10.0](https://gitlab.com/gitlab-org/gitlab-runner/issues/2710), vote https://gitlab.com/gitlab-org/gitlab-runner/issues/2797 to support a replacement before this happens – Alfageme Oct 24 '17 at 15:58
  • 2
    Thanks for pointing it out @Alfageme, apparently they're not going to deprecate it until a replacement is developed, see https://gitlab.com/gitlab-org/gitlab-runner/issues/2797#note_42944825 – elboletaire Oct 24 '17 at 16:39
  • @elboletaire can you please update your answer to reflect more precise instructions on where the gitlab-runner line should be placed. What i want is that after pushing a branch, the tests to be run locally and in the gitlab web ci interface, pipeline is shown as passed if everything goes okay, but i don't know where to start. I know i must install gitlab multi runner and docker locally, but that's it – tommyalvarez Feb 23 '18 at 12:34
  • I don't get you... you must run it from the console. If you want to run all the tests before pushing, just define a git pre-push webhook... – elboletaire Feb 23 '18 at 14:54
  • @elboletaire what i meant was that i wanted gitlab ci pipeline to use MY computer to build and run tests, which is 3x faster that gitlab ci shared runners, given a docker image: ruby:2.4 in my gitlab-ci.yml. Is there a way to force my gitlab private repo to use my computer as a gitlab-runner ? – tommyalvarez Mar 01 '18 at 14:16
  • I don't know mate, I use gitlab self-hosted, not gitlab.com. In self-hosted gitlabs I can set-up as many runners as I want. I don't know if you can configure other runners in gitlab.com. Sorry m8. – elboletaire Mar 01 '18 at 14:51
  • So how does this runner sees git repo files? – holms Jun 26 '18 at 03:40
  • You should run the runner in the same repo folder. It automatically clones/fetches de code with the latest changes (this is important to know, because you need to commit your changes always before trying the runner, otherwise it will check every time the same). – elboletaire Jun 26 '18 at 11:15
  • @tommyalvarez now that I'm using gitlab.com (I was only using a self-hosted version of gitlab) I can confirm you that you can also configure as many runners as you want for gitlab.com. Just go to your project ci/cd settings and click in the "runners" section. You'll see there how to register custom runners. BTW, note that this wasn't what was being asked. The question was about running tests locally, not about registering local runners for gitlab.com. – elboletaire Oct 27 '18 at 12:50
  • How do we set CI variables locally? Such as the SSH_PRIVATE_KEY that is commonly used to for deployments. – Kong Jin Jie Jan 03 '19 at 05:28
  • 3
    @KongJinJie, you can do `gitlab-runner exec docker --help` and you'll see all the options. In your case, for env vars, is as simple as adding `--env VARIABLE=value` as a param of gitlab-runner – elboletaire Jan 03 '19 at 12:47
  • Can I start a job locally (from cmd), but process in on one of the remote/shared runner? – mvorisek Jun 18 '19 at 10:06
  • Yes you can, but using gitlab API. Not something you can do with gitlab runner AFAIK. – elboletaire Jun 18 '19 at 12:21
  • How is this finding the gitlab yml pipeline stages? When I run this command I get FATAL: no job named `` – Matt Mar 24 '21 at 17:09
  • This is to run jobs one by one, not full stages. – elboletaire Mar 24 '21 at 17:18
  • oh how i wish this was useful – Randy L Sep 03 '21 at 21:54
  • 2
    An FYI for anyone hoping to use this to test things locally - `gitlab-runner exec` [does not support](https://docs.gitlab.com/runner/commands/#limitations-of-gitlab-runner-exec) a whole swathe of things in Gitlab's CI config, perhaps most notably [the `extends` key](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3794). Many others are not supported but also not listed on that page! So if you've used these features to make your CI config more maintainable or composable, `gitlab-runner` is not going to help. – detly Jan 14 '23 at 09:06
  • For me the command actually was: `gitlab-runner exec docker --cicd-config-file .gitlab-ci.yml ${job name}` – skofgar May 12 '23 at 16:04
162

I use this docker-based approach:

Edit: 2022-10

docker run --entrypoint bash --rm -w $PWD -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest -c 'git config --global --add safe.directory "*";gitlab-runner exec docker test'

For all git versions > 2.35.2. You must add safe.directory within the container to avoid fatal: detected dubious ownership in repository at.... This also true for patched git versions < 2.35.2. The old command will not work anymore.

Details

0. Create a git repo to test this answer

mkdir my-git-project
cd my-git-project
git init
git commit --allow-empty -m"Initialize repo to showcase gitlab-runner locally."

1. Go to your git directory

cd my-git-project

2. Create a .gitlab-ci.yml

Example .gitlab-ci.yml

image: alpine

test:
  script:
    - echo "Hello Gitlab-Runner"

3. Create a docker container with your project dir mounted

docker run -d \
  --name gitlab-runner \
  --restart always \
  -v $PWD:$PWD \
  -v /var/run/docker.sock:/var/run/docker.sock \
  gitlab/gitlab-runner:latest

(-d) run container in background and print container ID

(--restart always) or not?

(-v $PWD:$PWD) Mount current directory into the current directory of the container - Note: On Windows you could bind your dir to a fixed location, e.g. -v ${PWD}:/opt/myapp. Also $PWD will only work at powershell not at cmd

(-v /var/run/docker.sock:/var/run/docker.sock) This gives the container access to the docker socket of the host so it can start "sibling containers" (e.g. Alpine).

(gitlab/gitlab-runner:latest) Just the latest available image from dockerhub.

4. Execute with

Avoid fatal: detected dubious ownership in repository at... More info

docker exec -it -w $PWD gitlab-runner git config --global --add safe.directory "*"

Actual execution

docker exec -it -w $PWD gitlab-runner gitlab-runner exec docker test
#                ^          ^           ^            ^     ^      ^
#                |          |           |            |     |      |
#               (a)        (b)         (c)          (d)   (e)    (f)

(a) Working dir within the container. Note: On Windows you could use a fixed location, e.g. /opt/myapp.

(b) Name of the docker container

(c) Execute the command "gitlab-runner" within the docker container

(d)(e)(f) run gitlab-runner with "docker executer" and run a job named "test"

5. Prints

...
Executing "step_script" stage of the job script
$ echo "Hello Gitlab-Runner"
Hello Gitlab-Runner
Job succeeded
...

Note: The runner will only work on the commited state of your code base. Uncommited changes will be ignored. Exception: The .gitlab-ci.yml itself does not have be commited to be taken into account.

Note: There are some limitations running locally. Have a look at limitations of gitlab runner locally.

jschnasse
  • 8,526
  • 6
  • 32
  • 72
  • 17
    Very useful examples here help to understand how it works – Moacir Rosa Sep 04 '21 at 14:03
  • 1
    Note: it is important to make sure you use -v $PWD:$PWD because of the docker in docker situation (otherwise you will get an error `fatal: Could not read from remote repository.`). See https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4574 – vab2048 Nov 30 '21 at 22:20
  • 1
    Step 3 does not work in windows terminal. Use `docker run -d --name gitlab-runner --restart always -v ${PWD}:/usr/src/app -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest` instead. Only the mount file argument has been changed. $PWD has been replaced by ${PWD} to work in windows. Also you cannot use the same path in the docker container as it runs linux, so I replaced it with a generic path: /usr/src/app – Passiv Programmer Dec 12 '21 at 16:20
  • @PassivProgrammer added this to my answer. Thank you! – jschnasse Dec 12 '21 at 21:14
  • @PassivProgrammer I think you also have to change the working directory in step 4 in that case? I added a note at this step as well. Thank you for your input! – jschnasse Dec 12 '21 at 21:28
  • @jschnasse yes that is correct – Passiv Programmer Dec 13 '21 at 08:31
  • 4
    For those who want to use a single `docker run` command: `docker run --rm -w $PWD -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest exec docker test` – Jack Dec 20 '21 at 17:37
  • 1
    For those working/experimenting with [podman](podman.io): `podman run --rm -v $PWD:$PWD -w $PWD gitlab-runner exec shell build`. It does use the shell executor, however, in case that is a show stopper. As I need artifacts, which are not supported for the moment, I didn't experiment any further, like preventing the `builds` directory leaking the container. – jonas-schulze Sep 09 '22 at 16:21
  • 1
    When I do this I get an error message `fatal: detected dubious ownership in repository at ...` Does anyone know where this comes from? Looking around it seems that it's because the docker user doesn't own the repo directory, but that is a strange requirement. Does anyone know where this comes from? The [gitlab-runner-local](https://github.com/firecow/gitlab-ci-local) utility works. – Aleksandra Glesaaen Oct 29 '22 at 13:54
  • 1
    Hi @AleksandraGlesaaen, I assume you use a docker rootless setup? Maybe you can try something like `docker exec -it -w $PWD gitlab-runner git config --global --add safe.directory "*"` **before executing step 4** ? Similar to [like suggested here](https://www.kenmuse.com/blog/avoiding-dubious-ownership-in-dev-containers/). I haven't tried it yet but would be glad to make this setup work for rootless installations as well. Feedback appreciated. – jschnasse Oct 31 '22 at 07:22
  • Hi @AleksandraGlesaaen, I was able to reproduce and added two edits. One for the single line command and one on step 4 of the detailed manual. Feedback appreciated. – jschnasse Oct 31 '22 at 07:55
  • 1
    Hi @jschnasse, yes I run a rootless setup. I tried the updated answer with the extra step and it works, thank you so much! The single command version also works :) – Aleksandra Glesaaen Nov 01 '22 at 09:07
  • 1
    In PowerShell using `${PWD}` results in an "invalid mode" error from the Docker daemon, but specifying absolute path using forward slashes as separators works – Philipp Mar 14 '23 at 14:26
  • I assume `$PWD` without curly brackets will work in Powershell. @Philipp – jschnasse Mar 14 '23 at 17:46
  • @jschnasse `$PWD:$PWD` produces a syntax error that explicitly tells you to use curly braces instead – Philipp Mar 15 '23 at 14:34
27

I'm currently working on making a gitlab runner that works locally. Still in the early phases, but eventually it will become very relevant. It doesn't seem like gitlab want/have time to make this, so here you go. https://github.com/firecow/gitlab-runner-local

Firecow
  • 501
  • 1
  • 5
  • 11
  • 1
    npm install gives an error but installing with Linux library works like a charm. Thanks! I can use this! – Maarten Apr 28 '21 at 16:34
  • *Linux Binary - Months later I have a new laptop and had to test pipelines locally again. I am glad I found this answer again :) – Maarten Sep 25 '21 at 10:20
  • Does this support services and reading a gitlab-runner toml config file? – Farhood ET Aug 02 '22 at 09:37
7

The GitLab runner appears to not work on Windows yet and there is an open issue to resolve this.

So, in the meantime I am moving my script code out to a bash script, which I can easily map to a docker container running locally and execute.

In this case I want to build a docker container in my job, so I create a script 'build':

#!/bin/bash

docker build --pull -t myimage:myversion .

in my .gitlab-ci.yaml I execute the script:

image: docker:latest

services:
- docker:dind

before_script:
- apk add bash

build:
stage: build
script:
- chmod 755 build
- build

To run the script locally using powershell I can start the required image and map the volume with the source files:

$containerId = docker run --privileged -d -v ${PWD}:/src docker:dind

install bash if not present:

docker exec $containerId apk add bash

Set permissions on the bash script:

docker exec -it $containerId chmod 755 /src/build

Execute the script:

docker exec -it --workdir /src $containerId bash -c 'build'

Then stop the container:

docker stop $containerId

And finally clean up the container:

docker container rm $containerId
Glen Thomas
  • 10,190
  • 5
  • 33
  • 65
5

If you are running Gitlab using the docker image there: https://hub.docker.com/r/gitlab/gitlab-ce, it's possible to run pipelines by exposing the local docker.sock with a volume option: -v /var/run/docker.sock:/var/run/docker.sock. Adding this option to the Gitlab container will allow your workers to access to the docker instance on the host.

Pierco
  • 79
  • 1
  • 4
  • I'm currently trying to execute a task in the `.gitlab-ci.yml` file in my project, on a Runner deployed as a Docker container. Do I need to bind mount the src code of my project into the Runner in order for it to find/run the task? Or is this somehow possible with what you said in your answer, i.e. connecting the to remote client like in Docker machine 'eval "$(docker-machine env default)"'? – Greg Brown Sep 25 '18 at 21:39
  • 1
    @GregBrown or if sb else was wondering, have a look a this [issue](https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4275), eg.: `docker run --rm -v $PWD:$PWD -v /var/run/docker.sock:/var/run/docker.sock -v /opt/gitlab-runner/config:/etc/gitlab-runner --workdir $PWD gitlab/gitlab-runner exec docker deploy`. Option `--workdir` is the key. – The_Ham Jan 20 '21 at 22:22
5

Another approach is to have a local build tool that is installed on your pc and your server at the same time. So basically, your .gitlab-ci.yml will basically call your preferred build tool.

Here an example .gitlab-ci.yml that i use with nuke.build:

stages:
    - build
    - test
    - pack

variables:
    TERM: "xterm" # Use Unix ASCII color codes on Nuke

before_script:
    - CHCP 65001  # Set correct code page to avoid charset issues

.job_template: &job_definition
  except:
    - tags

build:
    <<: *job_definition
    stage: build
    script:
        - "./build.ps1"

test:
    <<: *job_definition
    stage: test
    script:
        - "./build.ps1 test"
    variables:
        GIT_CHECKOUT: "false"

pack:
    <<: *job_definition
    stage: pack
    script:
        - "./build.ps1 pack"
    variables:
        GIT_CHECKOUT: "false"
    only:
        - master
    artifacts:
        paths:
            - output/

And in nuke.build i've defined 3 targets named like the 3 stages (build, test, pack)

In this way you have a reproducible setup (all other things are configured with your build tool) and you can test directly the different targets of your build tool.

(i can call .\build.ps1 , .\build.ps1 test and .\build.ps1 pack when i want)

fruggiero
  • 943
  • 12
  • 20
4

I am on Windows using VSCode with WSL

I didn't want to register my work PC as a runner so instead I'm running my yaml stages locally to test them out before I upload them

$ sudo apt-get install gitlab-runner
$ gitlab-runner exec shell build

yaml

image: node:10.19.0 # https://hub.docker.com/_/node/
# image: node:latest

cache:
  # untracked: true
  key: project-name
  # key: ${CI_COMMIT_REF_SLUG} # per branch
  # key:
  #   files:
  #     - package-lock.json # only update cache when this file changes (not working) @jkr
  paths:
    - .npm/
    - node_modules
    - build

stages:
  - prepare # prepares builds, makes build needed for testing
  - test # uses test:build specifically @jkr
  - build
  - deploy

# before_install:

before_script:
  - npm ci --cache .npm --prefer-offline

prepare:
  stage: prepare
  needs: []
  script:
    - npm install

test:
  stage: test
  needs: [prepare]
  except:
    - schedules
  tags:
    - linux
  script:
    - npm run build:dev
    - npm run test:cicd-deps
    - npm run test:cicd # runs puppeteer tests @jkr
  artifacts:
    reports:
      junit: junit.xml
    paths:
      - coverage/

build-staging:
  stage: build
  needs: [prepare]
  only:
    - schedules
  before_script:
    - apt-get update && apt-get install -y zip
  script:
    - npm run build:stage
    - zip -r build.zip build
  # cache:
  #   paths:
  #     - build
  #   <<: *global_cache
  #   policy: push
  artifacts:
    paths:
      - build.zip

deploy-dev:
  stage: deploy
  needs: [build-staging]
  tags: [linux]
  only:
    - schedules
  #   # - branches@gitlab-org/gitlab
  before_script:
    - apt-get update && apt-get install -y lftp
  script:
    # temporarily using 'verify-certificate no'
    # for more on verify-certificate @jkr: https://www.versatilewebsolutions.com/blog/2014/04/lftp-ftps-and-certificate-verification.html
    # variables do not work with 'single quotes' unless they are "'surrounded by doubles'"
    - lftp -e "set ssl:verify-certificate no; open mediajackagency.com; user $LFTP_USERNAME $LFTP_PASSWORD; mirror --reverse --verbose build/ /var/www/domains/dev/clients/client/project/build/; bye"
  # environment:
  #   name: staging
  #   url: http://dev.mediajackagency.com/clients/client/build
  #   # url: https://stg2.client.co
  when: manual
  allow_failure: true

build-production:
  stage: build
  needs: [prepare]
  only:
    - schedules
  before_script:
    - apt-get update && apt-get install -y zip
  script:
    - npm run build
    - zip -r build.zip build
  # cache:
  #   paths:
  #     - build
  #   <<: *global_cache
  #   policy: push
  artifacts:
    paths:
      - build.zip

deploy-client:
  stage: deploy
  needs: [build-production]
  tags: [linux]
  only:
    - schedules
    # - master
  before_script:
    - apt-get update && apt-get install -y lftp
  script:
    - sh deploy-prod
  environment:
    name: production
    url: http://www.client.co
  when: manual
  allow_failure: true
Jacksonkr
  • 31,583
  • 39
  • 180
  • 284
3

The idea is to keep check commands outside of .gitlab-ci.yml. I use Makefile to run something like make check and my .gitlab-ci.yml runs the same make commands that I use locally to check various things before committing.
This way you'll have one place with all/most of your commands (Makefile) and .gitlab-ci.yml will have only CI-related stuff.

Onlyjob
  • 5,692
  • 2
  • 35
  • 35
2

I have written a tool to run all GitLab-CI job locally without have to commit or push, simply with the command ci-toolbox my_job_name.

The URL of the project : https://gitlab.com/mbedsys/citbx4gitlab

Emeric Verschuur
  • 553
  • 1
  • 4
  • 9
  • How mature is this tool? Apparently the last release was around 6 months ago. Can this be used in a production environment? – Farhood ET Aug 02 '22 at 09:39
  • You should try this tool yourself to do your own opinion... I use it every day, and it is used from more than 5 years in two companies by teams around 10 to 20 persons. – Emeric Verschuur Aug 02 '22 at 11:18
2

Years ago I build this simple solution with Makefile and docker-compose to run the gitlab runner in docker, you can use it to execute jobs locally as well and should work on all systems where docker works:

https://gitlab.com/1oglop1/gitlab-runner-docker

There are few things to change in the docker-compose.override.yaml

version: "3"
services:
    runner:
      working_dir: <your project dir>
      environment:
        - REGISTRATION_TOKEN=<token if you want to register>
      volumes:
        - "<your project dir>:<your project dir>"

Then inside your project you can execute it the same way as mentioned in other answers:

docker exec -it -w $PWD runner gitlab-runner exec <commands>..

oglop
  • 1,175
  • 1
  • 9
  • 15
0

I recommend using gitlab-ci-local https://github.com/firecow/gitlab-ci-local It's able to run specific jobs as well. It's a very cool project and I have used it to run simple pipelines on my laptop.

nirlevywm
  • 86
  • 5