Using github actions to release size-optimized golang artefacts
golang binaries can become rather large in size. upx will make your golang go brrr and shrink binaries siginificantly. Add upx to your github release action to make users happy.
Motivation§
A real world example: terraform 1.1.5 clocks in at around 61M.
upx
'ing terraform with parameter --ultra-brute
(while taking quite some
time) will reduce it to around 13M:
wget https://releases.hashicorp.com/terraform/1.1.5/terraform_1.1.5_linux_amd64.zip
unzip terraform_1.1.5_linux_amd64.zip
upx --ultra-brute terraform
du -h terraform | cut -f1
# > 13M
Overview§
We will create a minimal golang project and add a github release action, including upx.
If you want to follow along and test for yourself (which I tried hard to make simple), you'll need:
There's also an example repo on github.
Create a golang project§
Let's create a small sample project so we have something to work with. With a little magic (aka dependency), the binary will do nothing but have about 3M binary size.
go mod init upx-test
Add code with a dependency§
While tview is an awesome library to create rich cli interfaces, we will use it just to inflate the size of the binary.
// File: main.go
package main
import (
"github.com/rivo/tview"
)
func main() {
app := tview.NewApplication()
app.Stop()
}
Download dependencies and build§
go mod tidy && go build
View file size§
du -h upx-test | cut -f1
# > 2,9M
This gives us 3M to work with.
Run upx§
Even for such a smallish binary, upx can do quite a lot:
upx upx-test > /dev/null # Add `--ultra-brute` for maximum compression.
du -h upx-test | cut -f1
# > 1,7M
github action§
Now let's release binaries for every tag. First, create a github repository and push your code. — I doubt I need to go into details regarding this.
Here's the general plan for the github action:
- Create releases for all pushed tags starting with
v
. So tagging withv0.1
will createRelease v0.1
. We will just use the create-release action to do this. - Use a matrix build with the go-release-action to release compressed binaries for linux, macos and windows.
Create action description§
Create a new file .github/workflows/create-release.yaml
and add some basic
configuration:
# Name of the action
name: Create Release and Publish
# Run on every tagged push
on:
push:
tags:
- 'v*'
Add job to create a release§
We need a dedicated job to create the actual release. There's not much to say, the .yaml is pretty self explanatory:
jobs:
create-release:
name: Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
Add job to release the binaries§
This job will create the binaries and add them to the new release.
releases-matrix:
name: Release Binaries
runs-on: ubuntu-latest
strategy:
matrix: # A matrix build will combine all variables and we will end up
# with (linux/amd64), (linux/arm64), (windows/amd64) etc.
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
exclude: # We don't want arm binaries for windows
- goarch: arm64
goos: windows
steps:
- uses: actions/checkout@v2
- uses: wangyoucao577/go-release-action@v1.24
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
goos: ${{ matrix.goos }}
goarch: ${{ matrix.goarch }}
binary_name: "upx-test"
# `executable_compression` defaults to nothing, so make sure to add the
# `upx` command here. `--ultra-brute` will make upx try really hard to
# find the best compression, gut may take quite some time.
executable_compression: "upx --ultra-brute"
Closing thoughts§
That's it. Have a look at the example project – especially the automatically created release and the action of course.