Skip to content

build(goreleaser): publish a Homebrew formula via a tap#353

Open
mvanhorn wants to merge 1 commit intoasciimoo:masterfrom
mvanhorn:osc/161-homebrew-tap
Open

build(goreleaser): publish a Homebrew formula via a tap#353
mvanhorn wants to merge 1 commit intoasciimoo:masterfrom
mvanhorn:osc/161-homebrew-tap

Conversation

@mvanhorn
Copy link
Copy Markdown
Contributor

Summary

Closes #161 by wiring hister into a Homebrew tap via goreleaser. On every stable release tag, goreleaser will now push a hister formula to asciimoo/homebrew-tap in addition to producing the existing raw binaries.

Why this matters

@SKalt opened #161 with a short, specific ask: ~10 lines of goreleaser config to publish a Homebrew formula, motivated by "MacOS refusing to run the binary from the releases page without a signature." Without a tap, macOS users have to either trust an unsigned binary from the releases page or build from source.

Investigating .goreleaser.yml, the existing archives: block emits only formats: ['binary'], which produces raw per-platform binaries with no tarball. goreleaser's brews: step expects tarballs so its generated url do can point at the .tar.gz on the release page, so the archive config needs to emit tar.gz in addition to the current binary (keeping the raw binary available for anyone who wants it).

.github/workflows/release.yml already runs goreleaser and already passes secrets.GORELEASER_TOKEN. The same token is reused for the tap push; no workflow edit is needed.

Changes

All changes live in .goreleaser.yml:

  • archives: now emits ['tar.gz', 'binary'] with a name_template so the tap can locate each platform's tarball. Raw binaries are preserved.
  • New brews: block pointing at asciimoo/homebrew-tap, with a minimal install / test stanza and metadata (description, homepage, AGPL-3.0-or-later SPDX license). The test: block invokes hister --version, which is already wired in hister.go:85-89 via cobra.Command.Version = Version.

Maintainer prerequisites (one-time)

Shipping this requires two things outside of this PR, both flagged in the issue body:

  1. Create the asciimoo/homebrew-tap repository. It can start empty — goreleaser will populate the formula on the next release.
  2. Ensure secrets.GORELEASER_TOKEN has write access to the tap repo (read-only to this repo is fine, but it needs to push to the tap).

If either of those names would be different in practice (e.g., a different tap repo naming convention), happy to adjust the repository: block.

Testing

  • YAML parses and still has version, builds, archives, brews at the top level.
  • goreleaser check not available locally, so syntax is verified against goreleaser v2's documented schema at https://goreleaser.com/customization/homebrew/ (matches v2 repository: / name: / install: / test: shape).
  • hister --version exists today, so the formula's test: block is exercisable.

Closes #161

This contribution was developed with AI assistance (Claude Code).

Closes asciimoo#161

Adds a brews: block that pushes a `hister` formula to
`asciimoo/homebrew-tap` on every stable release, and extends
archives to emit tar.gz (needed by brew to download and extract) in
addition to the existing raw binary output.

The release workflow already passes GORELEASER_TOKEN; no workflow
change is needed beyond granting that token write access to the tap
repository. The tap repo itself has to be created once by the
maintainer, after which every new tag will publish/update the formula
automatically.
@asciimoo
Copy link
Copy Markdown
Owner

asciimoo commented Apr 19, 2026

Thanks for the contribution and thanks for clarifying the AI use.

goreleaser will now push a hister formula to asciimoo/homebrew-tap

Isn't it possible to build the homebrew artifacts in this repo and simply add them to the release artifacts just as we do with the release binaries?

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Yes, goreleaser can generate the formula as a file artifact, so attaching it to the release alongside the binaries is straightforward. The tradeoff is the install UX.

With a tap repo, a user runs:

brew tap asciimoo/tap
brew install hister

That works because Homebrew resolves formula names against tapped repositories.

