惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
P
Proofpoint News Feed
V
Visual Studio Blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
爱范儿
爱范儿
Microsoft Azure Blog
Microsoft Azure Blog
Recent Announcements
Recent Announcements
Vercel News
Vercel News
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
博客园 - 聂微东
D
DataBreaches.Net
酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Security Blog
Microsoft Security Blog
L
LangChain Blog
美团技术团队
H
Help Net Security
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
U
Unit 42
博客园 - 叶小钗
有赞技术团队
有赞技术团队
M
MIT News - Artificial intelligence
MongoDB | Blog
MongoDB | Blog

Lobsters

Lunacy | Red Vice CIFSwitch: a non-universal Linux local root vulnerability RIPE NCC session fixation: poaching logins with an Atlas probe GNOME 2.20 but its Web Components Agentic Search for Context Engineering – Leonie Monigatti Garnix is shutting down [not OC] akashina.tngl.sh/jjc Concerning Emacs (and Jazz) Nitpicking the shell history scene in ‘Tron: Legacy’ What's cooking on SourceHut? Q2 2026 The tenth OpenPGP email summit Clojure on Fennel part three: parsing WordPress at 23 Finding Miscompiles for Fun, Not Profit GitHub - creusot-rs/creusot: Creusot helps you prove your Rust code is correct. Announcing Rust 1.96.0 | Rust Blog A Love Letter to Neovim sqlite AGENTS.md Am I a Bad Friend? CSS vs. JavaScript • Josh W. Comeau Erlang Ecosystem Foundation - Supporting the BEAM community A brief note about slot access cost in Common Lisp Keyboard latency probe Rethinking the GNOME clipboard issues Back to the Building Blocks’ Building Blocks Tech Notes: Theseus: translating win32 to wasm Fast is better than slow Content-addressed Rust builds (or, what kache actually caches) Intent to Prototype: Embedding API Canada’s Bill C-22 and the security cost of collecting more data
Package managers that package package managers
Andrew Nesbitt · 2026-05-28 · via Lobsters

Mike Fiedler sent me a cursed table he’d put together while trying to close a loop of languages whose package managers each install the next one’s runtime. He got there in two hops: PyPI ships a Node binary as nodejs-wheel and npm ships a portable CPython as @bjia56/portable-python, so pip install and npm install can hand control back and forth indefinitely. I wanted the version where both axes are package managers rather than runtimes, partly because the diagonal then shows which ones ship themselves, and partly because once you start chaining brew install uv into uv tool install conan it’s natural to wonder how far you get before the chain bottoms out at curl | sh.

So I built one covering the 42 clients from the categorisation post, with data pulled from ecosyste.ms for the language registries and Repology for the distros, and each filled cell linking through to the package it found.

Matrix of which package managers package other package managers

The dense rows are the system package managers, and packaging arbitrary binaries is what they’re for. The AUR carries 40 of the 42, with nixpkgs, Homebrew, the DNF repos and Debian not far behind. The same tools are almost empty as columns, because nothing needs to redistribute apt or DNF when they already arrive with the operating system. Homebrew is the slightly odd case among the system managers since it isn’t tied to an operating system image, and the only place I found it packaged is the AUR, where someone has wrapped the install script as brew-git.

Conda sits between the two groups, with conda-forge carrying twenty-odd package managers alongside its compilers and runtimes in much the same way Homebrew does. There used to be a conda package on PyPI you could pip-install like any other Python tool, but every release of it has since been yanked because a pip-installed conda has no base environment to work from, so the only routes into conda now are the system rows and Spack’s miniconda3.

PyPI more generally is the densest of the language registries as a source, since a fair amount of cross-language tooling happens to be written in Python: Conan for C++ and the meson build system live there, as do the four competing PyPI clients which can all install each other. The npm registry covers the four JavaScript clients and Elm. RubyGems carries Bundler and CocoaPods, and crates.io has uv because uv is a Rust binary that publishes there as well as to PyPI. Maven Central turns out to redistribute npm, Yarn and Bun as jars via WebJars and mvnpm, which exist so that a Gradle or Maven build can fetch frontend dependencies without running a second package manager alongside it.

Twenty-five of the forty-two ship themselves on their own registry. For apt, DNF, pacman and apk that’s just how the tool gets updated, since the package manager is one more system package among the rest. On the language side pip install --upgrade pip is in a lot of people’s muscle memory, and npm, Cargo, Composer and Maven all use their own registries as the release channel for the same reason. Homebrew has no brew formula and updates by running git pull on its own checkout, which is why its diagonal cell stays empty.

A CVE filed against pip lands as pkg:pypi/pip and perhaps pkg:deb/python3-pip, but the Homebrew, conda-forge, nixpkgs and Spack packages in pip’s column are the same software with the same bug, and each of those redistributors has to file or map their own entry. Mapping Homebrew formulae back to upstream advisories was annoying enough that I wrote a tool for it, and that’s one column of forty-two.

A first attempt at filling the matrix probed each registry for a package literally named after each other manager. That doesn’t work, because every short name is already taken on every flat-namespace registry and almost never by the right thing: pip on npm is a 2012 CLI for the Freckle time tracker, homebrew on PyPI is an empty 0.0.0.1 with no description, and pacman on npm is a static site generator. Going the other way and asking ecosyste.ms which packages point at each manager’s canonical source repo gave much cleaner results, at the cost of a handful of false positives where someone has set repository in their package.json to rust-lang/cargo for a hello-world WASM tutorial.

The longest chain I’ve found without reusing a client runs fourteen hops from an Arch box to a working Elm compiler, with the middle stretch getting progressively more nested because Poetry and pdm only install into projects:

yay -S brew-git                                     # AUR
brew install spack
spack install miniconda3                            # gives conda
conda install -c conda-forge rust                   # bundles cargo
cargo install --locked uv
uv tool install pip
pip install poetry
poetry init -n && poetry add pdm
poetry run pdm init -n && poetry run pdm add conan
poetry run pdm run conan install --requires=nodejs/22.20.0 -g VirtualBuildEnv
source conanbuild.sh                                # nodejs bundles npm
npm install -g yarn
yarn global add pnpm
pnpm add -g bun
bun add -g elm

An earlier eleven-hop version of this went pip → poetry → pdm → uv → conda for the Python stretch until Jean-Christophe Morin pointed out that the PyPI conda package has been yanked, and rerouting around that turned up the Spack and Cargo detours. If you can beat fourteen the CSV is on GitHub and pull requests adding manual rows are welcome.