I’m using Goreleaser to build releases of Audax tools, since it works quite well in cross-compiling a Go program to various OS targets in a single run. I use this in a GitHub actions. Whenever I create a tag, the release pipeline will kick-off a run of Goreleaser, which will cross-compile Dynamo-Browse, package it, and publishing it to GitHub itself.

Recently, I found a bug in the MacOS Brew release of Dynamo-Browse. When the user tries to press c to copy the current item, the entire program will crash with a panic. I tracked the cause to the clipboard package that I’m using. Apparently, it requires Cgo to be usable on Darwin, and it will panic if CGO is not enabled1. But the GitHub action I was using was an Ubuntu runner. And since the package required some symbols from Foundation, which was only available on Darwin, I couldn’t just enable CGO.

However, I did learn that GitHub Actions actually supports MacOS runners. I don’t know why I didn’t think they wouldn’t. I guess, much like most of my experience with anything Apple, it would come at some sort of price. So you can imagine my delight in learning about this. And the first thing I did was try to use them in the release pipeline.

I first tried blindly switching from ubuntu-latest to macos. Sadly, it didn’t work as expected. It Turns out that the macos runners do not have Docker installed, which broke a number of the actions I was using. And since the original ubuntu-latest runner worked well for the Linux and Windows releases, I didn’t want to just junk them.

So what I did was split the release pipeline into three separate jobs:

  • The first, running on ubuntu-latest, builds Audax tools and runs the tests to verify that the code is in working order.
  • The second, also running on ubuntu-latest, will invoke the Goreleaser action to build the Windows & Linux targets.
  • At the same time, the third, running on macos, will install Goreleaser via brew and run it to build the MacOS target.

The resulting pipeline looks a little like this:

name: release
on:
  push:
    tags:
      - 'v*'
jobs:
  build:
    runs-on: ubuntu-latest
    services:
      # localstack Docker image
    steps:
      # Checkout, build test
  release-linux:
    needs: build
    runs-on: ubuntu-latest
    steps:
      # Checkout
      - name: Release
        uses: goreleaser/goreleaser-action@v1
        if: startsWith(github.ref, 'refs/tags/')
        with:
          version: latest
          args: release -f linux.goreleaser.yml --skip-validate --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
  release-macos:
    needs: build
    runs-on: macos-12
    steps:
      # Checkout
      - name: Setup Goreleaser
        run: |
          # Can't use goreleaser/goreleaser-action as that requires Docker
          # So need to launch it and run it using the shell.
          brew install goreleaser/tap/goreleaser
          brew install goreleaser
      - name: Release
        if: startsWith(github.ref, 'refs/tags/')
        run: |
          goreleaser release -f macos.goreleaser.yml --skip-validate --rm-dist
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          HOMEBREW_GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}

I’ve only used this to make a small maintenance release that fixes the clipboard panic problem, and so far it seems to work quite well.

After doing this, I also learnt that there’s actually a Goreleaser Docker image which could theoretically be used to cross-compile with CGO. I haven’t tried it, and honestly it sounds like too much trouble than it’s worth. Rather keep things simple.


  1. This is where I complain that the function that is panicing actually returns an error type. Instead of panacing and bringing down the whole program, why not just return an error? ↩︎