Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,54 @@ homebrew_casks:
# The binary is unsigned (Go only ad-hoc-signs darwin/arm64 so it *runs*);
# casks are quarantined on download, so strip the quarantine flag on install
# or launch dies with "koc cannot be verified".
hooks:
post:
install: |
#
# This is a raw `custom_block` rather than GoReleaser's `hooks.post.install`
# because that hook is emitted as Homebrew's `postflight do … end`, which
# Homebrew 6.0.16 deprecated in favour of the declarative `postflight_steps`:
# every `brew` command touching the cask printed
# Warning: Calling `postflight` is deprecated! Use `postflight_steps` instead.
# and Homebrew deprecations turn into errors a few releases later. GoReleaser
# cannot emit the `*_steps` stanzas yet (goreleaser/goreleaser#6870, PR #6873,
# milestone v2.19.0) and has no way to emit both spellings, so the cask picks
# one at load time and stays installable on every Homebrew that ever shipped
# this cask. When GoReleaser grows `hooks.post.install_steps` it still cannot
# express the fallback, so this block outlives that.
#
# Four things about the form below:
# - The probe is the *steps DSL*, not `respond_to?(:postflight_steps)`. The
# stanza landed in Homebrew 6.0.0 but the `run`/`on_macos` steps it needs
# only arrived in 6.0.13, so the obvious probe would pick the new branch
# on 6.0.0–6.0.12 and then die inside the block.
# - `{{ "{{staged_path}}" }}` renders as the literal `{{staged_path}}`.
# GoReleaser runs the whole generated cask through its template engine as
# a final pass, so the Homebrew token has to survive it escaped; Homebrew
# expands it in step args at install time. The steps DSL has no Ruby
# interpolation — that was the point of deprecating `postflight` — which
# is why the two branches spell the same path differently.
# - `run` is the steps DSL's escape hatch for a command; the sandbox it
# runs under already grants write access to the caskroom, which is where
# `staged_path` lives, so no `writable_paths` is needed.
# - `custom_block` is injected right after `cask "koc" do`, so the stanza
# order in the generated file is unconventional. It is still correct:
# Homebrew orders artifacts by class (Binary, then PostflightSteps — see
# Cask::Artifact::AbstractArtifact.sort_order), never by file position,
# so this runs after the binary is staged and linked.
custom_block: |
# Homebrew >= 6.0.13 takes the declarative steps; older clients keep the
# `postflight` block, which is only deprecated from 6.0.16 on.
steps_dsl = defined?(Homebrew::InstallSteps::DSL) && Homebrew::InstallSteps::DSL
if steps_dsl && steps_dsl.method_defined?(:run) && steps_dsl.method_defined?(:on_macos)
postflight_steps do
on_macos do
run "/usr/bin/xattr",
args: ["-dr", "com.apple.quarantine", "{{ "{{staged_path}}" }}/koc"]
end
end
else
postflight do
if OS.mac?
system_command "/usr/bin/xattr",
args: ["-dr", "com.apple.quarantine", "#{staged_path}/koc"]
end
end
end
28 changes: 28 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,34 @@ completions`**, run in its own secret-free step before GoReleaser. Locally, run
`make completions` before `goreleaser release`/`build` or the archives will be
missing `completions/`.

**The cask's quarantine step is a `custom_block`, not a GoReleaser hook.** The
binary is unsigned, so the cask has to strip `com.apple.quarantine` from the
staged `koc` or macOS refuses to launch it. GoReleaser emits
`homebrew_casks.hooks.post.install` as Homebrew's `postflight do … end`, which
Homebrew 6.0.16 deprecated in favour of the declarative `postflight_steps` —
every `brew` command touching the cask printed a deprecation warning, and
Homebrew deprecations become errors a few releases later. GoReleaser cannot emit
the `*_steps` stanzas yet (goreleaser/goreleaser#6870, PR #6873, milestone
v2.19.0) and has no way to emit *both* spellings, so `.goreleaser.yaml` writes
the stanza itself through `custom_block`: the cask probes at load time and picks
`postflight_steps` on Homebrew >= 6.0.13, `postflight` below that, so it installs
on every Homebrew and warns on none.

The three Homebrew versions that matter, because they are three releases apart
and the obvious probe gets it wrong: the `postflight_steps` **stanza** landed in
6.0.0, the `run`/`on_macos` **steps** the block actually calls landed in 6.0.13,
and `postflight` was **deprecated** in 6.0.16. So the probe asks
`Homebrew::InstallSteps::DSL.method_defined?(:run)`, not
`respond_to?(:postflight_steps)` — the latter is true on 6.0.0–6.0.12, where the
block body would then die. Two more things, both spelled out next to the block:
the Homebrew path token has to be escaped as `{{ "{{staged_path}}" }}` because
GoReleaser runs the whole generated cask through its own template engine as a
final pass (the legacy branch spells the same path `#{staged_path}`, since the
steps DSL has no Ruby interpolation); and `custom_block` lands right after
`cask "koc" do`, which is fine because Homebrew orders artifacts by class rather
than by file position. `hooks.post.install_steps`, when GoReleaser ships it,
still cannot express the fallback branch, so this block outlives it.

**Builds are byte-reproducible, and it takes more than one setting.** `builds:
mod_timestamp: {{ .CommitTimestamp }}` fixes the binary, but a tar/zip records
every *member's* mtime, so `LICENSE`, `README.md` and the generated completions
Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ brew install ftarasenko/tap/koc

No `--cask` flag is needed — nothing else in the tap shares the name. The binary
is unsigned, so the cask strips the macOS quarantine flag on install; on Apple
Silicon Go already ad-hoc-signs the binary so it runs.
Silicon Go already ad-hoc-signs the binary so it runs. That step picks its own
spelling at load time — Homebrew's declarative `postflight_steps` on 6.0.13 and
newer, the older `postflight` block below that — so the cask installs on any
Homebrew version and prints a deprecation warning on none.

### Shell completion

Expand Down
Loading