Skip to content

feat: runtime musl/glibc detection for correct libc variant selection#8490

Merged
jdx merged 6 commits intomainfrom
feat/runtime-musl-detection
Mar 7, 2026
Merged

feat: runtime musl/glibc detection for correct libc variant selection#8490
jdx merged 6 commits intomainfrom
feat/runtime-musl-detection

Conversation

@jdx
Copy link
Copy Markdown
Owner

@jdx jdx commented Mar 7, 2026

Summary

  • Detect musl libc at runtime (by checking for /lib/ld-musl-*) instead of using compile-time cfg!(target_env = "musl"), so a musl-built mise on a glibc system (or vice versa) selects the correct artifact variant
  • Set the musl qualifier on Platform::current() for musl systems, propagating through get_platform_key() and AssetPicker for correct lockfile keys and asset selection
  • Add linux-x64-musl and linux-arm64-musl to common_platforms() so mise lock resolves checksums/URLs for both glibc and musl variants
  • Add github:snyk/cli as a fallback registry backend for snyk

How it works with mise.lock

The lockfile now stores separate entries for glibc and musl:

[tools.snyk.platforms.linux-x64]
checksum = "sha256:abc..."
url = "https://github.com/snyk/cli/releases/.../snyk-linux"

[tools.snyk.platforms.linux-x64-musl]
checksum = "sha256:def..."
url = "https://github.com/snyk/cli/releases/.../snyk-alpine"

At install time, the runtime-detected platform key selects the correct entry. Existing lockfiles without musl entries gracefully fall through to normal asset resolution.

Backward compatibility

  • Existing lockfiles with only linux-x64 keys continue to work — musl systems just resolve assets normally (no lockfile cache hit) until mise lock is re-run
  • Non-Linux platforms are unaffected
  • The platform_variants mechanism (already used by bun) allows backends to opt into generating musl-specific lockfile entries

Closes #8456

Test plan

  • All existing unit tests pass (platform, asset_matcher, lockfile)
  • Lint checks pass
  • Verify on Alpine Linux that Platform::current() returns linux-x64-musl
  • Verify on glibc Linux that Platform::current() returns linux-x64 (no qualifier)
  • Test mise use github:snyk/cli on both musl and glibc systems

🤖 Generated with Claude Code


Note

Medium Risk
Changes platform-keying and libc selection logic that affects lockfile lookups and which binaries are downloaded on Linux, so regressions could cause missed lockfile hits or wrong artifact selection on some distros.

Overview
Improves Linux libc handling by detecting musl at runtime and propagating it into Platform::current() so lockfile platform keys can include a -musl qualifier when appropriate.

Expands default lockfile targeting from 5 to 7 platforms by adding linux-*-musl entries, updates Backend::get_platform_key() to use Platform::current().to_key(), and adjusts AssetPicker to avoid using compile-time cfg!(target_env = "musl") so cross-platform locking doesn’t leak the host libc into other targets.

Updates the registry entry for snyk to include github:snyk/cli as an additional backend option, and refreshes the mise lock e2e test expectations accordingly.

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

- Detect musl at runtime by checking for /lib/ld-musl-* instead of using
  compile-time cfg!(target_env = "musl"), so a musl-built mise on a glibc
  system (or vice versa) picks the correct artifact variant
- Set qualifier on Platform::current() for musl systems so lockfile keys
  and asset selection use the right libc variant
- Add linux-x64-musl and linux-arm64-musl to common_platforms() so
  mise lock resolves checksums/URLs for both glibc and musl variants
- Update get_platform_key() to include the qualifier from Platform::current()
- Add github:snyk/cli as a registry backend for snyk

Closes #8456

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, 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 mise's ability to handle different C standard library (libc) environments on Linux. By switching from compile-time to runtime detection of musl libc, it ensures that mise can correctly identify and select appropriate tool artifacts, improving compatibility and reliability across diverse Linux distributions, especially those using musl like Alpine. This change also updates how platform keys are generated and stored in lockfiles, allowing for distinct entries for glibc and musl variants.

Highlights

  • Runtime Libc Detection: Implemented runtime detection for musl libc on Linux systems, replacing compile-time checks to ensure correct artifact variant selection regardless of how mise itself was built.
  • Platform Qualifier Propagation: Platform::current() now sets a musl qualifier for musl systems, which is propagated through get_platform_key() and AssetPicker for accurate lockfile keys and asset selection.
  • Expanded Common Platforms: Added linux-x64-musl and linux-arm64-musl to common_platforms() to enable mise lock to resolve checksums and URLs for both glibc and musl variants.
  • Snyk Registry Fallback: Included github:snyk/cli as an additional fallback registry backend for Snyk.
