A Homebrew-compatible package manager, written in Rust.
Zapbrew installs Homebrew bottles without Ruby. It reads the same signed JSON
catalogs that brew reads, writes the same prefix layout, and reads a
documented set of Homebrew's HOMEBREW_* variables. It is a single binary with
no runtime dependency on a Homebrew checkout.
$ zapbrew install jq
==> Fetching oniguruma
==> Fetching jq
==> Pouring oniguruma--6.9.10.x86_64_linux.bottle.tar.gz
🍺 /tmp/zb-fresh/Cellar/oniguruma/6.9.10: 16 files, 1.7MB
==> Pouring jq--1.8.2.x86_64_linux.bottle.tar.gz
🍺 /tmp/zb-fresh/Cellar/jq/1.8.2: 21 files, 1.4MB- No Ruby. The formula and cask catalogs are fetched as JSON from
formulae.brew.shand verified as JWS signatures. Zapbrew never evaluates a Ruby formula. - Compatible on disk. Cellar, Caskroom,
opt,var/homebrew, tabs, and install receipts follow Homebrew's layout, andoptsymlinks point at the same keg paths. This is a byte-compatible layout, not a claim that a prefix can be safely shared withbrew— see Using it on an existing Homebrew prefix. - Compatible in the environment. Zapbrew reads a documented subset of
Homebrew's
HOMEBREW_*variables and defines none of its own. See configuration for the supported list. - Safety enforced by the build. The workspace sets
unsafe_code = "forbid"andclippy::unwrap_used = "deny"for every crate. - Tested. Per-crate unit tests, HTTP boundary tests against a local mock server, snapshot tests over user-visible output, and end-to-end command assertions.
- Rust 1.97.1. The version is pinned in
rust-toolchain.toml, sorustupselects it automatically. - Linux or macOS. Development and live verification for this repository run on
x86_64 Linux. macOS support is compile-checked for
aarch64-apple-darwinand covered by host-independent unit fixtures; it is not live-verified here. - Git, when you use
tapor update installed taps.
Build the binary:
git clone https://github.com/gosuda/Zapbrew
cd Zapbrew
cargo build --releaseQuery the catalog. No prefix is written and nothing is installed:
$ ./target/release/zapbrew info jq
==> jq: stable 1.8.2 (bottled)
Lightweight and flexible command-line JSON processor
https://jqlang.github.io/jq/
Not installed
From: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/j/jq.rb
License: MIT
==> Dependencies
Required (1): oniguruma
$ ./target/release/zapbrew search ripgrep
==> Formulae
ripgrep
ripgrep-allPoint the prefix, Cellar, and cache at one scratch location. Everything Zapbrew writes — the installation, downloads, and staging — lands inside it:
export HOMEBREW_PREFIX=/tmp/zb-demo
export HOMEBREW_CELLAR=$HOMEBREW_PREFIX/Cellar
export HOMEBREW_CACHE=$HOMEBREW_PREFIX/cache
mkdir -p "$HOMEBREW_CELLAR" "$HOMEBREW_CACHE"
./target/release/zapbrew install jq
"$HOMEBREW_PREFIX"/bin/jq --version # jq-1.8.2
./target/release/zapbrew list --versionsHOMEBREW_LOGS and HOMEBREW_TEMP are deliberately absent. Zapbrew writes no
log files, and it stages inside the Cellar rack and the cache rather than a
temporary directory, so those two variables are parsed and never read. Setting
them would suggest an isolation guarantee they do not provide — see
Accepted but not honored.
Setting HOMEBREW_PREFIX alone is normally enough, because the Cellar defaults
to $HOMEBREW_PREFIX/Cellar. Set HOMEBREW_CELLAR as well when your shell has
sourced brew shellenv, which exports HOMEBREW_CELLAR and would otherwise
override the new prefix.
To remove the sandbox, delete /tmp/zb-demo.
Zapbrew writes Homebrew's layout, so it can read an existing prefix and install
into it. That is a layout guarantee, not a concurrency guarantee: running both
managers against one prefix is outside what this documentation covers. When
relocation or linking fails mid-install, the transaction rolls back and reports
whatever it could not undo (see how it works), but
reconciling a partial keg against brew's own bookkeeping is left to you. Take
a backup before you first use it on a prefix you care about.
zapbrew shim manages an opt-in brew shim if you want existing scripts to
call Zapbrew. It is off by default.
Shell completions are generated at runtime. Each block writes to a user-writable location and needs no root.
bash:
mkdir -p ~/.local/share/bash-completion/completions
zapbrew completions bash > ~/.local/share/bash-completion/completions/zapbrewzsh — the directory must be on fpath before compinit runs:
mkdir -p ~/.local/share/zsh/site-functions
zapbrew completions zsh > ~/.local/share/zsh/site-functions/_zapbrew
# then in ~/.zshrc, before compinit:
# fpath+=("$HOME/.local/share/zsh/site-functions")fish:
mkdir -p ~/.config/fish/completions
zapbrew completions fish > ~/.config/fish/completions/zapbrew.fishZapbrew is a package consumer. It has no formula-authoring surface.
- No source builds.
--build-from-sourceis refused:zapbrew cannot build from source: formulae are Ruby definitions. Use bottles (default) or brew.A formula with no bottle for your platform cannot be installed. - No formula authoring.
edit,create,audit,bottle,test, andbumpdo not exist. Invoking them is an unrecognized-subcommand error. - No Ruby DSL, no tap formula evaluation. Taps are cloned and inspected, but formulae are resolved from the JSON API.
Use brew for any of the above.
30 commands. Full flags for each are in the command reference.
| Area | Commands |
|---|---|
| Install and remove | install, reinstall, uninstall, upgrade, autoremove |
| Inspect | info, list, outdated, deps, uses, leaves, search, desc |
| Link and pin | link, unlink, pin, unpin |
| Cache and cleanup | fetch, cleanup |
| Taps | tap, untap, tap-info |
| System | config, doctor, shellenv, update, services, postinstall, shim, completions |
Path queries short-circuit before any subcommand runs: --prefix, --cellar,
--caskroom, --cache, --repository, and --taps.
zapbrew infodoes not append, HEADfor a formula that has a HEAD spec.zapbrew --versionreportsHomebrew 5-compatible. It tracks Homebrew's CLI surface, not its version number.install --caskaccepts only--forceand--appdir. Every other install flag —--only-dependencies,--dry-run,--build-from-source,--HEAD,--interactive,--include-test— is refused with--caskrather than silently dropped, because the cask path cannot honor it.brewaccepts several of them.--cache <formula>is refused.brew --cache <formula>prints that formula's download path; Zapbrew supports only bare--cache.--HEADand--interactiveare refused for formulae too, like--build-from-source.
| Crate | Responsibility |
|---|---|
zapbrew-types |
Names, versions, bottle tags, and other domain types |
zapbrew-prefix |
Environment detection and prefix layout |
zapbrew-net |
HTTP fetching, caching, and checksum verification |
zapbrew-api |
Signed JSON catalog loading and JWS verification |
zapbrew-pour |
Bottle unpacking, relocation, linking, and unlinking |
zapbrew-ops |
One module per command, with transactional install and rollback |
zapbrew-cli |
Argument parsing, dispatch, and output formatting |
See CONTRIBUTING.md.
BSD-2-Clause. See LICENSE.
Zapbrew is an independent project. It is not affiliated with or endorsed by Homebrew.