Skip to content

fix: use VS Code default shell for command execution instead of /bin/sh#509

Open
p12tic wants to merge 1 commit into
Zoo-Code-Org:mainfrom
p12tic:fix-default-shell-selection
Open

fix: use VS Code default shell for command execution instead of /bin/sh#509
p12tic wants to merge 1 commit into
Zoo-Code-Org:mainfrom
p12tic:fix-default-shell-selection

Conversation

@p12tic
Copy link
Copy Markdown
Contributor

@p12tic p12tic commented Jun 6, 2026

Related GitHub Issue

Closes: #508

Description

When terminalShellIntegrationDisabled is true (the default), commands are executed via execa. Previously, execa used shell: true which defaults to /bin/sh on Linux, ignoring the user's VS Code terminal shell settings.

Now execa falls back to getShell() which respects:

  • VS Code terminal.integrated.defaultProfile.linux
  • os.userInfo().shell
  • $SHELL environment variable
  • Platform-specific defaults (/bin/bash on Linux, /bin/zsh on macOS)

Test Procedure

  • Select bash as VS code shell
  • Reload window to get clean state
  • Give zoo code this prompt "please run sleep 60"
  • Run ps aux | grep sleep to observe how Zoo code launched the command
  • See that /bin/bash being used as expected.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Screenshots / Videos

n/a

Documentation Updates

not needed

Additional Notes

n/a

Get in Touch

povilas_78868

Summary by CodeRabbit

  • Refactor
    • Improved shell selection mechanism for terminal processes.
  • Tests
    • Updated test suite to verify shell selection behavior.

When terminalShellIntegrationDisabled is true (the default), commands are
executed via execa. Previously, execa used shell: true which defaults to
/bin/sh on Linux, ignoring the user's VS Code terminal shell settings.

Now execa falls back to getShell() which respects:
- VS Code terminal.integrated.defaultProfile.linux
- os.userInfo().shell
- $SHELL environment variable
- Platform-specific defaults (/bin/bash on Linux, /bin/zsh on macOS)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 6, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

ExecaTerminalProcess now imports and uses getShell() to determine the shell option passed to execa when BaseTerminal.getExecaShellPath() is undefined, replacing a hardcoded true fallback. Tests are updated to mock getShell() and verify the shell option is set to its return value.

Changes

Shell Resolution via getShell()

Layer / File(s) Summary
Import getShell and update shell option resolution
src/integrations/terminal/ExecaTerminalProcess.ts
getShell is imported and used to provide the execa shell option when BaseTerminal.getExecaShellPath() is unset, replacing the true fallback.
Mock getShell and verify shell resolution in tests
src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts
getShell() is mocked from utils/shell via mockGetShell(). UTF-8 encoding and fallback assertions are updated to expect execa to receive the explicit shell path from getShell() instead of shell: true.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Possibly related issues

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#239: Modifies getShell() to return PowerShell on Windows instead of null, directly affecting what shell path ExecaTerminalProcess now passes to execa via the new fallback.

Suggested reviewers

  • taltas
  • navedmerchant
  • hannesrudolph
  • edelauna
  • JamesRobert20

Poem

