Jedi of CI/CD

Jedi of CI/CD

ยท

7 min read

Part One: Introduction

CI/CD or Continuous Integration and Continuous Deployment/Delivery automates the software development process from the initial code commit all the way through to deployment. It eliminates much of the manual human intervention traditionally required to get new code to production. It is important to note that, it is not a one-size-fits-all solution and it's implementation may vary depending on the complexity of the systems.

First, let's break down some key terms, so you're not overwhelmed by a bunch of fancy jargon. This piece's title makes sense when you understand the terms CI/CD Pipeline and GitHub Actions.

Continuous Integration (CI)

It is the practice of using automation to enable teams to merge code changes into the shared repository early and often. Each commit triggers an automated workflow on a CI server that runs a series of tasks to make sure the commit is safe to merge into the main branch.

Common tools used in CI

A good source code management system is the foundation. GitHub is a very popular example. It should hold everything needed to build the software. This includes source codes, test scripts and scripts to build the software applications.

GitHub Actions and Buildkite are some modern examples. Jenkins, CircleCI and TravisCI are also common.

Continuous Deployment (CD)

If we are being truthful, real continuous deployment is hard. Many teams only practice CD on the most basic types of systems. These systems are usually stateless, like the API or web server tiers. There are some infrastructure-specific tools that make CD easier to main. For example, on Kubernetes, ArgoCD is popular.

What is a CI/CD Pipeline?

A CI/CD Pipeline is basically a development practice that revolves around one big question: How do we get quality features out to our production environment quicker? Or in simpler terms, how can we speed up the process of releasing features without sacrificing quality?

Wondering how the CI/CD Pipeline speeds up our feature release process?

Check out the diagram below, which shows the usual cycle for delivering features, with or without the CI/CD pipeline.

Developers would have to manually do each step in the diagram above if there weren't CI/CD pipelines. Basically, someone on your team would have to run a command to start the build process. The same goes for testing and deploying.

In the CI/CD Pipeline, we set up a mechanism to make sure that each time a team member pushes something to the shared repo, it automatically starts the build process, runs the tests, deploys it to UAT (User Acceptance Testing) and finally to production.

Continuous Delivery occurs when a newly integrated change is automatically deployed to the UAT environment and then manually deployed to the production environment from there.

Continuous Deployment, on the other hand, happens when an update in the UAT environment is automatically deployed to the production environment as an official release.

๐Ÿ’ก
Note: If the deployment from the UAT environment to the production environment is triggered manually, it constitutes a Continuous Integration/Continuous Delivery setup. Conversely, if this deployment occurs automatically, it constitutes a Continuous Integration/Continuous Deployment setup.

What are GitHub Actions?

GitHub Actions is a CI/CD platform that automates your build, test, and deployment pipelines triggered by events in your GitHub repository. The workflow file, written in YAML, is stored in the .github/workflows directory. Each workflow comprises one or more jobs, each executed in its own virtual machine or container, called "runners," operating on Windows, Linux, or MacOS.

In the CI/CD Pipeline, GitHub Actions is the entity that automates the boring stuff. Think of it as some plugin that comes bundled with every GitHub repository you create. The plugin already exists in your repo to carry out whatever task you instruct it to. Typically, you'd define the tasks the plugin should handle through a YAML configuration file. Whatever commands you include in the configuration file translate to something like this โ€”

"Yo, GitHub Actions, whenever a pull request is opened on X branch, go ahead and automatically build and test the new change and whenever a new change is merged into or pushed to this X branch, deploy that change to Y server."

There are five core concepts in GitHub Actions:

  • Jobs

  • Workflows

  • Events

  • Actions

  • Runners

These concepts are too detailed for this article, we can do a separate article about them.

Part Two: Continuous Integration

In this section, we'll learn how to set up GitHub Actions to run builds and tests automatically whenever there's a push or pull request to the main branch of a repository.

