CI/CD Deployment pipeline for a React application using Next.js, Cypress, CircleCi and Netlify in 5 minutes
This guide will cover the creation of a deployment pipeline for an existing react application, in our case, we will be doing this for the Cutieverse, one of my personal projects. For this example, I will use next.js which is a react framework meant for production environments. It features hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more.
The overall idea of what we are doing is quite simple. First, we want to build the application and ensure that there are no errors whilst building it. Secondly, we want to run the test suite which in this scenario we will be using cypress for end to end testing. Ultimately, once the build and the tests pass we want to trigger the build deployment through a webhook.
Prerequisites:
- An existing react application published on Github (the repo can be private)
- CircleCi (I’ve chosen this platform due to the ease of use of the service, the same outcome can be accomplished using any different CI/CD platform)
- Cypress (for this example we will use cypress, but feel free to replace it with jest or any other library you want or have a mix in between)
- Netlify (any other web host works as long as it futures webhooks that can trigger deployments)
- Yarn
Let’s get started
First step
To begin with, create a CircleCi account in case you don’t already have one and link your existing GitHub repository to it.
The next thing you want to do is to create a folder in the root folder of your application named .circleci
and a file within it named config.yml
This file will hold the configuration file of our deployment pipeline.
For more info about this step feel free to read the CircleCi docs.
Second step
To continue you need to integrate a test suite in your existing react application. For this example, we will continue with Cypress which is a library meant to write end to end tests for web applications.
Because the cypress requires a running web server to test against we will use the node librarystart-server-and-test
to handle that.
- Install the node packages
yarn add -D cypress start-server-and-test
2. Start cypress for the first time
yarn run cypress open
This will add a bunch of folders to the root of your app:
- cypress/fixtures → mock server response
- cypress/integration → these our UI tests
- cypress/plugins → cypress plugins
- cypress/supports → reusable functions to use in our tests
By default, this will generate a whole bunch of test examples to
- cypress/integration/examples
Now you would probably like to start writing your tests. For the sake of testing our cypress implementation worked, I’ve got rid of all the examples they provide by default and wrote a simple test.
/// <reference types="cypress" />// Welcome to Cypress!
// To learn more about how Cypress works and
// what makes it such an awesome testing tool,
// please read our getting started guide:
// <https://on.cypress.io/introduction-to-cypress>describe('Testing the hero section of the home page', () => {
beforeEach(() => {
// Cypress starts out with a blank slate for each test
// so we must tell it to visit our website with the `cy.visit()` command.
// Since we want to visit the same URL at the start of all our tests,
// we include it in our beforeEach function so that it runs before each test
// we also setup the viewport
cy.viewport(1920, 1080)
cy.visit('/')
}) it('It works!', () => {
expect(true).to.equal(true)
})
})
To run the test suite use the following command:
yarn test:e2e
3. Update the package.json
file
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"export": "next export",
"prod": "next build && next export && serve -s out",
"test:cy": "cypress open",
"test:e2e": "start-server-and-test dev 3000 'cypress run --record --key <YOURKEY>'"
},
test:cy
will open the cypress GUItest:e2e
will start the server in dev mode on the 3000 port then run cypress
As you can see there are a few flags attached to this --record --key <YOURKEY>'
The record and key flags are optional but I chose to add them in order to take full advantage of the cypress functionality where I can come later on and see how every single test performed.
This is how it looks from their dashboard.
To do this you want to link your project to the cypress dashboard, get your project key and update the <YOURKEY> section.
Besides that, you’ll also have to update the cypress.json
file with the projectId
you get from the dashboard. It should look something similar to this at the end.
{
"baseUrl": "<http://localhost:3000>",
"projectId": "xxxxxx"
}
References: Next.js Testing Guide
Third step
Now that we have cypress up and running we want to set up the pipeline for our project.
You can simply copy and paste the code below or modify the pipeline according to your needs.
version: 2.1
jobs:
build:
working_directory: ~/app
docker:
- image: circleci/node:14.18.2
steps:
- checkout
- run:
name: Install Yarn
command: curl -o- -L <https://yarnpkg.com/install.sh> | bash
- restore_cache:
key: dependency-cache-{{ checksum "package.json" }}
- run:
name: Install Dependencies
command: yarn
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
paths:
- ./node_modules
- run:
name: Build project
command: yarn build
- save_cache:
key: dependency-cache-{{ checksum "package.json" }}
paths:
- ./node_modules
- run:
name: Export project
command: yarn export
test:
docker:
- image: cypress/base:14.17.0
environment:
## this enables colors in the output
TERM: xterm
working_directory: ~/app
steps:
- checkout
- restore_cache:
keys:
- v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
- v1-deps-{{ .Branch }}
- v1-deps
- run:
name: Install Yarn
command: curl -o- -L <https://yarnpkg.com/install.sh> | bash
- run:
name: Install Dependencies
# yarn installs the app dependencies
command: yarn && yarn global add serve
- save_cache:
key: v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
paths:
- ~/.cache ## cache both yarn and Cypress!
- run: yarn test:e2e
deploy:
working_directory: ~/app
docker:
- image: curlimages/curl:7.81.0
# use the webhook from netlify that was added as a env var to the circleci project to trigger the build
steps:
- run:
name: Deploy
command: curl -X POST -d {} $DEPLOY_WEBHOOK
workflows:
version: 2
build_and_test:
jobs:
- build
- test
# deploy only if the build and the test steps go through
- deploy:
requires:
- build
- test
The pipeline above consists of three stages.
- Build the production application
- Run the test suite using cypress and record the results
- Deploy our project to our web host (netlify)
As you can see here, I use an environment variable named DEPLOY_WEBHOOK
to make the curl request that triggers the webhook. Environment variables were used in this scenario to prevent security bridges or individuals from spamming the deployment webhook.
steps:
- run:
name: Deploy
command: curl -X POST -d {} $DEPLOY_WEBHOOK
To do this you first need to grab your deployment webhook or generate one from netlify or whichever provider you have.
You can easily create one on netlify from the build & deploy page.
Once you have the address of your webhook switch over to the CircleCi dashboard and add it as an environment variable.
Voila, we’re all set and done. Hope you enjoyed this guide and got a rough understanding of what it takes to build a deployment pipeline. From this point on everything pushed to the master branch will automatically be deployed if the build and tests pass.