If the formula lives only as a release asset, there's no tap for Homebrew to look in, so the user has to either:

  1. Download the .rb and run brew install --formula ./hister.rb (one-off, no brew upgrade flow)
  2. Or run brew install asciimoo/hister/hister, which still ends up creating a transient tap under the hood

The tap-based flow is what most goreleaser projects ship.

Happy to do both: keep the tap push so brew install asciimoo/tap/hister works, and also surface the generated .rb as a release asset so it's visible from the release page. Would that be acceptable, or is your preference specifically to avoid the separate asciimoo/homebrew-tap repo?

@asciimoo
Copy link
Copy Markdown
Owner

Or run brew install asciimoo/hister/hister, which still ends up creating a transient tap under the hood

Why is this inferior to the brew tap asciimoo/tap && brew install hister solution?

Would that be acceptable, or is your preference specifically to avoid the separate asciimoo/homebrew-tap repo?

My preference is to keep it as simple as possible. The ideal would be to be part of the main homebrew "repo" I guess.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Quick correction on my earlier framing - brew install asciimoo/hister/hister does not create a "transient" tap, it resolves to github.com/asciimoo/homebrew-hister, which would need to exist. So all three install paths require some tap repo:

  • asciimoo/homebrew-tap (what this PR sets up, one tap for any future asciimoo tools)
  • asciimoo/homebrew-hister (one tap per formula; brew install asciimoo/hister/hister resolves here)
  • Skip a tap entirely and attach hister.rb as a release asset, users run brew install --formula ./hister.rb, no brew upgrade path

If "simplest possible" means "one less repo in your org," the cleanest ending is homebrew-core. I did not propose it because:

  • homebrew-core has a notability bar (issue/star thresholds, active usage), worth a shot but not guaranteed
  • formula additions go through a separate PR to homebrew/homebrew-core, not via goreleaser
  • I would be happy to open that PR as a follow-up if you want to try the submission

In the meantime, if you would rather not stand up a tap repo at all, I can strip the brews: block out of this PR and just keep the archive format change. Users can still brew install --formula from a downloaded .rb, it just loses the brew upgrade flow.

What is your preference, land the tap approach, submit to homebrew-core instead, or drop the formula work from this PR?

@asciimoo
Copy link
Copy Markdown
Owner

@mvanhorn thank you for explaining every option in detail. I don't use brew and I'm not familiar with the ecosystem at all.

What is your preference

Of course, the best would be to get bundled by homebrew-core. If I understand correctly, it would mean that we don't have to do the builds on our side, right?

If there is a realistic chance to be part of homebrew-core, I'd try that direction and we can fallback to this tap method in case we won't succeed. What do you think about this plan?

homebrew-core has a notability bar (issue/star thresholds, active usage)

Hopefully we can grow big enough, to be eligible in the future even if we are below the bar currently. (To be fair, we have quite a few issues/PRs/contributions in relation to the projects age.)

@mvanhorn
Copy link
Copy Markdown
Contributor Author

@asciimoo happy to dig in.

Yes, homebrew-core handles the builds. Once a formula is accepted, their CI compiles from source on each platform and uploads pre-built bottles so users get fast binary installs. You wouldn't host or publish anything on your end.

The "homebrew-core first, tap fallback" plan is reasonable. Quick reality check on eligibility:

  • Notability bar: >=75 stars OR >=75 forks OR >=30 watchers. hister is at 743 stars, so that's fine.
  • Stable release: needs a tagged release that isn't pre-release.
  • Active maintenance: open issues/PRs turning over regularly — hister qualifies.
  • Age: no hard rule, but core maintainers sometimes push back on projects under ~6 months if activity looks bursty. At 3 months with 743 stars and steady contributions, it's borderline-fine; worst case they ask you to wait another cycle.

Suggested sequence: I'll open a homebrew-core formula PR first. If it's accepted, we close this PR unchanged. If they decline (most likely reason: "come back in a few months"), we merge this goreleaser-tap PR so brew users aren't blocked in the meantime. Want me to draft the homebrew-core PR?

@asciimoo
Copy link
Copy Markdown
Owner

@mvanhorn excellent, thanks! I'd appreciate a lot if you'd manage the homebrew-core dance.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

On it. Quick notability read first so you know what to expect:

  • Stars 767, forks 42, 13 tagged releases (v0.1.0 → v0.12.0, latest Apr 9), AGPL-3.0. That clears the usual homebrew-core bar (30+ forks or 75+ stars, OSI license, stable releases). Only soft concern is project age (~3.5 months since first commit), but the activity pattern and contributor list should carry it.
  • Process: I open a PR in Homebrew/homebrew-core with a generated formula pointing at the v0.12.0 tag, wait for the homebrew-core reviewer rotation, address any feedback (they'll likely ask for test do coverage). Typical round-trip is a week or two.
  • This PR (build(goreleaser): publish a Homebrew formula via a tap #353) stands on its own as a user-facing fallback (asciimoo/tap) while we wait for homebrew-core to land. I'd suggest merging this one as-is so MacOS users have something that works today, then flipping the README recommendation to brew install hister once homebrew-core merges.

Want me to get the homebrew-core PR drafted this week, or prefer I wait until #353 lands to avoid two open paths?

@asciimoo
Copy link
Copy Markdown
Owner

Process: I open a PR in Homebrew/homebrew-core with a generated formula pointing at the v0.12.0 tag

I'm just about to release a new version which has plenty of massive changes compared to 0.12.0 (perhaps potential bugs as well). Not sure if it would be better to wait with the release or to submit it before starting the homebrew-core dance.

Want me to get the homebrew-core PR drafted this week, or prefer I wait until #353 lands to avoid two open paths?

I'd prefer trying the core path first then fallback to this PR. It would save us one possible revert, but that's all the reasoning I have. If there are any stronger counter-argument, I'm fully open to do it differently.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Good question. My recommendation: submit at v0.12.0 now, bump later.

Reasons:

  1. Homebrew-core reviewers prefer a release that's been out for a bit and is known stable. A brand-new version with "plenty of massive changes (perhaps potential bugs as well)" is the opposite of that - if a reviewer pokes at it and finds a rough edge, they'll ask you to cut a patch release before accepting. So submitting at the newest possible version is actually slower, not faster.

  2. Once the formula is merged at v0.12.0, bumping it to the next version is a one-line change (url + sha256) that you can either:

    • do yourself via brew bump-formula-pr --url ... --sha256 ... (30 seconds), or
    • let Homebrew's autobump bot do automatically by tagging the formula with # autobump - it watches your GitHub releases and opens a bump PR within a few hours of each new tag.
  3. Decoupling means the homebrew-core review (can take a week or two of back-and-forth) runs in parallel with your release prep, instead of blocking each other.

So the plan would be:

  1. I open the homebrew-core PR this week targeting v0.12.0.
  2. You ship your new version on your normal timeline.
  3. Once both are in, I (or the autobump bot) open a follow-up PR to update the formula to the new version.

Proceed?

@asciimoo
Copy link
Copy Markdown
Owner

Homebrew-core reviewers prefer a release that's been out for a bit and is known stable.

Alright. Then I'm delaying the release for a few days, we are not in a rush and users who like to live dangerously can use our rolling release until then.

Proceed?

Let's gooo! I'd like to thank you again your very professional approach of handling this topic. E>

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Thanks @asciimoo - drafting the homebrew-core formula against v0.12.0 now, will link the PR here once it's open. Formula test block will just exercise hister --version, which is already wired via cobra.

@mvanhorn
Copy link
Copy Markdown
Contributor Author

Opened Homebrew/homebrew-core#278900 with the v0.12.0 formula. Passing it to the Homebrew maintainers from here — I won't be actively shepherding the review, so ping me on the PR if anything needs an upstream change.

@asciimoo
Copy link
Copy Markdown
Owner

@mvanhorn Thanks. I've subscribed to the PR. I'll ping you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement: distribute via brew

2 participants