From 6a30e8ce2d8a2b305257322b8ebfce434746f273 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 1 Jun 2022 00:30:49 -0700 Subject: [PATCH 1/3] installationNotes --- schemas/JSON/settings/settings.schema.0.2.json | 5 +++++ src/AppInstallerCLICore/Argument.cpp | 4 ++++ .../Commands/InstallCommand.cpp | 7 +++++++ src/AppInstallerCLICore/ExecutionArgs.h | 4 +++- src/AppInstallerCLICore/Resources.h | 3 +++ .../Workflows/InstallFlow.cpp | 18 +++++++++++++++++- .../Workflows/InstallFlow.h | 6 ++++++ .../Shared/Strings/en-us/winget.resw | 9 +++++++++ .../Public/winget/UserSettings.h | 2 ++ src/AppInstallerCommonCore/UserSettings.cpp | 1 + 10 files changed, 57 insertions(+), 2 deletions(-) diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index 50cb8d1894..27ec1954ea 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -102,6 +102,11 @@ "type": "boolean", "default": false }, + "suppressInstallNotes": { + "description": "Controls whether installation notes are suppressed after a successful install", + "type": "boolean", + "default": false + }, "PortablePackageUserRoot": { "description": "The default root directory where packages are installed to under User scope. Applies to the portable installer type.", "type": "string", diff --git a/src/AppInstallerCLICore/Argument.cpp b/src/AppInstallerCLICore/Argument.cpp index 4ca1348064..c94d50a89b 100644 --- a/src/AppInstallerCLICore/Argument.cpp +++ b/src/AppInstallerCLICore/Argument.cpp @@ -97,6 +97,10 @@ namespace AppInstaller::CLI return Argument{ "wait", NoAlias, Args::Type::Wait, Resource::String::WaitArgumentDescription, ArgumentType::Flag, false }; case Args::Type::ProductCode: return Argument{ "product-code", NoAlias, Args::Type::ProductCode, Resource::String::ProductCodeArgumentDescription, ArgumentType::Standard, false }; + case Args::Type::DisplayNotes: + return Argument{ "display-notes", NoAlias, Args::Type::DisplayNotes, Resource::String::DisplayNotesArgumentDescription, ArgumentType::Flag, false }; + case Args::Type::SuppressNotes: + return Argument{ "suppress-notes", NoAlias, Args::Type::SuppressNotes, Resource::String::SuppressNotesArgumentDescription, ArgumentType::Flag, false }; default: THROW_HR(E_UNEXPECTED); } diff --git a/src/AppInstallerCLICore/Commands/InstallCommand.cpp b/src/AppInstallerCLICore/Commands/InstallCommand.cpp index 9eb3cb16a9..e4c2c4a8da 100644 --- a/src/AppInstallerCLICore/Commands/InstallCommand.cpp +++ b/src/AppInstallerCLICore/Commands/InstallCommand.cpp @@ -46,6 +46,8 @@ namespace AppInstaller::CLI Argument::ForType(Args::Type::CustomHeader), Argument::ForType(Args::Type::AcceptSourceAgreements), Argument::ForType(Args::Type::Rename), + Argument::ForType(Args::Type::DisplayNotes), + Argument::ForType(Args::Type::SuppressNotes), }; } @@ -137,6 +139,11 @@ namespace AppInstaller::CLI throw CommandException(Resource::String::InvalidArgumentValueErrorWithoutValidValues, Argument::ForType(Args::Type::Locale).Name(), {}); } } + + if (execArgs.Contains(Execution::Args::Type::DisplayNotes) && execArgs.Contains(Execution::Args::Type::SuppressNotes)) + { + throw CommandException(Resource::String::BothDisplayAndSuppressNotesFlagsProvided, ""); + } } void InstallCommand::ExecuteInternal(Context& context) const diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index f75b351918..29206f1327 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -92,7 +92,9 @@ namespace AppInstaller::CLI::Execution CustomHeader, // Optional Rest source header AcceptSourceAgreements, // Accept all source agreements IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions - Wait, // Prompts the user to press any key before exiting. + Wait, // Prompts the user to press any key before exiting + DisplayNotes, // Showss the installation notes after a successful install + SuppressNotes, // Suppresses the installation notes from being shown after a successful install. // Used for demonstration purposes ExperimentalArg, diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index cd12634abb..d3dda938a4 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -33,6 +33,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(AvailableOptions); WINGET_DEFINE_RESOURCE_STRINGID(AvailableSubcommands); WINGET_DEFINE_RESOURCE_STRINGID(AvailableUpgrades); + WINGET_DEFINE_RESOURCE_STRINGID(BothDisplayAndSuppressNotesFlagsProvided); WINGET_DEFINE_RESOURCE_STRINGID(BothManifestAndSearchQueryProvided); WINGET_DEFINE_RESOURCE_STRINGID(BothPurgeAndPreserveFlagsProvided); WINGET_DEFINE_RESOURCE_STRINGID(Cancelled); @@ -55,6 +56,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(DependenciesFlowContainsLoop); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementError); WINGET_DEFINE_RESOURCE_STRINGID(DependenciesManagementExitMessage); + WINGET_DEFINE_RESOURCE_STRINGID(DisplayNotesArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(CountOutOfBoundsError); WINGET_DEFINE_RESOURCE_STRINGID(DisabledByGroupPolicy); WINGET_DEFINE_RESOURCE_STRINGID(Done); @@ -336,6 +338,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateCommandLongDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateCommandShortDescription); WINGET_DEFINE_RESOURCE_STRINGID(SourceUpdateOne); + WINGET_DEFINE_RESOURCE_STRINGID(SuppressNotesArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(SystemArchitecture); WINGET_DEFINE_RESOURCE_STRINGID(TagArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(ThankYou); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index 09ced34096..c766a51908 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -145,6 +145,21 @@ namespace AppInstaller::CLI::Workflow } } + void DisplayInstallationNotes(Execution::Context& context) + { + const auto& manifest = context.Get(); + auto installationNotes = manifest.CurrentLocalization.Get(); + + if (!context.IsTerminated() && !installationNotes.empty()) + { + if (context.Args.Contains(Execution::Args::Type::DisplayNotes) || + !context.Args.Contains(Execution::Args::Type::SuppressNotes) && !Settings::User().Get()) + { + context.Reporter.Info() << std::endl << installationNotes << std::endl; + } + } + } + void ShowPackageAgreements::operator()(Execution::Context& context) const { const auto& manifest = context.Get(); @@ -423,7 +438,8 @@ namespace AppInstaller::CLI::Workflow { context << Workflow::DownloadSinglePackage << - Workflow::InstallPackageInstaller; + Workflow::InstallPackageInstaller << + Workflow::DisplayInstallationNotes; } void EnsureSupportForInstall(Execution::Context& context) diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.h b/src/AppInstallerCLICore/Workflows/InstallFlow.h index 640d90305f..8872f86af3 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.h +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.h @@ -23,6 +23,12 @@ namespace AppInstaller::CLI::Workflow // Outputs: None void ShowInstallationDisclaimer(Execution::Context& context); + // Displays the installations notes after a successful install. + // Required Args: None + // Inputs: InstallationNotes + // Outputs: None + void DisplayInstallationNotes(Execution::Context& context); + // Shows the license agreements if the application has them. // Required Args: None // Inputs: Manifest diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index be5a075a7c..559e93db4c 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1337,4 +1337,13 @@ Please specify one of them using the `--source` option to proceed. Cannot purge install directory, as it was not created by WinGet + + Both `display-notes` and `suppress-notes` arguments are provided + + + Displays the installation notes + + + Suppresses the installation notes + \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index f215db2baa..851b749a26 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -87,6 +87,7 @@ namespace AppInstaller::Settings EnableSelfInitiatedMinidump, LoggingLevelPreference, InstallIgnoreWarnings, + SuppressInstallNotes, PortableAppUserRoot, PortableAppMachineRoot, UninstallPurgePortablePackage, @@ -139,6 +140,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocalePreference, std::vector, std::vector, {}, ".installBehavior.preferences.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallLocaleRequirement, std::vector, std::vector, {}, ".installBehavior.requirements.locale"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallIgnoreWarnings, bool, bool, false, ".installBehavior.ignoreWarnings"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::SuppressInstallNotes, bool, bool, false, ".installBehavior.suppressInstallNotes"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppUserRoot, std::string, std::filesystem::path, {}, ".installBehavior.portableAppUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortableAppMachineRoot, std::string, std::filesystem::path, {}, ".installBehavior.portableAppMachineRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortablePackage, bool, bool, false, ".uninstallBehavior.purgePortablePackage"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 799714d509..eb017ce275 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -239,6 +239,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(EFDirectMSI) WINGET_VALIDATE_PASS_THROUGH(EnableSelfInitiatedMinidump) WINGET_VALIDATE_PASS_THROUGH(InstallIgnoreWarnings) + WINGET_VALIDATE_PASS_THROUGH(SuppressInstallNotes) WINGET_VALIDATE_PASS_THROUGH(UninstallPurgePortablePackage) WINGET_VALIDATE_SIGNATURE(PortableAppUserRoot) From a9875c75a2a0d9bc1bdeadc8bb3512f5a4c1a1d7 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 1 Jun 2022 13:03:09 -0700 Subject: [PATCH 2/3] add unit test --- src/AppInstallerCLICore/Resources.h | 1 + .../Workflows/InstallFlow.cpp | 16 +++++++------- .../Shared/Strings/en-us/winget.resw | 3 +++ .../AppInstallerCLITests.vcxproj | 3 +++ .../AppInstallerCLITests.vcxproj.filters | 3 +++ .../InstallFlowTest_InstallationNotes.yaml | 16 ++++++++++++++ src/AppInstallerCLITests/WorkFlow.cpp | 21 +++++++++++++++++++ 7 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 src/AppInstallerCLITests/TestData/InstallFlowTest_InstallationNotes.yaml diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h index d3dda938a4..00761b54c4 100644 --- a/src/AppInstallerCLICore/Resources.h +++ b/src/AppInstallerCLICore/Resources.h @@ -196,6 +196,7 @@ namespace AppInstaller::CLI::Resource WINGET_DEFINE_RESOURCE_STRINGID(NoPackageFound); WINGET_DEFINE_RESOURCE_STRINGID(NoPackageSelectionArgumentProvided); WINGET_DEFINE_RESOURCE_STRINGID(NoPackagesFoundInImportFile); + WINGET_DEFINE_RESOURCE_STRINGID(Notes); WINGET_DEFINE_RESOURCE_STRINGID(NoUninstallInfoFound); WINGET_DEFINE_RESOURCE_STRINGID(NoVTArgumentDescription); WINGET_DEFINE_RESOURCE_STRINGID(OpenSourceFailedNoMatch); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index c766a51908..e793029034 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -24,6 +24,7 @@ using namespace AppInstaller::Manifest; using namespace AppInstaller::Repository; using namespace AppInstaller::Settings; using namespace AppInstaller::Utility; +using namespace AppInstaller::Utility::literals; namespace AppInstaller::CLI::Workflow @@ -147,15 +148,15 @@ namespace AppInstaller::CLI::Workflow void DisplayInstallationNotes(Execution::Context& context) { - const auto& manifest = context.Get(); - auto installationNotes = manifest.CurrentLocalization.Get(); - - if (!context.IsTerminated() && !installationNotes.empty()) + if (context.Args.Contains(Execution::Args::Type::DisplayNotes) || + !context.Args.Contains(Execution::Args::Type::SuppressNotes) && !Settings::User().Get()) { - if (context.Args.Contains(Execution::Args::Type::DisplayNotes) || - !context.Args.Contains(Execution::Args::Type::SuppressNotes) && !Settings::User().Get()) + const auto& manifest = context.Get(); + auto installationNotes = manifest.CurrentLocalization.Get(); + + if (!installationNotes.empty()) { - context.Reporter.Info() << std::endl << installationNotes << std::endl; + context.Reporter.Info() << Resource::String::Notes << ": "_liv << installationNotes << std::endl; } } } @@ -498,6 +499,7 @@ namespace AppInstaller::CLI::Workflow } installContext << Workflow::DownloadInstaller; installContext << Workflow::InstallPackageInstaller; + installContext << Workflow::DisplayInstallationNotes; } catch (...) { diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw index 559e93db4c..e750e3535f 100644 --- a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw +++ b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw @@ -1346,4 +1346,7 @@ Please specify one of them using the `--source` option to proceed. Suppresses the installation notes + + Notes + \ No newline at end of file diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 04a985704d..e7c6a57cc4 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -267,6 +267,9 @@ true + + true + true diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index 092f901c9d..66a35c91b0 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -444,6 +444,9 @@ TestData + + TestData + TestData diff --git a/src/AppInstallerCLITests/TestData/InstallFlowTest_InstallationNotes.yaml b/src/AppInstallerCLITests/TestData/InstallFlowTest_InstallationNotes.yaml new file mode 100644 index 0000000000..730e433fe2 --- /dev/null +++ b/src/AppInstallerCLITests/TestData/InstallFlowTest_InstallationNotes.yaml @@ -0,0 +1,16 @@ +PackageIdentifier: AppInstallerCliTest.TestInstaller +PackageVersion: 1.0.0.0 +PackageLocale: en-US +PackageName: AppInstaller Test Installer +ShortDescription: AppInstaller Test Installer +Publisher: Microsoft Corporation +Moniker: AICLITestExe +License: Test +InstallationNotes: testInstallationNotes +Installers: + - Architecture: x86 + InstallerUrl: https://ThisIsNotUsed + InstallerType: exe + InstallerSha256: 65DB2F2AC2686C7F2FD69D4A4C6683B888DC55BFA20A0E32CA9F838B51689A3B +ManifestType: singleton +ManifestVersion: 1.2.0 diff --git a/src/AppInstallerCLITests/WorkFlow.cpp b/src/AppInstallerCLITests/WorkFlow.cpp index ff54a68543..0f3a0eb253 100644 --- a/src/AppInstallerCLITests/WorkFlow.cpp +++ b/src/AppInstallerCLITests/WorkFlow.cpp @@ -795,6 +795,27 @@ TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]") REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } +TEST_CASE("InstallFlow_InstallationNotes", "[InstallFlow][workflow]") +{ + TestCommon::TempFile installResultPath("TestExeInstalled.txt"); + + std::ostringstream installOutput; + TestContext context{ installOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("InstallFlowTest_InstallationNotes.yaml").GetPath().u8string()); + context.Args.AddArg(Execution::Args::Type::DisplayNotes); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // Verify installation notes are displayed + REQUIRE(context.GetTerminationHR() == S_OK); + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + REQUIRE(installOutput.str().find("testInstallationNotes") != std::string::npos); +} + TEST_CASE("InstallFlow_ExpectedReturnCodes", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); From 44f297fd4c8f2dc19207e76e53e924eefdb1f074 Mon Sep 17 00:00:00 2001 From: Ryan Fu Date: Wed, 1 Jun 2022 13:53:41 -0700 Subject: [PATCH 3/3] spelling --- src/AppInstallerCLICore/ExecutionArgs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerCLICore/ExecutionArgs.h b/src/AppInstallerCLICore/ExecutionArgs.h index 29206f1327..c1c3835cb0 100644 --- a/src/AppInstallerCLICore/ExecutionArgs.h +++ b/src/AppInstallerCLICore/ExecutionArgs.h @@ -93,7 +93,7 @@ namespace AppInstaller::CLI::Execution AcceptSourceAgreements, // Accept all source agreements IncludeUnknown, // Used in Upgrade command to allow upgrades of packages with unknown versions Wait, // Prompts the user to press any key before exiting - DisplayNotes, // Showss the installation notes after a successful install + DisplayNotes, // Shows the installation notes after a successful install SuppressNotes, // Suppresses the installation notes from being shown after a successful install. // Used for demonstration purposes