Gitlab CI - Configuring Gitlab Runner
Gitlab CI - Configuring Gitlab Runner

In this post you will learn what is Gitlab CI or Continuous Integration and how you can use Gitlab runner for free.

What is CI?

The first question is obviously "What is continuous integration?".

Ci diagram

Here is a really nice diagram about how it all works. As you can see on the left we have some developers who are working together. They are doing different commits in different branches. At some point they want to create new releases. So they push their change to Github or Gitlab.

After they pushed their changes they are getting to Continuous Integration. As you can see we have this process in the circle. First of all CI builds the whole application to check that all new features and new code works. After this we can run different commands like tests, linting or any other command that you prefer. If our build or tests failed then the user who did changes will be notified. If everything is green then we have a new build and everybody are notified that new version is available for deployment.

This is essentially an idea of CI.

We continuously pushing our changes, they are checked and our code can be continuously deployed.

Do you need CI?

Now you can for sure say that you can call all this commands locally and you don't need CI. Yes you can do that but when you have more developers then people can forget to run something locally. For example they didn't check a build or didn't run tests, then bad pull request was merged in master and now master branch is broken. We want to avoid that and CI helps tremendously with it.

Pushing code to Gitlab

This brings us to Gitlab. What is Gitlab? It's a service where you can remotely push your changes. But it also has a feature which is called Gitlab CI or Gitlab Runner.

We can for free even in private repositories push our changes and then run some commands so our code and build is checked remotely.

This is exactly what we want to do.

Here I already prepared a create-react-app application. But it doesn't really matter what code, language or framework you want to push to Gitlab and check. It will work with any framework. But to test we need to have some project so here is an empty react application.

First of all we must push to Gitlab our project. You need to register account in Gitlab if you still didn't do that and create a blank project. I will name our project mla-gitlabci.

Now we must bind this repository to our project.

git init
git add .
git commit -am "Initial commit"
git remote add origin git@gitlab.com:ejiqpep/mla-gitlabci.git
git push origin master

We initialized Git inside our project, committed our files, added Gitlab url as a remote and pushed our changes.

Gitlab pushed project

As you can see we pushed our project to Gitlab.

Setting up Gitlab CI

Now it's time to configure Gitlab CI. This is why locally we must create a new file .gitlab-ci.yml.

In the file we must define stages and job. Stages are our steps of building process inside CI. For example first stage is "Building", second stage is "Testing" and third stage is "Linting". And inside every single stage you can have different jobs.

stages:
  - build
  - test

Here we defined 2 stages. Not let's add 1 job to every stage.

stages:
  - build
  - test

build:
  stage: build
  image: node
  script:
    - echo "Start building App"
    - npm install
    - npm run build
    - echo "Build successfully"

test:
  stage: test
  image: node
  script:
    - echo "Start testing App"
    - CI=true npm run test
    - echo "Testing successfully"

Here we defined 2 jobs. In every job we must set a stage to know where this job belongs. Also we define an image to node because our Gitlab CI isolates our code in Docker containers and for React application we need node environment.

Last thing is a sequence of scripts that we want to call. During the build job we want to print out logs, install all dependencies and build our react application. During the test job we want to call test command which will run all tests inside our application.

Our next step is to commit and push this file to remote.

git add .
git commit -am "Added CI"
git push

As you can see directly after the push we have a new pipeline running in the Gitlab.

CI Pipeline

Now here on the left we can click on our pipeline and see our stages and jobs

Stages and jobs

Here are our build and test stages with 1 job in each of them. Obviously you can have lots of jobs inside and you can run them in parallel.

Now here we can click on a specific job and check logs inside it.

Logs

Here we get lots of information. We see the image that Gitlab users, see our logs and outputs of our commands.

As you can see after some time our pipeline is green which means all our stages inside CI are done.

Cache and artifacts

Now you for sure want to ask is why we call npm install in both jobs? This happens because each job is completely isolated inside it's own environment. But it is not the best approach as we build the same modules again and again and it takes time.

It is totally fine to wait 10 minutes as it happens remotely but what can we do about that? We can use cache.

cache:
  paths:
    - node_modules/

As a root property we can define caches. Now after single call of npm install this folder will be cached between jobs.

It is important to remember that we use cache to create temporary files between jobs. We don't use it to create artifacts.

What are artifacts? This is something that you want not just to cache but use later. For example you want to build css,js.html to use later on production server. It is not cache but an artifact.

But here is the problem. Actually after I tried to add cache my application build was broken in CI because of eslint library. This is why in this case I used artifact as a cache. It is not the best approached but it worked just find.

stages:
  - build
  - test

build:
  stage: build
  image: node
  script:
    - echo "Start building App"
    - npm install
    - npm run build
    - echo "Build successfully"
  artifacts:
    expire_in: 1 hour
    paths:
      - build
      - node_modules/

test:
  stage: test
  image: node
  script:
    - echo "Start testing App"
    # - npm install
    - CI=true npm run test
    - echo "Testing successfully"

As you can see we defined an artifact which will expire in 1 hour inside a first job. Then we must remote npm install for the second job because we already have this folder.

After this changes our npm install will happen only once and the whole build will be twice as fast.

Pull requests

And the last thing that you must know is the usage of CI inside pull requests. Every time then you push your changes in new branch (typically some feature branch) Gitlab will run CI commands. If we create a pull request it will show us if pipeline was successful and if it is safe to merge our changes.

It is extremely important to wait for CI check and not just merge your changes immediately.

And actually if you want to learn Docker with Docker compose and deploy a real application to production I highly recommend you to check my course Docker and Docker Compose - Project Deployment From Scratch.