Painless Github releases with Drone and GoReleaser
By: Štěpán Vraný
Tags: Github, Continuous Delivery, Golang, Continuous Integration, DevOps

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 init command, this step will create a new .goreleaser.yml with following content:



# Make sure to check the documentation at

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:' # 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=0archive:replacements:darwin: Darwinlinux: Linuxwindows: Windows386: i386amd64: x86_64checksum:name_template: 'checksums.txt'snapshot:name_template: "{{ .Tag }}-next"changelog:sort: ascfilters: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 GO111MODULE environment 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 commit -m "initial release"

git tag -a v0.0.1 -m "initial release"

git remote add origin

git push origin master -f --tags git add .goreleaser.yml main.gogit commit -m "initial release"git tag -a v0.0.1 -m "initial release"git remote add origin git@github.com :vranystepan/goreleaser-demo.gitgit 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.yml file:

workspace:

base: /go

path: github.com/vranystepan/goreleaser-demo

fetch:

image: docker:git

commands:

- git fetch --tags



release:

when:

event:

- tag

image: golang:latest

secrets: [github_token]

commands:

- curl -sL pipeline:fetch:image: docker:gitcommands:- git fetch --tagsrelease:when:event:- tagimage: golang:latestsecrets: [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.