ohbin Declare binaries, not wrapper packages.
Your project needs ripgrep , or oasdiff , or some Rust linter that ships only as a GitHub release. Python can't install it. So you either tell every developer "go install it yourself" — and watch versions drift and CI break — or you hand-write a download-and-verify wrapper package, and copy it into every repo, for every tool.
ohbin deletes that. Declare the tool in pyproject.toml ; it's fetched on first use, SHA256-checked against a pinned hash, cached per host, and exec'd. One dev-dependency. Any number of tools.
uv add --dev git+https://github.com/prostomarkeloff/ohbin.git
Before & After
❌ The hand-rolled wrapper — a whole package, per tool, copied into every repo
# a download-and-verify wrapper · ~180 lines · written again for the next tool _PLATFORM_ASSETS = { ( "linux" , "x86_64" ): _Asset ( "ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz" , "4cf9f2741e6c…" ), ( "darwin" , "arm64" ): _Asset ( "ripgrep-14.1.1-aarch64-apple-darwin.tar.gz" , "24ad767777…" ), # ...two more, each SHA hand-copied from the release page } def ensure_binary () -> Path : asset = _resolve_asset () # platform.machine() guesswork with _flock ( cache / ".lock" ): # concurrency, if you bother _download ( url , archive ) # urllib + redirects (+ retries, if you bother) _verify_checksum ( archive , asset . sha256 ) # hashlib _extract ( archive , binary ) # tarfile, atomic rename return binary # + a wheel shim, [project.scripts], and a [tool.uv.sources] entry — in every repo
✅ ohbin — one dev-dependency, one table per tool
uv run ohbin add BurntSushi/ripgrep --version 14.1.1 --name rg --binary rg
[ tool . ohbin . tools . rg ] repo = " BurntSushi/ripgrep " version = " 14.1.1 " binary = " rg " # + one [..assets.<os>-<arch>] table per platform — written by `add`, checksums and all
... continue reading