Skip to content

fix(install): improve --locked support for python and ubi backends#8163

Merged
jdx merged 1 commit intomainfrom
fix/lockfile-backend-support
Feb 15, 2026
Merged

fix(install): improve --locked support for python and ubi backends#8163
jdx merged 1 commit intomainfrom
fix/lockfile-backend-support

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 15, 2026

Summary

  • core:python: Implement resolve_lock_info() so mise lock generates download URLs and SHA256 checksums from astral-sh/python-build-standalone releases, enabling --locked installs
  • ubi: Mark as not supporting lockfile URLs (supports_lockfile_url=false) since it's deprecated in favor of the github backend and doesn't resolve download URLs at lock time

Closes #7308

Test plan

  • Run mise lock python and verify mise.lock contains URL and checksum entries for python
  • Run mise install --locked with python in mise.toml and verify it installs successfully
  • Run mise lock with a ubi tool and verify it no longer errors on --locked mode

🤖 Generated with Claude Code


Note

Medium Risk
Changes affect lockfile URL/checksum generation and --locked install behavior for Python, and add network-dependent resolution logic; failures could break locked installs on some platforms.

Overview
core:python now implements platform-aware lock resolution so mise lock --platform … writes a deterministic download URL (from astral-sh/python-build-standalone) plus SHA256 checksum into mise.lock, enabling mise install --locked to work for Python without doing an install.

The deprecated ubi backend is explicitly marked as not supporting lockfile URLs so --locked no longer fails when a URL cannot be resolved at lock time. An e2e test (e2e/lockfile/test_lockfile_python) asserts the Python lockfile includes the expected release URL and sha256: entry.

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

Copilot AI review requested due to automatic review settings February 15, 2026 13:34
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @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 enhances the --locked installation functionality by providing robust lockfile generation for Python and gracefully handling the deprecated UBI backend. It ensures that Python installations can leverage locked dependencies with pre-calculated URLs and checksums, while preventing issues with UBI tools that no longer support this feature.

Highlights

  • Python Backend Lockfile Support: Implemented the resolve_lock_info() method for the Python backend. This enables mise lock to generate download URLs and SHA256 checksums for Python releases by querying astral-sh/python-build-standalone releases, thereby supporting --locked installs.
  • UBI Backend Lockfile URL Deprecation: Marked the UBI backend as not supporting lockfile URLs (supports_lockfile_url=false). This change prevents errors when using --locked mode with UBI tools, as the UBI backend is deprecated in favor of the GitHub backend and does not resolve download URLs at lock time.
Changelog
  • src/backend/ubi.rs
    • Added supports_lockfile_url method to explicitly disable lockfile URL support for the UBI backend.
  • src/plugins/core/python.rs
    • Imported fetch_checksum_from_shasums and PlatformInfo for lockfile resolution.
    • Added fetch_precompiled_for_target async function to retrieve precompiled Python version information for specific platform targets.
    • Implemented resolve_lock_info to generate download URLs and SHA256 checksums for Python versions from astral-sh/python-build-standalone releases.
    • Added helper functions python_os_for_target and python_arch_for_target to map platform targets to Python build strings.
Activity
  • jdx opened this pull request to address issue `--locked` doesn't work with all backends. #7308.
  • The initial commit introduces changes to src/backend/ubi.rs and src/plugins/core/python.rs to improve --locked support.
  • A test plan was provided, including steps to verify mise lock python generates correct lockfiles, mise install --locked works for Python, and mise lock no longer errors for UBI tools.
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
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 improves support for --locked installations for Python and UBI backends. For the deprecated UBI backend, it correctly disables lockfile URL support to prevent errors. For Python, it implements resolve_lock_info to fetch download URLs and checksums for precompiled versions, enabling locked installs across different platforms. The implementation is sound, but there is significant code duplication in the Python plugin between fetch_precompiled_for_target and fetch_precompiled_remote_versions. I've suggested a refactoring to improve maintainability.

