fix(shim): update mise-shim.exe during self-update on Windows#8075
Conversation
After the main binary update succeeds on Windows, download the same release zip and extract mise-shim.exe alongside the updated mise.exe. This is non-fatal — if the shim update fails, a warning is emitted but the overall self-update still succeeds. Fixes the issue where `mise self-update` only updated mise.exe but left mise-shim.exe at the old version, requiring manual download. 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 enhances the "mise" self-update mechanism specifically for Windows users. It resolves a previous inconsistency by ensuring that when "mise" updates itself, its associated "mise-shim.exe" is also automatically updated from the release archive. This provides a more seamless and complete update experience, preventing users from needing to manually manage shim versions. 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
Updates Windows self-update behavior so that mise self-update refreshes both mise.exe and the native mise-shim.exe from the same release archive, while keeping shim failures non-fatal.
Changes:
- Print updated version using a stored
Stringfor reuse. - Add Windows-only logic to download the release ZIP and extract
mise-shim.exe. - Emit warnings (without failing the overall update) when shim update fails or shim is missing from the archive.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/cli/self_update.rs
Outdated
| tokio::task::block_in_place(|| { | ||
| tokio::runtime::Handle::current().block_on(HTTP.download_file(&url, &zip_path, None)) | ||
| })?; |
There was a problem hiding this comment.
tokio::task::block_in_place and tokio::runtime::Handle::current() will panic if this code is executed outside a Tokio runtime (or inside a current-thread runtime where block_in_place is unsupported). Consider making update_mise_shim async and await the download from the existing async context, or use a runtime-agnostic approach (e.g., if a Tokio handle isn't available, create a dedicated runtime to block_on, or provide a synchronous download path).
| tokio::task::block_in_place(|| { | |
| tokio::runtime::Handle::current().block_on(HTTP.download_file(&url, &zip_path, None)) | |
| })?; | |
| if let Ok(handle) = tokio::runtime::Handle::try_current() { | |
| handle.block_on(HTTP.download_file(&url, &zip_path, None))?; | |
| } else { | |
| let rt = tokio::runtime::Runtime::new()?; | |
| rt.block_on(HTTP.download_file(&url, &zip_path, None))?; | |
| } |
src/cli/self_update.rs
Outdated
| let mut buf = Vec::new(); | ||
| shim_entry.read_to_end(&mut buf)?; | ||
| xx::file::write(&dest, &buf)?; |
There was a problem hiding this comment.
Writing the shim by buffering the entire file and then writing directly to the final destination increases the chance of leaving a partially-written mise-shim.exe if the process is interrupted mid-write. Prefer an atomic update pattern (write to a temporary file in the same directory, then rename/replace), and ideally stream from the ZIP entry into the temp file to avoid the extra buffering.
There was a problem hiding this comment.
Code Review
This pull request adds a helpful feature to update mise-shim.exe on Windows during a self-update. The overall approach of downloading the release archive and extracting the shim is correct. However, I've identified a critical bug in the URL construction that would cause the download to fail. Additionally, I've suggested an improvement to make the new function asynchronous for better performance and idiomatic code, which requires changes in two places. My review includes code suggestions to address these points.
| #[cfg(windows)] | ||
| fn update_mise_shim(version: &str) -> Result<()> { | ||
| use crate::http::HTTP; | ||
| use std::io::Read; | ||
|
|
||
| let url = format!( | ||
| "https://github.com/jdx/mise/releases/download/{version}/mise-{version}-{}-{}.zip", | ||
| *OS, *ARCH, | ||
| ); | ||
| debug!("Downloading mise-shim.exe from {url}"); | ||
|
|
||
| let temp_dir = tempfile::tempdir()?; | ||
| let zip_path = temp_dir.path().join("mise.zip"); | ||
| tokio::task::block_in_place(|| { | ||
| tokio::runtime::Handle::current().block_on(HTTP.download_file(&url, &zip_path, None)) | ||
| })?; | ||
|
|
||
| let file = fs::File::open(&zip_path)?; | ||
| let mut archive = zip::ZipArchive::new(file)?; | ||
|
|
||
| let mut shim_entry = match archive.by_name("mise/bin/mise-shim.exe") { | ||
| Ok(entry) => entry, | ||
| Err(_) => { | ||
| warn!("mise-shim.exe not found in release archive, skipping"); | ||
| return Ok(()); | ||
| } | ||
| }; | ||
|
|
||
| let dest = env::MISE_BIN | ||
| .parent() | ||
| .expect("MISE_BIN should have a parent directory") | ||
| .join("mise-shim.exe"); | ||
| let mut buf = Vec::new(); | ||
| shim_entry.read_to_end(&mut buf)?; | ||
| xx::file::write(&dest, &buf)?; | ||
|
|
||
| debug!("Updated mise-shim.exe at {}", dest.display()); | ||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
This new function has a critical bug and a potential improvement:
- Incorrect URL (Critical): The URL for downloading the release archive is constructed incorrectly. Release tags and asset names on GitHub for
miseare prefixed with av(e.g.,v2024.5.1), but theversionvariable here does not include it. This will cause downloads to fail with a 404 error. - Blocking in async context (Improvement): The function is synchronous and uses
tokio::task::block_in_place. Since it's called from anasynccontext, it's better to make itasyncandawaitthe download directly.
The suggestion below addresses both points. Note that you will also need to await the call to this function in run(), for which I've left a separate comment.
#[cfg(windows)]
async fn update_mise_shim(version: &str) -> Result<()> {
use crate::http::HTTP;
use std::io::Read;
let url = format!(
"https://github.com/jdx/mise/releases/download/v{version}/mise-v{version}-{}-{}.zip",
*OS, *ARCH,
);
debug!("Downloading mise-shim.exe from {url}");
let temp_dir = tempfile::tempdir()?;
let zip_path = temp_dir.path().join("mise.zip");
HTTP.download_file(&url, &zip_path, None).await?;
let file = fs::File::open(&zip_path)?;
let mut archive = zip::ZipArchive::new(file)?;
let mut shim_entry = match archive.by_name("mise/bin/mise-shim.exe") {
Ok(entry) => entry,
Err(_) => {
warn!("mise-shim.exe not found in release archive, skipping");
return Ok(());
}
};
let dest = env::MISE_BIN
.parent()
.expect("MISE_BIN should have a parent directory")
.join("mise-shim.exe");
let mut buf = Vec::new();
shim_entry.read_to_end(&mut buf)?;
xx::file::write(&dest, &buf)?;
debug!("Updated mise-shim.exe at {}", dest.display());
Ok(())
}
src/cli/self_update.rs
Outdated
| if let Err(e) = Self::update_mise_shim(&version) { | ||
| warn!("Failed to update mise-shim.exe: {e}"); | ||
| } |
There was a problem hiding this comment.
To accommodate the suggested change of making update_mise_shim an async function, this call needs to be awaited.
| if let Err(e) = Self::update_mise_shim(&version) { | |
| warn!("Failed to update mise-shim.exe: {e}"); | |
| } | |
| if let Err(e) = Self::update_mise_shim(&version).await { | |
| warn!warn("Failed to update mise-shim.exe: {e}"); | |
| } |
Hyperfine Performance
|
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.8 x -- echo |
23.6 ± 0.9 | 22.9 | 36.2 | 1.00 |
mise x -- echo |
23.9 ± 0.4 | 23.0 | 26.3 | 1.01 ± 0.04 |
mise env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.8 env |
23.1 ± 0.5 | 22.4 | 27.3 | 1.00 |
mise env |
23.3 ± 0.9 | 22.6 | 37.0 | 1.01 ± 0.04 |
mise hook-env
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.8 hook-env |
23.7 ± 0.4 | 23.0 | 25.8 | 1.00 |
mise hook-env |
23.9 ± 0.3 | 23.2 | 24.9 | 1.01 ± 0.02 |
mise ls
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative |
|---|---|---|---|---|
mise-2026.2.8 ls |
22.0 ± 0.7 | 21.2 | 31.1 | 1.00 |
mise ls |
22.1 ± 0.4 | 21.5 | 23.5 | 1.00 ± 0.04 |
xtasks/test/perf
| Command | mise-2026.2.8 | mise | Variance |
|---|---|---|---|
| install (cached) | 124ms | 125ms | +0% |
| ls (cached) | 77ms | 77ms | +0% |
| bin-paths (cached) | 81ms | 81ms | +0% |
| task-ls (cached) | 555ms | 555ms | +0% |
- Strip 'v' prefix from version before reconstructing URL with explicit 'v' prefix, ensuring correct download URL regardless of whether status.version() includes the prefix - Make update_mise_shim async and await the download instead of using block_in_place, since run() is already async Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
||
| let temp_dir = tempfile::tempdir()?; | ||
| let zip_path = temp_dir.path().join("mise.zip"); | ||
| HTTP.download_file(&url, &zip_path, None).await?; |
There was a problem hiding this comment.
Release archive downloaded twice during Windows self-update
Low Severity
update_mise_shim re-downloads the entire release zip archive that was already downloaded and processed by the self_update library in do_update_blocking. On Windows, every mise self-update invocation downloads the same (potentially large) archive twice — once for mise.exe and once for mise-shim.exe.
Addresses PR review feedback: - Verify the downloaded zip archive signature using zipsign before extracting mise-shim.exe, matching the verification done for mise.exe - Use atomic write (temp file + rename) to avoid leaving a partially written mise-shim.exe if the process is interrupted - Name the temp zip with the real archive name so zipsign signing context matches Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
### 🚀 Features - auto-select no-YJIT Ruby on older glibc systems by @jdx in [#8069](#8069) ### 🐛 Bug Fixes - **(shim)** update mise-shim.exe during self-update on Windows by @jdx in [#8075](#8075) - Bump xx to 2.5 by @erickt in [#8077](#8077) ### 📚 Documentation - **(ruby)** remove experimental language for precompiled binaries by @jdx in [#8073](#8073) ### New Contributors - @erickt made their first contribution in [#8077](#8077) ## 📦 Aqua Registry Updates #### Updated Packages (1) - [`carthage-software/mago`](https://github.com/carthage-software/mago)
## Summary - After `mise self-update` updates `mise.exe` on Windows, also extract and update `mise-shim.exe` from the same release archive - Non-fatal: if the shim update fails, a warning is emitted but the overall self-update still succeeds - Uses existing `zip`, `tempfile`, and `HTTP` client — no new dependencies Fixes the issue where `mise self-update` only updated `mise.exe` but left `mise-shim.exe` at the old version, requiring users to manually download it from GitHub releases. Reported after jdx#8045 added native `.exe` shim mode. ## Test plan - [ ] Verify `cargo clippy --workspace` passes (confirmed locally) - [ ] Test on Windows: `mise self-update --force` updates both `mise.exe` and `mise-shim.exe` - [ ] Test on Windows: if `mise-shim.exe` is missing from the archive, a warning is printed but the update succeeds - [ ] Verify non-Windows builds are unaffected (all new code is `#[cfg(windows)]`) 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches the Windows self-update path and adds new download/extract/replace logic for an executable; failures are non-fatal but a bug could leave the shim outdated or not replaced as expected. > > **Overview** > On Windows, `mise self-update` now also updates `mise-shim.exe` after updating `mise.exe` by downloading the matching release `.zip`, verifying its `zipsign` signature, extracting `mise/bin/mise-shim.exe`, and atomically replacing the local shim (non-fatal on failure). > > This adds a Windows-only dependency on `zipsign-api` and updates `Cargo.lock` accordingly, plus a small tweak to version styling output to reuse the string value. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 7267f26. 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>
### 🚀 Features - auto-select no-YJIT Ruby on older glibc systems by @jdx in [jdx#8069](jdx#8069) ### 🐛 Bug Fixes - **(shim)** update mise-shim.exe during self-update on Windows by @jdx in [jdx#8075](jdx#8075) - Bump xx to 2.5 by @erickt in [jdx#8077](jdx#8077) ### 📚 Documentation - **(ruby)** remove experimental language for precompiled binaries by @jdx in [jdx#8073](jdx#8073) ### New Contributors - @erickt made their first contribution in [jdx#8077](jdx#8077) ## 📦 Aqua Registry Updates #### Updated Packages (1) - [`carthage-software/mago`](https://github.com/carthage-software/mago)
It's now using mise-shim.exe jdx/mise#8075


Summary
mise self-updateupdatesmise.exeon Windows, also extract and updatemise-shim.exefrom the same release archivezip,tempfile, andHTTPclient — no new dependenciesFixes the issue where
mise self-updateonly updatedmise.exebut leftmise-shim.exeat the old version, requiring users to manually download it from GitHub releases. Reported after #8045 added native.exeshim mode.Test plan
cargo clippy --workspacepasses (confirmed locally)mise self-update --forceupdates bothmise.exeandmise-shim.exemise-shim.exeis missing from the archive, a warning is printed but the update succeeds#[cfg(windows)])🤖 Generated with Claude Code
Note
Medium Risk
Touches the Windows self-update path and adds new download/extract/replace logic for an executable; failures are non-fatal but a bug could leave the shim outdated or not replaced as expected.
Overview
On Windows,
mise self-updatenow also updatesmise-shim.exeafter updatingmise.exeby downloading the matching release.zip, verifying itszipsignsignature, extractingmise/bin/mise-shim.exe, and atomically replacing the local shim (non-fatal on failure).This adds a Windows-only dependency on
zipsign-apiand updatesCargo.lockaccordingly, plus a small tweak to version styling output to reuse the string value.Written by Cursor Bugbot for commit 7267f26. This will update automatically on new commits. Configure here.