Skip to main content

Bombadil — a desktop keeper for uv virtual environments

CI Rust 1.88+ uv 0.12.1 Apache 2.0


A desktop application for the Python virtual environments you already have. Bombadil keeps an account of them — which exist, which have drifted from what their manifest declares, what is installed in each — and gives you one place to sync, inspect and open them.

It never owns an environment. uv creates them, uv resolves them, and Bombadil is the thing that tells you the truth about them and gets out of the way. Delete Bombadil and every environment it showed you is still there, untouched.

"Bright blue his jacket was, and his boots were yellow." Tom Bombadil is the keeper of a bounded domain who owns nothing he tends. That is the whole design brief, and it is where the palette comes from.

What it does

Projects and environments. Register a project by its pyproject.toml. A project can carry several environments — a 3.11 and a 3.12, say — shown as a tree beneath it, with one marked default: the one Sync, the terminal and the dependency view act on. An environment can live beside the project or anywhere else on disk, including one another project already uses.

One glyph vocabulary, everywhere. Every row in every pane — projects, dependencies, workspace members, indexes — reports state the same way, and no two states ever look alike:

present it exists and matches
absent declared, not installed
drifted installed, but not what was asked for

Sync, with the output. uv sync streams into a drawer that follows the output and stays where you put it. A failure shows the transcript rather than a shrug.

Dependencies and workspace members. What the manifest declares against what is actually installed, joined on PEP 503-canonical names so calc_service and calc-service are one row. Monorepo members are listed with what a sync would add or remove before you run it.

Environment variables and secrets. Global variables and per-project ones, with the project's overriding. Mark a variable secret and its value moves to the OS keychain — it is never written to config.toml, never carried in a message, and never read back to be redisplayed.

Private indexes. Name, URL, and credentials in the keychain, injected as UV_INDEX_* at sync time. Global by default, selectable per project.

Pre-activate scripts. Shell scripts run before an environment is used, in an order you control, with their exported variables composed into everything Bombadil launches.

Terminals and PyCharm. Open a terminal in the project directory with the environment activated — every emulator's argv is built for the platform, and the app knows which ones cannot carry an environment rather than pretending. The PyCharm button opens the project folder with the same composed environment. (PyCharm's interpreter cannot be set from outside the IDE; bombadil_core::ide documents exactly why.)

Pinned interpreters, safely. uv sync -p X against an environment built on another version deletes it. Bombadil compares pins segment-wise against what pyvenv.cfg actually records, so 3.12.13 and 3.12 are not treated as a conflict, and a genuinely destructive pin change asks first.

Install

Bombadil bundles uv 0.12.1, so there is nothing else to install. By default it uses uv from PATH when that is at least 0.12.1 and falls back to the bundled copy; you can force either.

pip install bombadil     # or: uv tool install bombadil
cargo install bombadil

Either gets you the same application. The wheel carries the compiled binary and a console entry point and contains no Python at all — the route uv itself takes to PyPI — so bombadil lands on your PATH the same way.

Wheels are built for Linux (x86-64 and ARM), macOS on Apple Silicon, and Windows. An Intel Mac has no prebuilt wheel; cargo install bombadil builds there from source. bombadil is a meta crate: it owns the name worth typing and pulls in bombadil-gui, bombadil-core and bombadil-uv-bin behind it. It has to be a real binary rather than an alias, because cargo install installs binaries defined in the package being installed and never in its dependencies.

From a checkout instead:

git clone https://github.com/marcelotrevisani/bombadil
cd bombadil
just run-release          # or: cargo run --release -p bombadil

Rust 1.88 or newer, edition 2024. The build downloads the pinned uv release for your platform.

Configuration lives at ~/.config/bombadil/config.toml on Linux, ~/Library/Application Support/bombadil/ on macOS, and %APPDATA%\bombadil\config\ on Windows. Secrets live in the OS keychain, never in that file.

Development

just            # list every recipe
just run        # debug build
just test       # workspace tests
just ci         # fmt-check, clippy, test, cargo-deny, msrv -- what CI runs
just icon       # regenerate assets/ from the SVGs there (needs cairosvg)

On Linux, just install-desktop puts a desktop entry and the themed icons in ~/.local/share (per-user, no root; just uninstall-desktop removes exactly those). This is not decoration: GNOME on Wayland ignores the icon a window carries. It resolves a window's icon by matching its app_id to the basename of an installed .desktop file and reading that file's Icon= key out of the icon theme, so without the entry the panel shows a generic placeholder no matter what the binary holds. X11, Windows and macOS use the compiled-in icon, which is why it is set both ways.

808 tests across three crates, green on Linux, macOS and Windows.

