Painless Github releases with Drone and GoReleaser

A few days ago, I’ve joined Golang community @ spectrum.chat and I’ve found there an interesting tool GoReleaser, what a coincidence! Same day I was about to move CI/CD for one of my repository to Drone.io, so a decision was made: I’m gonna switch to these tools at the same time.

Motivation

If it is not obvious, I’m gonna provide you a brief summary. Manual releases are way too time consuming, boring and no one cares about those extra 10 minutes you have invested in this pointless manual procedure.

This short post will show you how to setup CI/CD pipeline which does all this stuff automatically based on git tags and SemVer versioning.

Installation of GoReleaser

You know Golang ecosystem, right? Everything there is just plug & play, download and profit. GoReleaser is not any different, download it, extract it and use it!

Simple application

Now let’s create our Golang application.

package main
import "fmt"
func main() {
fmt.Println("
Hello! My name is Elder Price, and I would like to share with you the most amazing book!")
}

Then we can initialize GoReleaser with goreleaser initcommand, this step will create a new .goreleaser.yml with following content:

# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
before:
hooks:
# you may remove this if you don't use vgo
- go mod download
# you may remove this if you don't need go generate
- go generate ./...
builds:
- env:
- CGO_ENABLED=0
archive:
replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

As we don’t do anything special now we’re good to go for now. Let’s try to build the first release locally.

goreleaser --snapshot --skip-publish --rm-dist
In GOPATH/src you can get error message about disabled modules. There are two solutions for this issue. Remove hooks from .goreleaser.yaml or set GO111MODULEenvironment variable to on

This command will create a snapshot version in the dist directory. Let’s see the actual output.

goreleaser-demo tree dist 
dist
├── checksums.txt
├── config.yaml
├── darwin_386
├── darwin_amd64
├── linux_386
├── linux_amd64
├── _v0.0.0-next_Darwin_i386.tar.gz
├── _v0.0.0-next_Darwin_x86_64.tar.gz
├── _v0.0.0-next_Linux_i386.tar.gz
└── _v0.0.0-next_Linux_x86_64.tar.gz

As you can see — we have binaries for all major platforms. Cool. Now we can create our first SemVer release. Let’s do this!

git add .goreleaser.yml main.go
git commit -m "initial release"
git tag -a v0.0.1 -m "initial release"
git remote add origin git@github.com:vranystepan/goreleaser-demo.git
git push origin master -f --tags
goreleaser --skip-publish --rm-dist

Now we should see the correct version in the dist directory. Let’s quickly check it.

dist
├── CHANGELOG.md
├── checksums.txt
├── config.yaml
├── darwin_386
│ └── goreleaser-demo
├── darwin_amd64
│ └── goreleaser-demo
├── goreleaser-demo_0.0.1_Darwin_i386.tar.gz
├── goreleaser-demo_0.0.1_Darwin_x86_64.tar.gz
├── goreleaser-demo_0.0.1_Linux_i386.tar.gz
├── goreleaser-demo_0.0.1_Linux_x86_64.tar.gz
├── linux_386
│ └── goreleaser-demo
└── linux_amd64
└── goreleaser-demo

Here we go!

CI/CD integration

As I’ve said before — I’m lazy bastard. And this state does not make the whole thing any easier. I want to run these tasks automatically but in a collaborative way!

You can publish your releases directly from your computer by omitting --skip-publish flag but such process smells a bit …

As GoReleaser is just single binary — we can integrate it with in CI/CD tooling. Now I’m gonna show you basic Drone.io pipeline but the release process looks the same way on other CI/CD platforms.

So this is my .drone.ymlfile:

workspace:
base: /go
path: github.com/vranystepan/goreleaser-demo
pipeline:
fetch:
image: docker:git
commands:
- git fetch --tags

release:
when:
event:
- tag
image: golang:latest
secrets: [github_token]
commands:
- curl -sL https://git.io/goreleaser | bash
As my dummy program does pretty much nothing I have skipped testing and linting. Make sure you have configured testing and linting pipeline in the real life scenarios!

Now we just need to enable Drone.io for this project, add Github API key to the project’s secrets and push our changes to the CVS.

git add .drone.yml
git commit -m "setup cd"
git tag -a v0.0.2 -m "dummy release"
git push origin master --tags

Push to master will immediately trigger process defined in .drone.yml file. You can check all logs in Drone.io interface.

Everything is just perfect here so we can check releases page in the Github project and see if everything works as expected.

Damn yes! This is just perfect. All the future releases will be published automatically so I don’t have to think about the correct sequence of steps in the release process anymore.

Caveats

It seems that GoReleaser generates some sort of automatic Changelog based on the commit history. According to SemVer, this is not 100% correct approach so you should use this automatic behaviour with some caution.

Thankfully, this behaviour can be overridden with --release-notes flag so you can publish release notes which are just silky smooth 😂