fix(pipx): honor install_before for uv and pipx installs#9190
Conversation
There was a problem hiding this comment.
Code Review
This pull request enables the install_before setting for the pipx backend, allowing transitive dependency resolution to respect a cutoff date using uv's --exclude-newer or pip's --uploaded-prior-to flags. The changes include updated documentation, new argument generation logic in the pipx backend with associated tests, and the isolation of shared libraries via the PIPX_SHARED_LIBS environment variable. Review feedback suggests using the = syntax for pip arguments to improve parsing reliability and recommends considering a centralized storage location for shared libraries to reduce disk usage while maintaining isolation.
283e6db to
9c00ebb
Compare
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Greptile SummaryThis PR extends
Confidence Score: 4/5Not yet safe to merge — the pip fallback path will fail every install when install_before is a relative duration due to a datetime precision issue identified in the review thread that remains unaddressed in the current commit. The uv path and all env-management changes are correct. However, pip_uploaded_prior_to_args formats the timestamp with jiff's Display (up to 9 nanosecond digits), and Python's datetime.fromisoformat() only accepts up to 6 fractional digits. Relative durations (the common case — e.g. "7d") produce Timestamp::now() values with sub-microsecond nanoseconds, causing pip to raise ValueError and fail the install on the pipx fallback path. This P1 issue was flagged in the review thread (line 302) but the fix ({before_date:.6} precision specifier) has not been applied in the current HEAD commit. src/backend/pipx.rs — specifically pip_uploaded_prior_to_args and its test Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[mise install pipx:tool] --> B{uv installed?}
B -- Yes --> C[uv tool install pkg]
B -- No --> D[pipx install pkg]
E[ctx.before_date Option Timestamp] --> F{Some?}
F -- No --> G[no extra args]
F -- Yes --> H[uv_exclude_newer_args]
F -- Yes --> I[pip_uploaded_prior_to_args]
H --> J[--exclude-newer timestamp - uv handles nanoseconds]
I --> K[--pip-args --uploaded-prior-to=timestamp - pip needs 6 fractional digits max]
J --> C
K --> D
L[env_remove PIPX_SHARED_LIBS] --> D
M[PIPX_HOME = install_path / PIPX_BIN_DIR = install_path/bin] --> D
Reviews (3): Last reviewed commit: "fix(pipx): use equals syntax for pip cut..." | Re-trigger Greptile |
9c00ebb to
2669f07
Compare
### 🐛 Bug Fixes - **(backend)** respect install_before in latest lookup by @risu729 in [#9193](#9193) - **(backend)** route explicit latest through stable lookup by @risu729 in [#9228](#9228) - **(backends)** deprecate b shorthand by @risu729 in [#9234](#9234) - **(config)** warn for deprecated env keys by @risu729 in [#9205](#9205) - **(config)** treat enable_tools empty as disable-all by @risu729 in [#9108](#9108) - **(github)** avoid auth on release asset downloads by @risu729 in [#9060](#9060) - **(gitlab)** warn when glab OAuth2 token is expired by @stanhu in [#9195](#9195) - **(npm)** honor install_before without day drift by @risu729 in [#9157](#9157) - **(npm)** warn on old bun and pnpm for install_before by @risu729 in [#9232](#9232) - **(pipx)** honor install_before for uv and pipx installs by @risu729 in [#9190](#9190) - **(registry)** allow shfmt on Windows by @zeitlinger in [#9191](#9191) ### 🚜 Refactor - **(backend)** remove unused rolling release helper by @risu729 in [#9175](#9175) - **(backend)** use file util for removals by @risu729 in [#9206](#9206) ### 📚 Documentation - **(config)** clarify always_keep_download behavior by @risu729 in [#9235](#9235) - **(configuration)** add rust to idiomatic version files by @jjt in [#9233](#9233) - **(contributing)** expand contribution guide introduction by @marianwolf in [#9208](#9208) - **(github)** document multiple release assets workaround by @risu729 in [#9236](#9236) ### 📦️ Dependency Updates - update actions/setup-node action to v6 by @renovate[bot] in [#9183](#9183) - update dependency @types/node to v25 by @renovate[bot] in [#9187](#9187) - update crazy-max/ghaction-import-gpg action to v7 by @renovate[bot] in [#9186](#9186) - update actions/cache action to v5 by @renovate[bot] in [#9181](#9181) - update amannn/action-semantic-pull-request action to v6 by @renovate[bot] in [#9184](#9184) - update apple-actions/import-codesign-certs action to v6 by @renovate[bot] in [#9185](#9185) - update dependency eslint to v10 by @renovate[bot] in [#9200](#9200) - update dependency toml to v4 by @renovate[bot] in [#9201](#9201) - update rust crate reqwest to 0.13 by @renovate[bot] in [#9171](#9171) - update ghcr.io/jdx/mise:deb docker digest to 523d826 by @renovate[bot] in [#9198](#9198) - update ghcr.io/jdx/mise:alpine docker digest to 05617e0 by @renovate[bot] in [#9196](#9196) - update ghcr.io/jdx/mise:rpm docker digest to c1992f9 by @renovate[bot] in [#9199](#9199) - update ghcr.io/jdx/mise:copr docker digest to 90db6cd by @renovate[bot] in [#9197](#9197) - update taiki-e/install-action digest to 58e8625 by @renovate[bot] in [#9209](#9209) - update fedora docker tag to v45 by @renovate[bot] in [#9213](#9213) - update docker/setup-buildx-action action to v4 by @renovate[bot] in [#9212](#9212) - update docker/metadata-action action to v6 by @renovate[bot] in [#9211](#9211) - update docker/login-action action to v4 by @renovate[bot] in [#9210](#9210) - update dependency typescript to v6 by @renovate[bot] in [#9202](#9202) - update docker/build-push-action action to v7 by @renovate[bot] in [#9203](#9203) - update github artifact actions (major) by @renovate[bot] in [#9215](#9215) - update rust crate duct to v1 by @renovate[bot] in [#9220](#9220) - update rust crate demand to v2 by @renovate[bot] in [#9219](#9219) - update rust crate clx to v2 by @renovate[bot] in [#9218](#9218) - update nick-fields/retry action to v4 by @renovate[bot] in [#9217](#9217) - update jdx/mise-action action to v4 by @renovate[bot] in [#9216](#9216) - update rust crate self_update to 0.44 by @renovate[bot] in [#9174](#9174) - migrate eslint config to flat format for v10 compat by @jdx in [#9222](#9222) - update actions/checkout action to v6 by @renovate[bot] in [#9182](#9182) - update rust crate toml to v1 by @renovate[bot] in [#9225](#9225) - update rust crate versions to v7 by @renovate[bot] in [#9226](#9226) - update rust crate which to v8 by @renovate[bot] in [#9227](#9227) - update rust crate rmcp to v1 by @renovate[bot] in [#9221](#9221) ### 📦 Registry - add sheldon by @3w36zj6 in [#9104](#9104) - add pocketbase by @ranfdev in [#9123](#9123) - add worktrunk ([aqua:max-sixty/worktrunk, cargo:worktrunk](https://github.com/max-sixty/worktrunk, cargo:worktrunk))#1 by @edouardr in [#8796](#8796) - add dependency-check ([aqua:dependency-check/DependencyCheck](https://github.com/dependency-check/DependencyCheck)) by @kapitoshka438 in [#9204](#9204) - add janet by @ranfdev in [#9241](#9241) ### New Contributors - @ranfdev made their first contribution in [#9241](#9241) - @jjt made their first contribution in [#9233](#9233) - @marianwolf made their first contribution in [#9208](#9208) - @edouardr made their first contribution in [#8796](#8796) ## 📦 Aqua Registry Updates #### New Packages (3) - [`LargeModGames/spotatui`](https://github.com/LargeModGames/spotatui) - [`android-sms-gateway/cli`](https://github.com/android-sms-gateway/cli) - [`velero-io/velero`](https://github.com/velero-io/velero) #### Updated Packages (1) - [`skim-rs/skim`](https://github.com/skim-rs/skim)
Supersedes and includes #9105.
Summary
install_beforeto transitive Python package resolution whenpipx:tools are installed through uv--exclude-newer <timestamp>flag on the uv install path--uploaded-prior-to <timestamp>flag throughpipx install --pip-argson the pipx fallback pathPIPX_SHARED_LIBSbefore invoking pipx so pipx falls back toPIPX_HOME/shareduv compatibility
--exclude-newerwas added in astral-sh/uv#2166 and is included in https://github.com/astral-sh/uv/releases/tag/0.1.15, so we do not need to check the uv version before using this flag.Why we can always use
--uploaded-prior-to--uploaded-prior-towas added in pypa/pip#13625 and released in pip 26.0, announced on January 31, 2026: https://discuss.python.org/t/announcement-pip-26-0-release/105947. PyPI currently lists pip 26.0.1 as the latest release as of April 17, 2026: https://pypi.org/project/pip/.pipx does not use a fixed bundled pip. It creates or reuses a shared virtual environment containing pip and ensures that shared pip is updated to the latest version: https://pipx.pypa.io/stable/how-pipx-works/. In the current pipx source, shared pip is installed/upgraded with
pip >= 23.1and no upper bound, so normal PyPI access gets the latest pip: https://github.com/pypa/pipx/blob/main/src/pipx/shared_libs.py.mise sets
PIPX_HOMEto the tool version's install path when invoking the pipx fallback. That install path is prepared before the backend runs: fresh installs create a new install directory, andmise install --forceuninstalls the existing version, removes the install path, and recreates it before callingpipx install. With pipx's default behavior,PIPX_SHARED_LIBSresolves to<install_path>/shared, which is new for that install and can be initialized/upgraded by pipx to the latest pip.Why unsetting
PIPX_SHARED_LIBSis acceptablePIPX_SHARED_LIBSis a real pipx override: it can point pipx's shared pip virtualenv outsidePIPX_HOME. pipx documents that it defaults toPIPX_HOME/shared, but can be overridden: https://pipx.pypa.io/stable/reference/environment-variables/. It can be useful for deliberate CI caching or ephemeralPIPX_HOMEsetups, but it only shares the packaging toolchain, especially pip. It does not make installed pipx apps share their application dependencies.For mise-managed
pipx:installs, inheriting an externalPIPX_SHARED_LIBSis unsafe for this feature because it can point at an older shared pip that does not support--uploaded-prior-to. It also makes the mise install path no longer self-contained, because pipx app venvs can contain absolute.pthpointers into that external shared libs path. Moving, deleting, or changing that external path can break laterpipx upgrade,pipx inject, orpipx runpipoperations.I could not find public discussions indicating that users commonly rely on
PIPX_SHARED_LIBSfor mise-managed pipx installs, and the default pipx guidance is to leave it unset unless there is a specific reason. So excluding this inherited variable for mise's pipx subprocesses should not be breaking for most users, and it avoids a footgun where a global shell/profile setting silently changes mise's install behavior.Related pipx docs for shared pip maintenance:
Tests
cargo fmt --checkgit diff --checkcargo test -q uv_exclude_newer_argscargo test -q pip_uploaded_prior_to_args