-
Notifications
You must be signed in to change notification settings - Fork 8.5k
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) #9116
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,6 +59,7 @@ static constexpr std::string_view AntialiasingModeKey{ "antialiasingMode" }; | |
static constexpr std::string_view TabColorKey{ "tabColor" }; | ||
static constexpr std::string_view BellStyleKey{ "bellStyle" }; | ||
static constexpr std::string_view PixelShaderPathKey{ "experimental.pixelShaderPath" }; | ||
static constexpr std::string_view EnvironmentKey{ "environment" }; | ||
|
||
static constexpr std::wstring_view DesktopWallpaperEnum{ L"desktopWallpaper" }; | ||
|
||
|
@@ -275,6 +276,75 @@ bool Profile::ShouldBeLayered(const Json::Value& json) const | |
return sourceMatches; | ||
} | ||
|
||
namespace | ||
{ | ||
Comment on lines
+279
to
+280
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I don't think we need an extra namespace here. Declaring the function as |
||
std::wstring ResolveEnvironmentVariableValue(const std::wstring& key, StringMap& envMap, std::map<std::wstring, bool> seenKeys) | ||
{ | ||
if (envMap.HasKey(key)) | ||
{ | ||
if (seenKeys.find(key) != seenKeys.end()) | ||
{ | ||
std::wstringstream error; | ||
error << L"Self referencing keys in environment settings: "; | ||
for (auto it : seenKeys) | ||
{ | ||
error << L"'" << it.first << L"', "; | ||
} | ||
error << L"'" << key << L"'"; | ||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::hstring(error.str())); | ||
} | ||
const std::wregex parameterRegex(L"\\$\\{env:(.*?)\\}"); | ||
std::wstring value(envMap.Lookup(key)), resolvedValue; | ||
auto textIter = value.cbegin(); | ||
std::wsmatch regexMatch; | ||
while (std::regex_search(textIter, value.cend(), regexMatch, parameterRegex)) | ||
{ | ||
if (regexMatch.size() != 2) | ||
{ | ||
std::wstringstream error; | ||
error << L"Unexpected regex match count '" << regexMatch.size() | ||
<< L"' (expected '2') when matching '" << std::wstring(textIter, value.cend()) << L"'"; | ||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::hstring(error.str())); | ||
} | ||
resolvedValue += std::wstring(textIter, regexMatch[0].first); | ||
const auto referencedKey = regexMatch[1]; | ||
if (referencedKey == key) | ||
{ | ||
std::wstringstream error; | ||
error << L"Self referencing key '" << key << L"' in environment settings"; | ||
throw winrt::hresult_error(WEB_E_INVALID_JSON_STRING, winrt::hstring(error.str())); | ||
} | ||
seenKeys[key] = true; | ||
resolvedValue += ResolveEnvironmentVariableValue(referencedKey, envMap, seenKeys); | ||
textIter = regexMatch[0].second; | ||
} | ||
std::copy(textIter, value.cend(), std::back_inserter(resolvedValue)); | ||
|
||
return resolvedValue; | ||
} | ||
const auto size = GetEnvironmentVariableW(key.c_str(), nullptr, 0); | ||
if (size > 0) | ||
{ | ||
std::wstring value(static_cast<size_t>(size), L'\0'); | ||
if (GetEnvironmentVariableW(key.c_str(), value.data(), size) > 0) | ||
{ | ||
// Documentation (https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surely, I've missed something here? |
||
// says: "If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer, | ||
// not including the terminating null character." | ||
// However, my SystemDrive "C:" returns 3 and so without trimming we will end up with a stray NULL string terminator | ||
// in the string. | ||
value.erase(std::find_if(value.rbegin(), value.rend(), [](wchar_t ch) { | ||
return ch != L'\0'; | ||
}).base(), | ||
value.end()); | ||
|
||
return value; | ||
} | ||
} | ||
return {}; | ||
} | ||
} | ||
|
||
// Method Description: | ||
// - Layer values from the given json object on top of the existing properties | ||
// of this object. For any keys we're expecting to be able to parse in the | ||
|
@@ -339,6 +409,26 @@ void Profile::LayerJson(const Json::Value& json) | |
JsonUtils::GetValueForKey(json, TabColorKey, _TabColor); | ||
JsonUtils::GetValueForKey(json, BellStyleKey, _BellStyle); | ||
JsonUtils::GetValueForKey(json, PixelShaderPathKey, _PixelShaderPath); | ||
|
||
const auto environmentKey = std::string(EnvironmentKey); | ||
if (json.isMember(environmentKey)) | ||
{ | ||
auto environmentNode = json[environmentKey]; | ||
StringMap rawEnvMap{}, resolvedEnvMap{}; | ||
for (Json::Value::const_iterator it = environmentNode.begin(); it != environmentNode.end(); ++it) | ||
{ | ||
rawEnvMap.Insert(JsonUtils::GetValue<hstring>(it.key()), JsonUtils::GetValue<hstring>(*it)); | ||
} | ||
|
||
auto view = rawEnvMap.GetView(); | ||
for (auto it = view.First(); it.HasCurrent(); it.MoveNext()) | ||
{ | ||
const std::wstring key((*it).Key()); | ||
resolvedEnvMap.Insert(key, ResolveEnvironmentVariableValue(key, rawEnvMap, {})); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this last parameter is always There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's used to detect self or circular references when resolving the environment variables so it needs to be updated and passed in as a parameter (not reference). I can default it in the function declaration so it is not needed to be specified here if that helps? |
||
} | ||
|
||
EnvironmentVariables(resolvedEnvMap); | ||
} | ||
} | ||
|
||
// Method Description: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,6 +21,9 @@ Author(s): | |
#include "../inc/cppwinrt_utils.h" | ||
#include "JsonUtils.h" | ||
#include <DefaultSettings.h> | ||
#include <winrt/impl/Windows.Foundation.Collections.2.h> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You shouldn't need to include this header directly here - we should be able to get |
||
|
||
using namespace winrt::Windows::Foundation::Collections; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is gonna sound like a nit, but it's usually best practice to avoid |
||
|
||
// fwdecl unittest classes | ||
namespace SettingsModelLocalTests | ||
|
@@ -114,6 +117,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation | |
|
||
GETSET_SETTING(Model::BellStyle, BellStyle, BellStyle::Audible); | ||
|
||
GETSET_SETTING(StringMap, EnvironmentVariables); | ||
|
||
private: | ||
static std::wstring EvaluateStartingDirectory(const std::wstring& directory); | ||
|
||
|
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.
Same deal in this header