🐰 A rabbit hops through shells with glee,
No more true fallbacks—now getShell() we see!
From bash to zsh, the user's way,
The terminal dances as they say! 🐚✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main change: using VS Code default shell instead of /bin/sh for command execution.
Description check ✅ Passed The PR description follows the template and includes all required sections: Related GitHub Issue (#508), Description explaining the change and rationale, Test Procedure with specific steps, completed Pre-Submission Checklist, and Additional Notes.
Linked Issues check ✅ Passed The PR successfully addresses issue #508 by replacing shell: true with getShell() fallback, respecting VS Code shell configuration and environment variables as required.
Out of Scope Changes check ✅ Passed All changes are scoped to the linked issue #508: modifications to ExecaTerminalProcess.ts and its tests to implement the getShell() fallback behavior are directly aligned with fixing the shell selection bug.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts (1)

102-123: 💤 Low value

Consider organizing shell selection tests into a separate describe block.

Tests at Lines 102-123 verify shell selection behavior but are nested under the "UTF-8 encoding fix" describe block. A dedicated describe block (e.g., "shell selection") would improve test organization and make the test structure clearer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts` around
lines 102 - 123, Move the two tests that assert shell selection into a new
describe block named "shell selection" to improve organization: create a
describe("shell selection", () => { ... }) and place the it("should use
execaShellPath when set", ...) and it("should fall back to getShell() when
execaShellPath is undefined", ...) inside it; keep the test bodies unchanged and
preserve references to BaseTerminal.setExecaShellPath, mockGetShell,
terminalProcess.run, and the execa mock so behavior is preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts`:
- Around line 113-123: The test leaks mock behavior because
mockGetShell.mockReturnValue("/usr/bin/zsh") permanently changes the mock;
change the test to use mockGetShell.mockReturnValueOnce("/usr/bin/zsh") (or
restore/reset the mock implementation) so
BaseTerminal.setExecaShellPath(undefined) test only affects this test; update
the call in the "should fall back to getShell() when execaShellPath is
undefined" spec to use mockReturnValueOnce on mockGetShell to ensure isolation
and prevent interfering with other tests that expect "/bin/bash".

---

Nitpick comments:
In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts`:
- Around line 102-123: Move the two tests that assert shell selection into a new
describe block named "shell selection" to improve organization: create a
describe("shell selection", () => { ... }) and place the it("should use
execaShellPath when set", ...) and it("should fall back to getShell() when
execaShellPath is undefined", ...) inside it; keep the test bodies unchanged and
preserve references to BaseTerminal.setExecaShellPath, mockGetShell,
terminalProcess.run, and the execa mock so behavior is preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c75a69c7-78ea-4c02-8ad0-f4e70ea5b3c2

📥 Commits

Reviewing files that changed from the base of the PR and between 47e6215 and 50b9df8.

📒 Files selected for processing (2)
  • src/integrations/terminal/ExecaTerminalProcess.ts
  • src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts

Comment on lines +113 to 123
it("should fall back to getShell() when execaShellPath is undefined", async () => {
BaseTerminal.setExecaShellPath(undefined)
mockGetShell.mockReturnValue("/usr/bin/zsh")
await terminalProcess.run("echo test")
const execaMock = vitest.mocked(execa)
expect(execaMock).toHaveBeenCalledWith(
expect.objectContaining({
shell: true,
shell: "/usr/bin/zsh",
}),
)
})
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix test isolation issue to prevent flakiness.

Using mockReturnValue() on Line 115 permanently changes the mock's behavior until explicitly reset. Since clearAllMocks() (Line 62) only clears call history but not mock implementations, the "/usr/bin/zsh" return value will persist if this test runs before others, causing the test at Line 66-80 to fail when it expects "/bin/bash".

🔧 Proposed fix using mockReturnValueOnce
 		it("should fall back to getShell() when execaShellPath is undefined", async () => {
 			BaseTerminal.setExecaShellPath(undefined)
-			mockGetShell.mockReturnValue("/usr/bin/zsh")
+			mockGetShell.mockReturnValueOnce("/usr/bin/zsh")
 			await terminalProcess.run("echo test")
 			const execaMock = vitest.mocked(execa)
 			expect(execaMock).toHaveBeenCalledWith(
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/integrations/terminal/__tests__/ExecaTerminalProcess.spec.ts` around
lines 113 - 123, The test leaks mock behavior because
mockGetShell.mockReturnValue("/usr/bin/zsh") permanently changes the mock;
change the test to use mockGetShell.mockReturnValueOnce("/usr/bin/zsh") (or
restore/reset the mock implementation) so
BaseTerminal.setExecaShellPath(undefined) test only affects this test; update
the call in the "should fall back to getShell() when execaShellPath is
undefined" spec to use mockReturnValueOnce on mockGetShell to ensure isolation
and prevent interfering with other tests that expect "/bin/bash".

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@edelauna
Copy link
Copy Markdown
Contributor

edelauna commented Jun 6, 2026

Thanks for the contribution! We have a broader PR open at #333 that covers the same underlying issue (#321) and goes further — it fixes shell selection and includes handling for Windows/WSL edge cases that were surfaced during review.

Could you coordinate with @F915 to see if there's anything here worth folding in

@edelauna edelauna closed this Jun 6, 2026
@edelauna
Copy link
Copy Markdown
Contributor

edelauna commented Jun 6, 2026

After review I'm going to reopen this PR since the other one is still in draft, if you'd like to review #333 to incorporate any existing feedback into your PR that would help with future review.

@edelauna edelauna reopened this Jun 6, 2026
@F915
Copy link
Copy Markdown

F915 commented Jun 6, 2026

I have an open PR (#333) that relates to the same issue (#321 / #508).

Noted in a review comment on #333, changing shell: true to shell: getShell() has a known limitation on Windows with WSL — getShell() can return /bin/bash, which is not a valid Windows shellPath.

The current version of #333 takes a different approach, using vscode.env.shell for shell resolution instead of modifying shell: true alone. It's still in draft — testing across platforms and terminal modes is not complete yet.

Would love to hear your thoughts if you have any.

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.

[BUG] Inline terminal always uses system shell instead of what shell is configured in VS code

3 participants