chore: Migrate brews to homebrew_casks - #351
Conversation
`github.com/goreleaser/goreleaser@latest` resolves to v1.26.2 because v2 lives under the `/v2` module path. v1 understands neither `archives.formats` nor `homebrew_casks`, so `make release-check` and `make dist` were broken on a machine without GoReleaser already installed.
`brews` became fully deprecated in GoReleaser v2.16, so `goreleaser check` now exits 2 and takes `make release` down with it. Verified: the old config exits 2 under v2.17.1, the new one exits 0. - `install` is replaced by the default `binary` stanza plus `generate_completions_from_executable` - Casks quarantine their downloads, so a postflight `xattr` hook is needed for the binary to run at all and for completion generation to work - `test` and `license` have no cask equivalent, so they are dropped
- Spell out `--cask`, matching masutaka/homebrew-tap#2. Homebrew falls back to the cask on its own once the formula is gone, so this is for clarity rather than correctness - Drop the macOS-only wording: Homebrew gates casks per artifact, and `binary` is not in `MACOS_ONLY_ARTIFACTS`
masutaka/github-nippou#351 で brews を homebrew_casks へ移行したため、 その受け皿を tap 側に用意する。 - brew update は git diff から移行を検知するので Formula 削除が必須 - tap_migrations.json は Casks/github-nippou.rb の存在が前提
Outcome, and a correction to this PR bodyThe migration itself worked. Two things this PR body got wrong. 1. Completions were brokenTwo bugs, fixed in #352: the quarantine hook fired after 2. "
|
Closes #283
Why now
Homebrew has not dropped Formulas — GoReleaser dropped
brews.brewswas soft-deprecated in v2.10 and fully deprecated in v2.16.0 (2026-05-24). Since thenbrew.Pipe.Default()setsctx.Deprecatedmerely because abrewssection exists, andgoreleaser checkexits 2 onctx.Deprecated. OurMakefilehasrelease: deps-release release-check, somake releasestops there. See the deprecation notice for GoReleaser's own migration guide.The last successful
releasejob ran on 2026-04-29 (v4.3.0), before v2.16.0 landed. The release workflow installslatestviagoreleaser-action, so the next tag would have failed.Verified locally against GoReleaser v2.17.1:
The
brewsimplementation itself still ships in v2.17.1 — GoReleaser only removes deprecated options on major versions, so releases would keep working until v3.0. Onlygoreleaser checkfails. Migrating now rather than deferring to a v3.0 emergency.What changed
homebrew_caskshas noinstallortestblock, which is what #283 flagged. Mapping:brews)homebrew_casks)bin.install 'github-nippou'binarystanza (defaults to the cask name)install:completion-generation Rubygenerate_completions_from_executable(v2.15+)test:license: "MIT"hooks.post.installto strip quarantine (newly required)url.verifiedto helpbrew auditTwo cask-specific notes:
com.apple.quarantine. Our binaries are neither signed nor notarized, so without thexattr -drpostflight hook users hit "github-nippou is damaged and cannot be opened" — andgenerate_completions_from_executablecannot execute the binary either. GoReleaser's template guaranteespostflightis emitted before it.shellsis explicit.shell_parameter_format: cobradefaults to includingpwsh; pinning to bash/zsh/fish keeps parity with the old formula.The
Makefilechange is separate and pre-existing:go install github.com/goreleaser/goreleaser@latestresolves to v1.26.2, since v2 lives under the/v2module path. v1 understands neitherarchives.formatsnorhomebrew_casks, so local verification was impossible. CI is unaffected (goreleaser-actionwithinstall-only).Generated cask
make distoutput (dist/homebrew/Casks/github-nippou.rb, abridged):Linux keeps working
Worth stating explicitly, because "Casks are macOS-only" is a common belief and it is not accurate: Homebrew gates casks per artifact, not per cask. From
Library/Homebrew/cask/artifact.rb#L42-L63:This cask uses
Binary,PostflightBlockandGeneratedCompletion— none of them are inMACOS_ONLY_ARTIFACTS, soCask#artifacts_supported_on_os?(:linux)returns true.Artifact::Binaryis just a symlink pluschmod +x, with no OS-specific code, andbrew installhas no cask-wide OS gate. The existence ofLINUX_ONLY_ARTIFACTSshows Homebrew supports Linux casks by design.The reason casks feel macOS-only is that the homebrew/cask tap is almost entirely
appandpkgartifacts — a property of its contents, not a limit of the mechanism.GoReleaser therefore keeps emitting the
on_linuxblock, and so does every cask in goreleaser/homebrew-tap and caarlos0/homebrew-tap. GoReleaser's own install docs listbrew install --cask goreleaser/tap/goreleaserwith no OS split. The quarantine hook is guarded byif OS.mac?, so it is skipped on Linux.Not verified on an actual Linux box — this is from reading Homebrew's source and GoReleaser's own distribution.
Install instructions
README.mdandREADME_ja.mdnow saybrew install --cask masutaka/tap/github-nippou, matching masutaka/homebrew-tap#2.--caskis not strictly required.load_formula_or_casktries the formula first and falls through to the cask whenFormulary.factoryraisesFormulaUnavailableError— which is exactly what happens once the formula is deleted from the tap. Existing muscle memory keeps working. Spelling it out just keeps both repos consistent, the way GoReleaser's own docs do.The macOS-only wording is gone for the reason above: casks are gated per artifact, not per cask.
Known trade-off
Losing
test domeans thegithub-nippou versionsmoke test goes away. Casks have no equivalent.Follow-up in masutaka/homebrew-tap (not in this PR)
The new cask lands at
Casks/github-nippou.rb; the old Formula sits at the repo root asgithub-nippou.rb. After the first cask release:Add
tap_migrations.jsonat the tap root:{ "github-nippou": "github-nippou" }(same shape as goreleaser/homebrew-tap and caarlos0/homebrew-tap)
rm github-nippou.rbOrder matters. The cask must exist first, because the migration branch is gated on
new_tap.cask_tokens.intersect?(...). And the formula file must actually be deleted, sincebrew updatedetects the migration from the git diff.No announcement needed, and users do not need to uninstall anything first. Homebrew handles formula→cask migration explicitly in
migrate_tap_migration:So
brew updatealone unlinks the old keg, cleans up, installs the cask, and then tells the user to runbrew uninstall --formula --force github-nippouat their convenience. Homebrew produces that message itself. WhereCaskroomdoes not exist yet, it prints the manual steps instead of auto-installing.Verification
make release-checkpasses (exit 0; old config exits 2)make distgenerates the cask abovebrew updateand confirm it auto-migrates (unlink → cleanup →install --cask). Do not uninstall first — that would bypass the path being tested.github-nippou versionand tab completion, and run thebrew uninstall --formula --force github-nippouthat Homebrew suggests (completions needed fix: Move the quarantine hook ahead of completion generation #352 — see comment below)brew install masutaka/tap/github-nippouon_linuxblock behaves as the source reading suggests