Skip to content

fix(gem): Windows support for gem backend#8031

Merged
jdx merged 6 commits intojdx:mainfrom
my1e5:copilot/fix-gem-installation-error
Feb 6, 2026
Merged

fix(gem): Windows support for gem backend#8031
jdx merged 6 commits intojdx:mainfrom
my1e5:copilot/fix-gem-installation-error

Conversation

@my1e5
Copy link
Contributor

@my1e5 my1e5 commented Feb 6, 2026

This PR provides fixes for Windows support for the gem backend.

Minimal example

$ mise --version
2026.2.5 windows-x64 (2026-02-06)

$ mise use ruby@3.4.8
ruby@3.4.8             3.6.9
mise C:\Users\my1e5\Projects\mise-project\mise.toml tools: ruby@3.4.8

$ mise use gem:ceedling@1.0.1
gem:ceedling@1.0.1     install                                                         ◜
mise ERROR Failed to install gem:ceedling@1.0.1: failed to execute command: gem install ceedling --version 1.0.1 --install-dir C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec: program not found
mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information

See previous discussion:

Fixes

A fix for the Failed to install error is possible by using gem.cmd on Windows:

const GEM_PROGRAM: &str = if cfg!(windows) { "gem.cmd" } else { "gem" };

Using this patch enables the gem to be installed without error:

$ ./mise.exe --version
2026.2.5-DEBUG windows-x64 (2026-02-06)

$ ./mise.exe use gem:ceedling@1.0.1
gem:ceedling@1.0.1 
mise C:\Users\my1e5\Projects\mise-project\mise.toml tools: gem:ceedling@1.0.1

But then another error presented itself:

$ ./mise.exe x -- ceedling --version

C:\Users\my1e5\Projects\mise-project>#!/usr/bin/env bash 
'#!' is not recognized as an internal or external command,
operable program or batch file.

C:\Users\my1e5\Projects\mise-project>GEM_HOME="C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec" exec C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec/bin\ceedling.bat "$@"
'GEM_HOME' is not recognized as an internal or external command,
operable program or batch file.

A fix for this was to use Windows-compatible wrapper scripts. Create .cmd batch files instead of bash scripts on Windows:

@echo off
set "GEM_HOME=C:\path\to\libexec"
"C:\path\to\libexec\bin\ceedling.bat" %*

Now it is working normally:

$ ./mise.exe x -- ceedling --version
Welcome to Ceedling!

  Ceedling => 1.0.1-fb1ce6c
  ----------------------
  C:/Users/my1e5/AppData/Local/mise/installs/gem-ceedling/1.0.1/libexec/gems/ceedling-1.0.1/

  Build Frameworks
  ----------------------
       CMock => 2.6.0
       Unity => 2.6.1
  CException => 1.3.4

AI Disclosure

This PR was co-authored by Copilot, but I have reviewed the changes and tested them myself on my own Windows PC.

Copilot AI and others added 4 commits February 6, 2026 10:58
Co-authored-by: my1e5 <10064103+my1e5@users.noreply.github.com>
Co-authored-by: my1e5 <10064103+my1e5@users.noreply.github.com>
Co-authored-by: my1e5 <10064103+my1e5@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 6, 2026 14:04
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @my1e5, 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 mise tool's gem backend by introducing robust Windows compatibility. It addresses critical issues that prevented Ruby gems from being installed and executed correctly on Windows, thereby expanding the utility of mise for Ruby developers working in a Windows environment.

Highlights

  • Windows Compatibility for Gem Backend: Implemented support for the gem backend on Windows, resolving issues with gem installation and execution.
  • Conditional gem Command Usage: The gem command is now conditionally set to gem.cmd on Windows to ensure proper execution of RubyGems commands.
  • Windows-Specific Wrapper Scripts: Introduced the creation of .cmd batch files as wrapper scripts for gem executables on Windows, replacing bash scripts to correctly handle environment variables and command execution.
  • Platform-Specific Code Paths: Refactored the code to use #[cfg(windows)] and #[cfg(unix)] attributes, ensuring that shebang rewriting and Ruby symlink creation logic are only applied on Unix-like systems, while Windows-specific wrapper script generation is handled separately.
Changelog
  • src/backend/gem.rs
    • Added GEM_PROGRAM constant to dynamically select 'gem' or 'gem.cmd' based on the operating system.
    • Updated CmdLineRunner calls to use the new GEM_PROGRAM constant for gem commands.
    • Encapsulated Unix-specific shebang rewriting and Ruby symlink creation logic within #[cfg(unix)] blocks.
    • Implemented a #[cfg(windows)] version of env_script_all_bin_files to generate .cmd wrapper scripts for gem executables, correctly setting GEM_HOME and executing the gem.
    • Improved error handling for path conversions (UTF-8) in env_script_all_bin_files.
    • Applied #[cfg(unix)] to create_ruby_symlink, rewrite_gem_shebangs, find_ruby_shebang, is_mise_ruby_path, and to_minor_version_shebang functions.
    • Applied #[cfg(any(unix, test))] to extract_minor_version.
Activity
  • The author, my1e5, identified and provided solutions for two distinct issues preventing gem backend functionality on Windows.
  • The initial problem involved gem install failing due to the gem command not being found, which was resolved by using gem.cmd.
  • A subsequent issue with executing installed gems, caused by bash-specific scripts, was fixed by generating Windows-compatible .cmd wrapper files.
  • The author confirmed that the changes were co-authored by Copilot but personally reviewed and tested on a Windows PC.
  • This PR follows up on previous discussions regarding Windows support for mise's gem backend, specifically referencing GitHub discussion Basic gem usage on Windows does not work #6338.
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 effectively adds Windows support for the gem backend by introducing platform-specific logic for executable names and wrapper script generation. The changes are well-implemented, using conditional compilation to separate Unix and Windows code paths. Additionally, error handling has been improved by replacing panicking .unwrap() calls with proper error propagation. I have one suggestion to refactor some duplicated code between the platform-specific functions to improve maintainability.