Comment on lines +466 to +517
/// Fetch precompiled versions for a specific platform target (used by lockfile generation).
/// Unlike `fetch_precompiled_remote_versions` which uses compile-time cfg!() macros,
/// this takes a PlatformTarget to support cross-platform lockfile generation.
async fn fetch_precompiled_for_target(
&self,
version: &str,
target: &PlatformTarget,
) -> eyre::Result<Option<(String, String)>> {
let arch = python_arch_for_target(target);
let os = python_os_for_target(target);
let platform = format!("{arch}-{os}");
let url_path = format!("python-precompiled-{arch}-{os}.gz");
let rsp = HTTP_FETCH
.get_bytes(format!("https://mise-versions.jdx.dev/tools/{url_path}"))
.await?;
let mut decoder = GzDecoder::new(rsp.as_ref());
let mut raw = String::new();
decoder.read_to_string(&mut raw)?;
let rank = |v: &str, date: &str, name: &str| {
let rc = if regex!(r"rc\d+$").is_match(v) { 0 } else { 1 };
let v = Versioning::new(v);
let date = date.parse::<i64>().unwrap_or_default();
let install_type = if name.contains("install_only_stripped") {
0
} else if name.contains("install_only") {
1
} else {
2
};
(v, rc, -date, install_type)
};
let result = raw
.lines()
.filter(|v| v.contains(&platform))
.flat_map(|v| {
regex!(r"^cpython-(\d+\.\d+\.[\da-z]+)\+(\d+).*")
.captures(v)
.map(|caps| {
(
caps[1].to_string(),
caps[2].to_string(),
caps[0].to_string(),
)
})
})
.sorted_by_cached_key(|(v, date, name)| rank(v, date, name))
.unique_by(|(v, _, _)| v.to_string())
.rev()
.find(|(v, _, _)| v == version)
.map(|(_, tag, filename)| (tag, filename));
Ok(result)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

This function fetch_precompiled_for_target has a lot of duplicated logic from fetch_precompiled_remote_versions. To improve maintainability and reduce code duplication, consider refactoring the common logic into a shared helper function. This helper could take the raw version data and platform string, and return a processed iterator of versions. Both fetch_precompiled_for_target and fetch_precompiled_remote_versions could then use this helper to perform their specific filtering and collection logic.

Copy link

@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 2 potential issues.

Bugbot Autofix is ON, but a Cloud Agent failed to start.

"windows" => "pc-windows-msvc",
_ => "unknown-linux-gnu",
}
}
Copy link

Choose a reason for hiding this comment

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

Linux musl qualifier ignored in OS mapping function

Medium Severity

python_os_for_target hardcodes "unknown-linux-gnu" for all non-macos, non-windows targets, ignoring the platform qualifier. When the PlatformTarget has a "musl" qualifier (e.g., linux-x64-musl), this function still returns "unknown-linux-gnu" instead of "unknown-linux-musl". This causes resolve_lock_info to generate lockfile URLs pointing to glibc Python binaries for musl platforms. The existing python_os function correctly uses built_info::CFG_ENV to distinguish gnu/musl, and the PlatformTarget exposes qualifier() for exactly this purpose, but it's not consulted here.

Fix in Cursor Fix in Web

Copy link
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 implements lockfile support for Python and marks UBI as not supporting lockfile URLs. It adds resolve_lock_info() to the Python backend to generate download URLs and SHA256 checksums from python-build-standalone releases, enabling mise lock to create lockfiles for Python tools. It also marks the UBI backend as not supporting lockfile URLs since it's deprecated.

Changes:

  • Implemented resolve_lock_info() for Python backend to generate lockfile metadata with URLs and checksums
  • Added helper functions python_os_for_target() and python_arch_for_target() to map platform targets to python-build-standalone naming conventions
  • Marked UBI backend as not supporting lockfile URLs via supports_lockfile_url() override

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/plugins/core/python.rs Added resolve_lock_info() implementation and helper functions to generate Python lockfile metadata from astral-sh/python-build-standalone releases
src/backend/ubi.rs Added supports_lockfile_url() override to return false since UBI is deprecated and doesn't resolve download URLs

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

/// Map a PlatformTarget arch to the python-build-standalone arch string.
fn python_arch_for_target(target: &PlatformTarget) -> &'static str {
match target.arch_name() {
"arm64" => "aarch64",
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The catch-all case in this function incorrectly maps the "x86" architecture (32-bit) to "x86_64" (64-bit). This could cause lockfile generation to fail or produce incorrect URLs for 32-bit platforms. Consider explicitly matching "x64" to "x86_64" and handling "x86" separately, possibly returning an error or "i686" if python-build-standalone supports it. The existing python_arch function at line 736 returns other for unrecognized architectures, preserving their original value.

Suggested change
"arm64" => "aarch64",
"arm64" => "aarch64",
"x86" => "i686",

Copilot uses AI. Check for mistakes.
fn python_os_for_target(target: &PlatformTarget) -> &'static str {
match target.os_name() {
"macos" => "apple-darwin",
"windows" => "pc-windows-msvc",
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The OS mapping doesn't handle the musl libc qualifier for Linux platforms. When a user specifies a platform like "linux-x64-musl", the qualifier is ignored and the function returns "unknown-linux-gnu". Python-build-standalone does provide musl variants (e.g., "x86_64-unknown-linux-musl"). Consider checking target.qualifier() and appending "-musl" to the base string when the qualifier is "musl", similar to how the Bun backend handles this in get_bun_arch_for_target (see src/plugins/core/bun.rs:317-331).

Suggested change
"windows" => "pc-windows-msvc",
"windows" => "pc-windows-msvc",
"linux" => {
if target.qualifier() == "musl" {
"unknown-linux-musl"
} else {
"unknown-linux-gnu"
}
}

Copilot uses AI. Check for mistakes.
Python: implement resolve_lock_info() so `mise lock` generates URLs and
checksums from astral-sh/python-build-standalone releases, enabling
--locked installs. Selects install_only_stripped tarballs (matching the
install behavior) with SHA256 checksums.

UBI: mark as not supporting lockfile URLs (supports_lockfile_url=false)
since it's deprecated in favor of the github backend and doesn't resolve
download URLs at lock time.

Add e2e test verifying python lockfile URL generation.

Closes #7308

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jdx jdx force-pushed the fix/lockfile-backend-support branch from 44dd526 to 444d620 Compare February 15, 2026 13:42
@jdx jdx enabled auto-merge (squash) February 15, 2026 13:53
@jdx jdx merged commit 4359a75 into main Feb 15, 2026
35 checks passed
@jdx jdx deleted the fix/lockfile-backend-support branch February 15, 2026 13:54
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 x -- echo 25.0 ± 1.3 23.2 41.2 1.00
mise x -- echo 25.3 ± 1.6 23.7 49.2 1.01 ± 0.08

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 env 24.6 ± 1.0 23.0 30.8 1.00
mise env 24.8 ± 1.3 23.1 41.7 1.01 ± 0.07

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 hook-env 25.3 ± 1.2 23.8 44.2 1.02 ± 0.06
mise hook-env 24.7 ± 0.9 23.3 34.3 1.00

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.13 ls 22.6 ± 0.5 21.7 26.0 1.00
mise ls 23.0 ± 0.5 21.8 24.6 1.02 ± 0.03

xtasks/test/perf

Command mise-2026.2.13 mise Variance
install (cached) 130ms 127ms +2%
ls (cached) 79ms 79ms +0%
bin-paths (cached) 83ms 83ms +0%
task-ls (cached) 838ms 816ms +2%

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

- **(vfox)** allow plugins to request env var redaction via
MiseEnvResult by @jdx in [#8166](#8166)
- add a default_host setting for rust by @aacebedo in
[#8154](#8154)
- add github_content package support for aqua backend by @risu729 in
[#8147](#8147)
- support devEngines.runtime in deno by @risu729 in
[#8144](#8144)

### 🐛 Bug Fixes

- **(asset_matcher)** penalize vsix files by @risu729 in
[#8151](#8151)
- **(edit)** strip formatting whitespace from TOML values in `mise edit`
by @jdx in [#8162](#8162)
- **(install)** improve --locked support for python and ubi backends by
@jdx in [#8163](#8163)
- **(npm)** suppress npm update notifier while npm install by @risu729
in [#8152](#8152)
- **(schema)** add task_templates, extends, and timeout by @risu729 in
[#8145](#8145)

### 🚜 Refactor

- **(registry)** remove [key=value] options syntax from backends by
@risu729 in [#8146](#8146)

### 📚 Documentation

- **(settings)** remove wrong tip for github_attestations by @risu729 in
[#8158](#8158)

### Chore

- **(release-plz)** delete embedded plugins directory before update by
@risu729 in [#8149](#8149)
- adds necessary env var to the mcp help message in cli by @joaommartins
in [#8133](#8133)

### New Contributors

- @joaommartins made their first contribution in
[#8133](#8133)

## 📦 Aqua Registry Updates

#### New Packages (5)

- [`containers/podlet`](https://github.com/containers/podlet)
-
[`hickford/git-credential-azure`](https://github.com/hickford/git-credential-azure)
-
[`hickford/git-credential-oauth`](https://github.com/hickford/git-credential-oauth)
- [`kovetskiy/mark`](https://github.com/kovetskiy/mark)
- [`openbao/openbao/bao`](https://github.com/openbao/openbao/bao)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
…dx#8163)

## Summary
- **core:python**: Implement `resolve_lock_info()` so `mise lock`
generates download URLs and SHA256 checksums from
astral-sh/python-build-standalone releases, enabling `--locked` installs
- **ubi**: Mark as not supporting lockfile URLs
(`supports_lockfile_url=false`) since it's deprecated in favor of the
github backend and doesn't resolve download URLs at lock time

Closes jdx#7308

## Test plan
- [ ] Run `mise lock python` and verify `mise.lock` contains URL and
checksum entries for python
- [ ] Run `mise install --locked` with python in `mise.toml` and verify
it installs successfully
- [ ] Run `mise lock` with a ubi tool and verify it no longer errors on
`--locked` mode

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes affect lockfile URL/checksum generation and `--locked` install
behavior for Python, and add network-dependent resolution logic;
failures could break locked installs on some platforms.
> 
> **Overview**
> `core:python` now implements platform-aware lock resolution so `mise
lock --platform …` writes a deterministic download URL (from
`astral-sh/python-build-standalone`) plus SHA256 checksum into
`mise.lock`, enabling `mise install --locked` to work for Python without
doing an install.
> 
> The deprecated `ubi` backend is explicitly marked as not supporting
lockfile URLs so `--locked` no longer fails when a URL cannot be
resolved at lock time. An e2e test (`e2e/lockfile/test_lockfile_python`)
asserts the Python lockfile includes the expected release URL and
`sha256:` entry.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
444d620. 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>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- **(vfox)** allow plugins to request env var redaction via
MiseEnvResult by @jdx in [jdx#8166](jdx#8166)
- add a default_host setting for rust by @aacebedo in
[jdx#8154](jdx#8154)
- add github_content package support for aqua backend by @risu729 in
[jdx#8147](jdx#8147)
- support devEngines.runtime in deno by @risu729 in
[jdx#8144](jdx#8144)

### 🐛 Bug Fixes

- **(asset_matcher)** penalize vsix files by @risu729 in
[jdx#8151](jdx#8151)
- **(edit)** strip formatting whitespace from TOML values in `mise edit`
by @jdx in [jdx#8162](jdx#8162)
- **(install)** improve --locked support for python and ubi backends by
@jdx in [jdx#8163](jdx#8163)
- **(npm)** suppress npm update notifier while npm install by @risu729
in [jdx#8152](jdx#8152)
- **(schema)** add task_templates, extends, and timeout by @risu729 in
[jdx#8145](jdx#8145)

### 🚜 Refactor

- **(registry)** remove [key=value] options syntax from backends by
@risu729 in [jdx#8146](jdx#8146)

### 📚 Documentation

- **(settings)** remove wrong tip for github_attestations by @risu729 in
[jdx#8158](jdx#8158)

### Chore

- **(release-plz)** delete embedded plugins directory before update by
@risu729 in [jdx#8149](jdx#8149)
- adds necessary env var to the mcp help message in cli by @joaommartins
in [jdx#8133](jdx#8133)

### New Contributors

- @joaommartins made their first contribution in
[jdx#8133](jdx#8133)

## 📦 Aqua Registry Updates

#### New Packages (5)

- [`containers/podlet`](https://github.com/containers/podlet)
-
[`hickford/git-credential-azure`](https://github.com/hickford/git-credential-azure)
-
[`hickford/git-credential-oauth`](https://github.com/hickford/git-credential-oauth)
- [`kovetskiy/mark`](https://github.com/kovetskiy/mark)
- [`openbao/openbao/bao`](https://github.com/openbao/openbao/bao)
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