bine helps you manage external binary tools required for your development
projects. It reads a .bine.json configuration file, downloads specified
binaries into a local, project-scoped cache, and makes them available for you to
run.
This ensures your whole team uses the exact same tool versions without polluting global system paths or requiring manual installation steps.
- Declarative: Define all your project's binary dependencies in a single
.bine.jsonfile. - Project-scoped: Binaries are stored in a local cache directory specific to
your project (e.g.:
~/.cache/bine/<project_name>), avoiding conflicts. - Reproducible: Pin exact versions to ensure your team and CI/CD environments use the same tools.
- Seamless integration: Use
bine runto execute tools directly, orbine envto add them to your shell'sPATH.
You can download bine as a self-contained static binary from the releases
page; just place it in a directory included in your system PATH to use it
from the command line.
For example:
curl -L -o ~/.local/bin/bine https://github.com/artefactual-labs/bine/releases/download/v0.21.0/bine_0.21.0_linux_amd64
chmod +x ~/.local/bin/bine
-
Create a configuration file.
In the root of your project, create a
.bine.jsonfile. This file defines the tools you need. It supports JSON with comments. -
Install the binaries.
Run
bine syncto download and install all the tools defined in your config file.bine sync -
Run a tool.
Use
bine runto execute a managed binary. It works just likenpxorbundle exec.bine run golangci-lint --help -
Add binaries to your PATH (recommended).
To use the tools directly in your shell, use
bine env:# Bash, Zsh (process substitution) source <(bine env --shell=bash) # Fish (source command) bine env --shell=fish | source # POSIX shells (sh, dash, etc.) eval "$(bine env)"
Now you can call your tools directly:
golangci-lint --help
The .bine.json file is the heart of the tool. Here is a detailed breakdown of
its structure:
{
// A unique name for your project. Used to create a scoped cache directory.
"project": "string",
// A list of binaries to manage.
"bins": [
{
// The name you use to run the binary, e.g. `bine run jq`.
"name": "jq",
// The semantic version to install, e.g. "1.8.0".
"version": "1.8.0",
//
// Option 1: Install from a release asset URL.
//
// The URL of the GitHub repository.
"url": "https://github.com/jqlang/jq",
// A template for the downloadable asset's filename, e.g. "jq-darwin-amd64".
"asset_pattern": "{name}-{goos}-{goarch}",
// A template for the Git tag. Defaults to "v{version}".
"tag_pattern": "{name}-{version}",
// A map to substitute template variables, useful when the asset names don't follow the common patterns.
"modifiers": {
"goos": {
// jq uses "macos" instead of "darwin" in its asset names.
"darwin": "macos"
}
}
//
// Option 2: Install from a Go package.
//
// The Go module path to install the binary from.
// "go_package": "golang.org/x/tools/cmd/stringer",
}
]
}The asset_pattern field uses templates to construct the correct download URL
for your platform.
| Variable | Description | Example |
|---|---|---|
{name} |
The binary's name from the config. |
jq |
{version} |
The version (without a v prefix). |
1.8.0 |
{goos} |
Go's OS identifier (runtime.GOOS). |
linux, darwin |
{goarch} |
Go's architecture identifier (runtime.GOARCH). |
amd64, arm64 |
{os} |
System's OS name (uname -s). |
Linux, Darwin |
{arch} |
System's architecture (uname -m). |
x86_64, arm64 |
{triple} |
The Rust-style target triple. | x86_64-unknown-linux-gnu |
It can be combined with the modifiers field to handle project-specific
naming conventions. See the example above for how to use it.
The tag_pattern field is used to construct the Git tag for the binary found
in GitHub releases. It defaults to v{version} but can be customized.
| Variable | Description | Example |
|---|---|---|
{name} |
The binary's name from the config. |
jq |
{version} |
The version (without a v prefix). |
1.8.0 |
bine provides several commands to manage your binaries.
Use bine --help to see the full list of commands and options:
COMMAND
bine -- Simple binary manager for developers.
USAGE
bine [FLAGS] <SUBCOMMAND> ...
bine helps manage external binary tools needed for development projects.
It downloads specified binaries from their sources into a local cache directory,
ensuring you have the right versions without cluttering your system.
SUBCOMMANDS
env Output shell commands to set up the PATH system variable.
get Download a binary and print its path.
list Print the list of binaries.
path Print the path of the binary store.
run Download a binary and run it.
sync Install all binaries defined in the configuration file.
upgrade Upgrade all binaries defined in the configuration file.
version Print the current version of bine.
FLAGS
-v, --verbosity INT Log verbosity level. The higher the number, the more verbose the output. (default: -1)
--cache-dir STRING Path to the cache directory.
--github-api-token STRING GitHub API token for authentication.
bine uses the GitHub Rest API to fetch release information and download
binaries from GitHub repositories. By default, unauthenticated requests are
limited to 60 requests per hour. Use the --github-api-token command-line
flag to pass your token or set the BINE_GITHUB_API_TOKEN environment variable.
Usage example in Bash:
export BINE_GITHUB_API_TOKEN=your_token_here
bine list --outdatedWhile go get -tool is a great feature for managing Go module-based binaries,
it doesn't help with other essential tools that aren't written in Go, e.g. jq
or shfmt.
That said, we use go get -tool to install bine itself in Go projects:
go get -tool github.com/artefactual-labs/bine@latest
Invoke bine with the go tool command to ensure it uses the Go toolchain's
environment:
$ go tool bine path
/home/ethan/.cache/bine/project/linux/amd64/bin
asdf is much better overall, but we created bine mainly as a greenfield
project so we could try out some new ideas, including using LLMs during
development. bine is less sophisticated and was born from the idea of
replacing dependency management in Make-based projects like makego.
mise is also ahead of bine in terms of usability and features. In fact, it may
even surpass asdf. If you haven't tried it yet, it's definitely worth a look.
mise can leverage existing asdf plugins like asdf-golang to go install
the Go tools your project needs.
Nix provides fully reproducible and isolated development environments, while bine focuses on simple version management for tools.
We have examples showing hwo to use bine with make and just.
For Fish shell users, you can create a simple function that not only sets up
your PATH but also enhances your prompt to show the current bine project
name.
Create a file named bine-env.fish in your Fish functions directory (e.g.,
~/.config/fish/functions/bine-env.fish) with the following content:
function bine-env
go tool bine env | source
if not functions -q original_fish_prompt
functions -c fish_prompt original_fish_prompt
end
# Requires 'jq' to parse `.bine.json`.
function fish_prompt
original_fish_prompt
set PROJECT_NAME (go tool bine config get project)
echo -n "→ [$PROJECT_NAME] "
end
endYou can now call bine-env from your project's root directory.
{ "project": "my-awesome-project", "bins": [ { // Fast linters runner for Go projects. "name": "golangci-lint", "url": "https://github.com/golangci/golangci-lint", "version": "2.0.2", "asset_pattern": "{name}-{version}-{goos}-{goarch}.tar.gz" } ] }