Skip to content

Commit

Permalink
Introduce PowerShell installer stub
Browse files Browse the repository at this point in the history
  • Loading branch information
carlos-zamora committed Feb 28, 2025
1 parent cb04e7f commit 9842bdc
Show file tree
Hide file tree
Showing 22 changed files with 485 additions and 291 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 10 additions & 0 deletions src/cascadia/TerminalApp/TabManagement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ namespace winrt::TerminalApp::implementation
}
const auto settings{ TerminalSettings::CreateWithNewTerminalArgs(_settings, newTerminalArgs, *_bindings) };

if (profile.Source() == L"Windows.Terminal.InstallPowerShell")
{
TraceLoggingWrite(
g_hTerminalAppProvider,
"InstallPowerShellStubInvoked",
TraceLoggingDescription("Event emitted when the 'Install Latest PowerShell' stub was invoked"),
TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
TelemetryPrivacyDataTag(PDT_ProductAndServiceUsage));
}

// Try to handle auto-elevation
if (_maybeElevate(newTerminalArgs, settings, profile))
{
Expand Down
3 changes: 2 additions & 1 deletion src/cascadia/TerminalSettingsEditor/Extensions.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@
x:Uid="Extensions_NavigateToProfileButton"
Click="NavigateToProfile_Click"
Style="{StaticResource SettingContainerResetButtonStyle}"
Tag="{x:Bind Profile.Guid}">
Tag="{x:Bind Profile.Guid}"
Visibility="{x:Bind mtu:Converters.InvertedBooleanToVisibility(Profile.Deleted)}">
<FontIcon Glyph="&#xE8A7;"
Style="{StaticResource SettingContainerFontIconStyle}" />
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ std::wstring_view AzureCloudShellGenerator::GetDisplayName() const noexcept
// - <none>
// Return Value:
// - a vector with the Azure Cloud Shell connection profile, if available.
void AzureCloudShellGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const
void AzureCloudShellGenerator::GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles)
{
if (AzureConnection::IsAzureConnectionAvailable())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model
std::wstring_view GetNamespace() const noexcept override;
std::wstring_view GetDisplayName() const noexcept override;
std::wstring_view GetIcon() const noexcept override { return {}; };
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const override;
void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) override;
};
};
2 changes: 1 addition & 1 deletion src/cascadia/TerminalSettingsModel/CascadiaSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ Model::CascadiaSettings CascadiaSettings::Copy() const

Model::FragmentSettings FragmentSettings::Copy() const
{
auto fragment{ winrt::make_self<FragmentSettings>(_source, _json, _jsonSource) };
auto fragment{ winrt::make_self<FragmentSettings>(_source, _Json, _jsonSource) };

std::vector<Model::FragmentProfileEntry> modifiedProfiles;
modifiedProfiles.reserve(_modifiedProfiles.Size());
Expand Down
14 changes: 7 additions & 7 deletions src/cascadia/TerminalSettingsModel/CascadiaSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
void _appendProfile(winrt::com_ptr<Profile>&& profile, const winrt::guid& guid, ParsedSettings& settings);
void _addUserProfileParent(const winrt::com_ptr<implementation::Profile>& profile);
bool _addOrMergeUserColorScheme(const winrt::com_ptr<implementation::ColorScheme>& colorScheme);
void _executeGenerator(const IDynamicProfileGenerator& generator);
void _executeGenerator(IDynamicProfileGenerator& generator);
void _cleanupPowerShellInstaller(bool isPowerShellInstalled);
winrt::com_ptr<implementation::ExtensionPackage> _registerFragment(const winrt::Microsoft::Terminal::Settings::Model::FragmentSettings& fragment, FragmentScope scope);

std::unordered_set<winrt::hstring, til::transparent_hstring_hash, til::transparent_hstring_equal_to> _ignoredNamespaces;
Expand Down Expand Up @@ -237,14 +238,13 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
public:
FragmentProfileEntry(winrt::guid profileGuid, hstring json) :
_profileGuid{ profileGuid },
_json{ json } {}
_Json{ json } {}

winrt::guid ProfileGuid() const noexcept { return _profileGuid; }
hstring Json() const noexcept { return _json; }
WINRT_PROPERTY(hstring, Json);

private:
winrt::guid _profileGuid;
hstring _json;
};

