Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading