Make variable resolvers based on environment including launch config env#299752
Make variable resolvers based on environment including launch config env#299752meganrogge merged 1 commit intomicrosoft:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adjusts terminal variable resolution so ${env:...} in terminal environment settings is resolved against an environment that better matches the terminal’s initial environment, including environment contributed by launch configurations (fixing #299520).
Changes:
- Build the variable resolver environment from the base terminal environment, then merge in
getShellEnvironment()to incorporate launch-config/shell-derived values. - In remote terminal creation on the server, use
baseEnv(notprocess.env) as the resolver environment to preserve expected PATH/content (eg remotecodeCLI). - Update terminal tests to stub
getShellEnvironment()on the test backend.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts | Extends the test backend stub with getShellEnvironment() to support the new call site. |
| src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts | Constructs resolver env from base env + merged shell env before creating the variable resolver. |
| src/vs/server/node/remoteTerminalChannel.ts | Uses baseEnv for variable resolution instead of process.env during remote terminal creation. |
| const envForResolver = { ...await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority) }; | ||
| terminalEnvironment.mergeEnvironments(envForResolver, await backend.getShellEnvironment()); |
There was a problem hiding this comment.
backend.getShellEnvironment() can contain variables from launch configuration / authority resolution that are explicitly set to null to unset them. mergeEnvironments supports null values to delete keys, but the remote backend implementation currently drops null values, so deletions won't be reflected in envForResolver and ${env:...} may resolve to a value that was intended to be removed. Consider preserving null entries in getShellEnvironment (or returning an ITerminalEnvironment) so deletions are applied correctly when merging.
| const envForResolver = { ...await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority) }; | |
| terminalEnvironment.mergeEnvironments(envForResolver, await backend.getShellEnvironment()); | |
| const baseEnv = await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority); | |
| const shellEnv = await backend.getShellEnvironment(); | |
| const envForResolver: ITerminalEnvironment = { ...baseEnv }; | |
| if (shellEnv) { | |
| terminalEnvironment.mergeEnvironments(envForResolver, shellEnv); | |
| } |
There was a problem hiding this comment.
The suggested code is incorrectly typed. envForResolver is typed as an ITerminalEnvironment (supports null values) and passed as the first parameter of mergeEnvironments which expects an IProcessEnvironment (no null values).
Furthermore, the more general suggestion of preserving null entries in getShellEnvironment would require changing the interfaces which is a significant change that we probably don't want to make in the context of a bugfix.
| // available, then overlay the shell environment on top so that launch configuration | ||
| // variables and shell-profile modifications take precedence. |
There was a problem hiding this comment.
The comment says the shell environment overlay includes "shell-profile modifications", but ITerminalBackend.getShellEnvironment is not consistently a full shell env across backends (eg remote backend returns only resolver-provided extension host env). Updating the comment to describe what’s actually being merged here (base env + resolver/launch-config-provided env) would avoid confusion for future maintenance.
| // available, then overlay the shell environment on top so that launch configuration | |
| // variables and shell-profile modifications take precedence. | |
| // available, then overlay any environment provided by the backend (for example from | |
| // the resolver or launch configuration) so that those values take precedence. |
| // Start with the full base environment so that all standard variables (e.g. PATH) are | ||
| // available, then overlay the shell environment on top so that launch configuration | ||
| // variables and shell-profile modifications take precedence. | ||
| const envForResolver = { ...await this._terminalProfileResolverService.getEnvironment(this.remoteAuthority) }; | ||
| terminalEnvironment.mergeEnvironments(envForResolver, await backend.getShellEnvironment()); | ||
| const variableResolver = terminalEnvironment.createVariableResolver(this._cwdWorkspaceFolder, envForResolver, this._configurationResolverService); |
There was a problem hiding this comment.
This changes how ${env:...} is resolved for terminal env settings by merging additional environment sources into the resolver input, but there’s no unit test asserting the new resolution behavior (eg launch-config env values taking precedence, and remote PATH/code CLI preservation). Adding a targeted test in the existing TerminalProcessManager test suite would help prevent regressions.
meganrogge
left a comment
There was a problem hiding this comment.
Makes sense to me. Thanks! cc @Tyriar
This PR fixes #299520.
The fix consists of making variable resolvers in local and remote terminal logic based on an environment that includes the environment coming from launch configurations.
As a result,
${env:...}expressions in terminal env settings resolve over an environment that is closer to the initial environment of terminals, and yield more meaningful values.In the test source, a stub of
getShellEnvironmentis added to support testing.