grew

A lean, mean, package-managing machine. In Go.

Go 1.26+

grew is what happens when you look at your package manager and think: "This could be so much simpler." Deterministic installs. Clean symlinks. A doctor that actually tells you what's wrong. No drama.

πŸ’¬ A word from the author:

"I've been a die-hard Homebrew user for longer than I care to admit. brew and I? We go way back. Late nights, broken PATH, the works β€” and I loved every minute of it. I love brew so much, in fact, that I thought: 'What if I just… made it better?' Audacious? Absolutely. Foolish? Possibly. Fun? You bet. grew is my love letter to brew β€” written in Go, with a cheeky grin."

✨ What it does

πŸ“¦

Formula + cask installs with SHA256 verification (no funny business)

✍️

Ed25519 bottle signing β€” cryptographic signatures verified against a local trust store

πŸ”’

Sandboxed builds & post-install β€” network denied, keg read-only for post-install, minimal env

πŸ“‹

Install snapshots β€” per-file SHA256 manifests for tamper detection via grew verify

πŸ“Œ

Lockfile β€” pin exact versions, hashes, and dependency trees for reproducible environments

πŸ”—

Deterministic linking with opt symlinks and dry-run support (look before you link)

🌳

Dependency resolver with an optional tree view (for the visually inclined)

🩺

Doctor + audit β€” checks perms, HTTPS, broken links, snapshot integrity, cask notarization, and formula quality

πŸ›‘οΈ

Hardened command execution β€” POSIX shell quoting via shellescape, -- end-of-options on all external commands, XML-safe plists, systemd specifier escaping

🧱

Zip Slip protection β€” archive extraction validates symlink indirection to prevent writes outside the destination directory

🐚

Services, aliases, shellenv β€” manage daemons, name things your way, wire up your shell

πŸš€ Getting Started

1. Get grew

The fastest way β€” downloads the latest release binary with SHA256 verification:

go install github.com/homegrew/grew/tools/getgrew@latest
getgrew
sudo grew setup

Or build from source

Prerequisites: Go 1.26+, git, and a dream.

git clone https://github.com/homegrew/grew.git
cd grew
make build          # or: go generate ./internal/... && go build -o grew

2. Set up the prefix

grew needs a home β€” a directory tree for the Cellar, symlinks, taps, and config:

# User-local install (no root needed) β€” installs to ~/.grew
./grew setup

# System install (recommended β€” isolates builds from $HOME)
sudo grew setup   # macOS ARM β†’ /opt/grew, Intel/Linux β†’ /usr/local/grew

The system prefix is more secure: sandboxed source builds can't reach ~/.ssh, ~/.gnupg, or other sensitive dotfiles.

3. Wire up your shell

# bash (~/.bashrc) or zsh (~/.zshrc)
eval "$(grew shellenv)"

# fish (~/.config/fish/config.fish)
grew shellenv fish | source

4. Install something

grew install jq                # the classic
grew install -s ldns           # build from source, like a purist
grew install --cask firefox    # going big

That's it. No dark rituals. No 47-step setup guide.

πŸ“– Day-to-day usage

grew link jq                   # stitch it in
grew deps --tree jq            # what hath jq wrought
grew upgrade                   # stay fresh
grew cleanup -n                # peek before you sweep
grew verify jq                 # check installed files against manifest
grew lock                      # pin your environment
grew audit --strict            # lint your formulas
grew doctor                    # check for common problems

πŸ—ΊοΈ Commands

Command What it does
installInstall a formula or cask (-s to build from source)
uninstallSend it to the void
reinstallUninstall + install from scratch
listSee what you've collected
infoStalk a package
searchFind the thing
linkWeave a formula into your PATH
unlinkCut the thread
updateRefresh tap definitions
upgradeGet the new hotness
outdatedThe hall of shame
cleanupMarie Kondo your Cellar
depsDependency spelunking
verifyCheck installed packages against their snapshot manifests
auditLint formula/cask definitions for quality and security
lockGenerate, check, or show a reproducible lockfile
signSign formula SHA256 hashes with an Ed25519 key
servicesManage background services (start, stop, restart, list)
setupOne-time prefix setup (user-local or system-wide with sudo)
aliasName things your way
doctorIt's not a bug, it's a misconfiguration
configWhat grew thinks it knows
shellenvWire up your shell
helpYou got this

βš™οΈ Configuration

grew keeps its stuff tidy under one roof. Tweak it with env vars:

Variable Default What it is
HOMEGREW_PREFIX~/.grewThe kingdom
HOMEGREW_APPDIR~/ApplicationsWhere casks live
HOMEGREW_TAP_VERIFYoffTap commit signature policy (off, warn, strict)
HOMEGREW_NO_INSTALL_FROM_API(unset)Force git clone instead of API tarball for taps

Directory Structure

Everything flows from the prefix:

~/.grew/
β”œβ”€β”€ Cellar/        ← installed packages (each keg has a .MANIFEST.json)
β”œβ”€β”€ Taps/          ← formula definitions (git-cloned or API-fetched)
β”œβ”€β”€ bin/           ← symlinked binaries
β”œβ”€β”€ etc/           ← trusted-keys (Ed25519 public keys, one per line)
β”œβ”€β”€ tmp/           ← ephemeral stuff
└── grew.lock      ← lockfile (opt-in, created by `grew lock`)

πŸ” Security: grew vs Homebrew

grew is designed to be more secure than Homebrew out of the box:

Feature grew Homebrew
Bottle signingEd25519 signatures verified against local trust storeNone β€” HTTPS + SHA256 only
Tap verificationOptional GPG/SSH commit signature enforcementNone
Post-install sandboxRead-only keg, no network, minimal envUnsandboxed
Source build sandboxmacOS Seatbelt / Linux bwrap+unshare, no networkmacOS Seatbelt only, no Linux
Install manifestsPer-file SHA256 snapshot at install timeNone
LockfileFull dependency tree with hashesNone
Integrity checkgrew verify + grew doctor snapshot checkNone
HTTPS enforcementAt parse time β€” HTTP URLs rejected before downloadAt download time
Path traversal protectionValidated at cellar, linker, loader, and archive extraction layersPartial
Shell injection preventionPOSIX shell quoting via shellescape for sandbox scripts; systemd and launchd values properly escapedN/A
Zip Slip protectionSymlink indirection attacks blocked during tar/zip extractionPartial
Command argument hardening-- end-of-options on all external commands (git, systemctl, launchctl, hdiutil, tar, etc.)Not consistently applied

Gradual rollout: signature verification doesn't block installs until you add keys to etc/trusted-keys. Tap verification is opt-in via HOMEGREW_TAP_VERIFY. Adopt security features at your own pace.

πŸ› οΈ Development

make check  # go test -v -race ./...
make build  # go generate + go build
make lint   # golangci-lint

Project layout: packages under internal/ (cmd, cellar, formula, cask, linker, depgraph, downloader, tap, sandbox, signing, snapshot, lockfile, service, config, version) and pkg/ (validation).

🀝 Contributing

  1. Fork it
  2. Branch it (git checkout -b feature/cool-thing)
  3. Commit it
  4. Push it
  5. PR it

PRs welcome. Drama not so much.

πŸ“¬ Links & Info

Acknowledgments: Best-README-Template, and everyone who ever squinted at a wall of package manager output and thought "there has to be a better way".

License: (Add a LICENSE to your repo!)