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.
The main benefits of using CI/CD are:
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.
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.
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.
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.