How to Set Up CI/CD with Github Actions

Picture of Jordan Baczuk
By Jordan Baczuk

What is Continuous Integration and Deployment?

Continuous integration refers to merging code from different sources together regularly. Continuous Deployment refers to regularly deploying applications to production servers (like deploying changes to your live website). Integrating and deploying code is usually a continual process. This allows the software to be tested and updated frequently, and automating this process can increase productivity. There are various tools for automating CI/CD pipelines, in this article we'll cover how to use Github Actions because it has a free tier and is built in to Github, which is a great tool for cloud-based version control.

What is a CI/CD Pipeline?

A CI/CD pipeline is a process with multiple stages, through which software will pass in order when it is integrated into the existing codebase and deployed to servers. It usually consists of several steps including various testing and deployment stages. Here is an example pipeline we will be building today:

  1. Checkout
  2. Lint
  3. Test
  4. Build
  5. Deploy (only for our CD pipeline)

What Are Some CI/CD Best Practices?

Run before committing There are some steps you can run locally before you commit and/or push code to the remote server. This will save costs by not using up run time on the CI/CD servers (with GA, you have a fixed monthly amount before you have to pay). You can use tools like Git Hooks or if you have node, you can use Husky (which uses git hooks under the hood).

Have multiple pipelines If you have staging servers where you test applications before they are deployed to production, you can have a separate pipeline. Usually that is run against a development branch, for example, if your git flow is such that all code on the development branch is deployed to staging.

Use Environment Variables For Secrets Sometimes you need to interact with services or servers which require API keys or SSH keys. These should not be stored in the pipeline code, rather in environment variables in the CI/CD server. Github Actions has a way to define these through their interface.

How to Use Github Actions for CI/CD?

In this section we'll set up two pipelines for a simple static react webpage deployed to GitHub pages. One pipeline with be for CI and another for CD. The first step, once you have a react project to use, is to create a .github/workflows folder in the root of the project. Inside this folder you can create any number of pipelines or "workflows". Github Actions uses YAML markdown, a python-esque alternative to a .json file. Create two .yml files, they can have any name, let's call them ci.yml and cd.yml.

ci.yml

1on: 2 push: 3 branches: [development] 4 pull_request: 5 branches: [master] 6 7jobs: 8 ci: 9 name: Continuous Integration 10 runs-on: ubuntu-latest 11 steps: 12 # First check out the code from the GitHub repo 13 - name: checkout 14 uses: actions/checkout@v1 15 16 # Use the latest long term stable version of node 17 - name: setup node 18 uses: actions/setup-node@v2 19 with: 20 node-version: '16' 21 22 # Install project dependencies 23 - name: install 24 run: yarn 25 26 # Run linter 27 - name: lint 28 run: yarn lint 29 30 # Run tests 31 - name: test 32 run: yarn test 33 34 # Build 35 - name: build 36 run: yarn build

cd.yml

1on: 2 push: 3 branches: [master] 4 5jobs: 6 cd: 7 name: Continuous Deployment 8 runs-on: ubuntu-latest 9 steps: 10 # First check out the code from the GitHub repo 11 - name: checkout 12 uses: actions/checkout@v1 13 14 # Use the latest long term stable version of node 15 - name: setup node 16 uses: actions/setup-node@v2 17 with: 18 node-version: '16' 19 20 # Install project dependencies 21 - name: install 22 run: yarn 23 24 # Run linter 25 - name: lint 26 run: yarn lint 27 28 # Run tests 29 - name: test 30 run: yarn test 31 32 # Build 33 - name: build 34 run: yarn build 35 36 # Deploy 37 - name: Deploy to GitHub Pages 38 uses: JamesIves/github-pages-deploy-action@4.1.5 39 with: 40 branch: gh-pages # The branch the action should deploy to. 41 folder: build # The folder the action should deploy. 42

You might have noticed these pipelines are very similar. In fact the only differences between the two are:

  1. When they are triggered
  2. The final deploy step in the CD pipeline

Let's look through each pipeline.

on

Github will execute each file in the .github/workflows folder if the on stage check passes. In ci.yml the jobs will only run if a push is made to the development branch, or a pull request is created against the master branch. The jobs in cd.yml, however will only run when code is pushed (or merged) into the master branch.

jobs

The jobs section allows you to create a list of jobs that will each run in a separate virtual machine on the GA servers. In our case we only have 1 job per file. We can give each job a name and declare on which operating system it will run using the name and runs-on properties, respectively.

steps

Here we can list multiple steps that the job will execute. The name property allows us to name to step. The uses property allows us to run an action defined externally. The first step uses the Checkout Action to checkout the repository in the virtual machine. The next step uses the setup-node action to specify which version of node we want to use. The with keyword allows us to pass parameters into the action. Next, we install our project dependencies using the run property, which allows us to run shell commands (note these commands must be defined in your package.json file). Similarly we run our linter command, and then our build command. Finally in the cd.yml pipeline, we use an action to deploy our site to GitHub pages. See Github Pages - Custom Domain for instructions to set up a custom domain to get the react site working (if interested).

An important thing to note is if any step in our job fails, the entire pipeline will stop. This allows us to prevent deploying if we have any test failures, linter errors, or build errors.

Where to Go From Here

This is only the beginning of what you can do with CI/CD tools, and only covers the basics of CI/CD. CI/CD is more than just the tools you use, it is a process that you follow, and you should determine what those processes are before you automate them.