How to setup a production-ready Devops environment for React without a penny — Part 2

Terrence W
5 min readNov 24, 2021

If you like me, have countless side projects, and want to deploy your project in industry-standard, but don’t want to spend too much money? I believe this series can help you.

In this series, I will teach you how to deploy your app in multi environment with CI/CD pipeline, using Firebase and Gitlab CI/CD only. And the most important part is, it costs ZERO dollar!

Part 1: Project setup and deployment
Part 2: CI/CD pipeline for auto deployment
Part 3: Multi-environment setup

In part 1, we have setup all the accounts and environment. If you haven’t read part 1 yet, go and check it in here. In part 2, we will focus on the CI/CD setup.

Step 1: Build CI/CD pipeline in Gitlab

Let’s talk about what CI/CD really is before showing how to use Gitlab CI/CD. Think about how we actually deploy our web. We can divide it into these steps:

  1. Run the test cases to make sure your code is runnable and work as expected.
  2. Build and bundle your react app by webpack or other tools to generate the target HTML/CSS/JS.
  3. Upload(Deploy) your bundled HTML/CSS/JS to your target web server.

Imagine you changes your code several times per day, and these steps are executed multiple times. This would be a continuous process in your development cycle. Step 1 and 2 will be called continuous integration(CI), and step 3 will be called continuous delivery(CD). As CICD jobs are repeatable, wouldn’t it be great if it can run automatically?

There are many great tools in the market, like Jenkins or Travis CI. Gitlab CI/CD is chosen in this article. It is because I use Gitlab as my repository, I don’t need to setup another account and I can already enjoy the service.

To define your pipeline, you need to create a .gitlab-ci.yml file under your root directory. Inside this yml file, we need to define what the pipeline need to do with our steps defined above.

Step 1: Testing stage (Optional)

Assume you have a workable test script in your react project, and the test script is npm run test. To let gitlab able to run this command, gitlab need to run it on a environment that has node. Fortunately, gitlab supports to run these tasks in a docker container. You can find any docker image you need in Docker Hub and use it in the CICD pipeline. In this case, we will use node:latest.

Then, we need to tell Gitlab what script need to run. In this case, the script should be npm install and npm run test.

Here is our complete version for testing stage:

image: node:lateststages:
- test
unit-test:
stage: test
script:
- npm install
- npm run test

Step 2: Build

Build and bundle is essential in modern js framework. For react project, the default script to build and output the js file is npm run build . The code to add in the pipeline is basically the code:

stages:
- build
build-prd:
stage: build
script:
- npm install
- npm run build

You will see for our job unit-test and build-prd, npm install is needed so that the next work can be executed. As a result, it wastes time and network resource to redo the work. Gitlab provides a solution called cache, which allow you to transfer some files(e.g. dependencies) to another job. For node module, we can remove the npm install in our job and add the following:

cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline

cache tells Gitlab which files in going to be stored, and before_script is run before every job.

Step 3: Deploy

In part 1, we have talked about how to use firebase CLI to deploy your app to firebase hosting. We use the same command to deploy it. The definition of the job is pretty much the same as step 2, except one thing: the files retrieval.

For every single job, the environment is separated. Therefore, it is impossible for a new job to deploy the files in /build, as there is no /build in this job. There are two solutions: (1)build and deploy are under the same job; (2) transfer the /build from build stage to deploy stage. In this example, we will use the second approach.

To transfer files to another stage, we can use artifacts to do so. To export the files in /build, you need to add this to the job:

artifacts:
paths:
- build/

To retrieve the artifacts in stage build, you need to add:

dependencies:
- build

Here is the full version of pipeline for build and deploy stage:

stages:
— deploy
- build
build-prd:
stage: build
script:
- npm install
- npm run build
artifacts:
paths:
- build/
deploy-prd:
stage: deploy
script:
- npm install
- firebase deploy --only hosting --token $FIREBASE_TOKEN
dependencies:
- dev-build

One thing is pay attention is the $FIREBASE_TOKEN, which is a environment variable. For any type of secret, you should not put it directly in your code. Gitlab provides a way to store the secret and access in the pipeline runner. To add the environment variable, go to project’s Settings > CI/CD > Variables.

Then, you can access the variable by $KEY.

Final Result

If you put all the code together, you should get something like this:

image: node:lateststages:
- test
- build
- deploy
# Cache modules in between jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
before_script:
- npm ci --cache .npm --prefer-offline
- npm i -g firebase-tools
unit-test:
stage: test
script:
- npm run test
prod-build:
stage: build
script:
- npm run build
only:
- master
artifacts:
paths:
- build/
prod-hosting:
stage: deploy
script:
- firebase deploy --only hosting --token $FIREBASE_TOKEN
dependencies:
- prod-build

Then, go and push your code to Gitlab, you would then see your pipeline under CI/CD and showing ‘running’. If everything okay, you would see ‘passed’ after a few minutes and your latest code is deployed to Firebase!

Summary

Now you have learnt how to perform automatic deployment. This should be enough for a playground project scope. In the next part, we will talk about a compulsory element in commercial web development — setup non-prod environment.

--

--

Terrence W

I am a full stack developer, mainly focus on Nodejs/React.