Skip to content

Preserve custom shell for PowerShell scripts#22336

Open
rangampallysonali wants to merge 1 commit into
PrefectHQ:mainfrom
rangampallysonali:fix-shelloperation-powershell-policy
Open

Preserve custom shell for PowerShell scripts#22336
rangampallysonali wants to merge 1 commit into
PrefectHQ:mainfrom
rangampallysonali:fix-shelloperation-powershell-policy

Conversation

@rangampallysonali

Copy link
Copy Markdown

Summary

Fix shell selection logic in ShellOperation._prep_trigger_command when using PowerShell script extensions (.ps1).

Previously, the condition:

if self.shell is None and sys.platform == "win32" or extension == ".ps1":

could evaluate to True whenever extension == ".ps1", even when a custom shell was explicitly provided. This caused Prefect to override the user-specified shell and force "powershell".

The condition has been updated to ensure PowerShell is only selected automatically when no shell has been supplied by the user.

Changes

Changed:

if self.shell is None and sys.platform == "win32" or extension == ".ps1":

to:

if self.shell is None and (
    sys.platform == "win32" or extension == ".ps1"
):

Motivation

This aligns the implementation with the expected behavior described in issue #21808 and preserves user-provided shell values when working with PowerShell scripts.

Testing

Verified that the existing Windows shell override tests continue to pass:

pytest src/integrations/prefect-shell/tests/test_commands_windows.py -k override_shell -v

Results:

  • 3 passed
  • 0 failed
@rangampallysonali

Copy link
Copy Markdown
Author

Hi maintainers, I opened this PR to address #21808 by preserving a user-provided shell when extension=".ps1" is used.

I verified the relevant Windows shell override tests locally:

pytest src/integrations/prefect-shell/tests/test_commands_windows.py -k override_shell -v

Result: 3 passed.

Please let me know if you'd like me to add a more targeted regression test or adjust the implementation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 018fb02ee0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines 479 to 482
if self.shell is None and (
sys.platform == "win32" or extension == ".ps1"
):
shell = "powershell"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Preserve PowerShell exit propagation for custom shells

When a user runs .ps1 commands with a PowerShell-compatible custom shell such as shell="powershell.exe" or shell="pwsh", this branch now skips the auto-PowerShell path, so the later if shell == "powershell" check does not append Exit $LastExitCode. For inputs like ShellOperation(commands=["ping bad-host"], shell="powershell.exe"), the script can terminate normally and Prefect reports success even though the native command failed; Microsoft’s powershell.exe -File docs state successful execution exits 0 unless the script terminates with exit. Please preserve the custom executable name while still treating PowerShell executables as PowerShell for exit-code propagation.

Useful? React with 👍 / 👎.

@rangampallysonali rangampallysonali force-pushed the fix-shelloperation-powershell-policy branch from 018fb02 to 0237c16 Compare June 21, 2026 02:25
@zzstoatzz

zzstoatzz commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

thanks for working on this @rangampallysonali ! I dug into this because the precedence fix is definitely needed, but the issue example still seems to fail after this PR

the remaining problem is that ShellOperation preserves shell="powershell.exe -executionpolicy bypass" after this change, but then passes that whole string as the first subprocess.Popen argument. on Windows that becomes a single executable name, so it fails with [WinError 2].

I verified this with a GitHub Actions repro in zzstoatzz/prefect-pack:

The shape of the follow-up fix was:

def _split_shell_command(shell: str) -> list[str]:
    return shlex.split(shell.lower(), posix=sys.platform != "win32") or [shell.lower()]


def _shell_command(shell: str, temp_file_name: str) -> list[str]:
    return [*_split_shell_command(shell), temp_file_name]

and then use _shell_command(shell, temp_file.name) instead of [shell, temp_file.name].

It is also worth recognizing powershell.exe / pwsh as PowerShell executables when deciding whether to append Exit $LastExitCode, otherwise powershell.exe with flags can run but loses the exit-code behavior.

I had opened #22340 while testing, but I am closing it so this contribution can stay here. Feel free to copy anything useful from that branch/body; the important bit is the Windows repro above and adding a regression test for ShellOperation(shell="powershell.exe -executionpolicy bypass", extension=".ps1").

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants