GoReleaser Deliver Go binaries as fast and easily as possible

Introduction

GoReleaser is a release automation tool for Go projects, the goal is to simplify the build, release and publish steps while providing variant customization options for all steps.

GoReleaser is built for CI tools; you only need to download and execute it in your build script. You can customize your release process by creating a .goreleaser.yml file.

The idea started with a simple shell script, but it quickly became more complex and I also wanted to publish binaries via Homebrew taps, which would have made the script even more hacky, so I let go of that and rewrote the whole thing in Go.

Installing Goreleaser

There are three ways to get going.

  1. Install Goreleaser via go get (goreleaser command will be globally available) go get github.com/goreleaser/goreleaser
  2. On a Mac use Homebrew.
  3. Install directly from the binaries.

Quick Start

In this example we will build, archive and release a Go project.

Create a GitHub repository and add a single main package:

// main.go
package main

func main() {
  println("Ba dum, tss!")
}

By default GoReleaser will build the current directory, but you can change the package path in the GoReleaser configuration file:

# .goreleaser.yml
# Build customization
builds:
  - binary: drum-roll
    goos:
      - windows
      - darwin
      - linux
    goarch:
      - amd64

GoReleaser skips invalid GOOS/GOARCH combinations.

With the above configuration the name of all created binaries will be drum-roll and GoReleaser will build one binary in 64bit architecture for each of the operating systems Windows, Linux and MacOS.

GoReleaser will then archive the resulting binaries of each OS/Arch pair into a separate file. The default format is {{.ProjectName}}_{{.Os}}_{{.Arch}}. You can change the archive’s name and format. You can also replace the OS and the Architecture with your own.

Another useful feature is to add additional files to the created archives:

# .goreleaser.yml
# Build customization
builds:
  - main: main.go
    binary: drum-roll
    goos:
      - windows
      - darwin
      - linux
    goarch:
      - amd64
# Archive customization
archive:
  format: tar.gz
  replacements:
    amd64: 64-bit
    darwin: macOS
    linux: Tux
  files:
    - drum-roll.licence.txt

This configuration will generate tar archives, each containing an additional file called drum-roll.licence.txt. The archives will be located in the dist folder:

  • ./dist/drum-roll_windows_64-bit.tar.gz
  • ./dist/drum-roll_macOS_64-bit.tar.gz
  • ./dist/drum-roll_Tux_64-bit.tar.gz

Next, you need to export a GITHUB_TOKEN environment variable, which should contain a GitHub token with the repo scope selected. It will be used to deploy releases to your GitHub repository. Create a token here.

$ export GITHUB_TOKEN=`YOUR_TOKEN`

GoReleaser uses the latest Git tag of your repository. Create a tag and push it to GitHub:

$ git tag -a v0.1.0 -m "First release"
$ git push origin v0.1.0

Note: We recommend the use of semantic versioning. We are not enforcing it though. We do remove the v prefix and then enforce that the next character is a number. So, v0.1.0 and 0.1.0 are virtually the same and both are accepted, while version0.1.0 is not.

If you don’t want to create a tag yet, you can also create a release based on the latest commit by using the --snapshot flag.

Now you can run GoReleaser at the root of your repository:

$ goreleaser

That’s all! Check your GitHub project’s release page. The release should look like this:

Environment Variables

GitHub Token

GoReleaser requires a GitHub API token with the repo scope selected to deploy the artifacts to GitHub. You can create one here.

This token should be added to the environment variables as GITHUB_TOKEN. Here is how to do it with Travis CI: Defining Variables in Repository Settings.

main.version

GoReleaser always sets a main.version ldflag. You can use it in your main.go file:

package main

var version = "master"

func main() {
  println(version)
}

version will be set to the current Git tag (the v prefix is stripped) or the name of the snapshot, if you’re using the --snapshot flag.

You can override this by changing the ldflags option in the build section.

Customization

GoReleaser provides multiple customizations via the .goreleaser.yml file. You can generate it by running goreleaser init or start from scratch. The defaults are sensible and fit for most projects.

We’ll cover all customizations available bellow.

Project Name

The project name is used in the name of the Brew formula, archives, etc. If none is given, it will be inferred from the name of the Git project.

# .goreleaser.yml
project_name: myproject

Builds

Builds can be customized in multiple ways. You can specify for which GOOS and GOARCH binaries are generated, and you can changed the name of the binary, flags, ldflags, hooks, etc.

Here is a commented builds section with all fields specified:

# .goreleaser.yml
builds:
  # You can have multiple builds defined as a yaml list
  -
    # Path to main.go file or main package.
    # Default is `.`.
    main: ./cmd/main.go

    # Name of the binary.
    # Default is the name of the project directory.
    binary: program

    # Set flags for custom build tags.
    # Default is empty.
    flags: -tags dev

    # Custom ldflags template.
    # This is parsed with the Go template engine and the following variables
    # are available:
    # - Date
    # - Commit
    # - Tag
    # - Version (Git tag without `v` prefix)
    # Date format is `2006-01-02_15:04:05`.
    # Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}`.
    ldflags: -s -w -X main.build={{.Version}}

    # Custom environment variables to be set during the builds.
    # Default is empty.
    env:
      - CGO_ENABLED=0

    # GOOS list to build for.
    # For more info refer to: https://golang.org/doc/install/source#environment
    # Defaults are darwin and linux.
    goos:
      - freebsd
      - windows

    # GOARCH to build for.
    # For more info refer to: https://golang.org/doc/install/source#environment
    # Defaults are 386 and amd64.
    goarch:
      - amd64
      - arm
      - arm64

    # GOARM to build for when GOARCH is arm.
    # For more info refer to: https://golang.org/doc/install/source#environment
    # Default is only 6.
    goarm:
      - 6
      - 7

    # List of combinations of GOOS + GOARCH + GOARM to ignore.
    # Default is empty.
    ignore:
      - goos: darwin
        goarch: 386
      - goos: linux
        goarch: arm
        goarm: 7

    # Hooks can be used to customize the final binary,
    # for example, to run generators.
    # Default is both hooks empty.
    hooks:
      pre: rice embed-go
      post: ./script.sh

Archive

The binaries built will be archived together with the README and LICENSE files into a tar.gz file. In the archive section you can customize the archive name, additional files, and format.

Here is a commented archive section with all fields specified:

