Skip to content

Commit

Permalink
Add default module root setting for configuration (microsoft#4974)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryfu-msft authored and coureurkennr1 committed Nov 29, 2024
1 parent 39d132b commit d5c58c9
Show file tree
Hide file tree
Showing 10 changed files with 116 additions and 4 deletions.
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
11 changes: 9 additions & 2 deletions src/AppInstallerCLICore/ConfigurationCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace AppInstaller::CLI
struct ModulePathInfo
{
SetProcessorFactory::PwshConfigurationProcessorLocation location;
std::optional<std::string_view> customLocation;
std::optional<std::string> customLocation;
};

ModulePathInfo GetModulePathInfo(Execution::Args& execArgs)
Expand All @@ -44,10 +44,17 @@ namespace AppInstaller::CLI
}
else
{
return { SetProcessorFactory::PwshConfigurationProcessorLocation::Custom, execArgs.GetArg(Execution::Args::Type::ConfigurationModulePath) };
return { SetProcessorFactory::PwshConfigurationProcessorLocation::Custom, std::string(execArgs.GetArg(Execution::Args::Type::ConfigurationModulePath)) };
}
}

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
25 changes: 25 additions & 0 deletions src/AppInstallerCLIE2ETests/ConfigureCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,31 @@ public void ConfigureFromTestRepo()
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_DefaultModuleRootSetting()
{
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.ConfigureConfigureBehavior(Constants.DefaultModuleRoot, string.Empty);
bool moduleExists = Directory.Exists(Path.Combine(moduleTestDir, Constants.SimpleTestModuleName));
if (moduleExists)
{
// Clean test directory to avoid impacting other tests.
Directory.Delete(moduleTestDir, true);
}

Assert.AreEqual(0, result.ExitCode);
Assert.True(moduleExists);
}

/// <summary>
/// Simple test to confirm that the module was installed in the right location.
/// </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
13 changes: 11 additions & 2 deletions src/AppInstallerCLIE2ETests/ErrorCommand.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// <copyright file="ErrorCommand.cs" company="Microsoft Corporation">
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
// </copyright>
Expand All @@ -14,6 +14,15 @@ namespace AppInstallerCLIE2ETests
/// </summary>
public class ErrorCommand
{
/// <summary>
/// Reset settings file to avoid affecting output from error command.
/// </summary>
[OneTimeSetUp]
public void OneTimeSetup()
{
WinGetSettingsHelper.InitializeWingetSettings();
}

/// <summary>
/// Tests 0.
/// </summary>
Expand Down Expand Up @@ -127,4 +136,4 @@ public void String()
Assert.True(result.StdOut.Contains("APPINSTALLER_CLI_ERROR_UNSUPPORTED_RESTSOURCE"));
}
}
}
}
20 changes: 20 additions & 0 deletions src/AppInstallerCLIE2ETests/Helpers/WinGetSettingsHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ public static void InitializeWingetSettings()
{
}
},
{
"configureBehavior",
new Hashtable()
{
}
},
};

// Run winget one time to initialize settings directory
Expand Down Expand Up @@ -108,6 +114,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

0 comments on commit d5c58c9

Please sign in to comment.