fix(backend): improve conda patchelf and dependency resolution for complex packages#8087
fix(backend): improve conda patchelf and dependency resolution for complex packages#8087
Conversation
Improve Linux library path fixing for conda packages with large dependency trees (ffmpeg, ghc, vim). Changes: - Discover all .so directories and include them in RPATH - Fix ELF interpreter if it points to a conda build path - Log patchelf errors instead of silently ignoring them - Add conda backends for ffmpeg, ghc, and vim Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary of ChangesHello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the Highlights
Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Pull request overview
Improve Linux conda package ELF patching to better handle complex dependency trees (e.g., ffmpeg/ghc/vim) by expanding RPATH handling, fixing broken interpreters, and adding conda backends/tests in the registry.
Changes:
- Discover all shared-library directories and generate comprehensive
$ORIGIN-relative RPATHs. - Detect and replace conda build-time ELF interpreters with local/system linkers.
- Add conda backends (and lightweight tests) for ffmpeg, ghc, and vim registry entries.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/backend/conda_linux.rs | Adds directory discovery for .so files, builds multi-entry RPATH, and patches interpreters with debug logging. |
| registry/vim.toml | Prefers conda backend and adds a version-based smoke test. |
| registry/ghc.toml | Prefers conda backend. |
| registry/ffmpeg.toml | Prefers conda backend and adds a version-based smoke test. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/backend/conda_linux.rs
Outdated
| fn build_rpath(path: &Path, install_dir: &Path, lib_dirs: &[String]) -> String { | ||
| let install_str = install_dir.to_str().unwrap_or(""); | ||
|
|
||
| // Start with the standard entries | ||
| let mut entries = Vec::new(); | ||
|
|
||
| if path.parent().is_some_and(|p| p.ends_with("bin")) { | ||
| "$ORIGIN/../lib" | ||
| } else if path.parent().is_some_and(|p| p.ends_with("lib")) { | ||
| "$ORIGIN" | ||
| } else { | ||
| // For other locations, use absolute path | ||
| lib_dir.to_str().unwrap_or("$ORIGIN/../lib") | ||
| entries.push("$ORIGIN/../lib".to_string()); | ||
| } else if let Some(parent) = path.parent() { | ||
| // For libraries, add $ORIGIN so they can find siblings | ||
| entries.push("$ORIGIN".to_string()); | ||
| // Also add path to lib/ from wherever this file is | ||
| if let Ok(rel) = parent.strip_prefix(install_dir) { | ||
| let depth = rel.components().count(); | ||
| if depth > 0 { | ||
| let up = "../".repeat(depth); | ||
| entries.push(format!("$ORIGIN/{}lib", up)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Add all discovered lib directories as $ORIGIN-relative paths | ||
| for lib_dir in lib_dirs { | ||
| if let Some(rel) = lib_dir.strip_prefix(install_str) { | ||
| let rel = rel.trim_start_matches('/'); | ||
| if let Some(parent) = path.parent() { | ||
| if let Ok(from_parent) = parent.strip_prefix(install_dir) { | ||
| let depth = from_parent.components().count(); | ||
| let up = "../".repeat(depth); | ||
| let entry = format!("$ORIGIN/{}{}", up, rel); | ||
| if !entries.contains(&entry) { | ||
| entries.push(entry); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
install_dir.to_str().unwrap_or("") can yield an empty string for non-UTF8 paths; in that case, strip_prefix("") will always succeed and may generate invalid/unsafe RPATH entries (because every string has the empty prefix). Prefer keeping lib_dirs as PathBuf and using Path::strip_prefix(install_dir) (path semantics) end-to-end; only convert to string at the final formatting step, and skip entries that cannot be represented safely.
src/backend/conda_linux.rs
Outdated
| for system_linker in &[ | ||
| "/lib64/ld-linux-x86-64.so.2", | ||
| "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2", | ||
| "/lib/ld-linux-x86-64.so.2", | ||
| "/lib/ld-linux-aarch64.so.1", | ||
| "/lib64/ld-linux-aarch64.so.1", | ||
| ] { | ||
| if Path::new(system_linker).exists() { | ||
| let result = Command::new("patchelf") | ||
| .args(["--set-interpreter", system_linker, path_str]) | ||
| .output(); | ||
| if let Ok(o) = result { | ||
| if !o.status.success() { | ||
| debug!( | ||
| "patchelf --set-interpreter failed for {}: {}", | ||
| path_str, | ||
| String::from_utf8_lossy(&o.stderr) | ||
| ); | ||
| } | ||
| } | ||
| break; | ||
| } | ||
| } |
There was a problem hiding this comment.
The loop breaks after the first existing system linker even if patchelf --set-interpreter fails (non-zero exit). This can leave the binary unpatched even though a later candidate might work. Consider breaking only on successful patchelf execution (status success), otherwise continue trying additional linker paths; similarly, if a local linker exists but setting it fails, consider attempting the system fallback list.
| fn set_rpath(path: &Path, rpath: &str) { | ||
| let _ = Command::new("patchelf") | ||
| .args(["--set-rpath", rpath, path.to_str().unwrap_or("")]) | ||
| let path_str = path.to_str().unwrap_or(""); | ||
| let result = Command::new("patchelf") | ||
| .args(["--set-rpath", rpath, path_str]) | ||
| .output(); | ||
| if let Ok(o) = result { | ||
| if !o.status.success() { | ||
| debug!( | ||
| "patchelf --set-rpath failed for {}: {}", | ||
| path_str, | ||
| String::from_utf8_lossy(&o.stderr) | ||
| ); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
set_rpath logs only when patchelf runs and returns a non-zero status; if spawning/executing patchelf fails (e.g., permission/ENOENT mid-run, sandboxing), the Err case is silently ignored. Add a debug log for the Err(e) branch as well (same for the Command::new("patchelf").output() calls in fix_interpreter) to make troubleshooting consistent with the stated intent of improving error logging.
| // Discover all directories containing .so files for comprehensive RPATH | ||
| let lib_dirs = find_lib_dirs(install_dir); | ||
|
|
||
| // Find all ELF files | ||
| let files = find_binary_files(install_dir, is_elf_file); | ||
|
|
||
| for file_path in files { | ||
| let rpath = determine_rpath(&file_path, &lib_dir); | ||
| set_rpath(&file_path, rpath); | ||
| for file_path in &files { | ||
| fix_interpreter(file_path, install_dir); | ||
| let rpath = build_rpath(file_path, install_dir, &lib_dirs); | ||
| set_rpath(file_path, &rpath); | ||
| } |
There was a problem hiding this comment.
This executes multiple patchelf subprocesses per ELF file (--print-interpreter + --set-interpreter sometimes + --set-rpath), which can become very expensive for large installs. A tangible optimization is to only call fix_interpreter for likely executables (e.g., files under bin/, or files with the executable bit set) and/or to avoid calling --print-interpreter unless needed; this keeps the RPATH work intact but reduces the number of subprocess invocations significantly.
There was a problem hiding this comment.
Code Review
This is a great pull request that significantly improves the conda backend's library path fixing on Linux, enabling support for complex packages like ffmpeg, ghc, and vim. The new logic for discovering library directories, building a comprehensive RPATH, and fixing ELF interpreters is a solid enhancement. I've added a few suggestions to further improve the robustness of the implementation, mainly concerning the handling of non-UTF8 file paths, which is a possibility on Linux systems.
src/backend/conda_linux.rs
Outdated
| fn find_lib_dirs(install_dir: &Path) -> Vec<String> { | ||
| let mut dirs = std::collections::HashSet::new(); | ||
| for entry in WalkDir::new(install_dir).into_iter().filter_map(|e| e.ok()) { | ||
| let path = entry.path(); | ||
| if path.is_file() { | ||
| if let Some(name) = path.file_name().and_then(|n| n.to_str()) { | ||
| if name.contains(".so") { | ||
| if let Some(parent) = path.parent() { | ||
| dirs.insert(parent.to_path_buf()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| let mut sorted: Vec<_> = dirs.into_iter().collect(); | ||
| sorted.sort(); | ||
| sorted | ||
| .into_iter() | ||
| .filter_map(|d| d.to_str().map(|s| s.to_string())) | ||
| .collect() | ||
| } |
There was a problem hiding this comment.
This function converts PathBuf to String and filters out non-UTF8 paths. This can be problematic on Linux where paths are not guaranteed to be UTF-8. It's more robust to work with PathBuf directly, which also simplifies the function. Note that this change will require build_rpath to be updated to accept &[PathBuf].
fn find_lib_dirs(install_dir: &Path) -> Vec<PathBuf> {
let mut dirs = std::collections::HashSet::new();
for entry in WalkDir::new(install_dir).into_iter().filter_map(|e| e.ok()) {
let path = entry.path();
if path.is_file() {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
if name.contains(".so") {
if let Some(parent) = path.parent() {
dirs.insert(parent.to_path_buf());
}
}
}
}
}
let mut sorted: Vec<_> = dirs.into_iter().collect();
sorted.sort();
sorted
}
src/backend/conda_linux.rs
Outdated
| fn build_rpath(path: &Path, install_dir: &Path, lib_dirs: &[String]) -> String { | ||
| let install_str = install_dir.to_str().unwrap_or(""); | ||
|
|
||
| // Start with the standard entries | ||
| let mut entries = Vec::new(); | ||
|
|
||
| if path.parent().is_some_and(|p| p.ends_with("bin")) { | ||
| "$ORIGIN/../lib" | ||
| } else if path.parent().is_some_and(|p| p.ends_with("lib")) { | ||
| "$ORIGIN" | ||
| } else { | ||
| // For other locations, use absolute path | ||
| lib_dir.to_str().unwrap_or("$ORIGIN/../lib") | ||
| entries.push("$ORIGIN/../lib".to_string()); | ||
| } else if let Some(parent) = path.parent() { | ||
| // For libraries, add $ORIGIN so they can find siblings | ||
| entries.push("$ORIGIN".to_string()); | ||
| // Also add path to lib/ from wherever this file is | ||
| if let Ok(rel) = parent.strip_prefix(install_dir) { | ||
| let depth = rel.components().count(); | ||
| if depth > 0 { | ||
| let up = "../".repeat(depth); | ||
| entries.push(format!("$ORIGIN/{}lib", up)); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Add all discovered lib directories as $ORIGIN-relative paths | ||
| for lib_dir in lib_dirs { | ||
| if let Some(rel) = lib_dir.strip_prefix(install_str) { | ||
| let rel = rel.trim_start_matches('/'); | ||
| if let Some(parent) = path.parent() { | ||
| if let Ok(from_parent) = parent.strip_prefix(install_dir) { | ||
| let depth = from_parent.components().count(); | ||
| let up = "../".repeat(depth); | ||
| let entry = format!("$ORIGIN/{}{}", up, rel); | ||
| if !entries.contains(&entry) { | ||
| entries.push(entry); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| entries.join(":") | ||
| } |
There was a problem hiding this comment.
Following the suggested change in find_lib_dirs to return Vec<PathBuf>, this function should be updated. Using install_dir.to_str().unwrap_or("") is risky and can lead to incorrect RPATHs if the path contains non-UTF8 characters. Using Path::strip_prefix is the correct and more robust way to handle this.
fn build_rpath(path: &Path, install_dir: &Path, lib_dirs: &[PathBuf]) -> String {
let mut entries = Vec::new();
if path.parent().is_some_and(|p| p.ends_with("bin")) {
entries.push("$ORIGIN/../lib".to_string());
} else if let Some(parent) = path.parent() {
// For libraries, add $ORIGIN so they can find siblings
entries.push("$ORIGIN".to_string());
// Also add path to lib/ from wherever this file is
if let Ok(rel) = parent.strip_prefix(install_dir) {
let depth = rel.components().count();
if depth > 0 {
let up = "../".repeat(depth);
entries.push(format!("$ORIGIN/{}lib", up));
}
}
}
// Add all discovered lib directories as $ORIGIN-relative paths
for lib_dir in lib_dirs {
if let Ok(rel_path) = lib_dir.strip_prefix(install_dir) {
if let Some(rel) = rel_path.to_str() {
if let Some(parent) = path.parent() {
if let Ok(from_parent) = parent.strip_prefix(install_dir) {
let depth = from_parent.components().count();
let up = "../".repeat(depth);
let entry = format!("$ORIGIN/{}{}", up, rel);
if !entries.contains(&entry) {
entries.push(entry);
}
}
}
}
}
}
entries.join(":")
}
src/backend/conda_linux.rs
Outdated
|
|
||
| /// Fix ELF interpreter if it points to a conda build path | ||
| fn fix_interpreter(path: &Path, install_dir: &Path) { | ||
| let path_str = path.to_str().unwrap_or(""); |
There was a problem hiding this comment.
Using unwrap_or("") can hide issues with non-UTF8 paths. If path is not valid UTF-8, path_str will be an empty string, and patchelf will fail silently or operate on the wrong file. It's better to handle the None case explicitly by logging and returning early.
let Some(path_str) = path.to_str() else {
debug!("Skipping non-UTF8 path in fix_interpreter: {}", path.display());
return;
};
src/backend/conda_linux.rs
Outdated
| fn set_rpath(path: &Path, rpath: &str) { | ||
| let _ = Command::new("patchelf") | ||
| .args(["--set-rpath", rpath, path.to_str().unwrap_or("")]) | ||
| let path_str = path.to_str().unwrap_or(""); |
There was a problem hiding this comment.
Similar to other parts of this file, unwrap_or("") can hide issues with non-UTF8 paths. If path is not valid UTF-8, path_str will be an empty string, and patchelf will fail silently. It's better to handle the None case explicitly.
let Some(path_str) = path.to_str() else {
debug!("Skipping non-UTF8 path in set_rpath: {}", path.display());
return;
};- Use PathBuf instead of String for find_lib_dirs, use
Path::strip_prefix for RPATH computation
- Handle non-UTF8 paths with early returns instead of unwrap_or("")
- Support aarch64 local linker (not just x86-64)
- Only break system linker loop on successful patchelf, try next
candidate on failure
- Log Err branches (spawn failures) in all patchelf calls
- Only call fix_interpreter for files in bin/libexec (perf: skip
shared libs that don't have PT_INTERP)
- Extract run_set_interpreter helper for cleaner control flow
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d4edd42 to
239dd67
Compare
|
bugbot run |
Use `name.ends_with(".so") || name.contains(".so.")` instead of
`name.contains(".so")` to avoid false positives like dbus.socket,
config.sorted, etc. that would add spurious directories to RPATH.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
bac10cc to
bf3ec60
Compare
|
bugbot run |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 x -- echo |
21.9 ± 0.4 | 20.9 | 23.9 | 1.00 |
mise x -- echo |
22.2 ± 0.6 | 21.1 | 26.5 | 1.02 ± 0.03 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 env |
21.6 ± 0.8 | 20.6 | 28.7 | 1.00 |
mise env |
21.8 ± 0.6 | 20.6 | 23.8 | 1.01 ± 0.05 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 hook-env |
22.0 ± 0.4 | 21.0 | 23.3 | 1.00 |
mise hook-env |
22.5 ± 0.6 | 21.3 | 25.0 | 1.03 ± 0.03 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.9 ls |
20.0 ± 0.3 | 19.4 | 21.1 | 1.00 |
mise ls |
20.5 ± 0.7 | 19.4 | 27.3 | 1.02 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.2.9 | mise | Variance |
|---|---|---|---|
| install (cached) | 119ms | 119ms | +0% |
| ls (cached) | 73ms | 73ms | +0% |
| bin-paths (cached) | 77ms | 77ms | +0% |
| task-ls (cached) | 532ms | 530ms | +0% |
Conda packages use a long `_h_env_placehold` prefix that gets replaced at install time. Since we extract packages directly (bypassing conda's install machinery), shell script wrappers retain hardcoded build paths and fail with exit code 127. This fixes ghc which uses shell script wrappers in bin/ that exec paths under the build-time prefix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
bugbot run |
Sysroot directories contain compilation stub libraries (libc.so, etc.) from the conda sysroot package. Including them in RPATH causes the bundled libc to be loaded instead of the system one, resulting in symbol lookup errors (e.g. __rtld_global_ro_memset_non_temporal_threshold). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stop forward scan at quotes, newlines, spaces, colons (not just /) in extract_placeholder_prefix to avoid overshooting past line boundaries - Exclude sysroot directories from RPATH discovery to prevent loading conda-bundled libc stubs instead of system libc Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
vim dynamically links against libpython3.10.so and libperl.so, so skipping these packages causes exit code 127 at runtime. Remove them from SKIP_PACKAGES since tools that declare these as dependencies genuinely need the shared libraries. Also fix potential panic in extract_placeholder_prefix when the delimiter character before a conda path is multi-byte UTF-8, and improve test_tool diagnostics by showing captured output on exit code 127. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix doc list item indentation warning in conda.rs SKIP_PACKAGES comment - Remove vim from conda backend (still fails with exit 127 despite patchelf improvements — likely needs LD_LIBRARY_PATH or deeper fixes) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| if let Err(e) = std::fs::write(path, new_text) { | ||
| debug!("failed to fix prefix in {}: {e}", path.display()); | ||
| } | ||
| } |
There was a problem hiding this comment.
Text prefix replacement handles only one build prefix
Medium Severity
fix_text_prefixes calls find_conda_prefix once, which returns the first _h_env_placehold-based prefix it discovers. It then replaces only that specific prefix string across all files. Since each conda dependency package is independently built with its own unique build prefix path, text files from other dependency packages (containing different prefixes) silently pass the !text.contains(&prefix) check and are never fixed. For complex packages with large dependency trees — exactly the PR's target use case — this means shell scripts, config files, or wrappers originating from dependency packages retain broken hardcoded paths.
Additional Locations (1)
Conda build strings encode which dependency versions a package was built against (e.g., py310 = Python 3.10). Use these pins to constrain dependency resolution so we install the correct version instead of the latest matching the loose spec (e.g., >=3.10 would pick 3.15). This fixes vim's conda backend which needs libpython3.10 but was getting Python 3.15 installed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| @@ -1,2 +1,2 @@ | |||
| backends = ["asdf:mise-plugins/mise-vim"] | |||
| backends = ["conda:vim", "asdf:mise-plugins/mise-vim"] | |||
There was a problem hiding this comment.
Known-broken conda:vim backend added as default
High Severity
The conda:vim backend is added as the first entry in vim.toml, but the PR description explicitly states under "Not included" that vim "still fails with exit code 127 despite patchelf improvements." Since backends().first() is used throughout the codebase as the default backend for tool installation (e.g., in backend_arg.rs), users installing vim will default to the known-broken conda backend. This will cause vim installations to fail or degrade to a slower fallback path.
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [#8106](#8106) - **(env)** add `tools` variable to tera template context by @jdx in [#8108](#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [#8110](#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [#8087](#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [#8100](#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [#8109](#8109) - **(github)** skip v prefix for "latest" version by @jdx in [#8105](#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [#8084](#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [#8081](#8081) - **(registry)** add Linux support for tuist by @fortmarek in [#8102](#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [#8086](#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [#8099](#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [#8101](#8101) - SLSA for in-toto statement with no signatures by @gerhard in [#8094](#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [#8035](#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [#8112](#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [#8080](#8080) - add conda backends for 10 asdf-only tools by @jdx in [#8083](#8083) - added podman-tui by @tony-sol in [#8098](#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [#8111](#8111) ### New Contributors - @ctaintor made their first contribution in [#8106](#8106) - @rileychh made their first contribution in [#8112](#8112) - @fortmarek made their first contribution in [#8102](#8102) - @pose made their first contribution in [#8035](#8035) - @gerhard made their first contribution in [#8094](#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [#8106](#8106) - **(env)** add `tools` variable to tera template context by @jdx in [#8108](#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [#8110](#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [#8087](#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [#8100](#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [#8109](#8109) - **(github)** skip v prefix for "latest" version by @jdx in [#8105](#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [#8084](#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [#8081](#8081) - **(registry)** add Linux support for tuist by @fortmarek in [#8102](#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [#8086](#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [#8099](#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [#8101](#8101) - SLSA for in-toto statement with no signatures by @gerhard in [#8094](#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [#8035](#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [#8112](#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [#8080](#8080) - add conda backends for 10 asdf-only tools by @jdx in [#8083](#8083) - added podman-tui by @tony-sol in [#8098](#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [#8111](#8111) ### New Contributors - @ctaintor made their first contribution in [#8106](#8106) - @rileychh made their first contribution in [#8112](#8112) - @fortmarek made their first contribution in [#8102](#8102) - @pose made their first contribution in [#8035](#8035) - @gerhard made their first contribution in [#8094](#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
…mplex packages (jdx#8087) ## Summary - Improve Linux library path fixing (patchelf RPATH) for conda packages with large dependency trees - Pin conda dependency versions from build strings to install the correct library versions - Add conda backends for ffmpeg, ghc, and vim ## Changes to `conda_linux.rs` ### Comprehensive RPATH Previously, RPATH was set to only `$ORIGIN/../lib` for binaries and `$ORIGIN` for libraries. Complex packages like ffmpeg have shared libraries spread across multiple subdirectories. Now we: - Discover all directories containing `.so` files (using precise `.so`/`.so.` matching) - Build RPATH strings that include all library directories using `$ORIGIN`-relative paths ### Interpreter fixing If a conda binary has a hardcoded ELF interpreter pointing to a conda build path (e.g. `/home/conda/feedstock_root/.../ld-linux-x86-64.so.2`), the binary can't load at all (exit code 127). Now we: - Detect conda build-time interpreter paths - Replace with a local linker from the install dir if available, or fall back to the system linker - Support both x86-64 and aarch64 architectures ### Error logging Previously, all patchelf errors were silently swallowed (`let _ = ...`). Now they are logged at debug level for troubleshooting, including spawn failures. ### Performance Only call `fix_interpreter` for files in `bin/`/`libexec/`, skipping shared libraries that don't have `PT_INTERP`. ## Changes to `conda.rs` - Build string version pinning ### Problem Conda build strings encode which dependency versions a package was built against (e.g., `py310` = Python 3.10, `pl5321` = Perl 5.32). However, the dependency resolver was ignoring this and installing the latest version matching a loose spec like `>=3.10`, which would pick Python 3.15 instead of 3.10. ### Fix Parse build string prefixes (`py`, `pl`) to extract version pins, then use them to constrain dependency resolution. Falls back to the original version spec if the pinned version isn't available. Example: vim package `vim-9.1.1858-py310pl5321h03900fa_0` now correctly gets Python 3.10.* instead of 3.15. ## Registry updates | Tool | conda package | Test command | |------|--------------|-------------| | ffmpeg | `ffmpeg` | `ffmpeg -version` | | ghc | `ghc` | `ghc --version` | | vim | `vim` | `vim --version` | ## Context These tools were removed from jdx#8083 because they failed with exit code 127 (shared library resolution failure). This PR improves both the patchelf mechanism and the dependency resolver to handle their complex dependency trees. ## Test plan - [x] `mise run lint` passes - [x] `mise run build` compiles successfully - [x] `mise x conda:vim -- vim --version` works locally (installs Python 3.10 correctly) - [x] CI `mise test-tool` for ffmpeg — passed - [x] CI `mise test-tool` for ghc — passed - [ ] CI `mise test-tool` for vim — pending 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches conda install/dependency resolution and Linux binary patching (`patchelf`), which can affect runtime behavior across many packages; changes are targeted but broad in impact if the heuristics mis-detect pins/prefixes or generate incorrect RPATH/interpreter settings. > > **Overview** > Improves conda-based installs for complex Linux packages by making `patchelf` fixes more robust: builds RPATHs from *all* discovered shared-library directories (excluding sysroots), optionally rewrites hardcoded ELF interpreters pointing at conda build paths, and logs `patchelf` failures instead of silently swallowing them. > > Enhances conda dependency resolution to honor build-string version pins (e.g. `py310` → Python 3.10) and stops skipping runtime `python`/`perl`/`ruby` packages so their shared libs can be present when tools link against them; also replaces conda placeholder prefixes in extracted text files. > > Adds `conda:` as a backend option for `ffmpeg`, `ghc`, and `vim` in the registry (with a new `ffmpeg` smoke test), and improves `mise test-tool` diagnostics by printing captured output for exit code `127`. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 8d801e0. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [jdx#8106](jdx#8106) - **(env)** add `tools` variable to tera template context by @jdx in [jdx#8108](jdx#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [jdx#8110](jdx#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [jdx#8087](jdx#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [jdx#8100](jdx#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [jdx#8109](jdx#8109) - **(github)** skip v prefix for "latest" version by @jdx in [jdx#8105](jdx#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [jdx#8084](jdx#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [jdx#8081](jdx#8081) - **(registry)** add Linux support for tuist by @fortmarek in [jdx#8102](jdx#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [jdx#8086](jdx#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [jdx#8099](jdx#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [jdx#8101](jdx#8101) - SLSA for in-toto statement with no signatures by @gerhard in [jdx#8094](jdx#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [jdx#8035](jdx#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [jdx#8112](jdx#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [jdx#8080](jdx#8080) - add conda backends for 10 asdf-only tools by @jdx in [jdx#8083](jdx#8083) - added podman-tui by @tony-sol in [jdx#8098](jdx#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [jdx#8111](jdx#8111) ### New Contributors - @ctaintor made their first contribution in [jdx#8106](jdx#8106) - @rileychh made their first contribution in [jdx#8112](jdx#8112) - @fortmarek made their first contribution in [jdx#8102](jdx#8102) - @pose made their first contribution in [jdx#8035](jdx#8035) - @gerhard made their first contribution in [jdx#8094](jdx#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)
### 🚀 Features - **(activate)** add shims directory as fallback when auto-install is enabled by @ctaintor in [jdx#8106](jdx#8106) - **(env)** add `tools` variable to tera template context by @jdx in [jdx#8108](jdx#8108) - **(set)** add --stdin flag for multiline environment variables by @jdx in [jdx#8110](jdx#8110) ### 🐛 Bug Fixes - **(backend)** improve conda patchelf and dependency resolution for complex packages by @jdx in [jdx#8087](jdx#8087) - **(ci)** fix validate-new-tools grep pattern for test field by @jdx in [jdx#8100](jdx#8100) - **(config)** make MISE_OFFLINE work correctly by gracefully skipping network calls by @jdx in [jdx#8109](jdx#8109) - **(github)** skip v prefix for "latest" version by @jdx in [jdx#8105](jdx#8105) - **(gitlab)** resolve tool options from config for aliased tools by @jdx in [jdx#8084](jdx#8084) - **(install)** use version_expr for Flutter to fix version resolution by @jdx in [jdx#8081](jdx#8081) - **(registry)** add Linux support for tuist by @fortmarek in [jdx#8102](jdx#8102) - **(release)** write release notes to file instead of capturing stdout by @jdx in [jdx#8086](jdx#8086) - **(upgrade)** tools are not uninstalled properly due to outdated symlink by @roele in [jdx#8099](jdx#8099) - **(upgrade)** ensure uninstallation failure does not leave invalid symlinks by @roele in [jdx#8101](jdx#8101) - SLSA for in-toto statement with no signatures by @gerhard in [jdx#8094](jdx#8094) - Vfox Plugin Auto-Installation for Environment Directives by @pose in [jdx#8035](jdx#8035) ### 📚 Documentation - use mise activate for PowerShell in getting-started by @rileychh in [jdx#8112](jdx#8112) ### 📦 Registry - add conda backend for mysql by @jdx in [jdx#8080](jdx#8080) - add conda backends for 10 asdf-only tools by @jdx in [jdx#8083](jdx#8083) - added podman-tui by @tony-sol in [jdx#8098](jdx#8098) ### Chore - sort settings.toml alphabetically and add test by @jdx in [jdx#8111](jdx#8111) ### New Contributors - @ctaintor made their first contribution in [jdx#8106](jdx#8106) - @rileychh made their first contribution in [jdx#8112](jdx#8112) - @fortmarek made their first contribution in [jdx#8102](jdx#8102) - @pose made their first contribution in [jdx#8035](jdx#8035) - @gerhard made their first contribution in [jdx#8094](jdx#8094) ## 📦 Aqua Registry Updates #### New Packages (2) - [`entireio/cli`](https://github.com/entireio/cli) - [`rmitchellscott/reManager`](https://github.com/rmitchellscott/reManager) #### Updated Packages (1) - [`atuinsh/atuin`](https://github.com/atuinsh/atuin)


Summary
Changes to
conda_linux.rsComprehensive RPATH
Previously, RPATH was set to only
$ORIGIN/../libfor binaries and$ORIGINfor libraries. Complex packages like ffmpeg have shared libraries spread across multiple subdirectories. Now we:.sofiles (using precise.so/.so.matching)$ORIGIN-relative pathsInterpreter fixing
If a conda binary has a hardcoded ELF interpreter pointing to a conda build path (e.g.
/home/conda/feedstock_root/.../ld-linux-x86-64.so.2), the binary can't load at all (exit code 127). Now we:Error logging
Previously, all patchelf errors were silently swallowed (
let _ = ...). Now they are logged at debug level for troubleshooting, including spawn failures.Performance
Only call
fix_interpreterfor files inbin//libexec/, skipping shared libraries that don't havePT_INTERP.Changes to
conda.rs- Build string version pinningProblem
Conda build strings encode which dependency versions a package was built against (e.g.,
py310= Python 3.10,pl5321= Perl 5.32). However, the dependency resolver was ignoring this and installing the latest version matching a loose spec like>=3.10, which would pick Python 3.15 instead of 3.10.Fix
Parse build string prefixes (
py,pl) to extract version pins, then use them to constrain dependency resolution. Falls back to the original version spec if the pinned version isn't available.Example: vim package
vim-9.1.1858-py310pl5321h03900fa_0now correctly gets Python 3.10.* instead of 3.15.Registry updates
ffmpegffmpeg -versionghcghc --versionvimvim --versionContext
These tools were removed from #8083 because they failed with exit code 127 (shared library resolution failure). This PR improves both the patchelf mechanism and the dependency resolver to handle their complex dependency trees.
Test plan
mise run lintpassesmise run buildcompiles successfullymise x conda:vim -- vim --versionworks locally (installs Python 3.10 correctly)mise test-toolfor ffmpeg — passedmise test-toolfor ghc — passedmise test-toolfor vim — pending🤖 Generated with Claude Code
Note
Medium Risk
Touches conda install/dependency resolution and Linux binary patching (
patchelf), which can affect runtime behavior across many packages; changes are targeted but broad in impact if the heuristics mis-detect pins/prefixes or generate incorrect RPATH/interpreter settings.Overview
Improves conda-based installs for complex Linux packages by making
patchelffixes more robust: builds RPATHs from all discovered shared-library directories (excluding sysroots), optionally rewrites hardcoded ELF interpreters pointing at conda build paths, and logspatchelffailures instead of silently swallowing them.Enhances conda dependency resolution to honor build-string version pins (e.g.
py310→ Python 3.10) and stops skipping runtimepython/perl/rubypackages so their shared libs can be present when tools link against them; also replaces conda placeholder prefixes in extracted text files.Adds
conda:as a backend option forffmpeg,ghc, andvimin the registry (with a newffmpegsmoke test), and improvesmise test-tooldiagnostics by printing captured output for exit code127.Written by Cursor Bugbot for commit 8d801e0. This will update automatically on new commits. Configure here.