Changelog
  • registry/snyk.toml
    • Added github:snyk/cli as a new backend for Snyk.
  • src/backend/asset_matcher.rs
    • Modified AssetPicker::with_libc to determine the libc variant at runtime using Platform::current() instead of relying on the cfg!(target_env = "musl") compile-time flag.
  • src/backend/mod.rs
    • Updated the default implementation of Backend::get_platform_key to use Platform::current().to_key(), ensuring that the libc qualifier (e.g., -musl) is included in the platform key.
  • src/platform.rs
    • Updated Platform::current() to perform runtime detection of musl libc on Linux by checking for ld-musl-* in /lib, and sets the qualifier field accordingly.
    • Introduced is_musl_system() function to encapsulate the logic for runtime musl detection.
    • Expanded Platform::common_platforms() to include linux-x64-musl and linux-arm64-musl variants.
    • Adjusted test_common_platforms to reflect the increased number of common platforms and assert the presence of the new musl variants.
Activity
  • All existing unit tests for platform, asset_matcher, and lockfile modules have passed.
  • Lint checks have passed.
  • Verification on Alpine Linux for Platform::current() returning linux-x64-musl is pending.
  • Verification on glibc Linux for Platform::current() returning linux-x64 is pending.
  • Testing mise use github:snyk/cli on both musl and glibc systems is pending.
  • The pull request was generated with Claude Code.
Using Gemini Code Assist

The 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 /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

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 .gemini/ folder in the base of the repository. Detailed instructions can be found here.

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

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces runtime detection of musl libc on Linux systems, which is a significant improvement over the previous compile-time check. This allows mise binaries to correctly select libc-specific tool variants regardless of how mise itself was compiled. The changes are well-structured, propagating the detected libc variant through the Platform struct to asset selection and lockfile key generation. The addition of musl variants to common_platforms ensures lockfiles can be generated for both glibc and musl environments. I have one suggestion to make the musl detection logic more concise and idiomatic.

Note: Security Review is unavailable for this PR.

src/platform.rs Outdated
Comment on lines +195 to +202
if let Ok(entries) = std::fs::read_dir("/lib") {
for entry in entries.flatten() {
if entry.file_name().to_string_lossy().starts_with("ld-musl-") {
return true;
}
}
}
false
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The logic for checking for musl can be made more concise and idiomatic by using iterator methods like any() and map() on the Result from read_dir.

        std::fs::read_dir("/lib")
            .map(|entries| {
                entries.flatten().any(|entry| {
                    entry
                        .file_name()
                        .to_string_lossy()
                        .starts_with("ld-musl-")
                })
            })
            .unwrap_or(false)

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR implements runtime musl/glibc detection on Linux so a glibc-compiled mise binary running on a musl system (e.g., Alpine) correctly selects musl-flavored artifacts. The implementation:

  • Detects musl by checking for glibc's dynamic linker (ld-linux-*) in /lib and /lib64 (if absent, confirms musl via ld-musl-* in /lib), replacing the previous ldd --version approach
  • Sets the musl qualifier in Platform::current() when running on musl, which propagates through get_platform_key() and AssetPicker for correct lockfile keys and asset selection
  • Expands common_platforms() from 5 to 7 entries with two new musl variants (linux-x64-musl, linux-arm64-musl) so mise lock captures both glibc and musl checksums
  • Defaults AssetPicker to gnu libc when unspecified (instead of compile-time musl detection) to avoid polluting cross-platform lockfile entries
  • Adds github:snyk/cli as a fallback registry backend for snyk

The runtime detection uses LazyLock for caching to avoid repeated filesystem checks. Existing lockfiles without musl entries continue to work due to is_compatible_with_current() matching by OS+arch only, providing graceful fallback until mise lock is re-run. All unit tests pass and e2e test_lock correctly verifies the 7 platform entries in lockfiles.

