Tech, DevOps

How to do CI/CD in GitLab

Continuous integration tools have made the process of integrating code a lot easier and definitely have sped things up.

This way, you can have more contributors to the codebase, while CI ensures that code is integrated into one place. You also have version control for managing branches - they can be as many as you want.

Continuous Delivery makes sure that the changes you made locally are published to the server automatically when you push new changes to the repository.

For this article, we exemplify CI and CD with Gitlab.

GitLab is great because it offers continuous integration (like GitHub), but also continuous delivery using runners, pipelines, issue tracking and a whole lot of other cool tools.

It is open-source  and a very good starting point for any beginner in the field of DevOps to learn CI/CD, understand the flow and basically get a hang of the whole process.

Benefits

The main benefits of using CI/CD are:

  • Sped-up development process: you only have to push the code to the repository and it will be deployed to the server.
  • Testing the product is much faster when small changes are reflected within the server. They can be tested and if there is a fault in some functionality, we can pinpoint the commit after which the fault started, as well as the person who did the commit making the whole process of development and testing kind of transparent.
  • Time and cost-efficiency: When we deploy the code after the application is completed, there are potential faults that may occur, and resolving them takes extra time and extra effort. If, however, we use CI/CD, all the feednack and changes that the client adds can be resolved and implemented parallel to development.

Our Setup

Application

We are going to deploy a React application which is basically a very simple weather app that uses OpenWeatherApi to fetch weather data.

We will use a Gitlab service called Gitlab pages to deploy our website. This service lets you deploy static applications directly from gitlab repository to the interweb.

GitLab Pipeline

I will assume you already have a React codebase set up on Gitlab at this point, so that we can focus on the topic here.

Configuration

Repository setup

After you have pushed the React codebase to the Gitlab repository, navigate towards settings of the repository, go to the general section and expand Visibility, project features, permissions section. Scroll down and make sure the pages switch is in ON state.


GitLab Pipeline

Next step will be to create a .gitlab-ci.yml file in your project root. This file will have instructions on how Gitlab will build your project and which folder it needs to serve on the internet. Nothing Complex i will explain all. A point to note as this file is yml so monitor your spaces because if there is single space more or less it could mess up things and cause errors.

Let’s start off by defining the stages in which we want our pipeline to run. You can think of stages as steps to deploying the application.

image: node:12.22.0 
stages:
    - build
    - deploy

Here, we are doing two things: first, we are telling GitLab to use Node version 12 because my application is using this version of node. If you are using a different version, just replace it. Next, we are defining that there will be two stages - build and deploy - and yes you can use any stage name you want.

cache:
    paths:
        - node_modules/

In this section, we are telling GitLab to cache the node_modules directory so that it does not download new node modules on every build. This will save time and resources because basically we are reusing code.

before_script:
    - rm -rf build
    - CI=false npm install

What we just did is we specified a tag before_script which will run before the stage script. Then, we remove the build folder (if there is any or it it was pushed by mistake to the codebase) and we install all dependencies using npm install command.

In the previous step we have specified to cache node_modules so npm install will use those caches; if there is a new dependency, it will install that and also add it to caches.

Here we have the script for stage build, so let's unwrap it:

pages:
    stage: build
    script:
        - CI=false npm run build
        - rm -rf public
        - cp build/index.html build/404.html
        - mv build public
    artifacts:
        paths:
            - public
    only:
        - master

Instead of 'pages', you can name it whatever you want, just remember to specify the stage in the next step. Script tag defines the start of the script and below it we can write the commands which Gitlab will execute in order to run the application.

CI=false npm run build is basically building the project and it is the same for all react projects, just like we built the project using this command. What's different is we used environment variable CI, which ensures that the build is not failed due to any warnings. If there are warnings, sometimes the build fails.

Now, after building the project and establishing a build directory which has everything for our application to run, we can remove the existing public directory so that we can use it for serving our application with GitLab. Rm-rf public removes the existing public directory.

Next we are copying index.html to a file named '404.html'. In React, a route which is not defined in our router, is redirected to 404.html page. If you don't have a 404.html page defined within the project, you will get an error from the web server. So we are defining a 404.html page with content the same as index.html.

Remember I said GitLab needs a public named directory to serve the internet? We have our application build into the build folder, so the line mv build public is just copying build folder into public folder.

Next on, the artifacts tag is telling GitLab that the build job has an output of this directory. As we need a public directory to serve, we need to specify in the artifacts tag what to save for the next step.

The only tag is defining the rule that this pipeline should run only if the push is to master branch. This comes in handy if we have different development environments like dev, prod and testing.

deploy_app:
    stage: deploy
    script:
        - echo "deploying  the app"
    artifacts:
    paths:
        - public

On the next stage which is 'deploy' we are not doing anything significant, we are just printing, 'deploying the app' and giving the path name of the folder to serve in the artifact tag. This stage can be skipped but I did it to explain in detail. Here we are using the same public folder that we specified in the previous stage. So the whole yml file will be this:

image: node:12.22.0 

stages:
    - build
    - deploy
cache:
    paths:
        - node_modules/
    
before_script:
    - rm -rf build
    - CI=false npm install
    
pages:
    stage: build
    script:
        - CI=false npm run build
        - rm -rf public
        - cp build/index.html build/404.html
        - mv build public
        
artifacts:
    paths:
        - public

# only:
#   - master
deploy_app:
    stage: deploy
    script:
        - echo "deploying  the app"
    artifacts:
    paths:
        - public

Now push the changes to the repository and open up CI/CD tab:

As soon as there is a new push, you should see a new pipeline job.

After successful deploy you should see something like this:

Navigate to settings and than pages tab and you should see this:

One more thing we need to do is create an .env file in the project root and specify the public URL for our application. The app will use it to access static files like css, js etc.

PUBLIC_URL=/<URL FROM PAGES TAB>

In my case it will be 'weatherapp'.

Push the changes with the .env file and click on the link and voilà!

Now if I change the city to London and push the changes, the app should auto-deploy the changes. Let’s test it out:

Commit message is “Changing city to london”.

The pipeline is running, so let's wait for it to complete and see the changes.

There you have it: we have successfully created a CI/CD pipeline using GitLab pages and GitLab pipelines.

This process is fairly easy and it will definitely speed up your development and testing phase of an application.

Have an app idea? It’s in good hands with us.

Contact us
Contact us