Skip to content
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

Conversation

christapley
Copy link
Contributor

@christapley christapley commented Feb 25, 2021

Summary of the Pull Request

  • Implement environment variables with validation
  • use StringMap as a container
  • recursively resolve ${env:NAME} vars in values in the StringMap or in
    the process' environment
  • implement some tests

References

PR Checklist

  • Closes (Feature Request) Terminal Environment setting (like in VScode) #2785
  • CLA signed. If not, go over here and sign the CLA
  • Tests added/passed
  • Documentation updated. If checked, please file a pull request on our docs repo and link it here: #xxx
  • Schema updated.
  • I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx

Detailed Description of the Pull Request / Additional comments

Validation Steps Performed

Added tests and manual validation

* use StringMap as a container
* recursively resolve ${env:NAME} vars in values in the StringMap or in
  the process' environment
* implement some tests
@ghost ghost added Area-Settings Issues related to settings and customizability, for console or terminal Area-TerminalConnection Issues pertaining to the terminal<->backend connection interface Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Product-Terminal The new Windows Terminal. labels Feb 25, 2021
@zadjii-msft zadjii-msft self-assigned this Mar 3, 2021
Copy link
Member

@zadjii-msft zadjii-msft left a 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.

src/cascadia/TerminalControl/IControlSettings.idl Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsModel/Profile.h Outdated Show resolved Hide resolved

void Profile::ValidateEvaluatedEnvironmentVariables() const
{
const auto envVars = _EvaluatedEnvironmentVariables();
Copy link
Member

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

Copy link
Member

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

src/cascadia/TerminalSettingsModel/JsonUtils.h Outdated Show resolved Hide resolved
std::decay_t<T> GetValue(const Json::Value& json);

template<>
struct ConversionTrait<winrt::Windows::Foundation::Collections::StringMap>
Copy link
Member

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?

Copy link
Contributor Author

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?

src/cascadia/TerminalSettingsModel/Profile.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsModel/Profile.cpp Outdated Show resolved Hide resolved
@ghost ghost added the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Mar 4, 2021
@zadjii-msft
Copy link
Member

@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.

@ghost ghost added the AutoMerge Marked for automatic merge by the bot when requirements are met label Mar 4, 2021
@ghost
Copy link

ghost commented Mar 4, 2021

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:

  • I'll only merge this pull request if it's approved by @DHowett

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".

@zadjii-msft zadjii-msft assigned DHowett and christapley and unassigned zadjii-msft Mar 4, 2021
@ghost ghost removed the Needs-Author-Feedback The original author of the issue/PR needs to come back and respond to something label Mar 6, 2021
* add comments to ResolveEnvironmentVariableValue
* use wil helper
* remove unused code
@ghost ghost removed the AutoMerge Marked for automatic merge by the bot when requirements are met label Mar 6, 2021
Comment on lines +607 to +612
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()));

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp Outdated Show resolved Hide resolved
src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp Outdated Show resolved Hide resolved
// 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 = {})

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?

Copy link
Contributor Author

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
$

Copy link
Member

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.

Copy link
Member

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.

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.

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?

Copy link
Member

Choose a reason for hiding this comment

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

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.

Comment on lines +579 to +583
if (!it->second.resolvedValue.empty())
{
// key has already been resolved, so just return it
return it->second.resolvedValue;
}

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.

Copy link
Contributor Author

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?

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.

src/cascadia/TerminalSettingsModel/Profile.cpp Outdated Show resolved Hide resolved
* allow appending or prepending process environment variables
* don't handle all exceptions on validation failure
@github-actions

This comment has been minimized.

@christapley
Copy link
Contributor Author

@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.

@zadjii-msft
Copy link
Member

@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.

@zadjii-msft zadjii-msft self-assigned this Mar 8, 2021
Copy link
Member

@zadjii-msft zadjii-msft left a 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);
}
/*++
Copy link
Member

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);
}
/*++
Copy link
Member

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 😢

@zadjii-msft zadjii-msft removed their assignment Mar 8, 2021
@christapley
Copy link
Contributor Author

@DHowett, let me know if there's anything you don't like or want changed. Thanks!

@DHowett
Copy link
Member

DHowett commented Mar 17, 2021

@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!

@christapley
Copy link
Contributor Author

Hey @DHowett, any feedback on this pr?

@zadjii-msft
Copy link
Member

@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 😢

@DHowett
Copy link
Member

DHowett commented Oct 19, 2021

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.

Copy link
Member

@DHowett DHowett left a 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.

  1. 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 the PATH 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¹.
  2. 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!:

image

// 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 = {})
Copy link
Member

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.

@zadjii-msft zadjii-msft added the Needs-Discussion Something that requires a team discussion before we can proceed label Nov 18, 2021
@zadjii-msft
Copy link
Member

(I'm tagging this for discussion but it almost needs it's own dedicated meeting / spec review at this point)

@zadjii-msft zadjii-msft added this to the 22H1 milestone Jan 4, 2022
@zadjii-msft zadjii-msft removed the Needs-Discussion Something that requires a team discussion before we can proceed label Jan 7, 2022
@zadjii-msft zadjii-msft modified the milestones: 22H1, Terminal v1.18 Nov 28, 2022
ghost pushed a commit that referenced this pull request Dec 13, 2022
### ⇒ [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}`
@zadjii-msft
Copy link
Member

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 ☺️

@ianjoneill
Copy link
Contributor

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 set_user_environment_var() method in til::env to do the resolving for us - which will deal with the self-referencing etc. (which will allow adding stuff to %PATH%) and variables that reference one another - assuming we iterate over them in a well-defined order - say alphabetical (ignoring case).

In fact if we change the format from ${env:NAME} to use the Windows standard %NAME% format, this will come for free - assuming you're happy with changing that.

I can take a look about merging and updating this as per the above over the next couple of weekends if you agree.

@zadjii-msft
Copy link
Member

@ianjoneill I'm cool with that. I'm cool with the %var% syntax too. I kinda liked the other one stylistically, but consistency with the rest of the OS is probably smarter anyways.

@ianjoneill
Copy link
Contributor

That's a lot of conflicts:

$ git merge main --no-ff
Auto-merging src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp
CONFLICT (content): Merge conflict in src/cascadia/LocalTests_SettingsModel/ProfileTests.cpp
Auto-merging src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
CONFLICT (content): Merge conflict in src/cascadia/LocalTests_SettingsModel/SerializationTests.cpp
Auto-merging src/cascadia/TerminalApp/AppLogic.cpp
CONFLICT (content): Merge conflict in src/cascadia/TerminalApp/AppLogic.cpp
Auto-merging src/cascadia/TerminalApp/Resources/en-US/Resources.resw
Auto-merging src/cascadia/TerminalApp/TerminalPage.cpp
CONFLICT (modify/delete): src/cascadia/TerminalApp/TerminalSettings.cpp deleted in main and modified in HEAD.  Version HEAD of src/cascadia/TerminalApp/TerminalSettings.cpp left in tree.
CONFLICT (modify/delete): src/cascadia/TerminalApp/TerminalSettings.h deleted in main and modified in HEAD.  Version HEAD of src/cascadia/TerminalApp/TerminalSettings.h left in tree.
CONFLICT (modify/delete): src/cascadia/TerminalApp/TerminalSettings.idl deleted in main and modified in HEAD.  Version HEAD of src/cascadia/TerminalApp/TerminalSettings.idl left in tree.
Auto-merging src/cascadia/TerminalControl/IControlSettings.idl
CONFLICT (content): Merge conflict in src/cascadia/TerminalControl/IControlSettings.idl
CONFLICT (modify/delete): src/cascadia/TerminalSettingsEditor/Profiles.h deleted in main and modified in HEAD.  Version HEAD of src/cascadia/TerminalSettingsEditor/Profiles.h left in tree.
CONFLICT (modify/delete): src/cascadia/TerminalSettingsEditor/Profiles.idl deleted in main and modified in HEAD.  Version HEAD of src/cascadia/TerminalSettingsEditor/Profiles.idl left in tree.
Auto-merging src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
Auto-merging src/cascadia/TerminalSettingsModel/CascadiaSettings.h
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/CascadiaSettings.h
Auto-merging src/cascadia/TerminalSettingsModel/JsonUtils.h
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/JsonUtils.h
Auto-merging src/cascadia/TerminalSettingsModel/Profile.cpp
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/Profile.cpp
Auto-merging src/cascadia/TerminalSettingsModel/Profile.h
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/Profile.h
Auto-merging src/cascadia/TerminalSettingsModel/Profile.idl
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/Profile.idl
Auto-merging src/cascadia/TerminalSettingsModel/TerminalWarnings.idl
CONFLICT (content): Merge conflict in src/cascadia/TerminalSettingsModel/TerminalWarnings.idl
Automatic merge failed; fix conflicts and then commit the result.

@ianjoneill
Copy link
Contributor

Follow-up: #15082

@zadjii-msft
Copy link
Member

Closed in favor of #15082

@zadjii-msft zadjii-msft closed this Apr 6, 2023
DHowett pushed a commit that referenced this pull request Apr 11, 2023
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Settings Issues related to settings and customizability, for console or terminal Area-TerminalConnection Issues pertaining to the terminal<->backend connection interface Issue-Feature Complex enough to require an in depth planning process and actual budgeted, scheduled work. Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

(Feature Request) Terminal Environment setting (like in VScode)
7 participants