-
Notifications
You must be signed in to change notification settings - Fork 8.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
implement env vars in settings (#2785) #9287
implement env vars in settings (#2785) #9287
Conversation
* use StringMap as a container * recursively resolve ${env:NAME} vars in values in the StringMap or in the process' environment * implement some tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything in this method compares the names of environment variables case-sensitively. Is that by design?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are the case-insensitive string comparisons in GetEnvironmentVariable, ExpandEnvironmentStrings, etc. culture-dependent? I hope not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay thanks for the extra comments!
{ | ||
BASIC_FACTORY(Profile); | ||
} | ||
/*++ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 thePATH
from 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 <chris.tapley.81@gmail.com>
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