Skip to content

feat: publish a Homebrew cask and make update brew-aware - #35

Merged
vyncint merged 2 commits into
mainfrom
feat/homebrew-tap
Aug 8, 2026
Merged

feat: publish a Homebrew cask and make update brew-aware#35
vyncint merged 2 commits into
mainfrom
feat/homebrew-tap

Conversation

@vyncint

@vyncint vyncint commented Aug 8, 2026

Copy link
Copy Markdown
Owner

What

brew install nvidia/openshell/openshell vyncint/tap/openshell-driver-applecontainer now works. Every release publishes a cask to vyncint/homebrew-tap.

A cask, not a formula: Homebrew treats pre-built binaries that way, and a cask can strip the quarantine bit our unsigned binary would otherwise be blocked by. brews: is deprecated in GoReleaser for exactly this reason.

The cask:

  • depends_on arch: :arm64 — an Intel Mac gets a clear message instead of "no such artifact"
  • strips quarantine in a postflight hook
  • uninstall launchctl: unloads the driver's launchd agent before its binary disappears
  • zap trash: removes driver state, plist and log
  • skip_upload: auto keeps pre-releases off the tap, so brew upgrade never moves anyone onto one
  • declares nvidia/openshell/openshell as a dependency — but see below

apple/container ships as a signed .pkg outside Homebrew and cannot be a dependency at all — it stays a caveat. install.sh remains the only path that installs everything.

Why OpenShell is named on the command line

Homebrew auto-taps only the names it is given, never a dependency's. Probed with a throwaway cask depending on an untapped third-party formula:

Warning: No available formula with the name "hashicorp/tap/terraform".
This command requires the tap hashicorp/tap.

It warns and continues — so on a host without the nvidia/openshell tap, depends_on formula: would install the driver with no gateway. Naming both taps in one brew install works on any host, and the declaration still earns its keep once the tap exists (brew installs the gateway with the driver, and warns before removing it out from under one).

Publishing

The workflow's GITHUB_TOKEN is scoped to this repository and cannot commit to the tap. Rather than a PAT, the tap is pushed over SSH with a deploy key that has write access to vyncint/homebrew-tap and nothing else — narrower than any PAT, and revocable from the tap repo alone. The workflow stages it from the HOMEBREW_TAP_DEPLOY_KEY secret into $RUNNER_TEMP and removes it in an always() step.

Two ways the install paths corrupted each other

Both found by testing, both fixed here:

  1. update on a Homebrew install. It resolves symlinks to find the running binary, which for a cask is Caskroom/<token>/<version>/<binary>. Replacing it in place left Homebrew convinced it still had the version it staged, so a later brew upgrade/uninstall acted on the wrong files. It now detects a cask install, delegates to brew upgrade --cask, and re-runs setup through the Homebrew symlink — the resolved path is gone once brew swaps the version directory. --version is rejected there, since a cask only tracks the tap's latest.

  2. install.sh over a Homebrew install. install(1) replaces a symlink rather than writing through it, so this left brew owning a binary that was no longer on PATH and brew upgrade silently stopped taking effect. It now detects the cask symlink and explains both ways out.

Verification

Built the cask with goreleaser release --snapshot, served it from a local tap, and exercised the whole lifecycle on this machine:

  • brew install --cask → links /opt/homebrew/bin/… → Caskroom, quarantine gone, binary runs
  • brew uninstall --cask → unloads local.openshell-driver-applecontainer, removes the plist, unlinks the binary
  • installing over an existing install.sh binary → Error: It seems there is already a Binary at …, aborts without touching it (hence the migration steps now in the README)
  • install(1) over a symlink → replaces it rather than writing through, confirming the second bug above
  • brew style clean for everything the config controls; the rest is GoReleaser's template

Also make lint, make sec, go test ./..., shellcheck install.sh, goreleaser check — all clean. The machine was restored to its pre-test state and re-setup afterwards.

Follow-up

The cask only reaches the tap on the next tag, so brew install starts working with v0.2.8.

vyncint added 2 commits August 8, 2026 19:47
Add `brew install vyncint/tap/openshell-driver-applecontainer`. GoReleaser
publishes a cask — not a formula — to vyncint/homebrew-tap on every release:
Homebrew treats pre-built binaries as casks, and a cask can strip the
quarantine bit our unsigned binary would otherwise be blocked by.

The cask declares OpenShell as a dependency so brew installs the gateway too,
unloads the driver's launchd agent on uninstall, and removes driver state on
--zap. apple/container ships as a signed .pkg outside Homebrew and stays a
caveat; install.sh remains the only path that installs everything.

The workflow's GITHUB_TOKEN is scoped to this repository, so the tap is pushed
over SSH with a deploy key that has write access to the tap and nothing else.

Also fix two ways the two install paths corrupted each other:

- `update` resolves symlinks to locate the running binary, which for a cask
  points inside Caskroom/<token>/<version>/. Replacing it there left Homebrew
  convinced it still had the version it staged. It now delegates to
  `brew upgrade --cask` and re-runs setup through the Homebrew symlink, and
  rejects --version, which a cask cannot honour.
- install.sh would replace brew's symlink with a real file — install(1)
  does not write through symlinks — leaving brew owning a binary no longer on
  PATH. It now detects a cask install and explains both ways out.

Verified against a local tap: install links and runs unquarantined, uninstall
unloads the agent and removes the plist, and installing over an install.sh
binary fails cleanly without touching it.

Signed-off-by: Vyncint Ng <vyncint@users.noreply.github.com>
Homebrew auto-taps only the names given on the command line, never a
dependency's, so on a host without the nvidia/openshell tap the cask's
`depends_on formula:` warns and resolves to nothing — the driver installs
without its gateway. Verified with a probe cask against an untapped
third-party formula.

Keep the declaration, which is correct and useful once the tap exists, but
document the install as `brew install nvidia/openshell/openshell
vyncint/tap/openshell-driver-applecontainer` — still one command, and it works
on any host because both names are auto-tapped. Caveats say the same for anyone
who installs the cask alone.

Signed-off-by: Vyncint Ng <vyncint@users.noreply.github.com>
@vyncint
vyncint merged commit 30036e9 into main Aug 8, 2026
22 checks passed
@vyncint
vyncint deleted the feat/homebrew-tap branch August 8, 2026 12:54
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.

1 participant