Comment on lines +172 to 253
#[cfg(unix)]
fn env_script_all_bin_files(install_path: &Path) -> eyre::Result<bool> {
let install_bin_path = install_path.join("bin");
let install_libexec_path = install_path.join("libexec");

file::create_dir_all(&install_bin_path)?;

get_gem_executables(install_path)?
.into_iter()
.for_each(|path| {
let exec_path = install_bin_path.join(path.file_name().unwrap());
file::write(
&exec_path,
formatdoc!(
r#"
#!/usr/bin/env bash
GEM_HOME="{gem_home}" exec {gem_exec_path} "$@"
"#,
gem_home = install_libexec_path.to_str().unwrap(),
gem_exec_path = path.to_str().unwrap(),
),
for path in get_gem_executables(install_path)? {
let file_name = path
.file_name()
.ok_or_else(|| eyre::eyre!("invalid gem executable path: {}", path.display()))?;
let exec_path = install_bin_path.join(file_name);
let gem_exec_path = path.to_str().ok_or_else(|| {
eyre::eyre!(
"gem executable path contains invalid UTF-8: {}",
path.display()
)
})?;
let gem_home = install_libexec_path.to_str().ok_or_else(|| {
eyre::eyre!(
"libexec path contains invalid UTF-8: {}",
install_libexec_path.display()
)
.unwrap();
file::make_executable(&exec_path).unwrap();
});
})?;
file::write(
&exec_path,
formatdoc!(
r#"
#!/usr/bin/env bash
GEM_HOME="{gem_home}" exec {gem_exec_path} "$@"
"#,
gem_home = gem_home,
gem_exec_path = gem_exec_path,
),
)?;
file::make_executable(&exec_path)?;
}

Ok(true)
}

#[cfg(windows)]
fn env_script_all_bin_files(install_path: &Path) -> eyre::Result<bool> {
let install_bin_path = install_path.join("bin");
let install_libexec_path = install_path.join("libexec");

file::create_dir_all(&install_bin_path)?;

for path in get_gem_executables(install_path)? {
// On Windows, create .cmd wrapper scripts
let file_stem = path
.file_stem()
.ok_or_else(|| eyre::eyre!("invalid gem executable path: {}", path.display()))?
.to_string_lossy();
let exec_path = install_bin_path.join(format!("{}.cmd", file_stem));
let gem_exec_path = path.to_str().ok_or_else(|| {
eyre::eyre!(
"gem executable path contains invalid UTF-8: {}",
path.display()
)
})?;
let gem_home = install_libexec_path.to_str().ok_or_else(|| {
eyre::eyre!(
"libexec path contains invalid UTF-8: {}",
install_libexec_path.display()
)
})?;
file::write(
&exec_path,
formatdoc!(
r#"@echo off
set "GEM_HOME={gem_home}"
"{gem_exec_path}" %*
"#,
gem_home = gem_home,
gem_exec_path = gem_exec_path,
),
)?;
}

Ok(true)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

These two platform-specific env_script_all_bin_files functions share some duplicated logic (e.g., path setup, looping, and error handling). To improve maintainability, consider refactoring this into a single function that calls a platform-specific helper for creating the wrapper script. This would centralize the common logic and reduce redundancy.

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 adds Windows support for the gem backend by addressing two main issues: using gem.cmd instead of gem on Windows, and creating Windows-compatible wrapper scripts for installed gem executables. The changes follow similar patterns already established in the codebase for the npm backend.

Changes:

  • Introduces a platform-specific GEM_PROGRAM constant that resolves to gem.cmd on Windows and gem on Unix
  • Adds a Windows-specific implementation of env_script_all_bin_files that creates .cmd batch wrapper scripts instead of bash scripts
  • Wraps Unix-specific gem setup functions (shebang rewriting, Ruby symlink creation) with #[cfg(unix)] attributes to prevent compilation on Windows

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

my1e5 and others added 2 commits February 6, 2026 14:12
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
file::write(
&exec_path,
formatdoc!(
r#"\n
Copy link
Owner

Choose a reason for hiding this comment

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

typo? or intentional?

Copy link
Contributor Author

@my1e5 my1e5 Feb 6, 2026

Choose a reason for hiding this comment

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

This change was suggested by the Copilot PR review - #8031 (comment)

Copy link
Owner

Choose a reason for hiding this comment

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

fair enough

@jdx jdx merged commit a054199 into jdx:main Feb 6, 2026
33 checks passed
jdx pushed a commit that referenced this pull request Feb 6, 2026
Sorry, I should have checked that suggestion
(#8031 (comment)) by
Copilot more carefully. The newline it suggested should be added causes
`'\n' is not recognized as an internal or external command, operable
program or batch file.`:
```
$ ./mise.exe x -- ceedling --version

C:\Users\my1e5\Projects\mise-project>\n
'\n' is not recognized as an internal or external command,
operable program or batch file.
Welcome to Ceedling!

  Ceedling => 1.0.1-fb1ce6c
  ----------------------
  C:/Users/my1e5/AppData/Local/mise/installs/gem-ceedling/1.0.1/libexec/gems/ceedling-1.0.1/
  
  Build Frameworks
  ----------------------
       CMock => 2.6.0
       Unity => 2.6.1
  CException => 1.3.4
```
This PR reverts that change.
mise-en-dev added a commit that referenced this pull request Feb 7, 2026
### 🚀 Features

- **(env)** add shell-style variable expansion in env values by @jdx in
[#8029](#8029)
- **(list)** add --all-sources flag to list command by @TylerHillery in
[#8019](#8019)

### 🐛 Bug Fixes

- **(gem)** Windows support for gem backend by @my1e5 in
[#8031](#8031)
- **(gem)** revert gem.rs script newline change by @my1e5 in
[#8034](#8034)
- **(lock)** write tools to lockfile matching their source config by
@jdx in [#8012](#8012)
- **(ls)** sort sources deterministically in --all-sources output by
@jdx in [#8037](#8037)
- **(task)** auto-install tools from mise.toml for file tasks by @jdx in
[#8030](#8030)

### 📚 Documentation

- fix wrong positions of `mise run` flags by @muzimuzhi in
[#8036](#8036)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:copr docker digest to 3e00d7d by
@renovate[bot] in [#8023](#8023)
- update ghcr.io/jdx/mise:alpine docker digest to 0ced1b3 by
@renovate[bot] in [#8022](#8022)

### 📦 Registry

- add tirith
([github:sheeki03/tirith](https://github.com/sheeki03/tirith)) by
@sheeki03 in [#8024](#8024)
- add mas by @TyceHerrman in
[#8032](#8032)

### Security

- **(deps)** update time crate to 0.3.47 to fix RUSTSEC-2026-0009 by
@jdx in [#8026](#8026)

### New Contributors

- @sheeki03 made their first contribution in
[#8024](#8024)
- @TylerHillery made their first contribution in
[#8019](#8019)

## 📦 Aqua Registry Updates

#### New Packages (1)

-
[`kubernetes-sigs/kubectl-validate`](https://github.com/kubernetes-sigs/kubectl-validate)

#### Updated Packages (6)

-
[`flux-iac/tofu-controller/tfctl`](https://github.com/flux-iac/tofu-controller/tfctl)
- [`gogs/gogs`](https://github.com/gogs/gogs)
- [`j178/prek`](https://github.com/j178/prek)
- [`syncthing/syncthing`](https://github.com/syncthing/syncthing)
- [`tuist/tuist`](https://github.com/tuist/tuist)
- [`yaml/yamlscript`](https://github.com/yaml/yamlscript)
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
This PR provides fixes for Windows support for the gem backend.

## Minimal example
```
$ mise --version
2026.2.5 windows-x64 (2026-02-06)

$ mise use ruby@3.4.8
ruby@3.4.8             3.6.9
mise C:\Users\my1e5\Projects\mise-project\mise.toml tools: ruby@3.4.8

$ mise use gem:ceedling@1.0.1
gem:ceedling@1.0.1     install                                                         ◜
mise ERROR Failed to install gem:ceedling@1.0.1: failed to execute command: gem install ceedling --version 1.0.1 --install-dir C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec: program not found
mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information
```
See previous discussion:
- jdx#6338

## Fixes

A fix for the `Failed to install` error is possible by using `gem.cmd`
on Windows:
```
const GEM_PROGRAM: &str = if cfg!(windows) { "gem.cmd" } else { "gem" };
```
Using this patch enables the gem to be installed without error:
```
$ ./mise.exe --version
2026.2.5-DEBUG windows-x64 (2026-02-06)

$ ./mise.exe use gem:ceedling@1.0.1
gem:ceedling@1.0.1 
mise C:\Users\my1e5\Projects\mise-project\mise.toml tools: gem:ceedling@1.0.1
```

But then another error presented itself:

```
$ ./mise.exe x -- ceedling --version

C:\Users\my1e5\Projects\mise-project>#!/usr/bin/env bash 
'#!' is not recognized as an internal or external command,
operable program or batch file.

C:\Users\my1e5\Projects\mise-project>GEM_HOME="C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec" exec C:\Users\my1e5\AppData\Local\mise\installs\gem-ceedling\1.0.1\libexec/bin\ceedling.bat "$@"
'GEM_HOME' is not recognized as an internal or external command,
operable program or batch file.
```
A fix for this was to use Windows-compatible wrapper scripts. Create
.cmd batch files instead of bash scripts on Windows:
```
@echo off
set "GEM_HOME=C:\path\to\libexec"
"C:\path\to\libexec\bin\ceedling.bat" %*
```
Now it is working normally:
```
$ ./mise.exe x -- ceedling --version
Welcome to Ceedling!

  Ceedling => 1.0.1-fb1ce6c
  ----------------------
  C:/Users/my1e5/AppData/Local/mise/installs/gem-ceedling/1.0.1/libexec/gems/ceedling-1.0.1/

  Build Frameworks
  ----------------------
       CMock => 2.6.0
       Unity => 2.6.1
  CException => 1.3.4
```

### AI Disclosure
This PR was co-authored by Copilot, but I have reviewed the changes and
tested them myself on my own Windows PC.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
Sorry, I should have checked that suggestion
(jdx#8031 (comment)) by
Copilot more carefully. The newline it suggested should be added causes
`'\n' is not recognized as an internal or external command, operable
program or batch file.`:
```
$ ./mise.exe x -- ceedling --version

C:\Users\my1e5\Projects\mise-project>\n
'\n' is not recognized as an internal or external command,
operable program or batch file.
Welcome to Ceedling!

  Ceedling => 1.0.1-fb1ce6c
  ----------------------
  C:/Users/my1e5/AppData/Local/mise/installs/gem-ceedling/1.0.1/libexec/gems/ceedling-1.0.1/
  
  Build Frameworks
  ----------------------
       CMock => 2.6.0
       Unity => 2.6.1
  CException => 1.3.4
```
This PR reverts that change.
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
### 🚀 Features

- **(env)** add shell-style variable expansion in env values by @jdx in
[jdx#8029](jdx#8029)
- **(list)** add --all-sources flag to list command by @TylerHillery in
[jdx#8019](jdx#8019)

### 🐛 Bug Fixes

- **(gem)** Windows support for gem backend by @my1e5 in
[jdx#8031](jdx#8031)
- **(gem)** revert gem.rs script newline change by @my1e5 in
[jdx#8034](jdx#8034)
- **(lock)** write tools to lockfile matching their source config by
@jdx in [jdx#8012](jdx#8012)
- **(ls)** sort sources deterministically in --all-sources output by
@jdx in [jdx#8037](jdx#8037)
- **(task)** auto-install tools from mise.toml for file tasks by @jdx in
[jdx#8030](jdx#8030)

### 📚 Documentation

- fix wrong positions of `mise run` flags by @muzimuzhi in
[jdx#8036](jdx#8036)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:copr docker digest to 3e00d7d by
@renovate[bot] in [jdx#8023](jdx#8023)
- update ghcr.io/jdx/mise:alpine docker digest to 0ced1b3 by
@renovate[bot] in [jdx#8022](jdx#8022)

### 📦 Registry

- add tirith
([github:sheeki03/tirith](https://github.com/sheeki03/tirith)) by
@sheeki03 in [jdx#8024](jdx#8024)
- add mas by @TyceHerrman in
[jdx#8032](jdx#8032)

### Security

- **(deps)** update time crate to 0.3.47 to fix RUSTSEC-2026-0009 by
@jdx in [jdx#8026](jdx#8026)

### New Contributors

- @sheeki03 made their first contribution in
[jdx#8024](jdx#8024)
- @TylerHillery made their first contribution in
[jdx#8019](jdx#8019)

## 📦 Aqua Registry Updates

#### New Packages (1)

-
[`kubernetes-sigs/kubectl-validate`](https://github.com/kubernetes-sigs/kubectl-validate)

#### Updated Packages (6)

-
[`flux-iac/tofu-controller/tfctl`](https://github.com/flux-iac/tofu-controller/tfctl)
- [`gogs/gogs`](https://github.com/gogs/gogs)
- [`j178/prek`](https://github.com/j178/prek)
- [`syncthing/syncthing`](https://github.com/syncthing/syncthing)
- [`tuist/tuist`](https://github.com/tuist/tuist)
- [`yaml/yamlscript`](https://github.com/yaml/yamlscript)
@my1e5 my1e5 deleted the copilot/fix-gem-installation-error branch March 3, 2026 10:39
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.

4 participants