-
Notifications
You must be signed in to change notification settings - Fork 8.6k
Introduce PowerShell installer stub #18639
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
base: dev/cazamor/sui/extensions-page
Are you sure you want to change the base?
Changes from 5 commits
2e7e37a
a7d6e27
cc1d632
0fe444e
36d28e2
2445500
8db1805
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 |
---|---|---|
|
@@ -19,6 +19,7 @@ | |
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED | ||
#include "SshHostGenerator.h" | ||
#endif | ||
#include "PowershellInstallationProfileGenerator.h" | ||
|
||
#include "ApplicationState.h" | ||
#include "DefaultTerminal.h" | ||
|
@@ -178,15 +179,90 @@ SettingsLoader::SettingsLoader(const std::string_view& userJSON, const std::stri | |
// (meaning profiles specified by the application rather by the user). | ||
void SettingsLoader::GenerateProfiles() | ||
{ | ||
_executeGenerator(PowershellCoreProfileGenerator{}); | ||
_executeGenerator(WslDistroGenerator{}); | ||
_executeGenerator(AzureCloudShellGenerator{}); | ||
_executeGenerator(VisualStudioGenerator{}); | ||
{ | ||
PowershellCoreProfileGenerator powerShellGenerator{}; | ||
_executeGenerator(powerShellGenerator); | ||
|
||
if (Feature_PowerShellInstallerProfileGenerator::IsEnabled()) | ||
{ | ||
const auto isPowerShellInstalled = !powerShellGenerator.GetPowerShellInstances().empty(); | ||
if (!isPowerShellInstalled) | ||
{ | ||
// Only generate the installer stub profile if PowerShell isn't installed. | ||
PowershellInstallationProfileGenerator pwshInstallationGenerator{}; | ||
_executeGenerator(pwshInstallationGenerator); | ||
} | ||
|
||
// Regardless of running the installer's generator, we need to do some cleanup still. | ||
_cleanupPowerShellInstaller(isPowerShellInstalled); | ||
} | ||
} | ||
|
||
WslDistroGenerator wslGenerator{}; | ||
_executeGenerator(wslGenerator); | ||
|
||
AzureCloudShellGenerator acsGenerator{}; | ||
_executeGenerator(acsGenerator); | ||
|
||
VisualStudioGenerator vsGenerator{}; | ||
_executeGenerator(vsGenerator); | ||
|
||
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED | ||
_executeGenerator(SshHostGenerator{}); | ||
SshHostGenerator sshGenerator{}; | ||
_executeGenerator(sshGenerator); | ||
#endif | ||
} | ||
|
||
// Retrieve the "Install Latest PowerShell" profile and... | ||
// - add a comment to the JSON to indicate it's conditionally applied | ||
// - (if PowerShell is installed) mark it for deletion | ||
void SettingsLoader::_cleanupPowerShellInstaller(bool isPowerShellInstalled) | ||
{ | ||
const hstring pwshInstallerNamespace{ PowershellInstallationProfileGenerator::Namespace }; | ||
if (extensionPackageMap.contains(pwshInstallerNamespace)) | ||
{ | ||
if (const auto& fragExtList = extensionPackageMap[pwshInstallerNamespace]->Fragments(); fragExtList.Size() > 0) | ||
{ | ||
Json::StreamWriterBuilder styledWriter; | ||
styledWriter["indentation"] = " "; | ||
styledWriter["commentStyle"] = "All"; | ||
|
||
auto fragExt = get_self<FragmentSettings>(fragExtList.GetAt(0)); | ||
|
||
// We want the comment to be the first thing in the object, | ||
// "closeOnExit" is the first property, so target that. | ||
auto fragExtJson = _parseJSON(til::u16u8(fragExt->Json())); | ||
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. To be honest, I kind of forgot how the extension code works, but this still seems kind of hacky to me. The fragment in this case is based on an extension, right? Don't extensions generate profiles only? I'm missing the big picture how we go from profiles to JSON here, hence my confusion. Basically, since extensions generate profiles (including 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. Oh this section is SUPER hacky! The sole purpose of this is to add the JSON comment so it appears like this: So yeah, we have to parse the JSON, add the comment, then store the new JSON with the comment back in.
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. Figured it'd be good to try and tell the user that this profile only appears if PowerShell is not installed. It's a weird little behavior that I figured at least a JSON comment would help point out, even if not many people read the JSON comment. |
||
fragExtJson[JsonKey(ProfilesKey)][0]["closeOnExit"].setComment(til::u16u8(fmt::format(FMT_COMPILE(L"// {}"), RS_(L"PowerShellInstallationProfileJsonComment"))), Json::CommentPlacement::commentBefore); | ||
fragExt->Json(hstring{ til::u8u16(Json::writeString(styledWriter, fragExtJson)) }); | ||
|
||
if (const auto& profileEntryList = fragExt->NewProfilesView(); profileEntryList.Size() > 0) | ||
{ | ||
auto profileEntry = get_self<FragmentProfileEntry>(profileEntryList.GetAt(0)); | ||
|
||
// We want the comment to be the first thing in the object, | ||
// "closeOnExit" is the first property, so target that. | ||
auto profileJson = _parseJSON(til::u16u8(profileEntry->Json())); | ||
profileJson["closeOnExit"].setComment(til::u16u8(fmt::format(FMT_COMPILE(L"// {}"), RS_(L"PowerShellInstallationProfileJsonComment"))), Json::CommentPlacement::commentBefore); | ||
profileEntry->Json(hstring{ til::u8u16(Json::writeString(styledWriter, profileJson)) }); | ||
|
||
// If PowerShell is installed, mark the installer profile for deletion | ||
if (isPowerShellInstalled) | ||
carlos-zamora marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
const auto profileGuid = profileEntryList.GetAt(0).ProfileGuid(); | ||
for (const auto& profile : userSettings.profiles) | ||
{ | ||
if (profile->Guid() == profileGuid) | ||
{ | ||
profile->Deleted(true); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
// A new settings.json gets a special treatment: | ||
// 1. The default profile is a PowerShell 7+ one, if one was generated, | ||
// and falls back to the standard PowerShell 5 profile otherwise. | ||
|
@@ -994,7 +1070,7 @@ bool SettingsLoader::_addOrMergeUserColorScheme(const winrt::com_ptr<implementat | |
|
||
// As the name implies it executes a generator. | ||
// Generated profiles are added to .inboxSettings. Used by GenerateProfiles(). | ||
void SettingsLoader::_executeGenerator(const IDynamicProfileGenerator& generator) | ||
void SettingsLoader::_executeGenerator(IDynamicProfileGenerator& generator) | ||
{ | ||
const auto generatorNamespace = generator.GetNamespace(); | ||
std::vector<winrt::com_ptr<implementation::Profile>> generatedProfiles; | ||
|
@@ -1588,7 +1664,11 @@ void CascadiaSettings::_resolveNewTabMenuProfiles() const | |
auto activeProfileCount = gsl::narrow_cast<int>(_activeProfiles.Size()); | ||
for (auto profileIndex = 0; profileIndex < activeProfileCount; profileIndex++) | ||
{ | ||
remainingProfilesMap.emplace(profileIndex, _activeProfiles.GetAt(profileIndex)); | ||
const auto& profile = _activeProfiles.GetAt(profileIndex); | ||
if (!profile.Deleted()) | ||
{ | ||
remainingProfilesMap.emplace(profileIndex, _activeProfiles.GetAt(profileIndex)); | ||
} | ||
} | ||
|
||
// We keep track of the "remaining profiles" - those that have not yet been resolved | ||
|
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.
Is it really necessary to extend the lifetime of these generators? They only get destroyed at the end of the scope now.
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.
What motivated this change was that the
PowershellCoreProfileGenerator
already does all the work of finding the existing PowerShell instances; it uses that information to generate (and configure) the appropriate amount of PowerShell profiles.We need an instance of the profile generator lying around so that we can reuse that information. That way, if we detect that there's an instance of PowerShell installed, we hide the installer stub.
Admittedly, it's not ideal, but its all in one function. Any ideas on how to get around it? Is it worth making the PowershellCoreProfileGenerator special? Or maybe I should pull out the
GetPowerShellInstances()
code into a static function and undo the work of keeping the generators around (I'll give that a try).