Confidence Score: 4/5

  • Safe to merge — runtime musl detection is correct and well-tested for primary use cases
  • The runtime musl/glibc detection implementation is sound. The code correctly uses filesystem checks to distinguish musl from glibc systems, with LazyLock caching to avoid repeated syscalls. Platform key generation properly incorporates the detected qualifier. The expansion of common_platforms to 7 entries (including musl variants) is correct. All existing tests pass and the test_lock updates verify the new lockfile entries are generated correctly. The implementation handles the main use case (Alpine Linux) correctly and maintains backward compatibility with existing lockfiles.
  • No files require special attention

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Platform::current called] --> B{OS is linux?}
    B -- No --> C[qualifier = None]
    B -- Yes --> D[Runtime libc detection\ncached via LazyLock]
    D --> E{glibc linker found\nin /lib or /lib64?}
    E -- Yes --> F[qualifier = None\nglibc system]
    E -- No --> G{musl linker found\nin /lib?}
    G -- Yes --> H[qualifier = musl]
    G -- No --> I[qualifier = None\nunknown libc]
    C --> J[to_key returns os-arch]
    F --> J
    I --> J
    H --> K[to_key returns os-arch-musl]
    J --> L[Backend::get_platform_key]
    K --> L
    L --> M[Lockfile lookup\nby platform key]
    L --> N[AssetMatcher.for_target\npropagates qualifier as libc]
    M --> O{Cache hit?}
    O -- Yes --> P[Use stored URL and checksum]
    O -- No --> Q[Resolve via release API]
Loading

Last reviewed commit: bd5f8d2

- Fix cross-platform lockfile bug: AssetPicker::with_libc now defaults
  to "gnu" when no qualifier is passed, instead of checking
  Platform::current(). This prevents `mise lock` on musl systems from
  incorrectly populating unqualified `linux-x64` entries with musl assets.
- Add ToolVersion::get_lock_platform() fallback: when looking up a
  qualified key like `linux-x64-musl`, falls back to the unqualified
  `linux-x64` key. This maintains backward compatibility with existing
  lockfiles in --locked mode.
- Update all lockfile read sites (github, java, locked-mode check) to
  use the new fallback method.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx
Copy link
Copy Markdown
Owner Author

jdx commented Mar 7, 2026

@greptileai

The aqua backend was still using tv.lock_platforms.get() directly
instead of tv.get_lock_platform(), missing the fallback from qualified
keys (linux-x64-musl) to unqualified keys (linux-x64) for pre-existing
lockfiles. This caused --locked mode to pass validation but then fail
to find the URL during aqua's actual install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove get_lock_platform() fallback from linux-x64-musl to linux-x64.
On musl systems with old lockfiles, this fallback would silently install
glibc binaries (which won't run) instead of either resolving fresh
(normal mode) or erroring with a helpful "run mise lock" message
(--locked mode). Both of those behaviors are correct and preferable
to silently installing the wrong binary.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

jdx and others added 2 commits March 7, 2026 02:22
- Replace /lib/ld-musl-* file check with ldd --version output parsing.
  The ld-musl linker file can exist on glibc systems with musl-tools
  installed for cross-compilation, causing false positive detection.
  ldd --version reliably distinguishes: musl's ldd prints "musl libc",
  glibc's prints GNU version info.
- Update e2e test_lock to expect 7 platforms (added musl variants).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace ldd --version subprocess with filesystem checks for the
dynamic linker. glibc systems always have /lib[64]/ld-linux-*, even
when musl-tools is installed for cross-compilation. Musl systems
(Alpine, Void musl, etc.) only have /lib/ld-musl-* without ld-linux-*.

Checking for the absence of glibc's linker first avoids false positives
and is faster than spawning a subprocess.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) March 7, 2026 02:36
@jdx jdx merged commit 08d3971 into main Mar 7, 2026
36 checks passed
@jdx jdx deleted the feat/runtime-musl-detection branch March 7, 2026 02:39
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 7, 2026

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.3 x -- echo 24.5 ± 0.5 23.7 30.1 1.00
mise x -- echo 25.2 ± 0.9 23.6 30.3 1.03 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.3 env 24.2 ± 0.8 22.9 29.3 1.00
mise env 25.4 ± 1.1 23.4 30.5 1.05 ± 0.06

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.3 hook-env 25.1 ± 1.0 23.9 30.8 1.01 ± 0.04
mise hook-env 24.8 ± 0.4 23.8 26.7 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.3.3 ls 24.2 ± 0.9 23.2 32.4 1.00
mise ls 24.3 ± 0.7 23.1 27.3 1.00 ± 0.05

xtasks/test/perf

Command mise-2026.3.3 mise Variance
install (cached) 156ms 154ms +1%
ls (cached) 85ms 84ms +1%
bin-paths (cached) 88ms 87ms +1%
task-ls (cached) 855ms 864ms -1%

mise-en-dev added a commit that referenced this pull request Mar 7, 2026
### 🚀 Features

