Photo by Diana Parkhouse on Unsplash

Distribute your Binaries hosted on a private GitLab repository with HomeBrew using GoReleaser šŸŽšŸ«£

developer-guy
Trendyol Tech
Published in
6 min readSep 20, 2022

--

A while ago, we wrote a blog about GoReleaser, ā€œThe fanciest way of releasing Go binaries with GoReleaser,ā€ and yes, Iā€™m still thinking the same šŸ™ˆ In that blog post, we gave a higher-level introduction to GoReleaser. In addition, we explored a bunch of features supported in it. So I recommend you take a look at it if you are not familiar enough with the GoReleaser project because in this blog post, Iā€™ll introduce you to another feature that GoReleaser shines on, and thatā€™s the automation of creating formulas to facilitate the process of distributing binaries through HomeBrew, which is as known as The Missing Package Manager for macOS (or Linux). HomeBrew provides a cookbook for us to give an overview of how to create formulas. Still, it can be challenging for most of us as we arenā€™t already familiar with building formulas. However, I believe you will be surprised when you notice how it can quickly be done with GoReleaser without much effort.

Today, on the one hand, Iā€™ll give you an overview of how you can start creating formulas using GoReleaser; on the other hand, weā€™ll be demoing that on a private repository hosted in GitLab.

Homebrew has a core repository of packages but also supports providing your tap for hosting your formulas, which we will be using in this guide. Thanks to HomeBrew, they created excellent documentation about how to develop and maintain our tap.

TL;DR A tap is a separate Git repository with a unique naming that only contains instructions for HomeBrew (so it knows how to install your stuff). They recommend that the repositoryā€™s name start with homebrew- so the short brew tap command can be used. For example, letā€™s assume that we give a name to the Git repository as ā€œ<my-org>homebrew-tools,ā€ then we should use the tap name as ā€œ<my-org>/toolsā€ while we are adding the tap.

According to the documentation, first, we need to create a Git repository but donā€™t forget itā€™ll be a private one. Therefore, I wonā€™t dive into all the details in GoReleaser and focus only on the brew part. As the next step, we need to tell GoReleaser where it should create formulas and how. Finally, we need to add another section for the brew to ā€œ.goreleaser.ymlā€, a configuration file for GoReleaser. You can reach out to all the options available in the brew section here. Most probably, once you configure it, you will end up something like the following:

Iā€™m not going to every detail in the example above; however, there are some points Iā€™d like to highlight, especially the completions, token, url_template, and private_token parts. Special thanks to Carlos A. Becker; I can hand over the completions part to his blog for a more pleasing experience because he already did excellent work there.

As we mentioned, we should create a Git repository, and here we created a Git repository named ā€œhomebrew-tools.ā€ As we kept it private, we should provide a token granted to write this repository and give that token to the ā€œtokenā€ field of the ā€œtapā€ section. You can reach out to the list of available options in the tap section.

As a next step, Iā€™d like to introduce you to one of the great features in GitLab named ā€œPackage Registriesā€ to explain why we have the ā€œ../packages/genericā€ part in the ā€œurl_template.ā€ We have to make it reachable thus (donā€™t forget the Git repository is private); the brew will be using this URL while downloading the package.

The GitLab Package Registry is a private or public registry for various familiar package managers. You can publish and share packages, which can be easily consumed as a dependency in downstream projects, which weā€™ll use in this guide. There is support for various packages in the Package Registry, and you can reach out to the list here. One is ā€œGeneric,ā€ which weā€™ll also use in this guide to release our binaries. Also, there is good news that GoReleaser has first-class support for the ā€œGeneric Package Registry,ā€

Normally, goreleaseruploads release files as ā€œattachments,ā€ which may have administrative limits. Notably, hosted gitlab.com instances have a 10MB attachment limit, which cannot be changed. Uploading to the Generic Package Registry does not have this restriction. To use it instead,v set use_package_registry it to true.

https://goreleaser.com/scm/gitlab/#generic-package-registry

# .goreleaser.yml
gitlab_urls:
use_package_registry: true
> https://goreleaser.com/scm/gitlab/#gitlab-enterprise-or-private-hosted

Last but not least, as the documentation says, we need a token with read_package_registry and/or write_package_registry scope permissions.

?private_token={{ .Env.DOWNLOADER_TOKEN }}

Thanks to Adam Meech for giving this idea of using ā€œprivate_tokenā€ as a request parameter, you can reach out to his blog post here, where I found the real solution.

This is how we pass environment variables to GoReleaser. Although, as you may notice, because of the work that needs to be done, we use two different tokens, one GITLAB_TOKEN and the other DOWNLOADER_TOKEN, there are various types of permissions in it.

Once everything is completed, you will end up having something like the followings:

Everything seems so relaxed at this point šŸ˜Ž

But how do you download packages from one tap we provide as a separate private Git repository? šŸ˜°

Donā€™t panic! Of course, there is already a solution for this, like everything else šŸ•ŗšŸ»

The only thing that you have to do is that provide the private Git repository URL to the command, just like the following:

brew tap pe-container/tools <private-gitlab-url>

Now, everything is ready for downloading the package from GitLabā€™s Generic Package Registry through the brew šŸš€

brew install kcfgctl

Thatā€™s the story, all you need to do to distribute your package stored in a private Git repository is include a bunch of tokens with necessary permissions. šŸ’Ŗ

I hope you like it, see you next time, please stay tuned šŸ“»

ā­ļøBonus: What did you do if your local package is not upgraded even if the new version of your project gets released?

If the ā€œbrew upgradeā€ command doesnā€™t work for you, you can do the following trick:

# cd into the local repo
cd "$(brew --repo sjbonner/tap)"

# find out the latest commit locally
git log -s -1
# compare the commit with your remote repo

# if the repo is outdated, run
brew update

https://stackoverflow.com/questions/58279117/homebrew-not-updating-the-version-of-my-package

ā­ļøBonus: What if I host my project on GitHub then what would be the solution to the same problem?

https://twitter.com/developerguyba/status/1570493682780090368?s=20&t=AsQLxgnSvA49RJwBku1b8g

References

--

--

developer-guy
Trendyol Tech

šŸ‡¹šŸ‡·KCD Turkey OrganizeršŸŽ–Best Sigstore EvangelistšŸ¦SSCS Twitter Community Admināœļø@chainguard_dev FanšŸ“¦Container AddictšŸ“…Organizer at @cloudnativetrā€¢@devopstr