Continuous Integration with Gitlab
It is important to understand that this is understood:
GitLab has built-in continuous integration
- Continuous Integration - Pushing small chunks of code and on every push, run a pipeline of scripts to build, test, and validate the code changes before merging them into the main branch
- Continuous Delivery/Deployment - Deploying your application to production at every push
Allow you to catch bugs and errors early in the development cycle
GitLab CI/CD is configured by a file called
.gitlab-ci.yml placed at the repository’s root.
The scripts in that file are executed by the gitlab runner - an open source project used to run your jobs and send the results back to GitLab.
In this file you can specify(basically everything):
- what you want to run
- define include and cache dependencies
- choose commands you want to run in sequence and those you want to run in parallel
- define where you want to deploy your app
- specify whether you will want to run the scripts automatically or trigger any of them manually
imagine that all the scripts you add to the configuration file are the same as the commands you run on a terminal in your computer
It is a good idea to check some of the gitlab CI templates for ideas on your specific project.
The scripts are grouped into jobs, and together they compose a pipeline
You can view the result of your CI under
CI -> Jobs
Gitlab CI Workflow
- Create Branch / Push new changes
- Automated build and test (Continuous Integration)
- Deploy Review App
- Review and Approve
- Deploy (Continuous Deployment)
You can go a bit deeper at each stage:
Continuous Integration could include code quality, performance testing, Junit tests, Container scanning and dependency scanning. Packing can be done to the container registry.
For a jekyll site to be built locally you would do
jekyll build. Before that you would need jekyll on your computer. For that you would have had to
gem install jekyll.
gitlab you tell it to do the same thing
The script would look like this:
script: - gem install jekyll - jekyll build
Each script is organised by a job:
job: script: - gem install jekyll - jekyll build
In the context of gitlab pages the job has a specific name called
pages: script: - gem install jekyll - jekyll build
GitLab Pages will only consider files in a directory called public, so we need to tell jekyll to output to that folder:
pages: script: - gem install jekyll - jekyll build -d public
We need to tell gitlab that the job generates artifacts in the public directory
pages: script: - gem install jekyll - jekyll build -d public artifacts: paths: - public
From jenkins 3.4 onwards, the default template requires bundler.
pages: script: - bundle install - bundle exec jekyll build -d public artifacts: paths: - public
But where did we specify that we are using ruby?
the first thing GitLab Runner will look for in your
Docker imagespecifying what do you need in your container to run that script
So we should add
image: ruby:2.3 to the top of the file
Tell gitlab to only run the job called
pages on the
You do that with
pages: script: - bundle install - bundle exec jekyll build -d public artifacts: paths: - public only: - master
There are three default stages on GitLab CI: build, test, and deploy
To specify which stage your job is running, simply add another line to your CI:
pages: stage: deploy script: - bundle install - bundle exec jekyll build -d public artifacts: paths: - public only: - master
Stages are good because it lets you build and test before deploying.
You could tell gitlab to run the tests on every branch other than master:
image: ruby:2.3 pages: stage: deploy script: - bundle install - bundle exec jekyll build -d public artifacts: paths: - public only: - master test: stage: test script: - bundle install - bundle exec jekyll build -d test artifacts: paths: - test except: - master
The test job is running on the stage
test, Jekyll will build the site in a directory called
test, and this job will affect all the branches except master.
The best benefit of applying stages to different jobs is that every job in the same stage builds in parallel
To avoid running the same script multiple times across your jobs, use
For example we run
bundle install for both jobs.
before_script: - bundle install
To cache the installation files for your projects dependencies, for building faster, you can use the parameter
we’ll cache Jekyll dependencies in a
vendor directory when we run
cache: paths: - vendor/ before_script: - bundle install --path vendor
image: ruby:2.3 cache: paths: - vendor/ before_script: - bundle install --path vendor pages: stage: deploy script: - bundle exec jekyll build -d public artifacts: paths: - public only: - master test: stage: test script: - bundle exec jekyll build -d test artifacts: paths: - test except: - master
Running with Docker
Docker, when used with GitLab CI, runs each job in a separate and isolated container using the predefined image that is set up in
This makes it easier to have a simple and reproducible build environment that can also run on your workstation
Register a gitlab runner
To use the gitlab runner with docker you need to register a new runner to use the
sudo gitlab-runner register \ --url "https://gitlab.example.com/" \ --registration-token "PROJECT_REGISTRATION_TOKEN" \ --description "docker-ruby-2.1" \ --executor "docker" \ --docker-image ruby:2.1 \ --docker-services postgres:latest \ --docker-services mysql:latest
The registered runner will use the
ruby:2.1 image and will run 2 services