Skip to content

brew bundle: Add support for uv tools #21579

@shaanmajid

Description

@shaanmajid

Verification

Provide a detailed description of the proposed feature

Add uv tool as a supported type in brew bundle, following the existing pattern for cargo and go. (Previously discussed in Homebrew/homebrew-bundle#1477, now archived).

uv tool install installs standalone Python tools into isolated per-tool virtual environments, with binaries linked to ~/.local/bin/. It's analogous to the Python equivalent of cargo install: isolated, user-local tool installations.

uv tool meets all criteria listed in https://docs.brew.sh/Brew-Bundle-and-Brewfile#adding-new-packages-support. Each installed tool has a receipt file at ~/.local/share/uv/tools/<name>/uv-receipt.toml. This is a structured TOML file that stores everything needed to reproduce the installation. A typical receipt file may look like like:

[tool]
requirements = [{ name = "ruff" }]
entrypoints = [
    { name = "ruff", install-path = "/Users/user/.local/bin/ruff", from = "ruff" },
]

A more complex installation (uv tool install mkdocs --with mkdocs-material --python 3.11) produces:

[tool]
requirements = [
    { name = "mkdocs" },
    { name = "mkdocs-material" },
]
python = "3.11"
entrypoints = [
    { name = "mkdocs", install-path = "/Users/user/.local/bin/mkdocs", from = "mkdocs" },
]

The full set of fields the receipt can contain:

Receipt field What it captures When it's set
requirements The tool package plus any --with packages. Each can carry a version specifier (e.g., {name = "ruff", specifier = "<0.15"}). First entry is the tool; extras come from --with. Always
python Explicit Python interpreter pin --python passed
constraints Dependency version constraints --constraint passed
overrides Dependency version overrides --overrides passed
build-constraint-dependencies Build-time dep constraints --build-constraints passed
options Resolver/installer settings (custom index URLs, resolution strategy, etc.) Non-default settings used
entrypoints Installed executables and their paths Always (derived by uv, not an install input)

Here's how the receipt fields map to a proposed Brewfile DSL, grouped by priority:

Priority Receipt field Brewfile syntax CLI equivalent Rationale
MVP Package name uv "ruff" uv tool install ruff Core functionality; covers vast majority of real installs.
MVP Version specifier uv "ruff", specifier: "<0.15" uv tool install 'ruff<0.15' Without this, reinstalling loses intentional version pins. Present in requirements[0].specifier.
MVP --with packages uv "mkdocs", with: ["mkdocs-material"] uv tool install mkdocs --with mkdocs-material Likely most common flag/option. Used for tools with plugins. Present as additional entries in requirements.
Follow-up python uv "tool", python: "3.11" uv tool install tool --python 3.11 Likely uncommon but intentional when used.
Out of scope constraints, overrides, build-constraint-dependencies, options - - Advanced resolver config for edge cases. Rare in project-level config, let alone standalone tool installs.
N/A entrypoints - - Derived during install, not an input.

What is the motivation for the feature?

brew bundle already supports cargo and go for the same use case: capturing user-installed CLI tools from language-specific package managers. uv tool is the Python counterpart.

When users install Python CLI tools via uv tool, those installations are invisible to brew bundle dump. The Brewfile doesn't reflect what's actually on the machine. This is the same gap that cargo and go support was added to fill.

How will the feature be relevant to at least 90% of Homebrew users?

This is relevant to brew bundle users in the same way the existing cargo and go support is; it extends dump/install/check/cleanup to cover another widely-used tool installer. Python is the most widely used programming language in developer surveys, and uv is becoming the dominant tool installer in that ecosystem. Many Homebrew users already have it installed (brew install uv). Many Homebrew users already have it installed, with ~1M installs in the last year, similar to Go, and far more than any of the rust tools.

What alternatives to the feature have been considered?

Not having the feature :(

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions