Skip to content
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

Add default module root setting for configuration #4974

Merged
merged 12 commits into from
Nov 26, 2024
15 changes: 15 additions & 0 deletions doc/Settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,21 @@ The `purgePortablePackage` behavior affects the default behavior for uninstallin
},
```

## Configure Behavior

The `configureBehavior` settings affect the default behavior of applying a configuration.

### Default Module Root
The `defaultModuleRoot` behavior affects the default root directory where modules are installed to. Defaults to `%LOCALAPPDATA%/Microsoft/WinGet/Configuration/Modules` if value is not set or is invalid.

> Note: This setting value must be an absolute path.

```json
"configureBehavior": {
"defaultModuleRoot": "C:/Program Files/Modules/"
},
```

## Telemetry

The `telemetry` settings control whether winget writes ETW events that may be sent to Microsoft on a default installation of Windows.
Expand Down
11 changes: 11 additions & 0 deletions schemas/JSON/settings/settings.schema.0.2.json
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,17 @@
}
}
},
"ConfigureBehavior": {
"description": "Configure settings",
"type": "object",
"properties": {
"defaultModuleRoot": {
"description": "The default root directory where PowerShell modules are installed to when applying a configuration.",
"type": "string",
"maxLength": 32767
}
}
},
"DownloadBehavior": {
"description": "Download settings",
"type": "object",
Expand Down
7 changes: 7 additions & 0 deletions src/AppInstallerCLICore/ConfigurationCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ namespace AppInstaller::CLI
}
}

std::filesystem::path defaultModuleRoot = Settings::User().Get<Settings::Setting::ConfigureDefaultModuleRoot>();

if (!defaultModuleRoot.empty())
{
return { SetProcessorFactory::PwshConfigurationProcessorLocation::Custom, defaultModuleRoot.u8string() };
}

return { SetProcessorFactory::PwshConfigurationProcessorLocation::WinGetModulePath, {} };
}
}
Expand Down
18 changes: 18 additions & 0 deletions src/AppInstallerCLIE2ETests/ConfigureCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ public void ConfigureFromTestRepo_Location(TestCommon.TestModuleLocation locatio
Constants.SimpleTestModuleName)));
}

/// <summary>
/// Simple test to confirm that the module was installed to the location specified in the DefaultModuleRoot settings.
/// </summary>
[Test]
public void ConfigureFromTestRepo_SettingsDefaultModuleRoot()
{
TestCommon.EnsureModuleState(Constants.SimpleTestModuleName, present: false);
string moduleTestDir = TestCommon.GetRandomTestDir();
WinGetSettingsHelper.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, moduleTestDir);

string args = TestCommon.GetTestDataFile("Configuration\\Configure_TestRepo_Location.yml");
var result = TestCommon.RunAICLICommand(CommandAndAgreementsAndVerbose, args);

WinGetSettingsHelper.ConfigureInstallBehavior(Constants.DefaultModuleRoot, string.Empty);
Assert.AreEqual(0, result.ExitCode);
Assert.True(Directory.Exists(Path.Combine(moduleTestDir, Constants.SimpleTestModuleName)));
}

/// <summary>
/// One resource fails, but the other is not dependent and should be executed.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions src/AppInstallerCLIE2ETests/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ public class Constants
public const string PortablePackageMachineRoot = "portablePackageMachineRoot";
public const string InstallBehaviorScope = "scope";
public const string InstallerTypes = "installerTypes";
public const string DefaultModuleRoot = "defaultModuleRoot";

// Configuration
public const string PSGalleryName = "PSGallery";
Expand Down
14 changes: 14 additions & 0 deletions src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,20 @@ public static void ConfigureInstallBehavior(string settingName, string value)
SetWingetSettings(settingsJson);
}

/// <summary>
/// Configure the configuration behavior.
/// </summary>
/// <param name="settingName">Setting name.</param>
/// <param name="value">Setting value.</param>
public static void ConfigureConfigureBehavior(string settingName, string value)
{
JObject settingsJson = GetJsonSettingsObject("configureBehavior");
var configureBehavior = settingsJson["configureBehavior"];
configureBehavior[settingName] = value;

SetWingetSettings(settingsJson);
}

/// <summary>
/// Configure the install behavior preferences.
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/AppInstallerCLITests/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,21 @@ TEST_CASE("SettingsDownloadDefaultDirectory", "[settings]")
}
}

TEST_CASE("SettingsConfigureDefaultModuleRoot", "[settings]")
{
auto again = DeleteUserSettingsFiles();

SECTION("Valid path")
{
std::string_view json = R"({ "configureBehavior": { "defaultModuleRoot": "C:/Foo/Bar" } })";
SetSetting(Stream::PrimaryUserSettings, json);
UserSettingsTest userSettingTest;

REQUIRE(userSettingTest.Get<Setting::ConfigureDefaultModuleRoot>() == "C:/Foo/Bar");
REQUIRE(userSettingTest.GetWarnings().size() == 0);
}
}

TEST_CASE("SettingsArchiveExtractionMethod", "[settings]")
{
auto again = DeleteUserSettingsFiles();
Expand Down
4 changes: 4 additions & 0 deletions src/AppInstallerCommonCore/Public/winget/UserSettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ namespace AppInstaller::Settings
UninstallPurgePortablePackage,
// Download behavior
DownloadDefaultDirectory,
// Configure behavior
ConfigureDefaultModuleRoot,
// Interactivity
InteractivityDisable,
#ifndef AICLI_DISABLE_TEST_HOOKS
Expand Down Expand Up @@ -185,6 +187,8 @@ namespace AppInstaller::Settings
SETTINGMAPPING_SPECIALIZATION(Setting::UninstallPurgePortablePackage, bool, bool, false, ".uninstallBehavior.purgePortablePackage"sv);
// Download behavior
SETTINGMAPPING_SPECIALIZATION(Setting::DownloadDefaultDirectory, std::string, std::filesystem::path, {}, ".downloadBehavior.defaultDownloadDirectory"sv);
// Configure behavior
SETTINGMAPPING_SPECIALIZATION(Setting::ConfigureDefaultModuleRoot, std::string, std::filesystem::path, {}, ".configureBehavior.defaultModuleRoot"sv);

// Network
SETTINGMAPPING_SPECIALIZATION(Setting::NetworkDownloader, std::string, InstallerDownloader, InstallerDownloader::Default, ".network.downloader"sv);
Expand Down
5 changes: 5 additions & 0 deletions src/AppInstallerCommonCore/UserSettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ namespace AppInstaller::Settings
return ValidatePathValue(value);
}

WINGET_VALIDATE_SIGNATURE(ConfigureDefaultModuleRoot)
{
return ValidatePathValue(value);
}

WINGET_VALIDATE_SIGNATURE(NetworkDownloader)
{
static constexpr std::string_view s_downloader_default = "default";
Expand Down
Loading