# .goreleaser.yml
archive:
  # You can change the name of the archive.
  # This is parsed with the Go template engine and the following variables
  # are available:
  # - ProjectName
  # - Tag
  # - Version (Git tag without `v` prefix)
  # - Os
  # - Arch
  # - Arm (ARM version)
  # Default is `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}`.
  name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

  # Set to true, if you want all files in the archive to be in a single directory.
  # If set to true and you extract the archive 'goreleaser_Linux_arm64.tar.gz',
  # you get a folder 'goreleaser_Linux_arm64'.
  # If set to false, all files are extracted separately.
  # Default is false.
  wrap_in_directory: true

  # Archive format. Valid options are `tar.gz`, `zip` and `binary`.
  # If format is `binary`, no archives are created and the binaries are instead uploaded directly.
  # In that case name_template and the below specified files are ignored.
  # Default is `tar.gz`.
  format: zip

  # Can be used to change the archive formats for specific GOOSs.
  # Most common use case is to archive as zip on Windows.
  # Default is empty.
  format_overrides:
    - goos: windows
      format: zip

  # Replacements for GOOS and GOARCH in the archive name.
  # Keys should be valid GOOSs or GOARCHs.
  # Values are the respective replacements.
  # Default is empty.
  replacements:
    amd64: 64-bit
    386: 32-bit
    darwin: macOS
    linux: Tux

  # Additional files/globs you want to add to the archive.
  # Defaults are any files matching `LICENCE*`, `LICENSE*`,
  # `README*` and `CHANGELOG*` (case-insensitive).
  files:
    - LICENSE.txt
    - README.md
    - CHANGELOG.md
    - docs/*
    - design/*.png

Checksum

GoReleaser generates a project_1.0.0_checksums.txt file and uploads it with the release, so your users can validate if the downloaded files are correct.

The checksum section allows customizations of the filename:

# .goreleaser.yml
checksum:
  # You can change the name of the checksums file.
  # This is parsed with the Go template engine and the following variables
  # are available:
  # - ProjectName
  # - Tag
  # - Version (Git tag without `v` prefix)
  # Default is `{{ .ProjectName }}_{{ .Version }}_checksums.txt`.
  name_template: "{{ .ProjectName }}_checksums.txt"

Snapshots

Sometimes we want to generate a full build of our project, but neither want to validate anything nor upload it to anywhere. GoReleaser supports this with the --snapshot flag and also with the snapshot customization section:

# .goreleaser.yml
snapshot:
  # Allows you to change the name of the generated snapshot
  # releases. The following variables are available:
  # - Commit
  # - Tag
  # - Timestamp
  # Default is `SNAPSHOT-{{.Commit}}`.
  name_template: SNAPSHOT-{{.Commit}}

FPM Linux Packages

GoReleaser can be wired to fpm to generate .deb, .rpm and other archives. Check its wiki for more info.

# .goreleaser.yml
fpm:
  # Your app's vendor.
  # Default is empty.
  vendor: Drum Roll Inc.
  # Your app's homepage.
  # Default is empty.
  homepage: https://example.com/

  # Your app's maintainer (probably you).
  # Default is empty.
  maintainer: Drummer <[email protected]>

  # Your app's description.
  # Default is empty.
  description: Software to create fast and easy drum rolls.

  # Your app's license.
  # Default is empty.
  license: Apache 2.0

  # Formats to be generated.
  formats:
    - deb
    - rpm

  # Packages your package depends on.
  dependencies:
    - git
    - zsh

  # Packages that conflict with your package.
  conflicts:
    - svn
    - bash

  # Files or directories to add to your package (beyond the binary).
  # Keys are source paths to get the files from.
  # Values are the destination locations of the files in the package.
  files:
    "scripts/etc/init.d/": "/etc/init.d"

Note that GoReleaser will not install fpm or any of its dependencies for you.

Snapcraft Linux Packages

GoReleaser can generate snap packages. Snaps are a new packaging format, that will let you publish your project directly to the Ubuntu store. From there it will be installable in all the supported Linux distros, with automatic and transactional updates.

You can read more about it in the snapcraft docs.

# .goreleaser.yml
snapcraft:

  # The name of the snap. This is optional.
  # Default is project name.
  name: drumroll

  # Single-line elevator pitch for your amazing snap.
  # 79 char long at most.
  summary: Software to create fast and easy drum rolls.

  # This the description of your snap. You have a paragraph or two to tell the
  # most important story about your snap. Keep it under 100 words though,
  # we live in tweetspace and your description wants to look good in the snap
  # store.
  description: |
    This is the best drum roll application out there.
    Install it and awe!

  # A guardrail to prevent you from releasing a snap to all your users before
  # it is ready.
  # `devel` will let you release only to the `edge` and `beta` channels in the
  # store. `stable` will let you release also to the `candidate` and `stable`
  # channels. More info about channels here:
  # https://snapcraft.io/docs/reference/channels
  grade: stable

  # Snaps can be setup to follow three different confinement policies:
  # `strict`, `devmode` and `classic`. A strict confinement where the snap
  # can only read and write in its own namespace is recommended. Extra
  # permissions for strict snaps can be declared as `plugs` for the app, which
  # are explained later. More info about confinement here:
  # https://snapcraft.io/docs/reference/confinement
  confinement: strict

  # Each binary built by GoReleaser is an app inside the snap. In this section
  # you can declare extra details for those binaries. It is optional.
  apps:

    # The name of the app must be the same name as the binary built.
    drumroll:

      # If your app requires extra permissions to work outside of its default
      # confined space, delcare them here.
      # You can read the documentation about the available plugs and the
      # things they allow:
      # https://snapcraft.io/docs/reference/interfaces).
      plugs: ["home", "network"]

      # If you want your app to be autostarted and to always run in the
      # background, you can make it a simple daemon.
      daemon: simple

Note that GoReleaser will not install snapcraft nor any of its dependencies for you.

Releasing

GoReleaser will create a release in GitHub with the current tag, upload all the archives and checksums, also generate a changelog from the commits new since the last tag.

Let’s see what can be customized in the release section:

# .goreleaser.yml
release:
  # Repo in which the release will be created.
  # Default is extracted from the origin remote URL.
  github:
    owner: user
    name: repo

  # If set to true, will not auto-publish the release.
  # Default is false.
  draft: true

  # If set to true, will mark the release as not ready for production.
  # Default is false.
  prerelease: true

  # You can change the name of the GitHub release.
  # This is parsed with the Go template engine and the following variables
  # are available:
  # - ProjectName
  # - Tag
  # - Version (Git tag without `v` prefix)
  # Default is ``
  name_template: "{{.ProjectName}}-v{{.Version}}"

Customize the changelog

You can customize how the changelog is generated using the changelog section in the config file:

# .goreleaser.yml
changelog:
  filters:
    # commit messages matching the regexp listed here will be removed from
    # the changelog
    # Default is emtpy
    exclude:
      - '^docs:'
      - typo
      - (?i)foo
    # could either be asc, desc or empty
    # Default is empty
    sort: asc

Custom release notes

You can specify a file containing your custom release notes, and pass it with the --release-notes=FILE flag. GoReleaser will then skip its own release notes generation, using the contents of your file instead. You can use Markdown to format the contents of your file.

On Unix systems you can also generate the release notes in-line by using process substitution. To list all commits since the last tag, but skip ones starting with Merge or docs, you could run this command:

goreleaser --release-notes <(git log --pretty=oneline --abbrev-commit $(git describe --tags --abbrev=0)^.. | grep -v '^[^ ]* \(Merge\|docs\)')

Homebrew

After releasing to GitHub, GoReleaser can generate and publish a homebrew-tap recipe into a repository that you have access to.

The brew section specifies how the formula should be created. You can check the Homebrew documentation and the formula cookbook for more details.

# .goreleaser.yml
brew:
  # Reporitory to push the tap to.
  github:
    owner: user
    name: homebrew-tap

  # Git author used to commit to the repository.
  # Defaults are shown.
  commit_author:
    name: goreleaserbot
    email: [email protected]

  # Folder inside the repository to put the formula.
  # Default is the root folder.
  folder: Formula

  # Caveats for the user of your binary.
  # Default is empty.
  caveats: "How to use this binary"

  # Your app's homepage.
  # Default is empty.
  homepage: "https://example.com/"

  # Your app's description.
  # Default is empty.
  description: "Software to create fast and easy drum rolls."

  # Packages your package depends on.
  dependencies:
    - git
    - zsh

  # Packages that conflict with your package.
  conflicts:
    - svn
    - bash

  # Specify for packages that run as a service.
  # Default is empty.
  plist: |
    <?xml version="1.0" encoding="UTF-8"?>
    ...

  # So you can `brew test` your formula.
  # Default is empty.
  test: |
    system "#{bin}/program --version"
    ...

  # Custom install script for brew.
  # Default is 'bin.install "program"'.
  install: |
    bin.install "program"
    ...

By defining the brew section, GoReleaser will take care of publishing the Homebrew tap. Assuming that the current tag is v1.2.3, the above configuration will generate a program.rb formula in the Formula folder of user/homebrew-tap repository:

class Program < Formula
  desc "How to use this binary"
  homepage "https://github.com/user/repo"
  url "https://github.com/user/repo/releases/download/v1.2.3/program_v1.2.3_macOs_64bit.zip"
  version "v1.2.3"
  sha256 "9ee30fc358fae8d248a2d7538957089885da321dca3f09e3296fe2058e7fff74"

  depends_on "git"
  depends_on "zsh"

  def install
    bin.install "program"
  end
end

Important“: Note that GoReleaser does not yet generate a valid homebrew-core formula. The generated formulas are meant to be published as homebrew taps, and in their current form will not be accepted in any of the official homebrew repositories.

Docker

Since v0.31.0, GoReleaser supports building and pushing Docker images.

How it works

You can declare multiple Docker images. They will be matched against the binaries generated by your builds section.

If you have only one build setup, the configuration is as easy as adding the name of your image to your .goreleaser.yml file:

dockers:
  - image: user/repo

You also need to create a Dockerfile in your project’s root folder:

FROM scratch
COPY mybin /
ENTRYPOINT ["/mybin"]

This configuration will build and push a Docker image named user/repo:tagname.

Customization

Of course, you can customize a lot of things:

# .goreleaser.yml
dockers:
  # You can have multiple Docker images.
  -
    # GOOS of the built binary that should be used.
    goos: linux
    # GOARCH of the built binary that should be used.
    goarch: amd64
    # GOARM of the built binary that should be used.
    goarm: ''
    # Name of the built binary that should be used.
    binary: mybinary
    # Docker image name.
    image: myuser/myimage
    # Path to the Dockerfile (from the project root).
    dockerfile: Dockerfile
    # Also tag and push myuser/myimage:latest.
    latest: true
    # If your Dockerfile copies files other than the binary itself,
    # you should list them here as well.
    extra_files:
    - config.yml

These settings should allow you to generate multiple Docker images, for example, using multiple FROM statements, as well as generate one image for each binary in your project.

Continous Integration

You may want to setup your project to auto-deploy your new tags on Travis, for example:

# .travis.yml
after_success:
  - test -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

Here is how to do it with CircleCI:

# circle.yml
deployment:
  tag:
    tag: /v[0-9]+(\.[0-9]+)*(-.*)*/
    owner: user
    commands:
      - curl -sL https://git.io/goreleaser | bash

If you test multiple versions or multiple OSes, you probably want to make sure GoReleaser is just run once. You could change the above example for Travis CI like this:

# .travis.yml
after_success:
  - test "$TRAVIS_OS_NAME" = "linux" -a -n "$TRAVIS_TAG" && curl -sL https://git.io/goreleaser | bash

GitHub Enterprise

You can use GoReleaser with GitHub Enterprise by providing its URLs in the .goreleaser.yml configuration file:

# .goreleaser.yml
github_urls:
    api: api.github.foo.bar
    upload: uploads.github.foo.bar
    download: github.foo.bar

If none are set, they default to GitHub’s public URLs.