CI your Go with GitHub actions

Dumas sylvain
5 min readAug 1, 2021

--

Photo by Roman Synkevych on Unsplash

GitHub releases actions relatively recently (end of year 2019) to be able to automate, customize, and execute your software development workflows right in your repository.

A well detailed documentation, some guides and a marketplace will help you to create workflows you need, or same develop some actions.

But before all, let’s begin by a few reminders about what is needed to test and build a Go application.

Testing and compiling source code in Go

Most Go tools are available with command line go <command> [arguments]

Documentation: https://golang.org/doc/cmd

In our case, the most interesting commands are:

NB: You can see in documentation that the go test command implicitly call the go vet command (can be disabled with -vet=off argument).

NB: The go vet command uses GCC (GNU Compiler Collection) by default, but it can be used without having GCC installed by setting environment variable (CGO_ENABLED=0 ).

cgo

Be careful, cgo (enabled by default) is used for some functionnality:

Ok, now it’s time to implement it ! Silence ! Actions !!

Photo by GR Stocks on Unsplash

Scene 1: Demo Time

From actions availables on GitHub marketplace, you can create a minimalist workflow.

NB: On GitHub hosted runners, GCC is installed by default like other tools (see documentation on runners here).

The step “See built file information” is only there to have some information about the generated binary 😉.

It is pretty cool, we have our first binary 😁. We can notice that our generated binary is linked to some system libraries.

But we can also create a little more detailed workflow that follows step by step everything that is done by the previous one.

So now we have a binary file, let’s try it on some Linux distribution, the step “Make the built file downloadable” permits to download binary file as an artifact.

Scene 2: Test time

In most of cases, binary are used in Docker image with small Linux system distribution like the debian buster slim, or alpine, … So to test in the same condition, we can use Docker command with a mounted volume to have access to our binary and run it for the Linux system.

Try with Debian slim

docker run --rm -v <your_local_folder_containing_my_app>:/app -ti debian:buster-slim /bin/bash

And then run it.

/app/my_app

No error occurs, the binary works well. Nice 😎!

Try with Alpine

docker run --rm -v <your_local_folder_containing_my_app>:/app -ti alpine:3.14 /bin/sh

And then run it.

/app/my_app

This time an error occurs 😱. The problem is that our binary use glibc, but alpine distribution uses musl libc, so he can not work like that.

Photo by Markus Winkler on Unsplash

Many solutions come to us:

  • to have glibc installed
  • use cross-compilation to avoid the link with glibc
  • build our binary on an alpine Linux distribution

Scene 3: Solution time

Solution 1: glibc installed

apk add --no-cache libc6-compat
/app/my_app

This time, no error occurs. The goal to use alpine distribution is to have a simple, small and secure Linux distribution, with all the minimal requirement. The additional installation of compatible libraries for glibc increases the overall size, and may result in loss of efficiency and security.

Solution 2: cross-compilation

To cross-compile, you must set environment variable CGO_ENABLED=0 . In the step “See built file information”, we continue on error because the command ldd my_app generates an error saying that the file is not a dynamic executable and it is … normal 😋.

No more errors. This solution works fine, but you can no longer use the C libraries, nor the cgo-based resolver that calls C library routines such as getaddrinfo and getnameinfo, and others things…

Solution 3: Build for the expected Linux system

The step “See built file information” show us that the generated binary is well linked with musl libc.

And the binary runs without error 😎.

The end

As we have seen, several solutions are available to us to compile a Go binary according to what we want or the conditions. Keep in mind that in Go, the binary will be dependent on the target system on which you want to run it, and in this case it is better to build it on this same system.

I hope you enjoyed this article, but above all that it will be useful to you😊. Feel free to give claps if the article helped you! 😻

--

--