fix(shellUtils): build terminal commands for the shell that runs them (#1822) - #2092
Merged
Conversation
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 14:13 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 14:14 — with
GitHub Actions
Inactive
abdelrahmanomar1515
had a problem deploying
to
test-trigger-is
August 6, 2026 14:14 — with
GitHub Actions
Error
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 14:28 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 14:28 — with
GitHub Actions
Inactive
abdelrahmanomar1515
had a problem deploying
to
test-trigger-is
August 6, 2026 14:29 — with
GitHub Actions
Error
abdelrahmanomar1515
force-pushed
the
fix-1822-shell-detection
branch
from
August 6, 2026 15:02
e5249e4 to
32581f6
Compare
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 15:02 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 15:02 — with
GitHub Actions
Inactive
abdelrahmanomar1515
had a problem deploying
to
test-trigger-is
August 6, 2026 15:03 — with
GitHub Actions
Error
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 15:25 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 6, 2026 15:25 — with
GitHub Actions
Inactive
abdelrahmanomar1515
had a problem deploying
to
test-trigger-is
August 6, 2026 15:26 — with
GitHub Actions
Error
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 7, 2026 08:25 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 7, 2026 08:26 — with
GitHub Actions
Inactive
abdelrahmanomar1515
requested a deployment
to
test-trigger-is
August 7, 2026 08:27 — with
GitHub Actions
Queued
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 7, 2026 08:30 — with
GitHub Actions
Inactive
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 7, 2026 08:30 — with
GitHub Actions
Inactive
abdelrahmanomar1515
had a problem deploying
to
test-trigger-is
August 7, 2026 08:30 — with
GitHub Actions
Error
abdelrahmanomar1515
marked this pull request as ready for review
August 7, 2026 08:44
rugpanov
approved these changes
Aug 7, 2026
rugpanov
left a comment
Contributor
There was a problem hiding this comment.
Reviewed via multi-source pass (Codex + Claude deep review + independent verification). Solid, well-reasoned refactor — commands are now built in the correct shell dialect. No blocking issues; the cmd caret-escaping, POSIX/PowerShell quoting, and call-site shell kinds all check out. One narrow nit: on a pwsh-default host, the reused-terminal (RunCommands) path can emit a stray & prefix into a non-pwsh terminal — not a hard bug, and the documented argv-array follow-up is the real fix.
anton-107
approved these changes
Aug 7, 2026
abdelrahmanomar1515
enabled auto-merge (squash)
August 7, 2026 15:31
…#1822) "Create a new Databricks project" was broken on Windows with cmd.exe as the terminal shell. The wizard built one POSIX command line and sent it to whatever shell the terminal was running, so cmd rejected it token by token and the CLI never started: C:\...\Temp>clear; echo "Executing: databricks bundle init ..." 'clear' is not recognized as an internal or external command, 'Follow' is not recognized as an internal or external command, 'Press' is not recognized as an internal or external command, clear, ";" as a separator and read are all POSIX-only; cmd needs cls, " & " and pause. The cause is structural rather than a missing branch. shellUtils read env.shell inside every helper and split only "PowerShell" vs everything else, using a substring match on the full path, so cmd.exe fell into the POSIX bucket. Because the branching sat behind a module-level vscode read, none of it was reachable from a unit test, which is why this shipped. Introduce detectShellKind(shell, platform) -> "cmd" | "powershell" | "posix" as a pure function and parameterise every helper on the result. env.shell is now read in exactly one place, currentShellKind(), following the platform-injection pattern already used by venvInterpreterPath. Matching is on the basename, so C:\cmder\...\bash.exe is no longer mistaken for cmd. The terminals keep resolving their own shell rather than pinning shellPath: supplying an explicit executable makes VS Code drop the resolved profile's args, which is what makes macOS shells login shells. Bugs found once the logic became testable, each live on main: - pwsh[.exe] was unhandled. PowerShell 6+/7 is the default on modern Windows; it matched neither "powershell" nor cmd, so those users got POSIX read, which does not exist there. - PowerShell paths were double-quoted, which interpolates. A directory C:\a$(whoami)b would execute whoami, and $RECYCLE.BIN expanded to .BIN. Both PowerShell and POSIX now single-quote, which is literal. - escapedCliPath used .replace, not .replaceAll, so every quote after the first was left unescaped. - POSIX used echo, whose builtin form interprets backslash escapes in zsh and dash even inside single quotes: \t printed a tab and \c truncated the line. Now printf '%s\n', with the message as an argument rather than the format string, so a literal % is safe. - Bare read is a usage error in dash ("read: arg count", exit 2). The hold-open step failed and the following exit closed the terminal, discarding the CLI error it exists to keep readable. Now read _. - %VAR% cannot be escaped in cmd, since it expands inside double quotes too. BundleInitWizard now refuses such an output directory instead of scaffolding into a directory the user never chose. RunCommands sends to a reused window.activeTerminal whose shell cannot be detected on the API level this extension targets, so it keeps main's double quotes via explicit *ForUnknownShell helpers; switching it to single quotes would newly break a focused cmd tab. Tests: 53 cases over three layers, chosen so each covers the others' blind spots. Table-driven classification across shell/platform pairs, exact expected command lines per shell, and round-trip execution through real shells (sh, bash, zsh, dash, plus cmd and PowerShell where present) asserting output matches input byte for byte. The round-trip layer is what caught the echo and read defects; the string assertions alone had passed. Every fix above was confirmed to have at least one test that fails when it is reverted. Fixes #1822 Co-authored-by: Isaac
AzureCliCheck had the same defect as the bundle init wizard: it hardcoded ";" as a separator and POSIX `echo`, then sent the line to whatever shell the terminal was running. On Windows with cmd.exe that fails the same way as #1822 -- cmd has no ";" separator, so the login command and the hold-open step are parsed as garbage and `az login` never runs. Assemble it from the shell-aware helpers instead, resolving the kind once via currentShellKind(). The terminal is created without `shellPath`, so it runs the resolved default profile, which is what currentShellKind reports. Two related fixes in the same line: - The az executable is now quoted. It was interpolated bare, so a custom azBinPath containing spaces (`C:\Program Files\Azure CLI\az.cmd`) split into multiple arguments. Quoting a bare command name still resolves via PATH in all three shells, so the default "az" is unaffected. - The tenant is now quoted. It comes from a token's `iss` claim, i.e. external data reaching a command line, and was interpolated bare. Also drops the empty-string placeholders the old template left behind: `--use-device-code` and `-t` are appended to an argument array rather than always emitted, so the command no longer contains runs of double spaces when either is absent. Tests: 7 cases asserting the assembled line verbatim for cmd, PowerShell and POSIX, plus the tenant-absent, codespaces, spaces-in-path and tenant-quoting cases. Confirmed to bite: reverting the separator to a hardcoded ";" fails the cmd case, and reverting cmd's `pause` to `read` fails it too. Co-authored-by: Isaac
The Linux CI runner has pwsh installed, so the probe found it and these four tests ran for real rather than skipping -- which is what they were written for, and they failed. Three defects, all in the tests rather than in the code under test: - Two timed out. Each looped over 6-7 inputs spawning a fresh pwsh, and a PowerShell cold start is ~0.5-1s, so six sequential spawns blew Mocha's 2000ms default. The POSIX round-trips never hit this because sh and dash start in single-digit milliseconds. Batch every case into one invocation joined by our own commandSeparator, which also exercises the separator, and raise the timeout on both windows-shell suites. - `Clear-Host` on Linux shells out to `clear`, which needs $TERM. There is no TTY under CI, so it warns on stderr and the assertion for empty stderr failed on the environment rather than on the command. Filter that one line. - `Clear-Host` writes its clear sequences to stdout, so comparing against "ok" failed on the escape codes. Assert on them instead: their presence is the proof it cleared, and "ok" surviving after them is the proof the separator did not swallow the next command. Verified against PowerShell 7.6.2 (via nix, not installed system-wide): 663 passing, 0 failing, 5 pending -- the same pending count CI reported, so all four now execute here. Reverting the PowerShell single-quoting fix makes the round-trip fail with `C:\a$VAR\dollar` arriving as `C:\a\dollar`, so the test constrains the shipped behaviour rather than just passing. Also ran 8 consecutive suites (4 with pwsh, 4 without) to confirm a lone failure seen in one 12s run was harness contention, not flakiness here. Co-authored-by: Isaac
…ce before &
The Windows CI leg ran the cmd round-trips for the first time and found two
real defects in the generated command lines, plus one bad assertion.
`echoLine` interpolated the message into cmd's `echo` unquoted, so any shell
operator in it was parsed rather than printed. The bundle-init banner embeds
the user's output directory, so a folder named `a&b` made cmd try to run `b`
as a command:
'pipe' is not recognized as an internal or external command,
Escape `^ & | < > ( )` with a caret instead. Quoting is not an option here:
cmd's `echo` prints the rest of the line raw, so the quotes would show. The
escaping deliberately skips text already inside double quotes -- cmd treats
operators there as literal, and a caret would *print*, which would corrupt
the quoted path the banner embeds. One regex pass with an alternation keeps
quoted runs intact.
The cmd separator loses its leading space, for the same reason: `echo one &`
printed "one" with a trailing space, since everything up to the `&` is text.
The third failure was `Clear-Host` throwing "The handle is invalid" with no
console attached. That is the runner's environment rather than our command
line, so the round-trip now resolves the verb with Get-Command instead of
running it -- which is also a stronger assertion, because a POSIX `clear`
leaking into the PowerShell branch resolves as an Application on Linux and
would have passed when merely executed. Asserting on the resolved name, not
the command type: Clear-Host is a Function in pwsh 7 but a Cmdlet in Windows
PowerShell 5.
Also removes an over-correction from the previous commit, which required the
clear escape sequences to be present in stdout. Whether Clear-Host emits them
depends on the detected terminal capabilities: they appear with $TERM set and
not on a TTY-less runner, so the requirement failed on CI.
Verified green in all three shapes this affects: with pwsh and $TERM, with
pwsh and $TERM unset (the CI shape, checked with `env -u TERM`), and with no
pwsh at all. 666 passing, 0 failing. Reverting the caret escaping fails 2
tests; reverting the Get-Command change fails 1.
Co-authored-by: Isaac
`read _` is an error in fish, where `_` is a read-only variable
("read: _: cannot overwrite read-only variable", exit 2). That fails the
hold-open step, and the `exit` that follows then closes the terminal and
discards exactly the CLI error the pause exists to keep readable.
There is no shorter spelling that works everywhere:
candidate sh bash zsh dash fish
read ok ok ok X X (echoes the input back)
read _ ok ok ok ok X (read-only variable)
read -r name ok ok ok ok X (no -r flag in fish)
read discard ok ok ok ok ok
So use an ordinary variable name. Note `-r` is not the fix it looks like:
fish has no such flag.
fish now takes part in the posix round-trips, probed for on PATH so the
tests skip rather than fail where it isn't installed (neither CI image
has it, nor does macOS out of the box).
Also add a round-trip that asserts the pause consumes its line
*silently*. The existing check only rejects usage errors, which bare
`read` in fish slips past: it exits 0 with empty stderr while echoing the
keypress back into the terminal. Reverting to `read _` fails 6 tests;
reverting to bare `read` fails 7, of which the fish echo-back is caught
only by the new assertion.
Co-authored-by: Isaac
`escapePathArgument`'s posix branch is wrong for fish, which treats `\` as an escape inside single quotes. `two\\slashes` arrives as `two\slashes`, and a trailing `\` makes fish report unbalanced quotes. Both are legal directory names on macOS and Linux, so this is reachable rather than theoretical. Not fixed here: fish would need its own ShellKind, since only the quoting differs and clear/;/printf/read are all shared with posix. That is wider than the #1822 fix this branch is for, so leave a note where the next person will look rather than only in the PR description. Co-authored-by: Isaac
abdelrahmanomar1515
force-pushed
the
fix-1822-shell-detection
branch
from
August 7, 2026 16:40
7fae88f to
7af3a57
Compare
abdelrahmanomar1515
temporarily deployed
to
test-trigger-is
August 7, 2026 16:40 — with
GitHub Actions
Inactive
Contributor
|
If integration tests don't run automatically, an authorized user can run them manually by following the instructions below: Trigger: Inputs:
Checks will be approved automatically on success. |
abdelrahmanomar1515
requested a deployment
to
test-trigger-is
August 7, 2026 16:42 — with
GitHub Actions
Queued
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1822.
Why
On Windows with cmd.exe as the terminal shell, "Create a new Databricks project" was
broken. The wizard built one POSIX command line and sent it to whatever shell the
terminal was running, so cmd rejected it token by token and the CLI never started:
clear,;as a separator, andreadare all POSIX-only. cmd needscls,&andpause.The reported wizard turned out not to be the only casualty. The same shared helpers are
used by "Databricks SSH Tunnel" and
az login, and both were broken in cmd onmainfor the same reason — details in their own sections below.The root cause is structural, not a missing branch.
shellUtilsreadenv.shellinsideevery helper and split only "PowerShell" vs "everything else", using a substring match on
the full path — so cmd.exe fell into the POSIX bucket. Because the branching sat behind a
module-level VS Code read, none of it could be reached from a unit test, so this class of
bug had no way to be caught. Fixing only the
clear/readsymptoms would have left thenext shell-specific bug just as invisible, which is why this is a refactor rather than a
two-line patch.
What changed
One classifier instead of scattered predicates.
detectShellKind(shell, platform) -> "cmd" | "powershell" | "posix"is a pure function;every helper takes that kind.
env.shellis now read in exactly one place,currentShellKind(), matching the existingvenvInterpreterPathinjection pattern inthis repo. Detection matches on the shell's basename, so
C:\cmder\...\bash.exeis no longer mistaken for cmd.Terminals still resolve their own shell. I deliberately did not pin
shellPath.Passing an explicit executable makes VS Code build a synthetic profile from
{path, args}and never supply the resolved profile's defaults(
terminalProfileResolverService.ts:113-124) —and on macOS the fallback profile is what appends
--login. So pinningshellPath: env.shellwould silently turn a login shell into a non-login one, stop~/.zprofilebeing sourced, and changePATH.env.shellexposes onlyprofile.path,never
args, so those args can't be recovered.Per file:
utils/shellUtils.tsShellKind+detectShellKind; all helpers parameterised; per-shell quoting;clearCmd/commandSeparator/echoLine/hasCmdUnsafeCharsaddedutils/shellUtils.test.tsbundle/BundleInitWizard.tscli/CliWrapper.tsescapedCliPathFor(kind);escapedCliPathdelegates to it — which also fixes the SSH tunnel, see belowconfiguration/auth/AzureCliCheck.tsaz loginline; also quotes the az path and the tenantrun/RunCommands.tsBugs found once the logic became testable
Each is a real defect on
main, not hypothetical:pwsh[.exe]was unhandled — PowerShell 6+/7, the default on modern Windows. Itmatched neither
"powershell"nor cmd, so those users got POSIXread, which does notexist in PowerShell.
C:\Users\me\$RECYCLE.BINexpanded toC:\Users\me\.BIN, and a directory namedC:\a$(whoami)bwould executewhoami. Both PowerShell and POSIX now use singlequotes, which are literal.
escapedCliPathquoted the CLI wrongly for both Windows shells, which broke theSSH tunnel as well as the init wizard — see the section below.
escapedCliPathused.replace, not.replaceAll— every quote after the firstwas left unescaped.
az logininterpolated both the az path and the tenant bare. A customazBinPathcontaining spaces (
C:\Program Files\Azure CLI\az.cmd) split into several arguments,and the tenant — which comes from a token's
issclaim, i.e. external data reaching acommand line — was unquoted. Both are quoted now. (Quoting a bare command name still
resolves via
PATHin all three shells, so the defaultazis unaffected.)echo, whose builtin form interprets backslash escapes in zsh and dasheven inside single quotes:
\tprinted a tab, and\ctruncated the rest of theline. Now
printf '%s\n', which is verbatim everywhere. (The message is a printfargument, not the format string, so a literal
%is safe.)readpause needs a named variable; every shorter spelling breaks a shell.Bare
readis a usage error in dash (read: arg count, exit 2), and in fish itexits 0 but echoes the keypress back into the terminal.
read _fixes dash but is anerror in fish, where
_is read-only.read -r nameis an error in fish too — it has no-rflag. Nowread discard, the only formsh,bash,zsh,dashandfishallaccept. A failed pause isn't cosmetic: the following
exitcloses the terminal anddiscards exactly the CLI error the pause exists to keep on screen. (Matrix in the testing
section below.)
echoprinted shell operators as operators. The message was interpolatedunquoted, so a banner containing
&,|or>was parsed rather than printed — and thebanner embeds the user's output directory, so a folder named
a&bmade cmd try to runbas a command ('pipe' is not recognized...). Now caret-escaped. Quoting isn't anoption: cmd's
echoprints the rest of the line raw, so the quotes would show. Theescaping skips text already inside double quotes, where cmd treats operators as literal
and a caret would itself print — that's the shape the real banner has.
echo one & ...printedonewith atrailing space, for the same raw-text reason. Now
&rather than&.%VAR%cannot be escaped in cmd — it expands even inside double quotes. Output dirC:\p%TEMP%q\projwould have scaffolded into a directory the user never chose, afterwhich
getSubProjectsreports "no Databricks projects detected".BundleInitWizardnowrefuses up front with an actionable message. (
!VAR!is included too, since it expandsthe same way under delayed expansion.)
The SSH tunnel had the same bug, and is fixed by the same change
Not just the init wizard: "Databricks SSH Tunnel" was broken in cmd on
maintoo, andin
pwsh.SshCommands.launchSshTunnelsends${this.cli.escapedCliPath} …(
src/ssh/SshCommands.ts:435), andmain's getter had no cmd branch at all:The consequence differs per shell, and neither starts the tunnel:
quotes are part of the name and it looks for a program literally called
'C:\…\databricks.exe'→is not recognized as an internal or external command.pwshfailedisPowershell()'s"powershell"substring match, so it also gotsingle quotes — and without the
&call operator a quoted string on its own line isjust a string expression, so PowerShell prints the path rather than running it.
No change to
SshCommands.tswas needed:escapedCliPathnow delegates toescapedCliPathFor(currentShellKind()), which routes cmd to double quotes and PowerShellto
& '…'. The call site was already correct — the terminal is created withoutshellPath(SshCommands.ts:428-433), which is exactly the precondition the getterdocuments, so the default profile really is the shell that parses the line. That's what
distinguishes it from
RunCommandsbelow.The tunnel's arguments need no quoting:
getSshConnectCommand(
CliWrapper.ts:128-142) emits only internally-generated tokens —ssh connect,--ide=vscode|cursor,--auto-approve,--auto-start-cluster, a cluster ID, and anaccelerator from a hardcoded list — with no spaces or metacharacters. And it's a single
command, so none of the
clear/read/separator/echosyntax that varies per shell isinvolved. Worth noting for a follow-up: those args are interpolated unquoted, which is
fine for today's fixed set but would silently become a bug if a flag ever carried a
user-supplied value. There's also no
src/ssh/test file, so this path is covered onlyindirectly through
escapeExecutableForTerminal's round-trips.RunCommandsis deliberately unchanged in behaviourrunFileUsingDbconnectsends towindow.activeTerminal ?? createTerminal(). For areused terminal the shell is not
env.shell, andTerminalState.shelldoesn't exist inthe
@types/vscode@1.86.0this extension targets, so no escaping choice is safe.main's"…"quoting happens to parse in both cmd and POSIX, so it survives amismatch by accident; switching its POSIX branch to single quotes would have newly
broken a focused cmd tab (cmd would look for a program literally named
'C:\Python\python.exe'). It now calls explicitescapePathArgumentForUnknownShell/escapeExecutableForUnknownShellhelpers that keep the double quotes and say why. Thereal fix there is a
ProcessExecution/task with an argv array, which needs no quoting —left as a follow-up.
Known gap: fish and backslashes
escapePathArgument's POSIX branch is not correct for fish, which — unlike sh, bash,zsh and dash — treats
\as an escape inside single quotes. Two shapes go wrong:/Users/me/two\\slashestwo\slashes/Users/me/trailing\Unexpected end of string, quotes are not balancedThis is reachable, not theoretical: a directory literally named
two\\slashesis legal onmacOS and Linux, and I confirmed it can be created. The first shape scaffolds into a
directory the user didn't choose (the same failure mode as
%VAR%in cmd); the secondfails to parse.
I've left this out of this PR deliberately — it needs a fourth
ShellKind(fish iscurrently classified
posix, which is right forclear/;/printfand wrong only forbackslash quoting), and that's a wider change than the bug this PR is about. Everything
else the wizard emits was verified byte-identical in fish. Happy to file it as a separate
issue.
How this was tested
Manually on Windows, across several
terminal.integrated.defaultProfile.windowssettings — this is the bug report's scenario and it now works.
yarn test:unit— 669 passing, 0 failing with PowerShell 7.6.2 and fish 4.8.1 present(599 before this branch, so 70 are new). Verified green in all three shapes this affects:
with
pwshand$TERMset, withpwshand$TERMunset (the CI shape, viaenv -u TERM), and with nopwshat all.yarn test:lintclean.fish is included in the POSIX round-trips, probed for on
PATHso the tests skiprather than fail where it isn't installed — neither CI image has it, nor does macOS out of
the box. It earns its place by not being strictly POSIX, which is how the
read _bugabove was found. The full
readmatrix, measured by running each candidate in each shell:readarg countread _read -r name-rflagread discardThree layers, chosen so the weaknesses of each are covered by another:
detectShellKindacross 13 shell/platform pairs plusregression cases: the cmder false positive,
cmdon Linux,pwshon macOS, and theempty-shell fallback per platform. Both inputs are parameters, so these are
deterministic on any host OS — no
process.platformpatching and no mocking anywherein the suite.
PowerShell and POSIX, so a reviewer can read the expected output for each shell
directly in the test.
built what the author intended — not that the shell agrees. Reverting the PowerShell
single-quoting fix makes the round-trip fail with
C:\a$VAR\dollararriving asC:\a\dollar, i.e. real PowerShell silently dropping the variable. The generated commands areexecuted through
/bin/sh,zsh,bash,dashandfish, and through cmd andPowerShell, asserting output matches input byte for byte across awkward messages and
paths: embedded quotes of both kinds,
$VAR,`cmd`,$(cmd),&/|/>,backslashes. This layer is what caught the
echoandreadbugs; the string assertionsalone had passed.
Verifying the tests actually bite. Since a test that passes either way proves nothing,
I reverted each fix individually and recorded the failures:
printfinstead ofechosh/zsh/dashonly;bashpassed, matching the real defectread discard→read _fishround-trips)read discard→ barereaddashandfish; the fish echo-back is caught only by the new silent-consume assertion%/!guardEvery fix has at least one test that fails without it. Doing this also exposed two
weaknesses in my own tests, both times an assertion too weak to see a real failure:
readround-trip originally used|| true, which masked dash's exit-2 usage error.It now asserts empty stderr and
status !== 2.readin fish exits 0 withempty stderr while echoing the keypress back into the terminal, so it slipped through.
A second round-trip now asserts the pause consumes its line silently (
stdout === ""),which is the only assertion that catches it.
Reviewer notes
cmd nor PowerShell can be exercised on a macOS dev machine, so both CI legs found real
problems that a green local run had missed:
pwshinstalled (PowerShell is probed for rather than gated onplatform), so it ran those four tests and caught two Mocha timeouts from per-case
pwshspawns plus two assertions that depended on$TERM.echo/separator defects listed above.Both are fixed and both legs are now green: Windows 660 passing / 0 failing with all
ten cmd and PowerShell round-trips executing (non-trivial durations, 0 pending), Linux 663
passing / 0 failing. So every assertion for the two shells [BUG] 'clear' is not recognized as an internal or external command #1822 is about has now run
against a real shell, not just against a string comparison.
.cmdfile rather thancmd /c <line>. Passing it as an argv entry lets Node apply its own Windows quoting,which escapes an embedded
"as\"— a sequence cmd doesn't understand — so the testwould measure Node's escaping instead of ours.
shellUtilsrefactor — which is also where the SSH tunnel fix lands, via
escapedCliPath, (2)az login, which had the identical defect, (3) making the PowerShell round-trips pass ona real
pwsh, (4) the two cmdecho/separator fixes the Windows leg found, (5) theread discardfix for fish.main, not one: the init wizard(reported), the SSH tunnel, and
az login. All three are fixed here. The manual Windowstesting covered the wizard — the bug report's scenario — so the tunnel and
az loginpaths are verified by the round-trip tests rather than by hand, and are worth a manual
look if you have a Windows box with cmd as the default profile.
echoLinetakes a single line and callers emit blank lines as separate commands.The old code embedded
\nin a double-quotedecho, which isn't portable.engines.vscodemoves past 1.86,terminal.state.shellbecomes available andwould make a good dev-only assertion: compare it against
detectShellKind(env.shell)and log a warning on disagreement. That would also cover sub-shells and WSL distro
wrappers, which basename matching can't see.
above — needs a fourth
ShellKind),RunCommandsmoving to aProcessExecutionwith anargv array, and the SSH tunnel's unquoted args.
This pull request and its description were written by Isaac.