Usage
CLI
Point it at a project directory, a lockfile, or a list of specs. With no argument it looks in the current directory and picks the most precise file it finds: uv.lock > pylock.toml > poetry.lock > Pipfile.lock > requirements*.txt > pyproject.toml.
# the current project, CPython 3.15, default platforms
wheelripe
# free-threaded 3.15t, every platform you ship
wheelripe uv.lock -p 3.15t --platform linux-x86_64,linux-aarch64,macos-arm64,windows-amd64
# no lockfile handy? pass specs
wheelripe numpy==1.26.4 pydantic-core==2.41.5 orjson
# older glibc in production
wheelripe requirements.txt --glibc 2.28Unpinned specs (orjson, numpy>=1.26) are checked at the newest release matching the specifier. Pinned ones (==, or anything from a lockfile) are checked at exactly that version, and if it has no wheel wheelripe looks forward for the first newer release that does.
PyTorch and private indexes
Point wheelripe at any PEP 691 (JSON) or PEP 503 (HTML) simple index with the options pip takes. Files from every index are pooled, like pip, and versions come from the file names, so local builds such as 2.14.0+cpu match the way pip matches them.
$ wheelripe torch==2.13.0 numpy==2.5.4 --index-url https://download.pytorch.org/whl/cpu --extra-index-url https://pypi.org/simple
cli · 2 packages · index download.pytorch.org/whl/cpu + pypi.org
BUMP torch 2.13.0 → 2.14.0 no cp315 wheel in 2.13.0 for macos-arm64, windows-amd64 (ships cp310–cp315); 2.14.0 is the first release with one · ready on linux-x86_64
READY 1 package (1 with binary wheels · 0 pure Python)
# a requirements file that already says where to look needs no flags
wheelripe requirements.txt # -i / --extra-index-url lines, also inside -r includes
# uv.lock: each package is checked on the registry uv locked it from
wheelripe uv.lock -p 3.15t
# a private index: credentials go to that host only and are never printed
wheelripe -i "https://$INDEX_USER:$INDEX_TOKEN@pypi.corp.example/simple" requirements.txtPrecedence, credentials and what happens when an index refuses are on the configuration page.
Explain a single wheel
$ wheelripe wheel orjson-3.13.0-cp315-cp315-manylinux_2_39_x86_64.whl -p 3.15t
orjson-3.13.0-cp315-cp315-manylinux_2_39_x86_64.whl on CPython 3.15t
no linux-x86_64 built for the GIL build cp315, not free-threaded cp315t; needs glibc 2.39, your floor is 2.34
no macos-arm64 built for the GIL build cp315, not free-threaded cp315t; built for manylinux_2_39_x86_64, not macos-arm64
no windows-amd64 ...Output formats
-f text (default, coloured unless NO_COLOR is set), -f json (schema), -f markdown (a table for PR comments or issues), -f github (workflow annotations). --summary FILE and --json-out FILE write extra copies alongside whatever goes to stdout.
GitHub Action
A weekly job that tells you when your last blocker clears:
# .github/workflows/py315.yml
name: ripe for 3.15?
on:
schedule: [{cron: "0 6 * * 1"}]
workflow_dispatch:
jobs:
wheels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: Blackman99/wheelripe@v0.1.0
id: ripe
with:
python-version: "3.15t"
platforms: linux-x86_64,macos-arm64,windows-amd64
fail-on: blocked
- if: steps.ripe.outputs.ripe == 'true'
run: echo "Every dependency has a 3.15t wheel. Time to upgrade."| Input | Default | Meaning |
|---|---|---|
path | . | Lockfile, requirements, pyproject or directory. Space-separated for several. |
python-version | 3.15 | Target CPython; append t for free-threaded. |
platforms | linux-x86_64,macos-arm64,windows-amd64 | Comma-separated platform keys. |
fail-on | bump | bump, blocked, build or never. |
glibc | 2.34 | Oldest glibc you deploy on. |
macos | 14 | Oldest macOS you support. |
index-url | Simple-API index to use instead of PyPI, e.g. https://download.pytorch.org/whl/cpu. | |
extra-index-url | More simple-API indexes, separated by spaces or newlines; pooled with the first. |
For a private index, build the URL from repository secrets. GitHub masks them in the log, and wheelripe never prints credentials anyway:
- uses: Blackman99/wheelripe@v0.1.0
with:
path: requirements.txt
index-url: https://${{ secrets.INDEX_USER }}:${{ secrets.INDEX_TOKEN }}@pypi.corp.example/simple
extra-index-url: https://pypi.org/simple| Output | Meaning |
|---|---|
ripe | true when everything installs from a wheel. |
blocked | Count blocked upstream (including requires-python exclusions). |
bump | Count fixed by a version bump. |
report | Path to the JSON report. |
The step also writes a Markdown table to the job summary and an annotation per problem package. It runs on Linux, macOS and Windows runners (CI tests all three).
MCP server
wheelripe mcp is a read-only stdio MCP server, so an agent can check before it bumps requires-python or a CI matrix.
{
"mcpServers": {
"wheelripe": {
"command": "uvx",
"args": ["--from", "git+https://github.com/Blackman99/wheelripe", "wheelripe", "mcp"]
}
}
}| Tool | Arguments | Returns |
|---|---|---|
check_python_upgrade | path or requirements[], python, platforms[], glibc, macos, index_url, extra_index_urls[] | Markdown table + the full JSON report as structured content. |
explain_wheel | filename, python, platforms[] | Installs yes/no per platform, with the reason. |
Both tools are annotated readOnlyHint; they only read local files and the package index.