- **(github)** keep exe extensions on Windows by @iki in
[#8424](#8424)
- **(task)** add `interactive` field for exclusive terminal access by
@jdx in [#8491](#8491)
- add header comment to generated lockfiles by @ivy in
[#8481](#8481)
- runtime musl/glibc detection for correct libc variant selection by
@jdx in [#8490](#8490)

### 🐛 Bug Fixes

- **(github)** use registry platform options during install by @jdx in
[#8492](#8492)
- **(http)** store tool opts as native TOML to fix platform switching by
@jdx in [#8448](#8448)
- **(installer)** error if MISE_INSTALL_PATH is a directory by @jdx in
[#8468](#8468)
- **(prepare)** resolve sources/outputs relative to `dir` when set by
@jdx in [#8472](#8472)
- **(ruby)** fetch precompiled binary by release tag instead of listing
all releases by @jdx in [#8488](#8488)
- **(schema)** support structured objects in task depends by @risu729 in
[#8463](#8463)
- **(task)** replace println!/eprintln! with calm_io in task output
macros by @vmaleze in [#8485](#8485)
- handle scoped npm package names without backend prefix by @jdx in
[#8477](#8477)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:copr docker digest to c485c4c by
@renovate[bot] in [#8484](#8484)
- update ghcr.io/jdx/mise:alpine docker digest to 8118bc7 by
@renovate[bot] in [#8483](#8483)

### 📦 Registry

- disable sd version test by @jdx in
[#8489](#8489)

### New Contributors

- @ivy made their first contribution in
[#8481](#8481)
- @iki made their first contribution in
[#8424](#8424)

## 📦 Aqua Registry Updates

#### New Packages (5)

- [`datadog-labs/pup`](https://github.com/datadog-labs/pup)
- [`k1LoW/mo`](https://github.com/k1LoW/mo)
- [`rtk-ai/rtk`](https://github.com/rtk-ai/rtk)
-
[`suzuki-shunsuke/docfresh`](https://github.com/suzuki-shunsuke/docfresh)
- [`yashikota/exiftool-go`](https://github.com/yashikota/exiftool-go)

#### Updated Packages (6)

- [`cloudflare/cloudflared`](https://github.com/cloudflare/cloudflared)
- [`mozilla/sccache`](https://github.com/mozilla/sccache)
- [`owenlamont/ryl`](https://github.com/owenlamont/ryl)
- [`spinel-coop/rv`](https://github.com/spinel-coop/rv)
-
[`technicalpickles/envsense`](https://github.com/technicalpickles/envsense)
- [`weaviate/weaviate`](https://github.com/weaviate/weaviate)
jdx pushed a commit that referenced this pull request Mar 7, 2026
## Summary

Expose the libc type detection added in #8490 to vfox plugins, in the
`RUNTIME` global table.

## Details

- Expose `RUNTIME.envType` to vfox Lua plugins (`"gnu"` for glibc Linux,
`"musl"` for musl Linux, `nil` otherwise)
- Add `env_type()` in `crates/vfox/src/config.rs` to detect libc at
runtime via `/lib` and `/lib64` linker names
- Add sync-note comments in `crates/vfox/src/config.rs` and
`src/platform.rs` to keep both implementations aligned
- Set `env_type` to `None` in `Runtime::with_platform()` because target
libc is unknown for platform overrides
- Document `RUNTIME.envType` in Lua types and plugin docs
- Add missing `RUNTIME.version` and `RUNTIME.pluginDirPath` entries to
`docs/tool-plugin-development.md` for parity

### Usage in plugins

```lua
if RUNTIME.envType == "musl" then
    -- download musl-compatible binary
elseif RUNTIME.envType == "gnu" then
    -- download glibc-compatible binary
end
```

## Design decisions

We intentionally duplicate musl/glibc detection in `crates/vfox` because
`vfox` is a separate crate boundary and sharing `is_musl_system()` would
add unnecessary coupling. The logic is small, self-contained, and
cross-referenced by sync notes in both locations to reduce drift.

## Test plan

- [x] Unit tests for `env_type()` return only valid values (`"gnu"`,
`"musl"`, or `nil`) on Linux and `nil` on non-Linux
- [x] Added regression test ensuring `RUNTIME.envType` is `nil` when
using platform override (`Runtime::with_platform`)
- [x] `cargo test` passes
- [x] Lint passes (`mise --cd crates/vfox run lint-fix`)

🤖 This PR description was generated with the assistance of Claude Opus
4.6.
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.

1 participant