Skip to content

fix(shim): update mise-shim.exe during self-update on Windows#8075

Merged
jdx merged 3 commits intomainfrom
fix/self-update-mise-shim
Feb 10, 2026
Merged

fix(shim): update mise-shim.exe during self-update on Windows#8075
jdx merged 3 commits intomainfrom
fix/self-update-mise-shim

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 9, 2026

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 #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


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.

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

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>
Copilot AI review requested due to automatic review settings February 9, 2026 21:33
@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 "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

  • Windows Shim Update: Implemented automatic updating of "mise-shim.exe" on Windows during "mise self-update" to ensure both "mise.exe" and its shim are kept current.
  • Robustness: The "mise-shim.exe" update process is non-fatal; if it fails, a warning is logged, but the main "mise" update still completes successfully.
  • Resource Utilization: Leverages existing "zip", "tempfile", and HTTP client functionalities, avoiding the introduction of new dependencies.
  • Bug Fix: Addresses a known issue where "mise self-update" on Windows would only update "mise.exe", leaving "mise-shim.exe" outdated, requiring manual intervention.
Changelog
  • src/cli/self_update.rs
    • Introduced a call to "Self::update_mise_shim" within the "self_update" method, specifically for Windows builds, to handle the shim executable update after "mise.exe" is updated.
    • Added a new "#[cfg(windows)]" function "update_mise_shim" responsible for downloading the release archive, extracting "mise-shim.exe", and replacing the existing shim.
    • Implemented error handling in "update_mise_shim" to log a warning and proceed if "mise-shim.exe" is not found in the downloaded archive.
Activity
  • The pull request was generated using Claude Code, an AI tool.
  • The author has provided a detailed test plan covering Windows-specific update scenarios and verification for non-Windows builds.
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

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

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 String for 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.

Comment on lines +188 to +190
tokio::task::block_in_place(|| {
tokio::runtime::Handle::current().block_on(HTTP.download_file(&url, &zip_path, None))
})?;
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

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).

Suggested change
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))?;
}

Copilot uses AI. Check for mistakes.
Comment on lines +207 to +209
let mut buf = Vec::new();
shim_entry.read_to_end(&mut buf)?;
xx::file::write(&dest, &buf)?;
Copy link

Copilot AI Feb 9, 2026

Choose a reason for hiding this comment

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

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.

Copilot uses AI. Check for mistakes.
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 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.

Comment on lines +175 to +213
#[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(())
}
Copy link
Contributor

Choose a reason for hiding this comment

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

critical

This new function has a critical bug and a potential improvement:

  1. Incorrect URL (Critical): The URL for downloading the release archive is constructed incorrectly. Release tags and asset names on GitHub for mise are prefixed with a v (e.g., v2024.5.1), but the version variable here does not include it. This will cause downloads to fail with a 404 error.
  2. Blocking in async context (Improvement): The function is synchronous and uses tokio::task::block_in_place. Since it's called from an async context, it's better to make it async and await the 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(())
    }

Comment on lines +97 to +99
if let Err(e) = Self::update_mise_shim(&version) {
warn!("Failed to update mise-shim.exe: {e}");
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

To accommodate the suggested change of making update_mise_shim an async function, this call needs to be awaited.

Suggested change
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}");
}

@github-actions
Copy link

github-actions bot commented Feb 9, 2026

Hyperfine Performance

mise x -- echo

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>
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.


let temp_dir = tempfile::tempdir()?;
let zip_path = temp_dir.path().join("mise.zip");
HTTP.download_file(&url, &zip_path, None).await?;
Copy link

Choose a reason for hiding this comment

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

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.

Fix in Cursor Fix in Web

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>
@jdx jdx merged commit a6fbdc8 into main Feb 10, 2026
35 checks passed
@jdx jdx deleted the fix/self-update-mise-shim branch February 10, 2026 02:14
mise-en-dev added a commit that referenced this pull request Feb 10, 2026
### 🚀 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)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## 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>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 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)
jc00ke added a commit to jc00ke/dotfiles that referenced this pull request Feb 19, 2026
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