Migrating from CircleCI to GitHub Actions
Building cross-platform Go binaries and Docker images
August 24, 2021
TL;DR
- Our GitHub action file
thatisuday/go-cross-build
to build cross-platform Go binariesskx/github-action-publish-binaries
to attach artifacts to the GitHub releasedocker/setup-qemu-action@v1
to build docker images for multiple platformsdocker/setup-buildx-action@v1
not required but recommendeddocker/metadata-action@v3
to attach labels and tags to the docker imagedocker/login-action@v1
to login to a Docker registrydocker/build-push-action@v2
to build the Docker images and push them to the registry
Our Previous CI Setup with CircleCI
When we wrote our Kamailio Exporter, we chose to go the Prometheus way, which is the use of a Makefile and the promu tool.
This required at least 3 files:
Our CI was doing two things:
- build cross-platform Go binaries
- attach them to the GitHub release
The interesting parts in the CircleCI config file were:
build:
machine: true
steps:
- checkout
- run: make promu
- run: promu crossbuild -v
- persist_to_workspace:
root: .
paths:
- .build
release_tags:
docker:
- image: circleci/golang:1.15
steps:
- checkout
- run: mkdir -v -p ${HOME}/bin
- run: curl -L 'https://github.com/aktau/github-release/releases/download/v0.7.2/linux-amd64-github-release.tar.bz2' | tar xvjf - --strip-components 3 -C ${HOME}/bin
- run: echo 'export PATH=${HOME}/bin:${PATH}' >> ${BASH_ENV}
- attach_workspace:
at: .
- run: make promu
- run: promu crossbuild tarballs
- run: promu checksum .tarballs
- run: promu release .tarballs
- store_artifacts:
path: .tarballs
destination: releases
First, we use promu crossbuild
to build Go binaries for every platform configured in .promu.yml
. Then, the release_tags
step makes tarballs and attaches them to the GitHub release.
With the 3 files combined, that’s a total of 233 lines to cross-build binaries and attach them to the GitHub release.
Using GitHub Actions
When writing our FreeSWITCH exporter, we wanted to checkout GitHub Actions.
We were able to provide the same results in just 28 lines of one file:
name: Build binaries
on:
release:
types: [created]
jobs:
binaries:
name: Cross-platform binary builds
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Generate build files
uses: thatisuday/go-cross-build@v1.1.0
with:
platforms: linux/amd64,linux/arm64
name: freeswitch_exporter
dest: dist
- name: Upload build artifacts
uses: skx/github-action-publish-binaries@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: ./dist/*
We use two community-built actions to do this:
thatisuday/go-cross-build
to build cross-platform Go binariesskx/github-action-publish-binaries
to attach artifacts to the GitHub release
Building and publishing docker images with GitHub Actions
With the FreeSWITCH exporter, we also wanted to provide docker images as well.
Here is how we did it:
docker:
name: Cross-platform Docker images
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
tags: type=semver,pattern={{version}}
images: florentchauveau/freeswitch_exporter
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
We use the following actions:
docker/setup-qemu-action@v1
because we want to build for multiple platformsdocker/setup-buildx-action@v1
not required but recommendeddocker/metadata-action@v3
to attach labels and tags to the docker imagedocker/login-action@v1
to login to DockerHub (using secrets)docker/build-push-action@v2
to build the docker images and push them to the registry
Writing the actions was pretty straightforward and worked right away.
The metadata action allows you to automatically apply tags and labels to your image. Because we are tagging our releases with semver, we chose to use this pattern for tags.
The v1.0.1 tagged release on GitHub created an Docker image tagged as florentchauveau/freeswitch_exporter:1.0.1
and latest
.
The following labels were automatically applied to the image:
"Labels": {
"author": "Florent CHAUVEAU <florent.chauveau@gmail.com>",
"org.opencontainers.image.created": "2021-08-22T18:30:45.018Z",
"org.opencontainers.image.description": "",
"org.opencontainers.image.licenses": "MIT",
"org.opencontainers.image.revision": "f46f3c6a35089892833241c3bc6356932a50ae7c",
"org.opencontainers.image.source": "https://github.com/florentchauveau/freeswitch_exporter",
"org.opencontainers.image.title": "freeswitch_exporter",
"org.opencontainers.image.url": "https://github.com/florentchauveau/freeswitch_exporter",
"org.opencontainers.image.version": "1.0.1"
}
Final notes
Playing with GitHub Actions turned out to be a good choice, because we were able to provide more, with fewer lines. It is also much simpler than using an external CI provider, 3 files, make, and promu. Simpler is (very often) better.