Managing development tools
I've been looking for a tool to manage development tools like linters, test runners, etc. The tools are typically development tools, so for example pipx would qualify.
A tool manager should make the environment reproducible and portable. The installed tools should work not only on my machine as well as on machine of everyone else who works on the same project, and they should not conflict with other tools anyone has installed on their systems.
Ideally the tool would have a declarative configuration, so it's clear what's used and easy to upgrade.
The problem with task managers (that aren't also tool managers) is that they don't control the tools, so a task that works on one system may fail on another. Worse, a task that works at one point in time may fail at another, because of a new tool version. So task manager should be either also a tool manager or it should be used with one.
On the other hand, managing tools is also a big subject, it's pretty much a package manager, needing some sort of a descriptor for a lot of tools in various languages, that need to be installable on various systems and architectures.
In short, the requirements boil down to:
- declarative tool SBOM
- tool isolation (but with access to project dependencies)
- task management
Installing tools directly in the system
The old way, where the user installs tools following the project readme. Not declarative, not reproducible, definitely not isolated.
❌ declarative ❌ isolated ❌ task management
Declarative package managers – Nix, Guix
Environments are reproducible, declarative (both use functional languages). Rather difficult to use and very intrusive – you pretty much install another operating system in your operating system. I don't see any technical obstacle from making them single-user application, but they chose against it. Unless you already use one of them, it's a total overkill.
Maybe they work better with dev containers, but even then there are alternatives.
✅ declarative ✅ isolated ❌ task management
pipx
pip for executable packages. Supports installation of multiple versions of the same tool, but there's no way to activate the required versions for a given project. And it only works for python tools (and those with a python wrapper).
❌ declarative ❌ isolated (tools are isolated from each other, but shared across projects) ❌ task management
uvx, npx, pnpx, etc
Set of tools that let you run their respective languages projects as programs. Each works only with tools in its language, but it's not the biggest problem, since typically the tools are written in the same language, or have wrappers.
They arent really designed to manage a toolbox, rather for one-off execution. There's no declarative versioning.
uvx is a part of uv, which is a package manager, and also python version manager, but somehow tool manager is out of scope 🤷.
❌ declarative ✅ isolated ❌ task management
Tools as dev dependencies
It's really not what dev dependencies are meant for: everything is installed in a shared environment (venv, nodemodules). In the nodejs environment this should be fine, every package maintains its own dependencies, even if versions differ – that's why you end up with huge nodemodules. In python you might end up with conflicting dependencies (this somehow doesn't stop poetry project to use dev dependency group this way). Again, they only work for tools written in the same language as the project.
✅ declarative ❌ isolated ❓ task management (not in python, package.json has scripts)
pre-commit
Technically ticks many boxes, but not really intended as a dev tool manager.
Uses git repositories as a distributed source of packages, keeps them isolated and cached per project. Versions are fully declarative – you can even pin full SHA hashes.
This is all great, but it's literal git's pre-commit utility, which means it's designed for fast and predictable execution. It's not intended as a general purpose tool manager – it doesn't provide the tools with full project environment (it doesn't include dependencies), it doesn't support test runners (also because testing is too slow for a pre-commit hook) and even support for some linters (see pythons mypy) is suboptimal (again, mypy works best with access to dependencies).
✅ declarative ✅ isolated (actually too isolated for my purpose) ❌ task management
Mise
Works both as a package manager (tools) and task manager (replacement for package.json scripts, rather than for make). The only tool with the express goal of being a tool and task manager.
It doesn't handle tools with plugins well (e.g. pydantic.mypy) – it uses a shared directory for installs, but only tool name and version are used for tools “identity”. which means if you have two projects, both using mypy, one using pydantic with mypy plugin, there's a conflict (there's a workaround to this, but not good for collaboration).
It also does funny things to $PATH so it doesn't work great with other tools, like IDEs or pre-commit.
✅ declarative ❌ isolated (buggy) ✅ task management
Dev containers
This isn't even a tool, it's simply a container (docker, podman, you name it) that has your tools installed and has access to your project directory.
It's great for isolation and reproducibility, as long your installs are versioned. The isolation makes curl | sudo sh - a bit more acceptable.
I haven't worked with it, but the concept seems very reasonable, especially for occasional build of applications that you only use (but not develop).
Conclusion
I don't see here a perfect tool, and I'm not even looking for a polyglot multi-project / monorepo manager.
Mise comes very close, and I'm currently using it but it has a problem with isolation and it doesn't seem interested in fixing it, so I wouldn't recommend it on it's own in a collaborative environment.
I'll take a closer look at mise in a dev container. Dev containers would provide the isolation and mise covers declarative tool and task management quite well.
