A CI/CD Pipeline With GitHub Actions for Go
Tags: go, devops, programming
Published: 2023-02-27 22:39:49

Continuous Integration/Continuous Deployment (CI/CD) is an essential practice for modern software development. It helps developers to streamline the software development process by automating testing, building, and deploying their applications. GitHub Actions is a powerful tool for setting up CI/CD pipelines for your projects. In this post, we will explore how to set up a CI/CD pipeline using GitHub Actions for Go, which will help you to deploy your applications quickly and reliably. We'll cover the steps needed to create a pipeline that deploys to a staging and production environment from the same branch.

## Creating the Staging Pipeline

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qs4zkonjewmx202k93tl.png)

First we utilize [golangci-lint](https://github.com/golangci/golangci-lint), this helps us to identify potential bugs, errors and stylistic issues. 
After that we will run unit tests and start the build and deployment process. For demonstration purposes we will perform a straightforward Docker build and tag command as our build process.

```yaml
name: Build

on:
  push:
    branches: 
      - main
  pull_request:
    branches: 
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    - name: Set up Go
      uses: actions/setup-go@v3
      with:
        go-version: 1.20
    - name: golangci-lint
      uses: golangci/golangci-lint-action@v3
      with:
        version: latest
    - name: Test
      run: go test -v ./...
    - name: Build
      run: docker build -t latest .
```

To enhance the above process, I recommend setting up notifications to a slack channel or a commonly used tool in your environment. This eliminates the need to constantly monitor the GitHub Action. Additionally, a step similar to this can be added to sent notifications on job failure. 

```yaml
 - name: Notify on failure
   if: failure()
   run: # do something
```

## Releasing to Production

As mentioned before we wan't to do this within the same branch. To do this we will create two jobs, one which creates a release and tags the latest commit and another one which is triggered by the tag to start the deployment process.
This needs a bit of preparation, because GitHub Actions can't trigger another action. 
To achieve this you can head to the branches settings and add a [deploy key](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/managing-deploy-keys).
For the rest of the article I assume the key is named `COMMIT_KEY`.

Let' have a look at the change log generation action. 
```yaml
name: Deploy Production

on:
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
          ssh-key: "${{ secrets.COMMIT_KEY }}" # set deploy key here
      - name: Create Release Tag
        id: release-tag
        # for demonstration purposes GitHub run number is used
        run: echo "tag=r-$(printf $GITHUB_RUN_NUMBER)" >> $GITHUB_OUTPUT
      - name: Find last successful workflow 
        uses: SamhammerAG/last-successful-build-action@v2
        with:
          branch: "main"
          workflow: "Build"
          verify: true
      - name: Push Tag
        run: |
          git tag ${{ steps.release-tag.outputs.tag }} ${{ github.sha }}
          git push origin ${{ steps.release-tag.outputs.tag }}
      - name: Create Release
        uses: ncipollo/release-action@v1
        with:
          allowUpdates: true
          draft: false
          name: ${{ github.ref_name }}
          tag: ${{ steps.release-tag.outputs.tag }}
          body: # create a change log 
          token: ${{ secrets.GITHUB_TOKEN }}
```

In this action we will fetch the commit of the last successful  run of our deploy to staging action. Because of this we can assume that our code is in a deployable state and we can skip running tests again. If you are using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) you could use [this action](https://github.com/requarks/changelog-action) to generate the body for the release.

Lastly, we have an action that initiates the release to the production environment triggered by a push event specifically targeting tags.

```yaml
name: Release Production

on:
  push:
    tags:
      - '' # use regex to limit to certain tags
jobs:
  build:
    runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v3
      with:
        ref: ${{ github.ref }} # set the ref
    - name: Build and Deploy # add your deployment process here
      run: docker build -t latest .
```

In conclusion, setting up a CI/CD pipeline with GitHub Actions for Go is a relatively simple process that can bring significant benefits to your development workflow. By automating the build, test, and deployment processes, you can reduce errors, improve efficiency, and free up your time to focus on more important tasks. With the examples provided in this post, you should have a good starting point for setting up your own CI/CD pipeline with GitHub Actions and Go. Always test and refine your pipeline to ensure that it meets your specific needs and requirements.