
Homebrew Cask Authoring
- 112 installs
- 15 repo stars
- Updated August 1, 2026
- connorads/dotfiles
Authors and maintains Homebrew Casks with correct token naming, stanzas, sha256, livecheck, and brew audit/style compliance plus local install testing.
About
Creates, updates, validates, and submits Homebrew Casks with correct token naming, minimal stanzas, and audit/style compliance. A developer uses it when authoring or maintaining a cask and running brew style/audit.
- Enforces correct token naming, stanzas, and audit/style compliance
- Covers livecheck, zap/uninstall, sha256, and local install testing
Homebrew Cask Authoring by the numbers
- 112 all-time installs (skills.sh)
- Ranked #525 of 1,438 DevOps & CI/CD skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/connorads/dotfiles --skill homebrew-cask-authoringAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 112 |
|---|---|
| repo stars | ★ 15 |
| Last updated | August 1, 2026 |
| Repository | connorads/dotfiles ↗ |
What it does
Authors and maintains Homebrew Casks with correct token naming, stanzas, sha256, livecheck, and brew audit/style compliance plus local install testing.
Files
Homebrew Cask Authoring
Author and maintain Homebrew Casks with correct token naming, stanzas, audit/style compliance, and local install testing.
Operating rules
- Prefer the official Homebrew documentation (Cask Cookbook, Acceptable Casks) when uncertain.
- Keep casks minimal: only add stanzas that are required for correct install/uninstall/cleanup.
- Avoid destructive system changes unless explicitly requested; call out any
rm/tap changes before suggesting them. - When testing local casks, ensure Homebrew reads from the local file (not the API).
- Treat local Homebrew tap overrides as temporary. When done testing/submitting, restore standard Homebrew state unless the user asks to keep the override.
Quick intake (ask these first)
Collect:
- App name (exact
.appbundle name) - Homepage (official)
- Download URL(s) (DMG/ZIP/PKG) and whether they differ by arch
- Version scheme (single version? per-arch?)
- Install artifact type (
app,pkg,suite, etc.) - Uninstall requirements (pkgutil ids, launch agents, kernel extensions)
- Desired cleanup (zap paths)
If any of these are unknown, propose a short plan to discover them.
Pre-flight checks (before writing the cask)
Before investing effort in a new cask, verify:
1. Notability: The app must have meaningful public presence. GitHub projects with <30 forks/watchers or <75 stars are likely to be rejected. Self-submission threshold is 3× higher (90 forks / 90 watchers / 225 stars) if the PR author also owns the upstream repo. See Acceptable Casks. 2. Repo age: GitHub repos less than 30 days old cause a hard brew audit --new failure. Wait until the repo is old enough. 3. Previously refused: Search closed unmerged PRs for the token. If previously rejected for unfixable reasons, do not re-submit. 4. Existing PRs: Check open PRs to avoid duplicating work. 5. Modern macOS compatibility: Casks that don't work on current macOS will be rejected outright. Avoid submitting x86-only / requires_rosetta new casks — they're on a deprecation path (blocked once macOS 27 is stable, removed after 28).
Workflow: create or update a cask
1) Choose the token
- Start from the
.appbundle name. - Remove
.appand common suffixes: "App", "for macOS", version numbers. - Remove "Mac" unless it distinguishes the product (e.g., "WinZip Mac" vs "WinZip").
- Drop "Desktop" by default — reviewers want the bare name. Maintainer guidance: "It should be ok to use
executorfor the token here, if the CLI is added tohomebrew-corelater it can useexecutor-cli." The bare name goes to whichever component lands in Homebrew first; subsequent siblings disambiguate (-cli,-cloud, etc.). - Only keep "Desktop" when:
- It's part of the actual product brand (e.g.,
Docker Desktop→docker-desktop,LTX Desktop→ltx-desktop), or - An upstream sibling component (CLI, cloud variant) already exists in Homebrew (formula or cask) under the bare name.
- A bare-named CLI that exists only upstream (npm, crates.io, etc.) and isn't yet packaged for Homebrew is not a reason to keep "Desktop" — submit the cask under the bare name and let the CLI take a suffix if/when it's added.
- The
cask token mentions desktopaudit cop isstrict_only(fires under--new); reviewers accept the suffix when justified by the rules above. Existing-desktopcasks (aks-desktop,grammarly-desktop,firefly-iota-desktop) don't validate the suffix as a generic pattern — check why each was named that way before citing them. Justify the choice in the PR description either way. - Downcase; replace spaces/underscores with hyphens.
- Remove non-alphanumerics except hyphens.
- Use
@beta,@nightly, or@<major>for variants.
Confirm the token before writing the file.
2) Draft a minimal cask
Use this canonical structure:
cask "token" do
version "1.2.3"
sha256 "..."
url "https://example.com/app-#{version}.dmg"
name "Official App Name"
desc "Short one-line description"
homepage "https://example.com"
app "AppName.app"
endRules of thumb:
- Prefer
httpsURLs. - Add
verified:when download host domain differs fromhomepagedomain. - Keep
descfactual and concise (no marketing).
3) Handle architecture (if needed)
Always confirm the binary's architectures — don't assume from vendor marketing. Mount the DMG (or unpack the artifact) and run:
lipo -archs "/Volumes/<Vol>/<AppName>.app/Contents/MacOS/<AppName>"Then:
- Single-arch (`arm64` only): add
depends_on arch: :arm64alongside anymacos:gate. Without it, Intel users on a supported macOS can install a cask they can't run — a user-facing install-time regression reviewers will flag. - Universal (`arm64 x86_64`): no arch gate needed.
- Different URLs and/or sha256 per CPU: use
arch+sha256 arm: ..., intel: ...when versions match. - Different versions per CPU: use
on_arm/on_intelblocks.
4) Add uninstall/zap stanzas
- `uninstall`: Required for
pkgandinstallerartifacts. Includepkgutil:identifiers, launch agents, etc. - For
.appcasks,uninstall quit:is still useful sobrew uninstallcleanly terminates a running app. If the app bundles helper processes (look inContents/Helpers/or runpgrep -lf <AppName>while it's running), pass an array of bundle IDs (e.g. main app +*.launcher) — a single ID leaves helpers stranded. - `quit:` / `signal:` no longer run during `brew upgrade`/`brew reinstall` by default (Nov 2025 change). If you need the app to be quit during upgrade, add
on_upgrade: :quit(oron_upgrade: [:quit, :signal]). - `zap`: Recommended for thorough cleanup (support dirs, preferences, caches) but not enforced by
brew audit. Reviewers expect it for new casks — verify paths are accurate. - Primary tool: `brew generate-zap <token>` (documented Mar 2026). Install and launch the app first, then run it to get a draft zap stanza. Still review the output — it can include noise. If it aborts after only printing the "Scanning" line, it likely hit a TCC permission error (e.g.
Operation not permitted @ dir_initialize - .../sharedfilelist/...); exit code can still look fine. Fall back tofind ~/Library -name "*<bundle-id>*"plus a manual sweep of the standard Electron locations (Application Support,Caches,HTTPStorages,Logs,Preferences,Saved Application State). - *Also scan while the app is in active use, not just after first launch.* Paths like
~/Library/HTTPStorages/<bundle-id>, session caches, and some preferences only appear after login/real interaction.generate-zapmay miss these if you only ran the app once. - If state still survives `--zap` + reinstall, scan outside `~/Library/`. Apps that bundle a Node CLI/server (
Contents/Resources/sidecar/) often persist to dotfolders (~/.<appname>) and XDG paths (~/.local/share/<appname>) —generate-zapand bundle-ID scans don't cover these. Cloning the upstream repo and grepping foros.homedir()/env-pathswill surface them. - Keep Keystone/GoogleUpdater-style shared components in
zaponly (neveruninstall) — they're shared across vendor apps. - `livecheck`:
strategy :extract_plistandversion :latestare automatically excluded from autobump — nono_autobump!needed. - `depends_on`: Optional. Only add when genuinely needed (e.g., specific macOS version, another cask dependency).
5) Validate and test locally
Run, in this order:
brew style --fix <token>
brew audit --cask --online <token>For new casks also run:
brew audit --cask --new <token>
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <token>
brew uninstall --cask <token>Then validate the full zap path with the app running:
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <token>
open /Applications/<AppName>.app # log in, use it
HOMEBREW_NO_INSTALL_FROM_API=1 brew uninstall --zap --cask <token>
pgrep -lf <AppName> # should be empty — if not, add bundle IDs to `uninstall quit:`Reinstalling after a plain brew uninstall (without --zap) should leave session data intact (so login persists). Reinstalling after --zap should require a fresh login. Verifying both confirms the zap paths are actually the ones that hold user state.
Important notes:
- Always install/uninstall by token name, not file path. Running
brew install ./Casks/t/token.rbwill fail when using a tap symlink — usebrew install --cask tokeninstead. HOMEBREW_NO_INSTALL_FROM_API=1forces Homebrew to use your local cask file rather than the API.brew audit --cask --newchecks GitHub repo age (must be >30 days) and notability — if the repo is too new, this will fail regardless of cask quality.brew auditprints nothing on success (silent = pass) — don't mistake empty output for the command failing to run.- When iterating on the `zap` stanza, reinstall before re-zapping.
brew uninstall --zapreads the cached cask from/opt/homebrew/Caskroom/<token>/.metadata/<version>/..., not your working copy. After editing zap paths:brew uninstall→brew install(refreshes the cached metadata) →brew uninstall --zap. The==> Trashing files:log will silently use the previous stanza otherwise.
If install fails:
- Re-check URL reachability,
sha256, and artifact name. - Re-run with verbosity:
brew install --cask --verbose <token>.
6) PR hygiene
Before suggesting submission:
- Ensure
brew styleand all relevantbrew auditcommands pass. - For new casks, check the token has not been previously refused/unmerged.
- One cask change per PR, minimal diffs, no drive-by formatting.
- Target the
mainbranch (notmaster).
Commit message format (first line <=50 chars):
- New cask:
token version (new cask) - Version update:
token version - Fix/change:
token: description
PR body: keep the default template, then replace the placeholder opener with a short prose sentence (hint: a bare URL as the first line may trigger the request-info bot — a full sentence like "Adds a new cask for App Name - short description." is safer). Keep all checklist items; tick only what was actually done.
7) AI disclosure
The PR template includes an AI disclosure section. If AI assisted with the PR:
- Check the AI checkbox in the template.
- Split the disclosure into two parts: what the agent ran (list the
brewcommands executed and note the human read the output) and what the human verified manually (app install, login, actual usage, zap path derivation, running-app uninstall). Reviewers value seeing both halves. - Call out any non-obvious things the agent's testing surfaced (e.g. a helper process needing a second bundle ID in
uninstall quit:).
Local development patterns
If the user is editing Homebrew/homebrew-cask locally and wants Homebrew to execute their working copy, use a tap symlink workflow.
Before changing the tap, print the current Homebrew state/commands so the restore path is visible in-context.
When the task is done (typically after local validation, commit, or PR creation), restore standard Homebrew state unless the user asks to keep the local override. Prompt before leaving Homebrew in a non-standard state.
Read the full end-to-end checklist here:
references/homebrew-cask-contribution-workflow.md
Homebrew Cask Contribution Workflow
A complete guide for contributing casks to Homebrew, covering local testing, validation, and submission.
Prerequisites
- Homebrew installed
- Git configured
- Forked
Homebrew/homebrew-caskrepository on GitHub
Setup: Link Your Local Repository
This override is temporary. Before changing anything, print the current Homebrew tap state/commands so the restore path is visible in-context. When testing is done, restore standard Homebrew state unless the user explicitly asks to keep the override.
Initial Setup
If you have the homebrew-cask repository checked out locally, make Homebrew use your working copy for testing:
# 1. Untap the official cask tap
brew untap homebrew/cask
# 2. Symlink your git checkout to Homebrew's tap location
ln -s ~/path/to/your/homebrew-cask $(brew --repository)/Library/Taps/homebrew/homebrew-cask
# Verify it worked
ls -la $(brew --repository)/Library/Taps/homebrew/Now any changes you make in your git repo are immediately live for Homebrew commands.
Restore Official Tap (After Testing)
Restore when local validation/submission is done, usually after commit or PR creation. If you are about to leave Homebrew in a non-standard state, prompt first.
# Remove your symlink
rm $(brew --repository)/Library/Taps/homebrew/homebrew-cask
# Re-add official tap
brew tap homebrew/caskCreating a New Cask
Pre-flight Checks
Before creating a new cask, verify the app meets Homebrew's acceptance criteria:
1. Notability: GitHub projects with <30 forks/watchers or <75 stars are likely rejected. The app must have meaningful public presence beyond "just brew install". See Acceptable Casks. 2. Repo age: GitHub repos less than 30 days old cause a hard brew audit --new failure. 3. Previously refused: Search closed unmerged PRs for the token. 4. Existing PRs: Check open PRs to avoid duplicates.
1. Determine the Token
The token is the unique identifier for your cask. Follow these rules:
From app name to token:
- Start with app bundle name (e.g.,
Google Chrome.app) - Remove
.appextension - Remove suffixes: "App", version numbers, "for macOS"
- Remove "Mac" unless it distinguishes the product
- Remove "Desktop" only when it's a generic suffix — keep it when intrinsic to the product name (e.g.,
Docker Desktop.app→docker-desktop,LTX Desktop.app→ltx-desktop). When in doubt, keep "Desktop". - Convert to lowercase
- Replace spaces/underscores with hyphens
- Remove non-alphanumeric characters (except hyphens)
Examples:
Google Chrome.app→google-chromeVLC Media Player.app→vlcSublime Text 2.app→sublime-textDocker Desktop.app→docker-desktop
Special cases:
- Beta/nightly:
app-name@beta,app-name@nightly - Version-specific:
app-name@5
2. Create the Cask File
cd ~/path/to/your/homebrew-cask/Casks
# Determine the subdirectory (first letter/number of token)
# For token "my-app", create in: m/my-app.rb
# For token "1password", create in: 1/1password.rb
# Create the cask file
vim <first-char>/<token>.rb3. Write the Cask Definition
Required stanzas (in canonical order):
cask "token-name" do
version "1.2.3"
sha256 "abc123..." # Get with: shasum -a 256 <downloaded-file>
url "https://example.com/app-#{version}.dmg"
name "Official App Name"
desc "Brief one-line description of what it does"
homepage "https://example.com"
app "AppName.app"
endEvery cask must have: version, sha256, url, name, desc, homepage, and at least one artifact stanza (app, pkg, installer, suite, etc.).
Canonical stanza order:
version → sha256 → url → name → desc → homepage → livecheck → auto_updates → depends_on → artifacts → uninstall → zap
Run brew style --fix <token> to auto-correct ordering.
Key points:
version: Use interpolation (#{version}) in URL when possiblesha256: Calculate withshasum -a 256 <file>url: HTTPS preferred; addverified:if domain differs from homepagedesc: Concise, no marketing fluff, start with capital lettername: Full official name with proper capitalization
Common optional stanzas:
depends_on macos:- OS requirements (only when genuinely needed)depends_on cask:- Other required casks (only when genuinely needed)livecheck- Version checking automationuninstall- Required forpkg/installerartifacts; optional otherwisezap- Thorough cleanup (user files, preferences, caches). Recommended for new casks but not enforced bybrew audit. Reviewers expect accurate paths — verify them manually.
4. Handle Different Architectures
If the app has separate downloads for Apple Silicon and Intel:
cask "app-name" do
arch arm: "aarch64", intel: "x86-64"
version "1.2.3"
sha256 arm: "abc123...",
intel: "def456..."
url "https://example.com/app-#{version}-#{arch}.dmg"
# ...
endIf versions differ by architecture:
cask "app-name" do
arch arm: "arm64", intel: "x86_64"
on_arm do
version "1.2.3"
sha256 "abc123..."
end
on_intel do
version "1.2.2"
sha256 "def456..."
end
url "https://example.com/app-#{version}-#{arch}.dmg"
# ...
endValidation Checklist
Follow the PR template requirements exactly:
For All Casks (New or Updated)
# 1. Fix code style
brew style --fix <token>
# 2. Run audit (checks structure, URLs, naming)
brew audit --cask --online <token>Both commands must pass with no errors before proceeding.
For New Casks Only
# 1. Check token follows naming rules
# Manually verify against docs: https://docs.brew.sh/Cask-Cookbook#token-reference
# 2. Check cask wasn't previously refused
# Search: https://github.com/Homebrew/homebrew-cask/pulls?q=is%3Apr+is%3Aclosed+is%3Aunmerged+<token>
# 3. Run new cask audit
brew audit --cask --new <token>
# 4. Test installation — always use TOKEN, never file path
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <token>
# 5. Verify the app works
open /Applications/AppName.app
# 6. Test uninstallation
brew uninstall --cask <token>
# 7. Verify cleanup
ls /Applications/ | grep AppName # Should return nothingImportant: Always install by token name (e.g., brew install --cask my-app), never by file path (e.g., ./Casks/m/my-app.rb). File path installs fail when using the tap symlink workflow.
Common audit issues:
- Missing
verified:when URL domain ≠ homepage domain - Description too long (>80 chars) or contains marketing fluff
- Token doesn't follow naming conventions
- SHA256 mismatch
- GitHub repo less than 30 days old (hard failure for
--newaudit) - App doesn't meet notability thresholds (<30 forks/watchers or <75 stars)
Testing Tips
Dry Run (See What Would Happen)
brew install --cask --dry-run <token>Force Reinstall (After Changes)
brew reinstall --cask <token>Test Uninstall with Zap
brew uninstall --cask --zap <token>Check What Files Were Installed
# For pkg-based casks
pkgutil --files <bundle.id>
# For app-based casks
ls -la /Applications/AppName.appCommon Cask Patterns
App with Binary
app "MyApp.app"
binary "#{appdir}/MyApp.app/Contents/MacOS/mytool"PKG Installer
pkg "Installer.pkg"
uninstall pkgutil: "com.vendor.app.*"Suite (Multiple Apps)
suite "AppSuite" # Directory containing multiple .app bundlesWith Dependencies
depends_on macos: ">= :monterey"
depends_on cask: "other-required-app"Livecheck (Version Auto-detection)
livecheck do
url "https://example.com/releases"
strategy :sparkle
endSubmitting Your Contribution
1. Commit Your Changes
cd ~/path/to/your/homebrew-cask
# Check what you've changed
git status
git diff
# Stage the new/modified cask
git add Casks/<letter>/<token>.rb
# Commit with correct message format (first line <=50 chars)
# New cask: "token version (new cask)"
# Version update: "token version"
# Fix/change: "token: description"
git commit -m "my-app 1.0.0 (new cask)"2. Push to Your Fork
git push origin <your-branch-name>3. Create Pull Request
Target the main branch (not master):
gh pr create --base main --title "my-app 1.0.0 (new cask)" --body-file - <<'EOF'
Built and tested locally on macOS [version].
[One sentence if not obvious from title.]
EOFOr via the GitHub web UI — fill in the PR template with:
- Brief description
- Checkboxes ticked ONLY if you completed each step
- AI disclosure (see below)
4. AI Disclosure
The PR template includes an AI disclosure section. If AI assisted with the PR:
- Check the AI checkbox in the template.
- Briefly describe how AI was used.
- Confirm that all changes were personally reviewed, tested, and verified — especially
zapstanza paths.
5. Respond to Review
Maintainers may request changes. To update:
# Make requested changes
vim Casks/<letter>/<token>.rb
# Re-run validation
brew style --fix <token>
brew audit --cask --online <token>
# Test again
brew reinstall --cask <token>
# Commit and push (do not squash after opening PR)
git add Casks/<letter>/<token>.rb
git commit -m "Address review feedback: <what you changed>"
git push origin <your-branch-name>Troubleshooting
"Cask not found"
Ensure you're using HOMEBREW_NO_INSTALL_FROM_API=1 to force local file usage:
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <token>File Path Install Fails
Do not install by file path (e.g., brew install ./Casks/t/token.rb). This fails with the tap symlink workflow. Always use the token name:
brew install --cask <token>Symlink Issues
Verify your symlink:
ls -la $(brew --repository)/Library/Taps/homebrew/homebrew-cask
# Should point to your git repoAudit Failures
Common fixes:
brew style --fix <token>for formatting- Check
verified:parameter if URL/homepage domains differ - Ensure
descis concise (<80 chars) - Verify SHA256:
shasum -a 256 <file> - GitHub repo <30 days old: wait until the repo ages past 30 days
- Notability thresholds not met: check Acceptable Casks criteria
Installation Failures
- Check the actual error message carefully
- Verify the download URL works in browser
- Test with
--verboseflag:brew install --cask --verbose <token> - Check if app requires specific macOS version
Quick Reference
Essential Commands:
# Setup
brew untap homebrew/cask
ln -s ~/homebrew-cask $(brew --repository)/Library/Taps/homebrew/homebrew-cask
# Validation
brew style --fix <token>
brew audit --cask --online <token>
brew audit --cask --new <token> # New casks only
# Testing (always use token, never file path)
HOMEBREW_NO_INSTALL_FROM_API=1 brew install --cask <token>
brew uninstall --cask <token>
brew reinstall --cask <token>
# Cleanup
rm $(brew --repository)/Library/Taps/homebrew/homebrew-cask
brew tap homebrew/caskFile Locations:
- Casks:
Casks/<first-char>/<token>.rb - Helper scripts:
developer/bin/
PR Target:
- Base branch:
main(notmaster)
Key Documentation:
- Token reference: https://docs.brew.sh/Cask-Cookbook#token-reference
- Acceptable casks: https://docs.brew.sh/Acceptable-Casks
- Full cookbook: https://docs.brew.sh/Cask-Cookbook