Conventions worth knowing before you patch it

  • view is dumb and untested. Logic worth a test lives in a free function that view calls. A change that only exists inside view ships without a test, and its commit says so.
  • A fix needs a mutation proof. Break the fix on purpose and watch a test fail. Seventeen tests in this repo's history passed for the wrong reason; one was in a test double that could not tell spawn from run, so every test on that seam inherited the blind spot.
  • Never compare a display()ed path to a string literal. PathBuf::join uses \ on Windows. Three CI failures here came from a platform assumption hiding in an assertion rather than in the code.
  • Secrets never enter a Message — it derives Debug — and never reach a ConfigPersist.
  • Runners, secret stores, persisters and filesystem probes are parameters, never constructed inline, so tests stay hermetic and no test can reach the real keychain.
  • BOOT is rationed. The attention colour appears only where the user must look. If it starts appearing elsewhere it stops meaning anything, and the test that guards this is the most important one in theme.
  • Never edit deny.toml to dodge a licence or advisory failure. Escalate.

Specs and implementation plans live in docs/superpowers/.

Releasing

Publishing is irreversible — crates.io versions can be yanked but never replaced, and a yanked version still resolves for anything that already has it in a lock file. So the pipeline gates everything before it uploads anything, and can be rehearsed:

  1. Run Release from the Actions tab with dry_run left on. It packages and verifies all three crates and uploads nothing.
  2. Bump version in the workspace Cargo.toml, commit, and tag it vX.Y.Z. The tag and the manifest must agree — the pipeline refuses otherwise, because a mismatch is silent and neither half can be taken back.
  3. Push the tag. It re-runs the full CI set against that exact commit, builds each crate from its own packaged tarball, then publishes bombadil-corebombadil-uv-binbombadil-guibombadil in dependency order, uploads one wheel per platform to PyPI, and opens a GitHub release.

crates.io and PyPI are published by separate jobs with separate tokens (RELEASE_TOKEN, PYPI_BOMBADIL), so failing to reach one never leaves the other half-done.

The crates.io token is RELEASE_TOKEN on the crates_deploy environment, so nothing outside the publish job can read it.

Crates

Crate What it is
bombadil The binary. Install this one; it brings the rest.
bombadil-gui The iced front end, as a library with one public function.
bombadil-core Model, config store, uv invocation, environment composition, keychain, terminals, IDE launch. No GUI.
bombadil-uv-bin Fetches and embeds the pinned uv binary at build time.

Licence

Apache-2.0. See LICENSE.

Bombadil ships uv (Apache-2.0 / MIT), Atkinson Hyperlegible Next and IBM Plex Mono (both SIL OFL 1.1). Every licence text is in the application itself, under Help → Bundled licences, as those licences require.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

bombadil-0.2.0-py3-none-win_amd64.whl (45.8 MB view details)

Uploaded Python 3Windows x86-64

bombadil-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (52.6 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

bombadil-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (50.3 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

bombadil-0.2.0-py3-none-macosx_11_0_arm64.whl (41.6 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

File details

Details for the file bombadil-0.2.0-py3-none-win_amd64.whl.

File metadata

  • Download URL: bombadil-0.2.0-py3-none-win_amd64.whl
  • Upload date:
  • Size: 45.8 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: maturin/1.14.1

File hashes

Hashes for bombadil-0.2.0-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 8326bd58ccc8d1c6c648720786135d8ceabc6212dbadb655cdc5af35dbd7fe10
MD5 2b300813fcc0f4b444beb6cb7ff095e2
BLAKE2b-256 003bee853c50d760f93c2f0419697d90fc0c67c3d59e0d089390897afd0a5273

See more details on using hashes here.

File details

Details for the file bombadil-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for bombadil-0.2.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ec28aa3e791937b8062103a81a932c100dd917322a1ea6c3f478d54457ded59a
MD5 7038fc6986f4ea427f30e311e2373dc3
BLAKE2b-256 51f92a54f32f92a5e401ae6ff0ac788bd85c1de3bab65f00e6e4dbb7b7526470

See more details on using hashes here.

File details

Details for the file bombadil-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for bombadil-0.2.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 c60b0ce848e2707197f51de74c427362af6a72c97741dbe8a157e47cc7c2515b
MD5 b00a020b269e39fc03584b670a8d07ea
BLAKE2b-256 c2718f46c015ccd5115480252554ebd6feaa0436b887f337fd9e4b498ad75b3b

See more details on using hashes here.

File details

Details for the file bombadil-0.2.0-py3-none-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for bombadil-0.2.0-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 23cbad416ca5ab7b45e24b4caaaa8ca76a5073da225a73bc8f061bb6be5dc632
MD5 5fd9a029d5022b63aa9dc8e47f85a189
BLAKE2b-256 63638f7eca26453a5dbe8250d5bc49bca711f43c416730a24874a55d9a0bea61

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page