struct FragmentColorSchemeEntry : FragmentColorSchemeEntryT<FragmentColorSchemeEntry>
Expand All @@ -268,7 +268,7 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
public:
FragmentSettings(hstring source, hstring json, hstring jsonSource) :
_source{ source },
_json{ json },
_Json{ json },
_jsonSource{ jsonSource },
_modifiedProfiles{ winrt::single_threaded_vector<Model::FragmentProfileEntry>() },
_newProfiles{ winrt::single_threaded_vector<Model::FragmentProfileEntry>() },
Expand All @@ -277,20 +277,20 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation
Model::FragmentSettings Copy() const;

hstring Source() const noexcept { return _source; }
hstring Json() const noexcept { return _json; }
hstring JsonSource() const noexcept { return _jsonSource; }
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> ModifiedProfiles() const noexcept { return _modifiedProfiles; }
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> NewProfiles() const noexcept { return _newProfiles; }
Windows::Foundation::Collections::IVector<Model::FragmentColorSchemeEntry> ColorSchemes() const noexcept { return _colorSchemes; }
WINRT_PROPERTY(hstring, Json);

public:
// views
Windows::Foundation::Collections::IVectorView<Model::FragmentProfileEntry> ModifiedProfilesView() const noexcept { return _modifiedProfiles.GetView(); }
Windows::Foundation::Collections::IVectorView<Model::FragmentProfileEntry> NewProfilesView() const noexcept { return _newProfiles.GetView(); }
Windows::Foundation::Collections::IVectorView<Model::FragmentColorSchemeEntry> ColorSchemesView() const noexcept { return _colorSchemes.GetView(); }

private:
hstring _source;
hstring _json;
hstring _jsonSource;
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> _modifiedProfiles;
Windows::Foundation::Collections::IVector<Model::FragmentProfileEntry> _newProfiles;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
#include "SshHostGenerator.h"
#endif
#include "PowershellInstallationProfileGenerator.h"

#include "ApplicationState.h"
#include "DefaultTerminal.h"
Expand Down Expand Up @@ -178,13 +179,77 @@ 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);

WslDistroGenerator wslGenerator{};
_executeGenerator(wslGenerator);

AzureCloudShellGenerator acsGenerator{};
_executeGenerator(acsGenerator);

VisualStudioGenerator vsGenerator{};
_executeGenerator(vsGenerator);

#if TIL_FEATURE_DYNAMICSSHPROFILES_ENABLED
_executeGenerator(SshHostGenerator{});
SshHostGenerator sshGenerator{};
_executeGenerator(sshGenerator);
#endif

PowershellInstallationProfileGenerator pwshInstallationGenerator{};
_executeGenerator(pwshInstallationGenerator);

_cleanupPowerShellInstaller(!powerShellGenerator.GetPowerShellInstances().empty());
}

// 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()));
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)
{
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:
Expand Down Expand Up @@ -993,7 +1058,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;
Expand Down Expand Up @@ -1590,7 +1655,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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@ namespace winrt::Microsoft::Terminal::Settings::Model
virtual std::wstring_view GetNamespace() const noexcept = 0;
virtual std::wstring_view GetDisplayName() const noexcept = 0;
virtual std::wstring_view GetIcon() const noexcept = 0;
virtual void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) const = 0;
virtual void GenerateProfiles(std::vector<winrt::com_ptr<implementation::Profile>>& profiles) = 0;
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
</ClInclude>
<ClInclude Include="PowershellCoreProfileGenerator.h" />
<ClInclude Include="PowershellInstallationProfileGenerator.h" />
<ClInclude Include="Profile.h">
<DependentUpon>Profile.idl</DependentUpon>
</ClInclude>
Expand Down Expand Up @@ -167,6 +168,7 @@
<DependentUpon>KeyChordSerialization.idl</DependentUpon>
</ClCompile>
<ClCompile Include="PowershellCoreProfileGenerator.cpp" />
<ClCompile Include="PowershellInstallationProfileGenerator.cpp" />
<ClCompile Include="Profile.cpp">
<DependentUpon>Profile.idl</DependentUpon>
</ClCompile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
<ClCompile Include="PowershellCoreProfileGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="PowershellInstallationProfileGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
<ClCompile Include="WslDistroGenerator.cpp">
<Filter>profileGeneration</Filter>
</ClCompile>
Expand Down Expand Up @@ -57,6 +60,9 @@
<ClInclude Include="PowershellCoreProfileGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="PowershellInstallationProfileGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
<ClInclude Include="WslDistroGenerator.h">
<Filter>profileGeneration</Filter>
</ClInclude>
Expand Down
Loading

0 comments on commit 9842bdc

Please sign in to comment.