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 with v0.1 will create Release 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.

Comments