Post

CI/CD with GitHub Actions

Today’s approach to software development requires software to be delivered frequently, reliably, and efficiently. We can achieve this by automating the process using CI/CD pipelines.

Continuous Integration (CI) means using automation tools to build, test, and merge code seamlessly. This is to ensure that code coming from different developers and components is free of errors. Automated tests help spot bugs at the early stage of software development and fix them immediately.

Continuous Delivery (CD) is the practice of releasing software in short cycles, with greater speed and frequency. New code is released in smaller batches, even a few times a day. This makes it more bug-resistant and easier to manage.

GitHub Actions makes it easy to automate all your software workflows, now with world-class CI/CD. Build, test, and deploy your code right from GitHub.

Key features

GitHub Actions offer some really nice features making it a viable option for projects of any size. Best of all, it is free (with limits), even on the free plan.

Below are some of the key features offered by GitHub Actions:

  • Hosted runners for every major OS hosted by GitHub
  • Self-hosted runners
  • Matrix builds to simultaneously test across multiple operating systems
  • Support for most of the popular programming languages
  • Live logs
  • Built in secret store
  • Multi-container testing
  • Community-powered workflows

Creating a workflow

Folder structure

Creating a new workflow requires that you create a new file with the following folder structure (within the root folder of your project):

1
.github/workflows/my-cicd-workflow.yml

Folder and file names starting with a dot (.) are hidden. When committing and pushing your code changes, GitHub will look for your workflows within this folder structure and automatically run them accordingly.

YAML workflow definition

GitHub Actions uses YAML to define workflow definitions.

For our example, we will have a look at a workflow for creating a containerized microservice written in Go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
name: CI

on:
  push:
    branches:
        - develop

jobs:
  build-and-publish-develop:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'

    steps:
    - uses: actions/checkout@v2

    - name: Setting up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.15

    - name: Build app
      run: go build -v ./...

    - name: Run unit tests
      run: go test -v ./...

    - name: Build and publish docker image
      uses: VaultVulp/[email protected]
      with:
        github-token: $
        image-name: my-microservice
        image-tag: develop

Let’s start by dissecting the workflow file and see what makes up a typical workflow:

The name of your worflow

1
name: CI

This translates to the name shown on GitHub, under the Actions menu:

image

Triggers

Triggers that will run our workflow. Here we tell GitHub to run our workflow as soon as we push code changes to the develop branch of our repository.

1
2
3
4
on:
  push:
    branches:
        - develop

Jobs

Jobs that will execute as soon as our triggers get triggered.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jobs:
  build-and-publish-develop:
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/develop'

    steps:
    - uses: actions/checkout@v2

    - name: Setting up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.15

    - name: Build app
      run: go build -v ./...

    - name: Run unit tests
      run: go test -v ./...

    - name: Build and publish docker image
      uses: VaultVulp/[email protected]
      with:
        github-token: $
        image-name: my-microservice
        image-tag: develop

Jobs run in sequential order and will fail the entire workflow as soon as one of them fails. Only once all the jobs successfully run does the workflow completes.

Runners

To run your jobs, you need to specify the runner it will run on. This is done by setting runs-on.

In our example, we are setting up our runner to use the latest version of Ubuntu:

1
runs-on: ubuntu-latest

Steps

Jobs consist of multiple steps which in turn can run other actions.

Below we have a step that uses a community-powered action called actions/setup-go@v2 that sets up a go environment for us:

1
2
3
4
5
6
7
 steps:
    - uses: actions/checkout@v2

    - name: Setting up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.15

Similarly, we also have steps to automatically build our app, run the unit tests, and publish the docker image on GitHub:

1
2
3
4
5
6
7
8
9
10
11
12
 - name: Build app
      run: go build -v ./...

    - name: Run unit tests
      run: go test -v ./...

    - name: Build and publish docker image
      uses: VaultVulp/[email protected]
      with:
        github-token: $
        image-name: tilliopos-data-service
        image-tag: develop

On GitHub, this translates to:

image

Community-powered actions

Another great feature of GitHub Actions is the extensive collection of community-powered, open-source actions you can use. These are great time savers, and more often than not, you will find that somebody has already published an action that you integrate into your workflow.

In just this example alone, I was able to use two different community-powered actions, one for setting up a Go environment: actions/setup-go@v2 and the other for building and publishing a Docker image to GitHub VaultVulp/gp-docker-action.

Example

Now that we have created our workflow definition, it is time to commit and push our code changes to GitHub:

1
2
3
git add .
git commit -m "Adding GitHub workflow"
git push origin -u develop

GitHub will detect that we have a workflow definition in .github/workflows/ and will automatically start to run it every time we push code changes.

Navigate to the Actions menu on GitHub and see your workflow in action:

image

Conlusion

If you haven’t yet considered automating your integration and deployment processes, you really should! And with GitHub Actions, this is now easier than ever.

What I like about GitHub Actions is how low the barrier to entry is. Getting started is really easy with minimal setup required and will save you a lot of time, money, effort, and frustration in the long run once set up.

This post is licensed under CC BY 4.0 by the author.