Setting up the Personal Access Token

  1. Build your application (here, I've already created a basic Next.js application using create-next-app).

  2. Create a GitHub repository for your project. It is fairly simple just navigate to GitHub, click on the "+" icon in the top right corner, and select "New repository."

  3. Create a access token in GitHub. (see here)

  4. We'll utilize the access token in our Actions secrets within the repository. This token will allow GitHub Actions to access our repository.

Setting up Action Secrets

  1. Navigate to the Settings tab in your repository.

  2. In the left sidebar, choose Secrets and variables.

  3. From the dropdown menu, pick Actions.

  1. Click on the New repository secret button.

  2. Enter the secret name in the Name field (this will be used in our workflow file).

  3. Paste your copied personal access token into the secret field, then click Add.

You're all set!

Creating .yml file

  1. Go to the root of your project directory and create a directory named ".github".

  2. Inside ".github", create another directory named "workflows" to hold your workflow files.

  3. Within the ".github/workflows" directory, create a new file called "main.yml" (you can name it whatever you want). Then, add the following code (be sure to change the directory name in the code below; I've used my directory name as cyberpunk-3100):

name: CI/CD for CyberPunk 

on:
  push:
    branches:
      - main

jobs:
  # Build Job
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Install Node.js 20
        uses: actions/setup-node@v3
        with:
          node-version: 20.x

      - name: Install Dependencies
        run: |
          cd cyberpunk-3100
          npm install

      - name: Build Project
        run: |
          cd cyberpunk-3100
          npm run build

      - name: Upload artifact to enable deployment
        uses: actions/upload-artifact@v4
        with:
          name: poduction file
          path: ./cyberpunk-3100/build

  # Deploy Job
  deploy:

    needs: build

    runs-on: ubuntu-latest
    steps:
      - name: Download artifact
        uses: actions/download-artifact@v3
        with:
          name: poduction file
          path: ./cyberpunk-3100/build

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.YOUR_KEY }} #this is the access token key
          publish_dir: ./cyberpunk-3100/build

We'll break down each line in the file.

name: CI/CD for CyberPunk This will act as the title of our workflow. When you visit the actions tab, each workflow you create will be distinguished by the name provided here on the list.

on: This is where you specify the events that should trigger the execution of the workflow. In the config file we pass two events and we specify the main branch as the target branch.

jobs: A workflow is essentially a collection of jobs.

runs-on: GitHub offers Ubuntu Linux, Microsoft Windows, and macOS runners for running your workflows. This is where you specify the type of runner you want to use and in our scenario, we're utilizing the Ubuntu Linux runner.

A job comprises a series of steps that are typically executed sequentially on the same runner. In our file above, each step is denoted by a hyphen. The name attribute indicates the name of the step. Each step can either be a shell script or an action. If it's an action, you define the step with uses, and if it's a shell script, you define the step with run.

๐Ÿ’ก
YAML is a markup language that has become a mainstay in declarative automation due to its human-friendly nature as a JSON superset (it uses a lot less brackets, braces, and quotes than other JSON markup language variants).

Now that you've created a workflow by adding the config file in the designated folder, you can commit and push your changes to your remote repository. If you go to the Actions tab of your remote repository, you should find a workflow by the name you've assigned to it listed there.

You can also incorporate deployment to Amazon Elastic Container Service (ECS) into your continuous deployment (CD) workflows. While this topic isn't covered in this article, let me know if you'd like to see a tutorial on it. I'd be more than happy to provide one. The primary goal of this article is to demonstrate how to effortlessly set up a CI/CD pipeline without any complications.

Wrapping Up

In summary, I've clarified the concepts of GitHub Actions and CI/CD Pipeline. Additionally, we've explored how to set up GitHub Actions to automate the deployment of our code. With GitHub Actions, constructing a CI/CD pipeline is a simple process, allowing you to concentrate more on your code rather than the tasks that follow it.

Have any questions? Feel free to reach out to me on Twitter at pranavbawg, or find me on GitHub.

ย