Custom Homebrew tap for pinning development tool versions used by requestly/requestly-api-client.
Upstream Homebrew formulae track latest releases. When upstream introduces breaking changes that affect our workflow, we build and host pinned versions here. Currently hosts beads v0.55.4.
homebrew-tools/
├── Formula/
│ └── beads@0.55.4.rb # Homebrew formula (points to GitHub release asset)
├── README.md # User-facing install docs
└── CLAUDE.md # This file
GitHub releases host the actual binary tarballs. The formula downloads from there.
The requestly-api-client repo references this tap in its CONTRIBUTING.md onboarding steps:
brew install requestly/tools/beads@0.55.4Upstream beads 0.57+ introduced these breaking changes:
bd importbroken — fails withError 1062: duplicate primary keywhen importing issues.jsonl into an existing DB. The--skip-existingflag also fails.- Hook format changed — switched from embedded templates to "section markers". Conflicts with the husky-based hook setup in requestly-api-client.
bd sync --statusremoved — flag deprecated/removed, breaks scripts.bd exportremoved — replaced bybd backupcommands.
Upstream beads ships macOS binaries without CGO (CGO_ENABLED=0 in their .goreleaser.yml for darwin targets). Beads requires CGO for embedded Dolt database support (via dolthub/driver and dolthub/go-icu-regex). Without CGO, you get:
Error: failed to open database: embedded mode requires CGO: dolt: this binary was built without CGO support
This affects ALL non-Homebrew install paths:
- GitHub release binaries —
CGO_ENABLED=0 - npm (
@beads/bd) — same slim binary, same error go installwithout explicit CGO setup — defaults to no CGO
Only brew install beads works because Homebrew builds from source with CGO enabled. But brew only ships the latest version (0.57.0+).
brew install go icu4c@78The key trick is creating a static-only icu4c library directory. If you point -L at the standard brew icu4c path (/opt/homebrew/opt/icu4c@78/lib/), the linker finds .dylib files and links dynamically — making the binary depend on brew install icu4c@78 at runtime. By copying only the .a files to a separate directory, the linker has no choice but to link statically.
# 1. Clone the target version
git clone --branch v<VERSION> --depth 1 https://github.com/steveyegge/beads.git /tmp/beads-build
cd /tmp/beads-build
# 2. Create a directory with ONLY static icu4c libraries (no .dylib)
# This forces the linker to use static linking for icu4c.
# Without this, the linker finds .dylib files first and links dynamically,
# which means the binary needs `brew install icu4c@78` at runtime.
mkdir -p icu-static-lib
cp /opt/homebrew/opt/icu4c@78/lib/libicuuc.a icu-static-lib/
cp /opt/homebrew/opt/icu4c@78/lib/libicui18n.a icu-static-lib/
cp /opt/homebrew/opt/icu4c@78/lib/libicudata.a icu-static-lib/
# 3. Build with CGO enabled, pointing at static-only lib dir
CGO_ENABLED=1 \
CGO_CFLAGS="-I/opt/homebrew/opt/icu4c@78/include" \
CGO_CXXFLAGS="-I/opt/homebrew/opt/icu4c@78/include" \
CGO_LDFLAGS="-L$(pwd)/icu-static-lib -lstdc++ -lc++" \
go build -o bd -ldflags="-s -w -X main.Version=<VERSION> -X main.Build=static" ./cmd/bd# 1. Check version
./bd --version
# Expected: bd version <VERSION> (static)
# 2. Verify NO icu4c dynamic dependency (this is critical)
otool -L ./bd
# Should show ONLY these system libs:
# /usr/lib/libresolv.9.dylib
# /usr/lib/libc++.1.dylib
# CoreFoundation.framework
# Security.framework
# /usr/lib/libSystem.B.dylib
#
# If you see /opt/homebrew/opt/icu4c@78/lib/libicu*.dylib — the static
# linking failed. Delete icu-static-lib and redo step 2, making sure
# NO .dylib files are in that directory.
# 3. Test it works with the requestly-api-client repo
cd /path/to/requestly-api-client
/tmp/beads-build/bd import -i .beads/issues.jsonl --dry-run
# Should succeed (no CGO error, no import error)# 1. Create tarball
tar czf bd-<VERSION>-darwin-arm64.tar.gz bd
# 2. Compute sha256 (needed for the formula)
shasum -a 256 bd-<VERSION>-darwin-arm64.tar.gz
# 3. Create GitHub release and upload
gh release create beads-<VERSION> \
bd-<VERSION>-darwin-arm64.tar.gz \
--title "beads <VERSION> (static, macOS arm64)" \
--notes "Static build with icu4c linked in. No external dependencies."
# 4. Update the formula: Formula/beads@<VERSION>.rb
# - Update `url` to point to the new release asset
# - Update `sha256` with the value from step 2
# - Update `version` fieldThe current build is arm64 only. To add amd64:
- Build on an Intel Mac (or cross-compile — harder with CGO)
- Create
bd-<VERSION>-darwin-amd64.tar.gz - Add an
elseclause to the formula:on_macos do if Hardware::CPU.arm? url "...darwin-arm64.tar.gz" sha256 "..." else url "...darwin-amd64.tar.gz" sha256 "..." end end
Same approach but build on Linux. Add an on_linux block to the formula. Linux builds are simpler — upstream goreleaser already uses CGO_ENABLED=1 for Linux.
-
Linker finds .dylib instead of .a — The most common mistake. If you pass
-L/opt/homebrew/opt/icu4c@78/lib, the linker prefers.dylibover.a. Always use the static-only directory trick described above. -
Missing CGO_CXXFLAGS — The
go-icu-regexdependency has C++ source files that need icu4c headers. If you only setCGO_CFLAGSbut notCGO_CXXFLAGS, you getfatal error: 'unicode/regex.h' file not found. -
Duplicate library warnings — The build emits
ld: warning: ignoring duplicate libraries. This is harmless — internal CGO directives in beads' dependencies add redundant-lflags. -
macOS Gatekeeper blocks binary — Only happens when downloaded via a browser (adds
com.apple.quarantinexattr). Binaries fromcurl,git clone, orbrew installare not affected. Fix:xattr -d com.apple.quarantine ./bd. -
Binary size (~155MB) — Normal for a Go binary with embedded Dolt. The brew formula downloads a gzipped tarball (~55MB).