Setup GitHub Action for Go project
Currently, I am working on a Command Line Interface (CLI) tool in Go on GitHub.
In addition to this, I want to implement an automatic build and deployment process so that for all common operating systems, complete binaries can be created.
What is needed for this, I will showcase in today’s post.
GitHub Actions
GitHub offers an easy way to build and deploy projects through its actions.
To achieve this, workflow steps are typically defined in YAML files, which should be executed when specific events occur (like pushing or tagging).
Therefore, GitHub automatically recognizes these files, usually placing them directly in the .github/workflows
directory within the repository.
Create credentials
To create resources on GitHub we have to generate a Personal Access Token (PAT).
For this we have to select Generate new token (classic).
Define a Note
, set the Expiration
to No expiration
and check repo
scope and all of its children.
Then click on Generate token
button below and save the displayed token at a safe place.
Setup secrets
Secrets are very similar to environment variables that, however, are stored in a way that allows them to be read only from GitHub Actions for the purpose of execution.
To create secrets, we have to switch to the repository settings under https://github.com/<owner>/<repo>/settings/secrets/actions
.
In the Repository secrets
section, we now add an entry called GH_PAT
and set its value to the token that was generated in the previous section.
First action: Create a Release on GitHub
For the entire process, we need 2 actions.
In the first one, we have to create a release entry in our GitHub project.
To do this, we need to add the file .github/workflows/tag.yaml
with the following content to GitHub and upload it there:
name: Release by tag
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ncipollo/release-action@v1
with:
token: ${{ secrets.GH_PAT }}
This action is executed the time we push tags to the repository.
After the checkout, ncipollo/release-action will create an empty entry in the release section under https://github.com/<owner>/<repo>/releases
with the name of the new, underlying tag, using GH_PAT
secret.
Second action: Build and deploy
Now we need to create a second file, called .github/workflows/tag.yaml
, with the following content:
name: Build binaries for release
on:
release:
types: [created]
permissions:
contents: write
packages: write
jobs:
releases-matrix:
name: Release Go binaries
runs-on: ubuntu-latest
strategy:
matrix:
goos: [darwin, freebsd, linux, openbsd, windows]
goarch: ["386", amd64, arm, arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: arm
goos: darwin
- goarch: arm64
goos: windows
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
with:
retry: 10
overwrite: true
github_token: ${{ secrets.GH_PAT }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
goversion: "1.22"
md5sum: false
sha256sum: true
project_path: "./"
binary_name: "my-cli-app"
The action is executed after a new release entry has been created in our repository.
First we define a matrix of target operating systems and CPU architechtures:
# ...
strategy:
matrix:
goos: [darwin, freebsd, linux, openbsd, windows]
goarch: ["386", amd64, arm, arm64]
exclude:
- goarch: "386"
goos: darwin
- goarch: arm
goos: darwin
- goarch: arm64
goos: windows
# ...
This will include MacOS, FreeBSD, OpenBSD, Windows and Linux for Intel and ARM environments (32- and 64-bit). But on the other hand we need to exclude Apple Systems with 32-bit and Windows ARM with 64-bit CPUs, because they are not supported (anymore).
After the checkout part, we need to setup wangyoucao577/go-release-action, which helps us to build and upload our binaries:
# ...
steps:
- uses: actions/checkout@v4
- uses: wangyoucao577/go-release-action@v1
with:
retry: 10
overwrite: true
github_token: ${{ secrets.GH_PAT }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
goversion: "1.22"
md5sum: false
sha256sum: true
project_path: "./"
binary_name: "my-cli-app"
At the end you should adjust goversion
and binary_name
settings for your needs.
Start first release
Push both .yaml
files to your GitHub repository with a first working version of your Go code.
Then create a new Git tag locally and use a valid version number as name, beginning with a v
prefix:
git tag -a v0.0.1 -m "version 0.0.1"
To synchronize anything with the remote server, you have to execute
git push --tags
In conclusion, you can now observe via the address https://github.com/<owner>/<repo>/actions
to see if everything is working as expected.
To build and deploy a new version, simply repeat the last two Git commands again.