implement env vars in settings (#2785)#9287
implement env vars in settings (#2785)#9287christapley wants to merge 8 commits intomicrosoft:mainfrom
Conversation
* use StringMap as a container
* recursively resolve ${env:NAME} vars in values in the StringMap or in
the process' environment
* implement some tests
zadjii-msft
left a comment
There was a problem hiding this comment.
I'm really happy with the tests for this - they make me feel quite a bit more confident in this.
I'm just gonna be a stickler about the comments on ResolveEnvironmentVariableValue though. I want to make sure the next person who comes through and has to touch that has a bit of an easier job 😄
That's really my only major concern. I think the rest of my comments basically count as nits.
|
|
||
| void Profile::ValidateEvaluatedEnvironmentVariables() const | ||
| { | ||
| const auto envVars = _EvaluatedEnvironmentVariables(); |
There was a problem hiding this comment.
Idea: could we cache the result of _EvaluatedEnvironmentVariables inside the Profile, the first time it's called, and then just return that value out of EvaluatedEnvironmentVariables? That way we wouldn't need to evaluate the envvars every time a new tab/pane for this profile is created
There was a problem hiding this comment.
Though, I suppose this helps re-evaluate envvars. If the environment changed between the last time the settings have loaded, and when a new tab is created, then the way it is would use the updated values.
Oof okay now my head hurts. @DHowett help me understand how this interacts with #7239 and #1125
| std::decay_t<T> GetValue(const Json::Value& json); | ||
|
|
||
| template<> | ||
| struct ConversionTrait<winrt::Windows::Foundation::Collections::StringMap> |
There was a problem hiding this comment.
If we move this ConversionTrait near the end of the file, would we need the fwddecls for nit: SetValueForKey and GetValue above?
There was a problem hiding this comment.
I believe this is a bit of a chicken and egg story. I can't put it below SetValueForKey or GetValue as they both use the ConversionTrait template. So while, yes, it could go lower I don't believe it can go low enough to not require the fwd declarations and then it wouldn't be near the other ConversionTrait template specialisations.
The other alternative is that I don't put this ConversionTrait specialisation in JsonUtil.h. It could go in Profile.cpp if you think this is better?
|
@msftbot make sure @DHowett signs off on this. I think he had some thoughts on three-way merging the variables or something? He mentioned something in team sync a couple weeks ago, but I forget what it was exactly. As a heads up, he's OOF currently, so it might be a few days before he's able to get back to this. |
|
Hello @zadjii-msft! Because you've given me some instructions on how to help merge this pull request, I'll be modifying my merge approach. Here's how I understand your requirements for merging this pull request:
If this doesn't seem right to you, you can tell me to cancel these instructions and use the auto-merge policy that has been configured for this repository. Try telling me "forget everything I just told you". |
* add comments to ResolveEnvironmentVariableValue * use wil helper * remove unused code
| if (regexMatch.size() != 2) | ||
| { | ||
| std::wstringstream error; | ||
| error << L"Unexpected regex match count '" << regexMatch.size() | ||
| << L"' (expected '2') when matching '" << std::wstring(textIter, value.rawValue.cend()) << L"'"; | ||
| throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::hstring(error.str())); |
There was a problem hiding this comment.
Perhaps that should be a fail-fast exception if it can only happen when parameterRegex is incorrectly defined.
There was a problem hiding this comment.
@zadjii-msft, what do you think about this? Should we RaiseFailFastException and terminate under this (and other) unexpected error conditions?
There was a problem hiding this comment.
I always lean towards not fail-fasting if at all possible. This seems like a perfectly fine way to gracefully handle the error for now.
| // that each layer in the recursion only has keys seen leading up to it | ||
| // Return Value: | ||
| // - The resolved value for key | ||
| std::wstring ResolveEnvironmentVariableValue(const std::wstring& key, std::map<std::wstring, VariablePair>& envMap, std::list<std::wstring> seenKeys = {}) |
There was a problem hiding this comment.
Everything in this method compares the names of environment variables case-sensitively. Is that by design?
There was a problem hiding this comment.
It was intended that way, however, I am struggling to find a use case where it needs case sensitivity. @zadjii-msft, should I make the environment variables case insensitive?
Even with a profile like git bash (which supports case sensitive env vars when running, they can't be applied through terminal:
{
"acrylicOpacity": 0.75,
"background": "#000000",
"closeOnExit": true,
"colorScheme": "Campbell",
"commandline": "\"%PROGRAMFILES%\\git\\bin\\bash.exe\" --login -i -l",
"cursorColor": "#FFFFFF",
"cursorShape": "bar",
"fontFace": "Consolas",
"fontSize": 10,
"guid": "{00000000-0000-0000-0000-000000012345}",
"historySize": 9001,
"icon": "%PROGRAMFILES%\\git\\mingw64\\share\\git\\git-for-windows.ico",
"name": "Git Bash",
"padding": "0, 0, 0, 0",
"snapOnInput": true,
"useAcrylic": true,
"environment": {
"key": "a",
"KEY": "b"
}
}
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ echo $KEY
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ echo $key
b
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ export MY_KEY=a
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ export my_key=b
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ echo $MY_KEY
a
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$ echo $my_key
b
Chris@DESKTOP-R8BPF5L MINGW64 /c/WINDOWS/SysWOW64
$
There was a problem hiding this comment.
Yikes, this one I'm not sure about. I always assumed that env vars were case insensitive on Windows. So maybe the resolution here should be as well. That seems to make sense to me, but I don't think this is work blocking over tbh. @DHowett might have other thoughts though.
There was a problem hiding this comment.
Alas, yes.. I believe Windows' environment block expects to never find multiple case-differing copies of a single environment variable. We may want to keep Windows' tenets in the profile loader rather than Linux's.
There was a problem hiding this comment.
Are the case-insensitive string comparisons in GetEnvironmentVariable, ExpandEnvironmentStrings, etc. culture-dependent? I hope not.
There was a problem hiding this comment.
You can look in PT Run's environment helper class. I copied the entire behavior of windows. The helper behaves exactly the same as Windows does.
Are global profile environment variables planned?
There was a problem hiding this comment.
@htcfreek your insight might be appreciated over in #1125, where @miniksa is working on https://github.com/microsoft/terminal/blob/dev/miniksa/env/src/inc/til/env.h
There was a problem hiding this comment.
I can help with how windows behave. But I can't help eith coding. I don't know much about C++ language.
| if (!it->second.resolvedValue.empty()) | ||
| { | ||
| // key has already been resolved, so just return it | ||
| return it->second.resolvedValue; | ||
| } |
There was a problem hiding this comment.
This won't notice if the environment variable has been previously resolved with an empty value. But okay, it's only a shortcut.
There was a problem hiding this comment.
I could make this more explicit and store something so that we know it has been resolved if you think it is worth the complexity?
There was a problem hiding this comment.
I think the complexity is not worth it until this becomes so slow that users notice.
* allow appending or prepending process environment variables * don't handle all exceptions on validation failure
This comment has been minimized.
This comment has been minimized.
|
@zadjii-msft, should I resolve the merge conflicts by rebasing off your main and force pushing? I know this is not ideal for people reviewing this PR but I don't have any other way of resolving the conflicts from my end. |
|
@christapley I think github let me resolve the conflict for you? I really hope that worked - I haven't had a ton of success with the web conflict resolver in the past. |
* Use INHERITABLE_SETTING macro
zadjii-msft
left a comment
There was a problem hiding this comment.
Okay thanks for the extra comments!
| { | ||
| BASIC_FACTORY(Profile); | ||
| } | ||
| /*++ |
There was a problem hiding this comment.
ho boy, I bet I blew up the line endings when I merged this on github 😢
| { | ||
| BASIC_FACTORY(TerminalSettings); | ||
| } | ||
| /*++ |
There was a problem hiding this comment.
uhg same here, this is my fault 😢
|
@DHowett, let me know if there's anything you don't like or want changed. Thanks! |
|
@christapley sorry I'm so backlogged. I have ~ ~ thoughts ~ ~ about environment variables and want to make sure I have them in order before bossing you around on a code review. 😄 Thanks for the patience! |
|
Hey @DHowett, any feedback on this pr? |
|
@christapley FWIW @DHowett is stuck doing manager-y things for at least the rest of the week, so you might have a bit of a delay(1) getting some response on this one (1): more than the 6 months it's already taken, sorry 😢 |
|
Good lord. I am so sorry to leave you hanging like that. It's my responsibility to uphold my end of the bargain when it comes to community management and PR review. |
DHowett
left a comment
There was a problem hiding this comment.
Okay, this is the review I've waited months to wrap my head around. I completely understand if you've lost interest in pursuing this change.
I'm mostly concerned with how this interacts with environment expansion/environment refresh ala #7239.
Here's my thoughts.
- The environment might change outside of Terminal, and Terminal will need to re-evaluate everything in Profile's environment map.
- If we don't, a profile could specify
${env:PATH}and resurrect thePATHfrom before the system environment changed. - Q: Should we defer expansion until the connection is about to start? This makes error reporting difficult.
- Sub-Q: If error reporting is difficult, can we just not translate bad entries? I suspect Windows does this when the environment block is self-referencing or recursively-self-referencing¹.
- If we don't, a profile could specify
- Not every connection type will support an environment or environment variables. Only
ConptyConnection, which deals in native processes, really knows/cares.
If we defer expansion until the connection is about to start, we might also want to simply tackle all environment block re-evaluation. That's an immense ask, I know. The biggest issue plaguing Terminal Preview today is that we generate a new environment block for every connection, and it loses environment variables WT inherited from its parent. We'd need a 3-way merge (this has been discussed in other threads, but I am barely hanging on to a working browser state as it is and I don't want to lose this comment-in-progress.) The follow-on effects from this include, like, powershell x86 failing to find its modules, because it relies on inheriting the x64 powershell's PSModulePath from its parent. Yeah.
I love where this is going, or has gone while I was not looking at it, and I think it might be a great stone with which we can knock out a bunch of birds at the same time.
What do you think?
¹ Aha!:
| // that each layer in the recursion only has keys seen leading up to it | ||
| // Return Value: | ||
| // - The resolved value for key | ||
| std::wstring ResolveEnvironmentVariableValue(const std::wstring& key, std::map<std::wstring, VariablePair>& envMap, std::list<std::wstring> seenKeys = {}) |
There was a problem hiding this comment.
Alas, yes.. I believe Windows' environment block expects to never find multiple case-differing copies of a single environment variable. We may want to keep Windows' tenets in the profile loader rather than Linux's.
|
(I'm tagging this for discussion but it almost needs it's own dedicated meeting / spec review at this point) |
### ⇒ [doc link](https://github.com/microsoft/terminal/blob/dev/migrie%2Fs%2F642-logging/doc/specs/drafts/%23642%20-%20Buffer%20Exporting%20and%20Logging/%23642%20-%20Buffer%20Exporting%20and%20Logging.md) ⇐ ## Summary of the Pull Request This is an intentionally brief spec to address the full scope of #642. The intention of this spec is to quickly build consensus around all the features we want for logging, and prepare an implementation plan. ### Abstract > A common user need is the ability to export the history of a terminal session to > a file, for later inspection or validation. This is something that could be > triggered manually. Many terminal emulators provide the ability to automatically > log the output of a session to a file, so the history is always captured. This > spec will address improvements to the Windows Terminal to enable these kinds of > exporting and logging scenarios. ## PR Checklist * [x] Specs: #642 * [x] References: #5000, #9287, #11045, #11062 * [x] I work here ## Detailed Description of the Pull Request / Additional comments _\*<sup>\*</sup><sub>\*</sub> read the spec <sub>\*</sub><sup>\*</sup>\*_ ## Open Discussion * [ ] What formatting string syntax and variables do we want to use? * [ ] How exactly do we want to handle "log printable output"? Do we include backspaces? Do we only log on newlines? * [ ] > maybe consider even simpler options like just `${date}` and `${time}`, and allow for future variants with something like `${date:yyyy-mm-dd}` or `${time:hhmm}`
|
Hey now that #14999 has merged, I think we can run at this hill again. Admittedly, this PR is MANY months out of date - the changes might need to just be rebased onto a new branch. However, this might be a good time to try again. @ianjoneill this might be something you're interested in doing, if @christapley is cool with that. (don't feel obligated to though) Sorry that this ended up stagnating like this! Our environment variable story was singularly unpleasant for a long time, hopefully we can finally get past all that |
|
I've had a look over this - and I think I'd agree with @DHowett's comment above - the resolving of the variables should happen when the connection is started (where we refresh the environment block). Following #14839, we'd get the In fact if we change the format from I can take a look about merging and updating this as per the above over the next couple of weekends if you agree. |
|
@ianjoneill I'm cool with that. I'm cool with the |
|
That's a lot of conflicts: |
|
Follow-up: #15082 |
|
Closed in favor of #15082 |
Existing environment variables can be referenced by enclosing the name in percent characters (e.g. `%PATH%`). Resurrects #9287 by @christapley. Tests added and manually tested. Closes #2785 Closes #9233 Co-authored-by: Chris Tapley <[email protected]>

Summary of the Pull Request
the process' environment
References
PR Checklist
Detailed Description of the Pull Request / Additional comments
Validation Steps Performed
Added tests and manual validation