Skip to content

fix(pipx): ensure Python minor version symlink exists for postinstall hooks#7869

Merged
jdx merged 4 commits intomainfrom
fix/pipx-postinstall-symlink
Jan 28, 2026
Merged

fix(pipx): ensure Python minor version symlink exists for postinstall hooks#7869
jdx merged 4 commits intomainfrom
fix/pipx-postinstall-symlink

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Jan 28, 2026

Summary

  • Fixes a timing issue where postinstall hooks for pipx packages fail when using uvx
  • When uvx creates a venv, it uses a Python symlink pointing to the minor version path (e.g., python/3.12/)
  • However, this symlink is only created by runtime_symlinks::rebuild(), which runs AFTER postinstall hooks
  • This causes postinstall hooks that invoke the installed tool to fail with "bad interpreter" errors
  • The fix creates the minor version symlink early in fix_venv_python_symlink() if it doesn't exist

Test plan

  • Added e2e test test_pipx_postinstall_hook that installs a pipx package with a postinstall hook invoking the tool
  • Test passes locally
  • Linting passes

Fixes #7864

🤖 Generated with Claude Code


Note

Ensures pipx postinstall hooks work with uvx by creating the Python minor-version symlink early.

  • Adds ensure_minor_version_symlink() in src/backend/pipx.rs and invokes it from fix_venv_python_symlink() to create python/X.Y -> ./X.Y.Z before running postinstall hooks
  • Updates venv symlink fix-up to use the minor-version path when the target exists
  • Adds e2e e2e/backend/test_pipx_postinstall_hook validating that a postinstall hook invoking mkdocs succeeds and that python/3.12 exists

Written by Cursor Bugbot for commit f044462. This will update automatically on new commits. Configure here.

… 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>
Copilot AI review requested due to automatic review settings January 28, 2026 14:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.12python/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.

Comment thread src/backend/pipx.rs Outdated
#[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+)/");
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
@jdx
Copy link
Copy Markdown
Owner Author

jdx commented Jan 28, 2026

bugbot run

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/backend/pipx.rs Outdated
- 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>
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 28, 2026

Hyperfine Performance

mise x -- echo

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%

@jdx jdx enabled auto-merge (squash) January 28, 2026 15:25
@jdx jdx merged commit 2fdc11b into main Jan 28, 2026
35 checks passed
@jdx jdx deleted the fix/pipx-postinstall-symlink branch January 28, 2026 15:31
mise-en-dev added a commit that referenced this pull request Jan 28, 2026
### 🚀 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants