fix(pipx): ensure Python minor version symlink exists for postinstall hooks#7869
fix(pipx): ensure Python minor version symlink exists for postinstall hooks#7869
Conversation
… hooks When installing pipx packages via uvx, the venv Python symlink is converted to use the minor version path (e.g., python/3.12 instead of python/3.12.1). However, this minor version symlink is only created by runtime_symlinks::rebuild(), which runs AFTER postinstall hooks. This caused postinstall hooks that invoke the installed tool to fail with "bad interpreter" errors because the venv Python symlink pointed to a path that didn't exist yet. The fix creates the minor version symlink early in fix_venv_python_symlink() if it doesn't already exist, ensuring the venv symlink works immediately for postinstall hooks. Fixes #7864 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a timing issue where pipx postinstall hooks fail when using uvx due to missing Python minor version symlinks. The fix ensures the minor version symlink (e.g., python/3.12 → python/3.12.1) is created early during venv setup, before postinstall hooks execute.
Changes:
- Added
ensure_minor_version_symlink()function to create minor version symlinks early if they don't exist - Modified
fix_venv_python_symlink()to call the new function before postinstall hooks run - Added e2e test validating that postinstall hooks can invoke installed tools
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/backend/pipx.rs | Implements early creation of Python minor version symlink to fix postinstall hook timing issue |
| e2e/backend/test_pipx_postinstall_hook | Adds test verifying postinstall hooks work with uvx when invoking installed tools |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #[cfg(unix)] | ||
| fn ensure_minor_version_symlink(full_version_path: &Path) -> Result<()> { | ||
| // Extract version components from path like .../python/3.12.1/bin/python3 | ||
| let re = regex!(r"/python/(\d+\.\d+)\.(\d+)/"); |
There was a problem hiding this comment.
The regex pattern differs from the one in path_with_minor_version() which uses r\"/python/(\\d+)\\.(\\d+)\\.\\d+/\". While functionally similar, the inconsistency makes maintenance harder. Consider extracting a shared regex constant or using the same pattern format for consistency.
|
bugbot run |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
- Use same regex pattern as path_with_minor_version() for consistency - Use "./" prefix in symlink target (e.g., "./3.12.1" instead of "3.12.1") to match runtime_symlinks convention and allow is_runtime_symlink() to properly identify it for cleanup/updates Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.8 x -- echo |
15.0 ± 0.4 | 14.1 | 16.9 | 1.00 |
mise x -- echo |
15.4 ± 0.4 | 14.6 | 16.8 | 1.03 ± 0.03 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.8 env |
14.6 ± 0.4 | 13.8 | 16.0 | 1.00 |
mise env |
15.1 ± 0.5 | 14.0 | 22.6 | 1.03 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.8 hook-env |
15.0 ± 0.5 | 14.0 | 18.5 | 1.00 |
mise hook-env |
15.4 ± 0.4 | 14.4 | 18.3 | 1.03 ± 0.04 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.1.8 ls |
13.8 ± 0.6 | 12.8 | 21.7 | 1.00 |
mise ls |
14.3 ± 0.4 | 13.3 | 15.7 | 1.03 ± 0.05 |
xtasks/test/perf
| Command | mise-2026.1.8 | mise | Variance |
|---|---|---|---|
| install (cached) | 84ms | 82ms | +2% |
| ls (cached) | 54ms | 54ms | +0% |
| bin-paths (cached) | 55ms | 56ms | -1% |
| task-ls (cached) | 231ms | 231ms | +0% |
### 🚀 Features - **(doctor)** add backend mismatch warnings by @jdx in [#7847](#7847) - **(http)** add rename_exe support for archive extraction by @jdx in [#7874](#7874) - **(http)** send x-mise-ci header for CI environment tracking by @jdx in [#7875](#7875) - **(install)** auto-install plugins from [plugins] config section by @jdx in [#7856](#7856) - **(registry)** add vercel by @mikecurtis in [#7844](#7844) - **(task)** support glob patterns in task_config.includes by @jdx in [#7870](#7870) - **(task)** add task templates for reusable task definitions by @jdx in [#7873](#7873) ### 🐛 Bug Fixes - **(backend)** change registry mismatch log from info to debug by @jdx in [#7858](#7858) - **(ci)** use squash merge for auto-merge-release workflow by @jdx in [7e5e71e](7e5e71e) - **(ci)** remove --auto flag to merge immediately when CI passes by @jdx in [23ed2ed](23ed2ed) - **(github)** select platform-matching provenance file for SLSA verification by @jdx in [#7853](#7853) - **(go)** filter out version "1" from available versions by @jdx in [#7871](#7871) - **(install)** skip CurDir components when detecting archive structure by @jdx in [#7868](#7868) - **(pipx)** ensure Python minor version symlink exists for postinstall hooks by @jdx in [#7869](#7869) - **(registry)** prevent duplicate -stable suffix in Flutter download URLs by @jdx in [#7872](#7872) - **(task)** pass env to usage parser for env-backed arguments by @jdx in [#7848](#7848) - **(task)** propagate MISE_ENV to child tasks when using -E flag by @jdx in [06ee776](06ee776) - **(vfox-dotnet)** use os.execute() to fix Windows installation by @prodrigues1912 in [#7843](#7843) ### 📚 Documentation - update cache-behavior with env_cache information by @jdx in [#7849](#7849) ###◀️ Revert - remove task inheritance from parent configs in monorepos by @jdx in [#7851](#7851) - Revert "fix(ci): remove --auto flag to merge immediately when CI passes" by @jdx in [0606187](0606187) ### 📦 Registry - add mago ([aqua:carthage-software/mago](https://github.com/carthage-software/mago)) by @scop in [#7845](#7845) ### Chore - **(ci)** auto-merge release branch into main daily at 4am CST by @jdx in [#7852](#7852) ### New Contributors - @mikecurtis made their first contribution in [#7844](#7844) - @prodrigues1912 made their first contribution in [#7843](#7843)
Summary
python/3.12/)runtime_symlinks::rebuild(), which runs AFTER postinstall hooksfix_venv_python_symlink()if it doesn't existTest plan
test_pipx_postinstall_hookthat installs a pipx package with a postinstall hook invoking the toolFixes #7864
🤖 Generated with Claude Code
Note
Ensures pipx postinstall hooks work with uvx by creating the Python minor-version symlink early.
ensure_minor_version_symlink()insrc/backend/pipx.rsand invokes it fromfix_venv_python_symlink()to createpython/X.Y -> ./X.Y.Zbefore running postinstall hookse2e/backend/test_pipx_postinstall_hookvalidating that a postinstall hook invokingmkdocssucceeds and thatpython/3.12existsWritten by Cursor Bugbot for commit f044462. This will update automatically on new commits. Configure here.