Skip to content

fix(task): discover and run shebang file tasks on Windows#7941

Merged
jdx merged 1 commit into
mainfrom
fix/windows-shebang-task-discovery
Feb 1, 2026
Merged

fix(task): discover and run shebang file tasks on Windows#7941
jdx merged 1 commit into
mainfrom
fix/windows-shebang-task-discovery

Conversation

@jdx

@jdx jdx commented Feb 1, 2026

Copy link
Copy Markdown
Owner

Summary

  • On Windows, extensionless file tasks (e.g., tasks/build with #!/usr/bin/env bash) were invisible to mise because is_executable only checked file extensions
  • Now is_executable on Windows also checks for a shebang (#!) in the first 2 bytes, so these tasks are discovered
  • During execution, the shebang is parsed to determine the correct shell (e.g., bash.exe for #!/usr/bin/env bash) instead of falling back to cmd /c
  • Unix paths in shebangs like /bin/bash are converted to just the binary name on Windows

Test plan

  • Verify existing Windows e2e task tests still pass
  • Verify new executes a shebang task with bash e2e test passes on Windows CI
  • Verify Linux/macOS behavior is unchanged (no code paths affected on non-Windows)

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it changes Windows task discovery/executability checks and alters how file tasks choose their execution shell, which could affect existing task behavior and command invocation.

Overview
Enables Windows to discover and run extensionless file tasks with a #! shebang, treating them as executable for task discovery.

Updates task execution to avoid direct execution for shebang-only files and instead pick a shell from the file’s shebang (with Windows-friendly path normalization) or from the file extension (notably .ps1 via pwsh -File) before falling back to the default file shell.

Adds a Windows e2e test covering a no-extension bash-shebang task.

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

Copilot AI review requested due to automatic review settings February 1, 2026 10:37
@gemini-code-assist

Copy link
Copy Markdown
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 significantly enhances mise's task execution capabilities on Windows by introducing robust support for shebang-driven scripts. Previously, extensionless files with shebangs were not recognized as executable tasks. The changes enable mise to correctly identify these tasks, parse their shebangs to determine the appropriate shell for execution, and handle Unix-style paths within shebangs for seamless cross-platform compatibility.

Highlights

  • Shebang Discovery on Windows: The is_executable function on Windows now checks for shebangs (#!) in the first two bytes of a file, allowing mise to discover and recognize extensionless script tasks that rely on shebangs.
  • Shebang-driven Execution: During task execution on Windows, the shebang line is parsed to determine the correct interpreter (e.g., bash.exe for #!/usr/bin/env bash), ensuring that scripts are run with their intended shell instead of falling back to cmd /c.
  • Unix Path Conversion: Unix-style paths specified in shebangs (e.g., /bin/bash) are now automatically converted to just the binary name (e.g., bash) for proper resolution and execution on Windows systems.
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.

Copilot AI left a comment

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.

Pull request overview

This PR fixes task discovery and execution for shebang-based file tasks on Windows. Previously, extensionless tasks with shebangs (e.g., #!/usr/bin/env bash) were not recognized as executable on Windows because the is_executable function only checked file extensions.

Changes:

  • Modified is_executable on Windows to also detect files with shebangs by checking for #! in the first 2 bytes
  • Updated task executor to parse shebangs and use the appropriate shell (e.g., bash.exe) instead of defaulting to cmd /c
  • Added e2e test to verify shebang task execution on Windows

Reviewed changes

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

File Description
src/file.rs Added has_shebang function to detect shebang prefix and extracted has_known_executable_extension helper
src/task/task_executor.rs Updated execution logic to differentiate between OS-executable files and shebang files, added shell_from_shebang parser
e2e-win/task.Tests.ps1 Added test case for executing extensionless bash shebang tasks

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

Comment thread src/task/task_executor.rs Outdated
Comment on lines +493 to +506
let can_exec_directly = if cfg!(windows) {
// On Windows, shebang-only files can't be executed directly by the OS.
// They need to be run through a shell (e.g. bash.exe).
#[cfg(windows)]
{
crate::file::has_known_executable_extension(file)
}
#[cfg(not(windows))]
{
is_executable(file)
}
} else {
is_executable(file)
};

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

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

The conditional compilation attributes inside the if cfg!(windows) block are redundant. The runtime check already determines the platform, making the compile-time #[cfg(...)] attributes unnecessary and potentially confusing. Simplify by removing the inner #[cfg] blocks and using only the runtime if cfg!(windows) check.

Copilot uses AI. Check for mistakes.
Comment thread src/task/task_executor.rs
shell
};
let args: Vec<String> = parts.map(|s| s.to_string()).collect();
Some(once(shell.to_string()).chain(args).collect())

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

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

The once function is used but not imported at the top of the file. Add use std::iter::once; to the imports section for clarity.

Copilot uses AI. Check for mistakes.
Comment thread e2e-win/task.Tests.ps1
@"
#!/usr/bin/env bash
echo "from-bash"
"@ | Out-File -FilePath "tasks\shebangtask" -Encoding utf8NoBOM -NoNewline

Copilot AI Feb 1, 2026

Copy link

Choose a reason for hiding this comment

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

The -NoNewline flag will strip the trailing newline from the here-string, but the shebang line itself (line 59) should end with a newline character for proper parsing. Remove -NoNewline to ensure the file has correct line endings.

Suggested change
"@ | Out-File -FilePath "tasks\shebangtask" -Encoding utf8NoBOM -NoNewline
"@ | Out-File -FilePath "tasks\shebangtask" -Encoding utf8NoBOM

Copilot uses AI. Check for mistakes.
On Windows, file-based tasks without a recognized extension (e.g., .bat,
.ps1) were filtered out during task discovery because `is_executable`
only checked file extensions. This meant extensionless bash scripts in
task directories were invisible to mise on Windows.

Now, `is_executable` on Windows also checks for a shebang (#!) in the
first 2 bytes of the file. During execution, the shebang is parsed to
determine the correct shell (e.g., bash.exe for #!/usr/bin/env bash)
instead of falling back to `cmd /c` which can't run bash scripts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jdx jdx force-pushed the fix/windows-shebang-task-discovery branch from cd0f6f2 to 1f8bd9d Compare February 1, 2026 10:39

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

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.

Code Review

This pull request adds support for discovering and running shebang file tasks on Windows, which is a great improvement. The changes correctly modify is_executable to detect shebangs and update the task execution logic to use an interpreter. The new e2e test is also a good addition. I have one suggestion to improve the shebang parsing logic to handle quoted arguments correctly.

Comment thread src/task/task_executor.rs
Comment on lines +887 to +896
use std::io::{BufRead, BufReader};
let f = std::fs::File::open(path).ok()?;
let mut reader = BufReader::new(f);
let mut first_line = String::new();
reader.read_line(&mut first_line).ok()?;
let shebang = first_line.strip_prefix("#!")?;
let shebang = shebang.strip_prefix("/usr/bin/env -S").unwrap_or(shebang);
let shebang = shebang.strip_prefix("/usr/bin/env").unwrap_or(shebang);
let mut parts = shebang.split_whitespace();
let shell = parts.next()?;

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.

high

The use of split_whitespace() for parsing the shebang can lead to incorrect behavior when arguments are quoted. For instance, a shebang like #!/usr/bin/env bash -c "echo hello" would be parsed incorrectly. It's better to use shell_words::split, which is already a dependency in this project and correctly handles quoted arguments.

    let mut parts = shell_words::split(shebang.trim()).ok()?;
    if parts.is_empty() {
        return None;
    }
    let shell = parts.remove(0);
    // On Windows, convert unix paths like /bin/bash to just the binary name
    let shell = if cfg!(windows) {
        shell.rsplit('/').next().unwrap_or(&shell).to_string()
    } else {
        shell
    };
    let args: Vec<String> = parts;
    Some(once(shell).chain(args).collect())

@github-actions

github-actions Bot commented Feb 1, 2026

Copy link
Copy Markdown

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.12 x -- echo 20.0 ± 0.4 19.2 23.1 1.00
mise x -- echo 20.3 ± 0.7 19.3 24.4 1.02 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.12 env 19.4 ± 0.5 18.6 24.2 1.00
mise env 19.9 ± 0.5 18.8 21.7 1.03 ± 0.04

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.12 hook-env 20.0 ± 0.3 19.3 21.2 1.00
mise hook-env 20.5 ± 2.0 19.4 63.9 1.02 ± 0.10

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.1.12 ls 18.0 ± 0.4 17.2 19.3 1.00
mise ls 18.2 ± 0.5 17.4 20.5 1.01 ± 0.03

xtasks/test/perf

Command mise-2026.1.12 mise Variance
install (cached) 111ms 111ms +0%
ls (cached) 70ms 70ms +0%
bin-paths (cached) 74ms 74ms +0%
task-ls (cached) 532ms 538ms -1%

@jdx jdx merged commit 3a28344 into main Feb 1, 2026
35 checks passed
@jdx jdx deleted the fix/windows-shebang-task-discovery branch February 1, 2026 10:56
lucasew pushed a commit to lucasew/CONTRIB-mise that referenced this pull request Feb 18, 2026
## Summary
- On Windows, extensionless file tasks (e.g., `tasks/build` with
`#!/usr/bin/env bash`) were invisible to mise because `is_executable`
only checked file extensions
- Now `is_executable` on Windows also checks for a shebang (`#!`) in the
first 2 bytes, so these tasks are discovered
- During execution, the shebang is parsed to determine the correct shell
(e.g., `bash.exe` for `#!/usr/bin/env bash`) instead of falling back to
`cmd /c`
- Unix paths in shebangs like `/bin/bash` are converted to just the
binary name on Windows

## Test plan
- [ ] Verify existing Windows e2e task tests still pass
- [ ] Verify new `executes a shebang task with bash` e2e test passes on
Windows CI
- [ ] Verify Linux/macOS behavior is unchanged (no code paths affected
on non-Windows)

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes Windows task discovery/executability
checks and alters how file tasks choose their execution shell, which
could affect existing task behavior and command invocation.
> 
> **Overview**
> Enables Windows to **discover and run extensionless file tasks with a
`#!` shebang**, treating them as executable for task discovery.
> 
> Updates task execution to **avoid direct execution for shebang-only
files** and instead pick a shell from the file’s shebang (with
Windows-friendly path normalization) or from the file extension (notably
`.ps1` via `pwsh -File`) before falling back to the default file shell.
> 
> Adds a Windows e2e test covering a no-extension bash-shebang task.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
1f8bd9d. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
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