How to cut a release of ModelsUsage and make brew install work. No
Apple Developer account involved — the build is ad-hoc signed and
recipients do a one-time Gatekeeper allow on first launch.
Everything goes through tools/build-app.sh.
# Personal use — runs on YOUR Mac after one right-click → Open.
./tools/build-app.sh # default: ad-hoc signed
# Share with someone — hand them the zip; they do the same dance once.
./tools/build-app.sh
ls build/ModelsUsage-*.zipbuild/ is gitignored; rebuilds are clean each time.
./tools/build-app.shSelf-signature via codesign --sign -. Hardened runtime stays on and
the bundle is tamper-evident, but it identifies no developer, so
recipients see "app from an unidentified developer" the first time and
need the First launch allow.
./tools/build-app.sh --skip-signNo signature at all — only useful as a build sanity check.
Version and bundle ID come from env vars or tools/.env (gitignored):
# tools/.env (optional)
BUNDLE_ID="dev.cato.ModelsUsage"Defaults if unset:
BUNDLE_ID = dev.local.ModelsUsageVERSION= latestv*git tag with thevstripped, falling back to0.1.0if there's no tagBUILD=git rev-list --count HEAD
Override on the command line for a one-off:
VERSION=0.3.0-rc1 ./tools/build-app.sh# 1. Pick a version (semver, no leading "v").
VERSION=0.1.0
# 2. Tag and push. build-app.sh reads `git describe --tags`, so tagging
# BEFORE building makes the bundle's CFBundleShortVersionString match
# the release name.
git tag v$VERSION
git push origin v$VERSION
# 3. Build the ad-hoc-signed bundle + companion zip. Produces:
# build/ModelsUsage.app
# build/ModelsUsage-$VERSION.zip
./tools/build-app.sh
# 4. Grab the checksum — Homebrew needs it.
shasum -a 256 build/ModelsUsage-$VERSION.zip
# 5. Publish the GitHub release and attach the zip.
gh release create v$VERSION \
build/ModelsUsage-$VERSION.zip \
--title "v$VERSION" \
--notes "ModelsUsage v$VERSION. Ad-hoc signed — first launch needs a Gatekeeper allow (see README)."Add --draft to review before going live, or --prerelease for
-rc/-beta builds (those bypass the git tag — pass VERSION on the
command line and skip step 2).
brew install --cask catokolas/tap/modelsusage works once a release is
on GitHub and a cask file exists in the tap. No submission to
homebrew-core is needed (and it would reject an ad-hoc signed,
un-notarized app anyway).
- A GitHub release with the
ModelsUsage-<version>.zipasset (the "Cut a release" recipe above). - A public tap repo named
homebrew-<name>. This already exists atcatokolas/homebrew-tap(reachable ascatokolas/tap— Homebrew strips thehomebrew-prefix). It already hosts themacspoonstweakscask; ModelsUsage just adds a second cask file alongside it. - A cask file
Casks/modelsusage.rbin that tap (below).
Create Casks/modelsusage.rb in catokolas/homebrew-tap:
cask "modelsusage" do
version "0.1.0"
sha256 "<paste shasum -a 256 of the release zip here>"
url "https://github.com/catokolas/ModelsUsage/releases/download/v#{version}/ModelsUsage-#{version}.zip"
name "Models Usage"
desc "Native macOS dashboard for AI coding-tool token usage (KIX, Claude Code, Codex)"
homepage "https://github.com/catokolas/ModelsUsage"
depends_on macos: :sonoma
app "ModelsUsage.app"
zap trash: [
"~/Library/Application Support/ModelsUsage",
"~/Library/Caches/ModelsUsage",
]
endNotes on the stanzas:
- No
depends_on cask: "hammerspoon"— unlike MacSpoonsTweaks, ModelsUsage reads the log files / KIX API directly and needs no external runtime. The KIX token lives in the Keychain (itemdev.local.ModelsUsage);zapcan't remove Keychain items, so users delete that manually if desired:security delete-generic-password -s dev.local.ModelsUsage -a kix-token. depends_on macos: :sonoma— mirrors the macOS 14 floor inPackage.swift.zap trash: […]— whatbrew uninstall --zapremoves alongside the app (the settings JSON and the per-source aggregate caches).- No
auto_updates true— no Sparkle; Homebrew is the update channel (brew upgrade --cask modelsusage). - Lowercase token + filename (
cask "modelsusage"/Casks/modelsusage.rb); the displaynamecan stay CamelCase.
Each new release only needs version + sha256 updated:
# In the ModelsUsage repo, right after `gh release create …`:
NEW_SHA=$(shasum -a 256 build/ModelsUsage-$VERSION.zip | cut -d' ' -f1)
echo "version: $VERSION sha256: $NEW_SHA"
# Then in catokolas/homebrew-tap, edit Casks/modelsusage.rb:
# - set `version "$VERSION"`
# - set `sha256 "$NEW_SHA"`
# commit + push.# Pre-flight audit. DON'T pass `--new` or `--strict` — those enforce
# homebrew-cask SUBMISSION rules (75+ GitHub stars, Apple notarization)
# that a personal tap with an ad-hoc signed app can't meet, and which
# don't block install.
brew audit --cask catokolas/tap/modelsusage
# Real install:
brew install --cask catokolas/tap/modelsusage
# Cleanup test:
brew uninstall --cask --zap modelsusageStuck quarantine flag (common with zips from email/browsers). Clear it:
xattr -dr com.apple.quarantine /Applications/ModelsUsage.appThen open as usual. brew install --cask normally strips this for you.
Ad-hoc signatures change on every build, so the Keychain stops trusting the new binary and re-prompts for the stored KIX token. Click Always Allow; it stays quiet until the next rebuild. The app only touches the Keychain when KIX is an active source.
Keep tools/Entitlements.plist minimal (<dict/> only) — Apple's
strict plist parser rejects some XML in the entitlements dict.
It looks under .build/apple/Products/Release/ (universal build) then
.build/release/. If both miss, the swift build -c release step
failed — run it manually and inspect.