diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index 151f337e5b..853b1b8c46 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -35,6 +35,10 @@ Added a new `--ignore-unavailable` flag to the `install` command. When installin Added the contract version 30 `InstallOptions.InstallDependenciesOnly` property for COM callers that need to install a package's dependencies without installing the requested package. +### `installBehavior.disableLicenseDisclaimer` setting + +Added a new `installBehavior.disableLicenseDisclaimer` setting that hides the license disclaimer ("This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.") shown before a package is installed. Defaults to `false`. + ## Bug Fixes ### Portable installer alias handling diff --git a/doc/Settings.md b/doc/Settings.md index 269a3d029e..267e351f03 100644 --- a/doc/Settings.md +++ b/doc/Settings.md @@ -88,6 +88,16 @@ The `disableInstallNotes` behavior affects whether installation notes are shown }, ``` +### Disable License Disclaimer + +The `disableLicenseDisclaimer` behavior affects whether the license disclaimer ("This application is licensed to you by its owner. Microsoft is not responsible for, nor does it grant any licenses to, third-party packages.") is shown before a package is installed. The notice shown for packages provided through Microsoft Store is not affected. Defaults to `false` if value is not set or is invalid. + +```json + "installBehavior": { + "disableLicenseDisclaimer": true + }, +``` + ### Portable Package User Root The `portablePackageUserRoot` setting affects the default root directory where packages are installed to under `User` scope. This setting only applies to packages with the `portable` installer type. Defaults to `%LOCALAPPDATA%/Microsoft/WinGet/Packages/` if value is not set or is invalid. diff --git a/schemas/JSON/settings/settings.schema.0.2.json b/schemas/JSON/settings/settings.schema.0.2.json index e162aaac12..8b1fa62416 100644 --- a/schemas/JSON/settings/settings.schema.0.2.json +++ b/schemas/JSON/settings/settings.schema.0.2.json @@ -204,6 +204,11 @@ "type": "boolean", "default": false }, + "disableLicenseDisclaimer": { + "description": "Controls whether the license disclaimer is shown before a package is installed", + "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/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f0eb5731a1..59b0f3ad00 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -364,7 +364,7 @@ namespace AppInstaller::CLI::Workflow { context.Reporter.Info() << Execution::PromptEmphasis << Resource::String::InstallationDisclaimerMSStore << std::endl; } - else + else if (!Settings::User().Get()) { context.Reporter.Info() << Resource::String::InstallationDisclaimer1 << std::endl << diff --git a/src/AppInstallerCLITests/InstallFlow.cpp b/src/AppInstallerCLITests/InstallFlow.cpp index f5eff1290c..32789b65e6 100644 --- a/src/AppInstallerCLITests/InstallFlow.cpp +++ b/src/AppInstallerCLITests/InstallFlow.cpp @@ -176,6 +176,33 @@ TEST_CASE("InstallFlowNonZeroExitCode", "[InstallFlow][workflow]") REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); } +TEST_CASE("InstallFlow_LicenseDisclaimer", "[InstallFlow][workflow]") +{ + bool disableDisclaimer = GENERATE(false, true); + + 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_Exe.yaml").GetPath().u8string()); + + TestUserSettings settings; + settings.Set(bool{ disableDisclaimer }); + + InstallCommand install({}); + install.Execute(context); + INFO(installOutput.str()); + + // The install must succeed either way; only the disclaimer changes. + REQUIRE(context.GetTerminationHR() == S_OK); + REQUIRE(std::filesystem::exists(installResultPath.GetPath())); + + bool disclaimerShown = installOutput.str().find(Resource::LocString(Resource::String::InstallationDisclaimer1).get()) != std::string::npos; + REQUIRE(disclaimerShown == !disableDisclaimer); +} + TEST_CASE("InstallFlow_InstallationNotes", "[InstallFlow][workflow]") { TestCommon::TempFile installResultPath("TestExeInstalled.txt"); diff --git a/src/AppInstallerCLITests/UserSettings.cpp b/src/AppInstallerCLITests/UserSettings.cpp index 8f9e31e150..37111ef4ee 100644 --- a/src/AppInstallerCLITests/UserSettings.cpp +++ b/src/AppInstallerCLITests/UserSettings.cpp @@ -681,6 +681,27 @@ TEST_CASE("SettingsMaxResumes", "[settings]") } } +TEST_CASE("SettingsDisableLicenseDisclaimer", "[settings]") +{ + auto again = DeleteUserSettingsFiles(); + + SECTION("Default value") + { + UserSettingsTest userSettingTest; + + REQUIRE_FALSE(userSettingTest.Get()); + } + + SECTION("Enabled") + { + std::string_view json = R"({ "installBehavior": { "disableLicenseDisclaimer": true } })"; + SetSetting(Stream::PrimaryUserSettings, json); + UserSettingsTest userSettingTest; + + REQUIRE(userSettingTest.Get()); + } +} + TEST_CASE("LoggingChannels", "[settings]") { auto again = DeleteUserSettingsFiles(); diff --git a/src/AppInstallerCommonCore/Public/winget/UserSettings.h b/src/AppInstallerCommonCore/Public/winget/UserSettings.h index e000c6adda..2bb35ef06d 100644 --- a/src/AppInstallerCommonCore/Public/winget/UserSettings.h +++ b/src/AppInstallerCommonCore/Public/winget/UserSettings.h @@ -118,6 +118,7 @@ namespace AppInstaller::Settings InstallSkipDependencies, ArchiveExtractionMethod, DisableInstallNotes, + DisableLicenseDisclaimer, PortablePackageUserRoot, PortablePackageMachineRoot, MaxResumes, @@ -210,6 +211,7 @@ namespace AppInstaller::Settings SETTINGMAPPING_SPECIALIZATION(Setting::InstallSkipDependencies, bool, bool, false, ".installBehavior.skipDependencies"sv); SETTINGMAPPING_SPECIALIZATION(Setting::ArchiveExtractionMethod, std::string, Archive::ExtractionMethod, Archive::ExtractionMethod::ShellApi, ".installBehavior.archiveExtractionMethod"sv); SETTINGMAPPING_SPECIALIZATION(Setting::DisableInstallNotes, bool, bool, false, ".installBehavior.disableInstallNotes"sv); + SETTINGMAPPING_SPECIALIZATION(Setting::DisableLicenseDisclaimer, bool, bool, false, ".installBehavior.disableLicenseDisclaimer"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageUserRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageUserRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::PortablePackageMachineRoot, std::string, std::filesystem::path, {}, ".installBehavior.portablePackageMachineRoot"sv); SETTINGMAPPING_SPECIALIZATION(Setting::InstallDefaultRoot, std::string, std::filesystem::path, {}, ".installBehavior.defaultInstallRoot"sv); diff --git a/src/AppInstallerCommonCore/UserSettings.cpp b/src/AppInstallerCommonCore/UserSettings.cpp index 5f3074f0e8..0d47fa8fcd 100644 --- a/src/AppInstallerCommonCore/UserSettings.cpp +++ b/src/AppInstallerCommonCore/UserSettings.cpp @@ -293,6 +293,7 @@ namespace AppInstaller::Settings WINGET_VALIDATE_PASS_THROUGH(InteractivityDisable) WINGET_VALIDATE_PASS_THROUGH(InstallSkipDependencies) WINGET_VALIDATE_PASS_THROUGH(DisableInstallNotes) + WINGET_VALIDATE_PASS_THROUGH(DisableLicenseDisclaimer) WINGET_VALIDATE_PASS_THROUGH(UninstallPurgePortablePackage) WINGET_VALIDATE_PASS_THROUGH(NetworkWingetAlternateSourceURL) WINGET_VALIDATE_PASS_THROUGH(MaxResumes)