From 06ef780be93bd33be756ef9d5ef980a223736448 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Mon, 30 Mar 2026 12:37:04 -0500 Subject: [PATCH 1/7] Export custom and override arguments --- .../JSON/packages/packages.schema.2.0.json | 10 ++ src/AppInstallerCLICore/PackageCollection.cpp | 22 +++ src/AppInstallerCLICore/PackageCollection.h | 4 +- .../Workflows/ImportExportFlow.cpp | 22 ++- .../Workflows/InstallFlow.cpp | 26 ++++ .../PackageCollection.cpp | 128 ++++++++++++++++++ .../CompositeSource.cpp | 3 +- .../Public/winget/RepositorySearch.h | 4 + .../RepositorySearch.cpp | 2 + 9 files changed, 218 insertions(+), 3 deletions(-) diff --git a/schemas/JSON/packages/packages.schema.2.0.json b/schemas/JSON/packages/packages.schema.2.0.json index b953f323a7..77fc1e3711 100644 --- a/schemas/JSON/packages/packages.schema.2.0.json +++ b/schemas/JSON/packages/packages.schema.2.0.json @@ -96,6 +96,16 @@ "machine" ], "default": "user" + }, + + "OverrideArguments": { + "description": "Override arguments to pass directly to the installer, replacing all other installer arguments", + "type": "string" + }, + + "CustomSwitches": { + "description": "Additional arguments to append to the installer arguments", + "type": "string" } } } diff --git a/src/AppInstallerCLICore/PackageCollection.cpp b/src/AppInstallerCLICore/PackageCollection.cpp index 567a01b5f0..3b959a2673 100644 --- a/src/AppInstallerCLICore/PackageCollection.cpp +++ b/src/AppInstallerCLICore/PackageCollection.cpp @@ -41,6 +41,8 @@ namespace AppInstaller::CLI const std::string PackagesJson_Package_Version = "Version"; const std::string PackagesJson_Package_Channel = "Channel"; const std::string PackagesJson_Package_Scope = "Scope"; + const std::string PackagesJson_Package_OverrideArguments = "OverrideArguments"; + const std::string PackagesJson_Package_CustomSwitches = "CustomSwitches"; static const StaticStrings& Instance() { @@ -154,6 +156,16 @@ namespace AppInstaller::CLI PackageCollection::Package package{ Utility::LocIndString{ id }, Utility::Version{ version }, Utility::Channel{ channel } }; package.Scope = Manifest::ConvertToScopeEnum(scope); + if (packageNode.isMember(ss.PackagesJson_Package_OverrideArguments)) + { + package.OverrideArgs = packageNode[ss.PackagesJson_Package_OverrideArguments].asString(); + } + + if (packageNode.isMember(ss.PackagesJson_Package_CustomSwitches)) + { + package.CustomSwitches = packageNode[ss.PackagesJson_Package_CustomSwitches].asString(); + } + return package; } }; @@ -202,6 +214,16 @@ namespace AppInstaller::CLI packageNode[ss.PackagesJson_Package_Scope] = std::string{ Manifest::ScopeToString(package.Scope) }; } + if (!package.OverrideArgs.empty()) + { + packageNode[ss.PackagesJson_Package_OverrideArguments] = package.OverrideArgs; + } + + if (!package.CustomSwitches.empty()) + { + packageNode[ss.PackagesJson_Package_CustomSwitches] = package.CustomSwitches; + } + return sourceNode[ss.PackagesJson_Packages].append(std::move(packageNode)); } diff --git a/src/AppInstallerCLICore/PackageCollection.h b/src/AppInstallerCLICore/PackageCollection.h index fcc5c907fd..4ca5ad1a89 100644 --- a/src/AppInstallerCLICore/PackageCollection.h +++ b/src/AppInstallerCLICore/PackageCollection.h @@ -28,8 +28,10 @@ namespace AppInstaller::CLI Utility::LocIndString Id; Utility::VersionAndChannel VersionAndChannel; - Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; + Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; std::filesystem::path InstalledLocation; + std::string OverrideArgs; + std::string CustomSwitches; }; // A source along with a set of packages available from it. diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index f6d8bf8ec1..129d055103 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -140,7 +140,27 @@ namespace AppInstaller::CLI::Workflow // but take the exported version from the installed package if needed. PackageCollection::Package exportPackage; exportPackage.Id = availablePackageVersion->GetProperty(PackageVersionProperty::Id); - exportPackage.InstalledLocation = Utility::ConvertToUTF16(installedPackageVersion->GetMetadata()[PackageVersionMetadata::InstalledLocation]); + + const auto& installedMetadata = installedPackageVersion->GetMetadata(); + + auto locationItr = installedMetadata.find(PackageVersionMetadata::InstalledLocation); + if (locationItr != installedMetadata.end()) + { + exportPackage.InstalledLocation = Utility::ConvertToUTF16(locationItr->second); + } + + auto overrideItr = installedMetadata.find(PackageVersionMetadata::UserOverrideArguments); + if (overrideItr != installedMetadata.end()) + { + exportPackage.OverrideArgs = overrideItr->second; + } + + auto customItr = installedMetadata.find(PackageVersionMetadata::UserCustomSwitches); + if (customItr != installedMetadata.end()) + { + exportPackage.CustomSwitches = customItr->second; + } + if (includeVersions) { exportPackage.VersionAndChannel = { version.get(), channel.get() }; diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index f702629c20..becae75bbb 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -1021,5 +1021,31 @@ namespace AppInstaller::CLI::Workflow version.SetMetadata(Repository::PackageVersionMetadata::UserIntentLocale, itr->second); } } + + if (context.Args.Contains(Execution::Args::Type::Override)) + { + version.SetMetadata(Repository::PackageVersionMetadata::UserOverrideArguments, context.Args.GetArg(Execution::Args::Type::Override)); + } + else + { + auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserOverrideArguments); + if (itr != installedMetadata.end()) + { + version.SetMetadata(Repository::PackageVersionMetadata::UserOverrideArguments, itr->second); + } + } + + if (context.Args.Contains(Execution::Args::Type::CustomSwitches)) + { + version.SetMetadata(Repository::PackageVersionMetadata::UserCustomSwitches, context.Args.GetArg(Execution::Args::Type::CustomSwitches)); + } + else + { + auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserCustomSwitches); + if (itr != installedMetadata.end()) + { + version.SetMetadata(Repository::PackageVersionMetadata::UserCustomSwitches, itr->second); + } + } } } diff --git a/src/AppInstallerCLITests/PackageCollection.cpp b/src/AppInstallerCLITests/PackageCollection.cpp index 5285d77cf6..f8f94fdd94 100644 --- a/src/AppInstallerCLITests/PackageCollection.cpp +++ b/src/AppInstallerCLITests/PackageCollection.cpp @@ -30,6 +30,8 @@ const std::string s_PackagesJson_Packages = "Packages"; const std::string s_PackagesJson_Package_PackageIdentifier = "PackageIdentifier"; const std::string s_PackagesJson_Package_Version = "Version"; const std::string s_PackagesJson_Package_Channel = "Channel"; +const std::string s_PackagesJson_Package_OverrideArguments = "OverrideArguments"; +const std::string s_PackagesJson_Package_CustomSwitches = "CustomSwitches"; namespace { @@ -96,6 +98,8 @@ namespace ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_PackageIdentifier, packageItr->Id); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Version, packageItr->VersionAndChannel.GetVersion().ToString(), true); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Channel, packageItr->VersionAndChannel.GetChannel().ToString(), true); + ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_OverrideArguments, packageItr->OverrideArgs, true); + ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_CustomSwitches, packageItr->CustomSwitches, true); } } } @@ -121,6 +125,8 @@ namespace { REQUIRE(firstPackageItr->Id == secondPackageItr->Id); REQUIRE(firstPackageItr->VersionAndChannel.ToString() == secondPackageItr->VersionAndChannel.ToString()); + REQUIRE(firstPackageItr->OverrideArgs == secondPackageItr->OverrideArgs); + REQUIRE(firstPackageItr->CustomSwitches == secondPackageItr->CustomSwitches); } } } @@ -171,6 +177,39 @@ TEST_CASE("PackageCollection_Write_MultipleSources", "[PackageCollection]") ValidateJsonWithCollection(PackagesJson::CreateJson(pc), pc); } +TEST_CASE("PackageCollection_Write_OverrideAndCustomSwitches", "[PackageCollection]") +{ + PackageCollection::Source source; + source.Details.Name = "TestSource"; + source.Details.Arg = "https://aka.ms/winget"; + source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Identifier = "TestSourceId"; + + PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; + packageWithOverride.OverrideArgs = "/silent /norestart"; + source.Packages.emplace_back(std::move(packageWithOverride)); + + PackageCollection::Package packageWithCustom{ LocIndString{ "test.withCustom"sv }, Version{ "2.0" }, Channel{ "" } }; + packageWithCustom.CustomSwitches = "--no-telemetry"; + source.Packages.emplace_back(std::move(packageWithCustom)); + + PackageCollection::Package packageWithBoth{ LocIndString{ "test.withBoth"sv }, Version{ "3.0" }, Channel{ "" } }; + packageWithBoth.OverrideArgs = "/override"; + packageWithBoth.CustomSwitches = "--extra"; + source.Packages.emplace_back(std::move(packageWithBoth)); + + PackageCollection::Package packageWithNeither{ LocIndString{ "test.withNeither"sv }, Version{ "4.0" }, Channel{ "" } }; + source.Packages.emplace_back(std::move(packageWithNeither)); + + PackageCollection pc + { + "1.0.0.0", + std::vector{ source } + }; + + ValidateJsonWithCollection(PackagesJson::CreateJson(pc), pc); +} + TEST_CASE("PackageCollection_Read_SingleSource_1_0", "[PackageCollection]") { auto json = ParseJsonString(R"( @@ -272,6 +311,95 @@ TEST_CASE("PackageCollection_Read_SingleSource_2_0", "[PackageCollection]") ValidateEqualCollections(parseResult.Packages, expected); } +TEST_CASE("PackageCollection_Read_OverrideAndCustomSwitches_2_0", "[PackageCollection]") +{ + auto json = ParseJsonString(R"( + { + "$schema": "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate": "2021-01-01T12:00:00.000-00:00", + "WinGetVersion": "1.0.0", + "Sources": [ + { + "Packages": [ + { + "PackageIdentifier": "test.withOverride", + "OverrideArguments": "/silent /norestart" + }, + { + "PackageIdentifier": "test.withCustom", + "CustomSwitches": "--no-telemetry" + }, + { + "PackageIdentifier": "test.withBoth", + "OverrideArguments": "/override", + "CustomSwitches": "--extra" + }, + { + "PackageIdentifier": "test.withNeither" + } + ], + "SourceDetails": { + "Argument": "https://aka.ms/winget", + "Identifier": "TestSourceId", + "Name": "TestSource", + "Type": "Microsoft.PreIndexed.Package" + } + } + ] + })"); + + auto parseResult = PackagesJson::TryParseJson(json); + REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); + REQUIRE(parseResult.Errors.empty()); + + REQUIRE(parseResult.Packages.Sources.size() == 1); + const auto& packages = parseResult.Packages.Sources[0].Packages; + REQUIRE(packages.size() == 4); + + REQUIRE(packages[0].Id == "test.withOverride"); + REQUIRE(packages[1].Id == "test.withCustom"); + REQUIRE(packages[2].Id == "test.withBoth"); + REQUIRE(packages[3].Id == "test.withNeither"); +} + +TEST_CASE("PackageCollection_WriteRead_OverrideAndCustomSwitches", "[PackageCollection]") +{ + PackageCollection::Source source; + source.Details.Name = "TestSource"; + source.Details.Arg = "https://aka.ms/winget"; + source.Details.Type = "Microsoft.PreIndexed.Package"; + source.Details.Identifier = "TestSourceId"; + + PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; + packageWithOverride.OverrideArgs = "/silent /norestart"; + source.Packages.emplace_back(std::move(packageWithOverride)); + + PackageCollection::Package packageWithCustom{ LocIndString{ "test.withCustom"sv }, Version{ "2.0" }, Channel{ "" } }; + packageWithCustom.CustomSwitches = "--no-telemetry"; + source.Packages.emplace_back(std::move(packageWithCustom)); + + PackageCollection::Package packageWithBoth{ LocIndString{ "test.withBoth"sv }, Version{ "3.0" }, Channel{ "" } }; + packageWithBoth.OverrideArgs = "/override"; + packageWithBoth.CustomSwitches = "--extra"; + source.Packages.emplace_back(std::move(packageWithBoth)); + + PackageCollection::Package packageWithNeither{ LocIndString{ "test.withNeither"sv }, Version{ "4.0" }, Channel{ "" } }; + source.Packages.emplace_back(std::move(packageWithNeither)); + + PackageCollection original + { + "1.0.0.0", + std::vector{ source } + }; + + auto json = PackagesJson::CreateJson(original); + ValidateJsonWithCollection(json, original); + + auto parseResult = PackagesJson::TryParseJson(json); + REQUIRE(parseResult.Result == PackagesJson::ParseResult::Type::Success); + ValidateEqualCollections(parseResult.Packages, original); +} + TEST_CASE("PackageCollection_Read_MultipleSources_1_0", "[PackageCollection]") { auto json = ParseJsonString(R"( diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 78c0ad5e22..bdda9cd367 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -330,7 +330,8 @@ namespace AppInstaller::Repository { auto trackingMetadata = m_trackingPackageVersion->GetMetadata(); for (auto metadataItem : { PackageVersionMetadata::InstalledArchitecture, PackageVersionMetadata::InstalledLocale, - PackageVersionMetadata::UserIntentArchitecture, PackageVersionMetadata::UserIntentLocale, PackageVersionMetadata::PinnedState }) + PackageVersionMetadata::UserIntentArchitecture, PackageVersionMetadata::UserIntentLocale, PackageVersionMetadata::PinnedState, + PackageVersionMetadata::UserOverrideArguments, PackageVersionMetadata::UserCustomSwitches }) { auto itr = trackingMetadata.find(metadataItem); auto existingItr = result.find(metadataItem); diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 3ad4fff9cf..c26bab770d 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -213,6 +213,10 @@ namespace AppInstaller::Repository NoModify, // No Repair flag NoRepair, + // The --override arguments provided by the user when installing the package + UserOverrideArguments, + // The --custom switches provided by the user when installing the package + UserCustomSwitches, }; // Convert a PackageVersionMetadata to a string. diff --git a/src/AppInstallerRepositoryCore/RepositorySearch.cpp b/src/AppInstallerRepositoryCore/RepositorySearch.cpp index bfef0ba999..72c76ca00a 100644 --- a/src/AppInstallerRepositoryCore/RepositorySearch.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySearch.cpp @@ -88,6 +88,8 @@ namespace AppInstaller::Repository case PackageVersionMetadata::PinnedState: return "PinnedState"sv; case PackageVersionMetadata::UserIntentArchitecture: return "UserIntentArchitecture"sv; case PackageVersionMetadata::UserIntentLocale: return "UserIntentLocale"sv; + case PackageVersionMetadata::UserOverrideArguments: return "UserOverrideArguments"sv; + case PackageVersionMetadata::UserCustomSwitches: return "UserCustomSwitches"sv; default: return "Unknown"sv; } } From 392545db85ac4fe210e182c466f45d0fb2ac5c48 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Mon, 30 Mar 2026 12:45:03 -0500 Subject: [PATCH 2/7] Add Tests --- src/AppInstallerCLITests/CompositeSource.cpp | 55 +++++++++++++++ src/AppInstallerCLITests/ExportFlow.cpp | 74 ++++++++++++++++++++ 2 files changed, 129 insertions(+) diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp index 94d3523d70..d28f108e26 100644 --- a/src/AppInstallerCLITests/CompositeSource.cpp +++ b/src/AppInstallerCLITests/CompositeSource.cpp @@ -1115,6 +1115,61 @@ TEST_CASE("CompositeSource_TrackingPackageFound_MetadataPopulatedFromTracking", REQUIRE(metadata[Repository::PackageVersionMetadata::PinnedState] == "PinnedByManifest"); } +TEST_CASE("CompositeSource_TrackingPackageFound_UserInstallerArgsPopulatedFromTracking", "[CompositeSource]") +{ + std::string availableID = "Available.ID"; + std::string pfn = "sortof_apfn"; + + CompositeWithTrackingTestSetup setup; + auto installedPackage = setup.MakeInstalled().WithPFN(pfn); + auto availablePackage = setup.MakeAvailable().WithPFN(pfn).WithId(availableID).WithDefaultName(s_Everything_Query); + + setup.Installed->Everything.Matches.emplace_back(installedPackage, Criteria()); + setup.Installed->SearchFunction = [&](const SearchRequest& request) + { + RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); + + SearchResult result; + result.Matches.emplace_back(installedPackage, Criteria()); + return result; + }; + + setup.Available->Everything.Matches.emplace_back(availablePackage, Criteria()); + setup.Available->SearchFunction = [&](const SearchRequest& request) + { + if (request.Filters.empty()) + { + RequireSearchRequestIncludes(request.Inclusions, PackageMatchField::PackageFamilyName, MatchType::Exact, pfn); + } + else + { + REQUIRE(request.Filters.size() == 1); + RequireSearchRequestIncludes(request.Filters, PackageMatchField::Id, MatchType::CaseInsensitive, availableID); + } + + SearchResult result; + result.Matches.emplace_back(availablePackage, Criteria()); + return result; + }; + + auto manifestId = setup.Tracking->GetIndex().AddManifest(availablePackage); + + // UserOverrideArguments and UserCustomSwitches are only stored in the tracking catalog, + // so they must be merged from there into the composite installed version's metadata. + setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserOverrideArguments, "/silent /norestart"); + setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserCustomSwitches, "--no-telemetry"); + + SearchResult result = setup.Search(); + + REQUIRE(result.Matches.size() == 1); + REQUIRE(result.Matches[0].Package); + REQUIRE(GetInstalledVersion(result.Matches[0].Package)); + + auto metadata = GetInstalledVersion(result.Matches[0].Package)->GetMetadata(); + REQUIRE(metadata[Repository::PackageVersionMetadata::UserOverrideArguments] == "/silent /norestart"); + REQUIRE(metadata[Repository::PackageVersionMetadata::UserCustomSwitches] == "--no-telemetry"); +} + TEST_CASE("CompositeSource_TrackingFound_AvailableNot", "[CompositeSource]") { std::string availableID = "Available.ID"; diff --git a/src/AppInstallerCLITests/ExportFlow.cpp b/src/AppInstallerCLITests/ExportFlow.cpp index 652e62f9cf..c48fd42e08 100644 --- a/src/AppInstallerCLITests/ExportFlow.cpp +++ b/src/AppInstallerCLITests/ExportFlow.cpp @@ -2,10 +2,15 @@ // Licensed under the MIT License. #include "pch.h" #include "WorkflowCommon.h" +#include "TestSource.h" #include +#include using namespace TestCommon; using namespace AppInstaller::CLI; +using namespace AppInstaller::Repository; +using namespace AppInstaller::Manifest; +using namespace AppInstaller::Manifest::YamlParser; TEST_CASE("ExportFlow_ExportAll", "[ExportFlow][workflow]") { @@ -115,3 +120,72 @@ TEST_CASE("ExportFlow_ExportAll_WithVersions", "[ExportFlow][workflow]") return p.Id == "AppInstallerCliTest.TestExeUnknownVersion" && p.VersionAndChannel.GetVersion().ToString() == "unknown"; })); } + +TEST_CASE("ExportFlow_ExportAll_WithUserInstallerArgs", "[ExportFlow][workflow]") +{ + TestCommon::TempFile exportResultPath("TestExport.json"); + + std::ostringstream exportOutput; + TestContext context{ exportOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + + // Create a test source with packages that have UserOverrideArguments and UserCustomSwitches set + auto testSource = CreateTestSource({}); + + TestSourceResult exeWithOverride( + "AppInstallerCliTest.TestExeInstaller"sv, + [](std::vector& matches, std::weak_ptr source) + { + auto manifest = YamlParser::CreateFromPath(TestDataFile("InstallFlowTest_Exe.yaml")); + auto manifest2 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe.yaml")); + auto manifest3 = YamlParser::CreateFromPath(TestDataFile("UpdateFlowTest_Exe_2.yaml")); + + auto testPackage = TestCompositePackage::Make( + manifest, + TestCompositePackage::MetadataMap + { + { PackageVersionMetadata::InstalledType, "Exe" }, + { PackageVersionMetadata::UserOverrideArguments, "/silent /override" }, + { PackageVersionMetadata::UserCustomSwitches, "--custom-flag" }, + }, + std::vector{ manifest3, manifest2, manifest }, + source); + for (auto& availablePackage : testPackage->Available) + { + availablePackage->IsSameOverride = [](const IPackage*, const IPackage*) { return true; }; + } + matches.emplace_back( + ResultMatch( + testPackage, + PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, "AppInstallerCliTest.TestExeInstaller"))); + }); + + testSource->AddResult(exeWithOverride); + + OverrideForCompositeInstalledSource(context, testSource); + context.Args.AddArg(Execution::Args::Type::OutputFile, exportResultPath); + + ExportCommand exportCommand({}); + exportCommand.Execute(context); + INFO(exportOutput.str()); + + const auto& exportedCollection = context.Get(); + REQUIRE(exportedCollection.Sources.size() == 1); + + const auto& exportedPackages = exportedCollection.Sources[0].Packages; + REQUIRE(exportedPackages.size() == 1); + + const auto& pkg = exportedPackages[0]; + REQUIRE(pkg.Id == "AppInstallerCliTest.TestExeInstaller"); + REQUIRE(pkg.OverrideArgs == "/silent /override"); + REQUIRE(pkg.CustomSwitches == "--custom-flag"); + + // Verify the values are in the exported JSON file + std::ifstream exportFile(exportResultPath.GetPath()); + Json::Value exportedJson; + exportFile >> exportedJson; + + const auto& jsonPackage = exportedJson["Sources"][0]["Packages"][0]; + REQUIRE(jsonPackage["OverrideArguments"].asString() == "/silent /override"); + REQUIRE(jsonPackage["CustomSwitches"].asString() == "--custom-flag"); +} From 7d1378b8d7a1a114e464b5f010a542ea3ddc5775 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Mon, 30 Mar 2026 12:59:48 -0500 Subject: [PATCH 3/7] Add import support --- .../Workflows/ImportExportFlow.cpp | 10 ++++ .../AppInstallerCLITests.vcxproj | 6 +++ .../AppInstallerCLITests.vcxproj.filters | 6 +++ src/AppInstallerCLITests/ImportFlow.cpp | 52 +++++++++++++++++++ .../ImportFile-Good-WithCustomSwitches.json | 21 ++++++++ .../ImportFile-Good-WithOverrideArgs.json | 21 ++++++++ 6 files changed, 116 insertions(+) create mode 100644 src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json create mode 100644 src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index 129d055103..bb1f833342 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -318,6 +318,16 @@ namespace AppInstaller::CLI::Workflow searchContext.Args.AddArg(Execution::Args::Type::Channel, channelString); } + if (!packageRequest.OverrideArgs.empty()) + { + searchContext.Args.AddArg(Execution::Args::Type::Override, packageRequest.OverrideArgs); + } + + if (!packageRequest.CustomSwitches.empty()) + { + searchContext.Args.AddArg(Execution::Args::Type::CustomSwitches, packageRequest.CustomSwitches); + } + packageSubContexts.emplace_back(std::move(searchContextPtr)); } } diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj index 7e9f524056..387398354d 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj @@ -441,6 +441,12 @@ true + + true + + + true + diff --git a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters index d04de5b6a6..39ecacf9be 100644 --- a/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters +++ b/src/AppInstallerCLITests/AppInstallerCLITests.vcxproj.filters @@ -798,6 +798,12 @@ TestData + + TestData + + + TestData + TestData diff --git a/src/AppInstallerCLITests/ImportFlow.cpp b/src/AppInstallerCLITests/ImportFlow.cpp index 938cbf05d0..28096bdd64 100644 --- a/src/AppInstallerCLITests/ImportFlow.cpp +++ b/src/AppInstallerCLITests/ImportFlow.cpp @@ -231,6 +231,58 @@ TEST_CASE("ImportFlow_MachineScope", "[ImportFlow][workflow]") REQUIRE(installResultStr.find("/scope=machine") != std::string::npos); } +TEST_CASE("ImportFlow_WithOverrideArgs", "[ImportFlow][workflow]") +{ + TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); + + std::ostringstream importOutput; + TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForImportSource(context); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithOverrideArgs.json").GetPath().string()); + + ImportCommand importCommand({}); + importCommand.Execute(context); + INFO(importOutput.str()); + + // Verify package was installed with override args (override replaces all installer args) + REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); + std::ifstream installResultFile(exeInstallResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/overrideArgs") != std::string::npos); + // Override replaces all args, so default silent switches should not be present + REQUIRE(installResultStr.find("/silentwithprogress") == std::string::npos); +} + +TEST_CASE("ImportFlow_WithCustomSwitches", "[ImportFlow][workflow]") +{ + TestCommon::TempFile exeInstallResultPath("TestExeInstalled.txt"); + + std::ostringstream importOutput; + TestContext context{ importOutput, std::cin }; + auto previousThreadGlobals = context.SetForCurrentThread(); + OverrideForImportSource(context); + OverrideForShellExecute(context); + context.Args.AddArg(Execution::Args::Type::ImportFile, TestDataFile("ImportFile-Good-WithCustomSwitches.json").GetPath().string()); + + ImportCommand importCommand({}); + importCommand.Execute(context); + INFO(importOutput.str()); + + // Verify package was installed with custom switches appended to the default args + REQUIRE(std::filesystem::exists(exeInstallResultPath.GetPath())); + std::ifstream installResultFile(exeInstallResultPath.GetPath()); + REQUIRE(installResultFile.is_open()); + std::string installResultStr; + std::getline(installResultFile, installResultStr); + REQUIRE(installResultStr.find("/customSwitches") != std::string::npos); + // Custom switches are appended, so default silent switches should still be present + REQUIRE(installResultStr.find("/silentwithprogress") != std::string::npos); +} + TEST_CASE("ImportFlow_Dependencies", "[ImportFlow][workflow][dependencies]") { std::ostringstream importOutput; diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json new file mode 100644 index 0000000000..2e1f2768af --- /dev/null +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate": "2021-01-01T12:00:00.000-00:00", + "Sources": [ + { + "Packages": [ + { + "PackageIdentifier": "TestExeInstallerWithNothingInstalled", + "CustomSwitches": "/customSwitches" + } + ], + "SourceDetails": { + "Argument": "//arg", + "Identifier": "*TestSource", + "Name": "TestSource", + "Type": "Microsoft.TestSource" + } + } + ], + "WinGetVersion": "1.0.0" +} diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json new file mode 100644 index 0000000000..8c4a99b9fe --- /dev/null +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://aka.ms/winget-packages.schema.2.0.json", + "CreationDate": "2021-01-01T12:00:00.000-00:00", + "Sources": [ + { + "Packages": [ + { + "PackageIdentifier": "TestExeInstallerWithNothingInstalled", + "OverrideArguments": "/overrideArgs" + } + ], + "SourceDetails": { + "Argument": "//arg", + "Identifier": "*TestSource", + "Name": "TestSource", + "Type": "Microsoft.TestSource" + } + } + ], + "WinGetVersion": "1.0.0" +} From 88d2c30af4d69996426bd3d2ba355f41e9ba685e Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Mon, 30 Mar 2026 13:15:24 -0500 Subject: [PATCH 4/7] Release Notes --- doc/ReleaseNotes.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index b325c92b48..8e81caddc3 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -28,6 +28,10 @@ match criteria that factor into the result ordering. This will prevent them from ## Minor Features +### Preserve installer arguments across export and import + +`winget export` now captures the `--override` and `--custom` arguments that were used when a package was originally installed and saves them into the export file. When subsequently running `winget import`, those values are automatically re-applied during installation — `--override` replaces all installer arguments and `--custom` appends extra switches — so packages can be reinstalled with the same customizations without any manual intervention. Both fields are optional and independent of each other; packages without stored installer arguments are unaffected. + ### --no-progress flag Added a new `--no-progress` command-line flag that disables all progress reporting (progress bars and spinners). This flag is universally available on all commands and takes precedence over the `visual.progressBar` setting. Useful for automation scenarios or when running WinGet in environments where progress output is undesirable. From 9d6f62cda6a2a888b1d64bb9421838736c655e42 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Mon, 30 Mar 2026 13:31:37 -0500 Subject: [PATCH 5/7] Fix failing test case --- src/AppInstallerCLITests/PackageCollection.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLITests/PackageCollection.cpp b/src/AppInstallerCLITests/PackageCollection.cpp index f8f94fdd94..7f5a49737d 100644 --- a/src/AppInstallerCLITests/PackageCollection.cpp +++ b/src/AppInstallerCLITests/PackageCollection.cpp @@ -203,7 +203,7 @@ TEST_CASE("PackageCollection_Write_OverrideAndCustomSwitches", "[PackageCollecti PackageCollection pc { - "1.0.0.0", + "1.0.0", std::vector{ source } }; @@ -388,7 +388,7 @@ TEST_CASE("PackageCollection_WriteRead_OverrideAndCustomSwitches", "[PackageColl PackageCollection original { - "1.0.0.0", + "1.0.0", std::vector{ source } }; From 1f171763ccb290d28a03b680deb43d15329ea6dc Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Tue, 31 Mar 2026 07:19:17 -0500 Subject: [PATCH 6/7] Update to be initial values only --- .../JSON/packages/packages.schema.2.0.json | 8 ++-- src/AppInstallerCLICore/PackageCollection.cpp | 20 ++++---- src/AppInstallerCLICore/PackageCollection.h | 4 +- .../Workflows/ImportExportFlow.cpp | 16 +++---- .../Workflows/InstallFlow.cpp | 35 ++++++++------ src/AppInstallerCLITests/CompositeSource.cpp | 10 ++-- src/AppInstallerCLITests/ExportFlow.cpp | 14 +++--- .../PackageCollection.cpp | 47 ++++++++++++------- .../CompositeSource.cpp | 2 +- .../Public/winget/RepositorySearch.h | 8 ++-- .../RepositorySearch.cpp | 4 +- 11 files changed, 92 insertions(+), 76 deletions(-) diff --git a/schemas/JSON/packages/packages.schema.2.0.json b/schemas/JSON/packages/packages.schema.2.0.json index 77fc1e3711..babc41d27e 100644 --- a/schemas/JSON/packages/packages.schema.2.0.json +++ b/schemas/JSON/packages/packages.schema.2.0.json @@ -98,13 +98,13 @@ "default": "user" }, - "OverrideArguments": { - "description": "Override arguments to pass directly to the installer, replacing all other installer arguments", + "InitialOverrideArguments": { + "description": "Override arguments used when the package was initially installed; preserved on upgrade", "type": "string" }, - "CustomSwitches": { - "description": "Additional arguments to append to the installer arguments", + "InitialCustomSwitches": { + "description": "Additional custom switches used when the package was initially installed; preserved on upgrade", "type": "string" } } diff --git a/src/AppInstallerCLICore/PackageCollection.cpp b/src/AppInstallerCLICore/PackageCollection.cpp index 3b959a2673..5d7b3eafa8 100644 --- a/src/AppInstallerCLICore/PackageCollection.cpp +++ b/src/AppInstallerCLICore/PackageCollection.cpp @@ -41,8 +41,8 @@ namespace AppInstaller::CLI const std::string PackagesJson_Package_Version = "Version"; const std::string PackagesJson_Package_Channel = "Channel"; const std::string PackagesJson_Package_Scope = "Scope"; - const std::string PackagesJson_Package_OverrideArguments = "OverrideArguments"; - const std::string PackagesJson_Package_CustomSwitches = "CustomSwitches"; + const std::string PackagesJson_Package_InitialOverrideArguments = "InitialOverrideArguments"; + const std::string PackagesJson_Package_InitialCustomSwitches = "InitialCustomSwitches"; static const StaticStrings& Instance() { @@ -156,14 +156,14 @@ namespace AppInstaller::CLI PackageCollection::Package package{ Utility::LocIndString{ id }, Utility::Version{ version }, Utility::Channel{ channel } }; package.Scope = Manifest::ConvertToScopeEnum(scope); - if (packageNode.isMember(ss.PackagesJson_Package_OverrideArguments)) + if (packageNode.isMember(ss.PackagesJson_Package_InitialOverrideArguments)) { - package.OverrideArgs = packageNode[ss.PackagesJson_Package_OverrideArguments].asString(); + package.InitialOverrideArgs = packageNode[ss.PackagesJson_Package_InitialOverrideArguments].asString(); } - if (packageNode.isMember(ss.PackagesJson_Package_CustomSwitches)) + if (packageNode.isMember(ss.PackagesJson_Package_InitialCustomSwitches)) { - package.CustomSwitches = packageNode[ss.PackagesJson_Package_CustomSwitches].asString(); + package.InitialCustomSwitches = packageNode[ss.PackagesJson_Package_InitialCustomSwitches].asString(); } return package; @@ -214,14 +214,14 @@ namespace AppInstaller::CLI packageNode[ss.PackagesJson_Package_Scope] = std::string{ Manifest::ScopeToString(package.Scope) }; } - if (!package.OverrideArgs.empty()) + if (!package.InitialOverrideArgs.empty()) { - packageNode[ss.PackagesJson_Package_OverrideArguments] = package.OverrideArgs; + packageNode[ss.PackagesJson_Package_InitialOverrideArguments] = package.InitialOverrideArgs; } - if (!package.CustomSwitches.empty()) + if (!package.InitialCustomSwitches.empty()) { - packageNode[ss.PackagesJson_Package_CustomSwitches] = package.CustomSwitches; + packageNode[ss.PackagesJson_Package_InitialCustomSwitches] = package.InitialCustomSwitches; } return sourceNode[ss.PackagesJson_Packages].append(std::move(packageNode)); diff --git a/src/AppInstallerCLICore/PackageCollection.h b/src/AppInstallerCLICore/PackageCollection.h index 4ca5ad1a89..1f13f04983 100644 --- a/src/AppInstallerCLICore/PackageCollection.h +++ b/src/AppInstallerCLICore/PackageCollection.h @@ -30,8 +30,8 @@ namespace AppInstaller::CLI Utility::VersionAndChannel VersionAndChannel; Manifest::ScopeEnum Scope = Manifest::ScopeEnum::Unknown; std::filesystem::path InstalledLocation; - std::string OverrideArgs; - std::string CustomSwitches; + std::string InitialOverrideArgs; + std::string InitialCustomSwitches; }; // A source along with a set of packages available from it. diff --git a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp index bb1f833342..979bf8119e 100644 --- a/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/ImportExportFlow.cpp @@ -149,16 +149,16 @@ namespace AppInstaller::CLI::Workflow exportPackage.InstalledLocation = Utility::ConvertToUTF16(locationItr->second); } - auto overrideItr = installedMetadata.find(PackageVersionMetadata::UserOverrideArguments); + auto overrideItr = installedMetadata.find(PackageVersionMetadata::InitialOverrideArguments); if (overrideItr != installedMetadata.end()) { - exportPackage.OverrideArgs = overrideItr->second; + exportPackage.InitialOverrideArgs = overrideItr->second; } - auto customItr = installedMetadata.find(PackageVersionMetadata::UserCustomSwitches); + auto customItr = installedMetadata.find(PackageVersionMetadata::InitialCustomSwitches); if (customItr != installedMetadata.end()) { - exportPackage.CustomSwitches = customItr->second; + exportPackage.InitialCustomSwitches = customItr->second; } if (includeVersions) @@ -318,14 +318,14 @@ namespace AppInstaller::CLI::Workflow searchContext.Args.AddArg(Execution::Args::Type::Channel, channelString); } - if (!packageRequest.OverrideArgs.empty()) + if (!packageRequest.InitialOverrideArgs.empty()) { - searchContext.Args.AddArg(Execution::Args::Type::Override, packageRequest.OverrideArgs); + searchContext.Args.AddArg(Execution::Args::Type::Override, packageRequest.InitialOverrideArgs); } - if (!packageRequest.CustomSwitches.empty()) + if (!packageRequest.InitialCustomSwitches.empty()) { - searchContext.Args.AddArg(Execution::Args::Type::CustomSwitches, packageRequest.CustomSwitches); + searchContext.Args.AddArg(Execution::Args::Type::CustomSwitches, packageRequest.InitialCustomSwitches); } packageSubContexts.emplace_back(std::move(searchContextPtr)); diff --git a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp index becae75bbb..46d1eb85b9 100644 --- a/src/AppInstallerCLICore/Workflows/InstallFlow.cpp +++ b/src/AppInstallerCLICore/Workflows/InstallFlow.cpp @@ -996,6 +996,8 @@ namespace AppInstaller::CLI::Workflow installedMetadata = context.Get()->GetMetadata(); } + bool isUpdate = WI_IsFlagSet(context.GetFlags(), ContextFlag::InstallerExecutionUseUpdate); + if (context.Args.Contains(Execution::Args::Type::InstallArchitecture)) { version.SetMetadata(Repository::PackageVersionMetadata::UserIntentArchitecture, context.Args.GetArg(Execution::Args::Type::InstallArchitecture)); @@ -1022,29 +1024,32 @@ namespace AppInstaller::CLI::Workflow } } - if (context.Args.Contains(Execution::Args::Type::Override)) - { - version.SetMetadata(Repository::PackageVersionMetadata::UserOverrideArguments, context.Args.GetArg(Execution::Args::Type::Override)); - } - else + // InitialOverrideArguments and InitialCustomSwitches capture the args from the original install. + // They are set only on fresh install and preserved (not updated) on upgrade. + if (!isUpdate) { - auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserOverrideArguments); - if (itr != installedMetadata.end()) + if (context.Args.Contains(Execution::Args::Type::Override)) { - version.SetMetadata(Repository::PackageVersionMetadata::UserOverrideArguments, itr->second); + version.SetMetadata(Repository::PackageVersionMetadata::InitialOverrideArguments, context.Args.GetArg(Execution::Args::Type::Override)); } - } - if (context.Args.Contains(Execution::Args::Type::CustomSwitches)) - { - version.SetMetadata(Repository::PackageVersionMetadata::UserCustomSwitches, context.Args.GetArg(Execution::Args::Type::CustomSwitches)); + if (context.Args.Contains(Execution::Args::Type::CustomSwitches)) + { + version.SetMetadata(Repository::PackageVersionMetadata::InitialCustomSwitches, context.Args.GetArg(Execution::Args::Type::CustomSwitches)); + } } else { - auto itr = installedMetadata.find(Repository::PackageVersionMetadata::UserCustomSwitches); - if (itr != installedMetadata.end()) + auto overrideItr = installedMetadata.find(Repository::PackageVersionMetadata::InitialOverrideArguments); + if (overrideItr != installedMetadata.end()) + { + version.SetMetadata(Repository::PackageVersionMetadata::InitialOverrideArguments, overrideItr->second); + } + + auto customItr = installedMetadata.find(Repository::PackageVersionMetadata::InitialCustomSwitches); + if (customItr != installedMetadata.end()) { - version.SetMetadata(Repository::PackageVersionMetadata::UserCustomSwitches, itr->second); + version.SetMetadata(Repository::PackageVersionMetadata::InitialCustomSwitches, customItr->second); } } } diff --git a/src/AppInstallerCLITests/CompositeSource.cpp b/src/AppInstallerCLITests/CompositeSource.cpp index d28f108e26..b45993ac1a 100644 --- a/src/AppInstallerCLITests/CompositeSource.cpp +++ b/src/AppInstallerCLITests/CompositeSource.cpp @@ -1154,10 +1154,10 @@ TEST_CASE("CompositeSource_TrackingPackageFound_UserInstallerArgsPopulatedFromTr auto manifestId = setup.Tracking->GetIndex().AddManifest(availablePackage); - // UserOverrideArguments and UserCustomSwitches are only stored in the tracking catalog, + // InitialOverrideArguments and InitialCustomSwitches are only stored in the tracking catalog, // so they must be merged from there into the composite installed version's metadata. - setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserOverrideArguments, "/silent /norestart"); - setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::UserCustomSwitches, "--no-telemetry"); + setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::InitialOverrideArguments, "/silent /norestart"); + setup.Tracking->GetIndex().SetMetadataByManifestId(manifestId, Repository::PackageVersionMetadata::InitialCustomSwitches, "--no-telemetry"); SearchResult result = setup.Search(); @@ -1166,8 +1166,8 @@ TEST_CASE("CompositeSource_TrackingPackageFound_UserInstallerArgsPopulatedFromTr REQUIRE(GetInstalledVersion(result.Matches[0].Package)); auto metadata = GetInstalledVersion(result.Matches[0].Package)->GetMetadata(); - REQUIRE(metadata[Repository::PackageVersionMetadata::UserOverrideArguments] == "/silent /norestart"); - REQUIRE(metadata[Repository::PackageVersionMetadata::UserCustomSwitches] == "--no-telemetry"); + REQUIRE(metadata[Repository::PackageVersionMetadata::InitialOverrideArguments] == "/silent /norestart"); + REQUIRE(metadata[Repository::PackageVersionMetadata::InitialCustomSwitches] == "--no-telemetry"); } TEST_CASE("CompositeSource_TrackingFound_AvailableNot", "[CompositeSource]") diff --git a/src/AppInstallerCLITests/ExportFlow.cpp b/src/AppInstallerCLITests/ExportFlow.cpp index c48fd42e08..ec4e710971 100644 --- a/src/AppInstallerCLITests/ExportFlow.cpp +++ b/src/AppInstallerCLITests/ExportFlow.cpp @@ -129,7 +129,7 @@ TEST_CASE("ExportFlow_ExportAll_WithUserInstallerArgs", "[ExportFlow][workflow]" TestContext context{ exportOutput, std::cin }; auto previousThreadGlobals = context.SetForCurrentThread(); - // Create a test source with packages that have UserOverrideArguments and UserCustomSwitches set + // Create a test source with packages that have InitialOverrideArguments and InitialCustomSwitches set auto testSource = CreateTestSource({}); TestSourceResult exeWithOverride( @@ -145,8 +145,8 @@ TEST_CASE("ExportFlow_ExportAll_WithUserInstallerArgs", "[ExportFlow][workflow]" TestCompositePackage::MetadataMap { { PackageVersionMetadata::InstalledType, "Exe" }, - { PackageVersionMetadata::UserOverrideArguments, "/silent /override" }, - { PackageVersionMetadata::UserCustomSwitches, "--custom-flag" }, + { PackageVersionMetadata::InitialOverrideArguments, "/silent /override" }, + { PackageVersionMetadata::InitialCustomSwitches, "--custom-flag" }, }, std::vector{ manifest3, manifest2, manifest }, source); @@ -177,8 +177,8 @@ TEST_CASE("ExportFlow_ExportAll_WithUserInstallerArgs", "[ExportFlow][workflow]" const auto& pkg = exportedPackages[0]; REQUIRE(pkg.Id == "AppInstallerCliTest.TestExeInstaller"); - REQUIRE(pkg.OverrideArgs == "/silent /override"); - REQUIRE(pkg.CustomSwitches == "--custom-flag"); + REQUIRE(pkg.InitialOverrideArgs == "/silent /override"); + REQUIRE(pkg.InitialCustomSwitches == "--custom-flag"); // Verify the values are in the exported JSON file std::ifstream exportFile(exportResultPath.GetPath()); @@ -186,6 +186,6 @@ TEST_CASE("ExportFlow_ExportAll_WithUserInstallerArgs", "[ExportFlow][workflow]" exportFile >> exportedJson; const auto& jsonPackage = exportedJson["Sources"][0]["Packages"][0]; - REQUIRE(jsonPackage["OverrideArguments"].asString() == "/silent /override"); - REQUIRE(jsonPackage["CustomSwitches"].asString() == "--custom-flag"); + REQUIRE(jsonPackage["InitialOverrideArguments"].asString() == "/silent /override"); + REQUIRE(jsonPackage["InitialCustomSwitches"].asString() == "--custom-flag"); } diff --git a/src/AppInstallerCLITests/PackageCollection.cpp b/src/AppInstallerCLITests/PackageCollection.cpp index 7f5a49737d..921bc63507 100644 --- a/src/AppInstallerCLITests/PackageCollection.cpp +++ b/src/AppInstallerCLITests/PackageCollection.cpp @@ -30,8 +30,8 @@ const std::string s_PackagesJson_Packages = "Packages"; const std::string s_PackagesJson_Package_PackageIdentifier = "PackageIdentifier"; const std::string s_PackagesJson_Package_Version = "Version"; const std::string s_PackagesJson_Package_Channel = "Channel"; -const std::string s_PackagesJson_Package_OverrideArguments = "OverrideArguments"; -const std::string s_PackagesJson_Package_CustomSwitches = "CustomSwitches"; +const std::string s_PackagesJson_Package_InitialOverrideArguments = "InitialOverrideArguments"; +const std::string s_PackagesJson_Package_InitialCustomSwitches = "InitialCustomSwitches"; namespace { @@ -98,8 +98,8 @@ namespace ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_PackageIdentifier, packageItr->Id); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Version, packageItr->VersionAndChannel.GetVersion().ToString(), true); ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_Channel, packageItr->VersionAndChannel.GetChannel().ToString(), true); - ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_OverrideArguments, packageItr->OverrideArgs, true); - ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_CustomSwitches, packageItr->CustomSwitches, true); + ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_InitialOverrideArguments, packageItr->InitialOverrideArgs, true); + ValidateJsonStringProperty(*jsonPackageItr, s_PackagesJson_Package_InitialCustomSwitches, packageItr->InitialCustomSwitches, true); } } } @@ -125,8 +125,8 @@ namespace { REQUIRE(firstPackageItr->Id == secondPackageItr->Id); REQUIRE(firstPackageItr->VersionAndChannel.ToString() == secondPackageItr->VersionAndChannel.ToString()); - REQUIRE(firstPackageItr->OverrideArgs == secondPackageItr->OverrideArgs); - REQUIRE(firstPackageItr->CustomSwitches == secondPackageItr->CustomSwitches); + REQUIRE(firstPackageItr->InitialOverrideArgs == secondPackageItr->InitialOverrideArgs); + REQUIRE(firstPackageItr->InitialCustomSwitches == secondPackageItr->InitialCustomSwitches); } } } @@ -186,16 +186,16 @@ TEST_CASE("PackageCollection_Write_OverrideAndCustomSwitches", "[PackageCollecti source.Details.Identifier = "TestSourceId"; PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; - packageWithOverride.OverrideArgs = "/silent /norestart"; + packageWithOverride.InitialOverrideArgs = "/silent /norestart"; source.Packages.emplace_back(std::move(packageWithOverride)); PackageCollection::Package packageWithCustom{ LocIndString{ "test.withCustom"sv }, Version{ "2.0" }, Channel{ "" } }; - packageWithCustom.CustomSwitches = "--no-telemetry"; + packageWithCustom.InitialCustomSwitches = "--no-telemetry"; source.Packages.emplace_back(std::move(packageWithCustom)); PackageCollection::Package packageWithBoth{ LocIndString{ "test.withBoth"sv }, Version{ "3.0" }, Channel{ "" } }; - packageWithBoth.OverrideArgs = "/override"; - packageWithBoth.CustomSwitches = "--extra"; + packageWithBoth.InitialOverrideArgs = "/override"; + packageWithBoth.InitialCustomSwitches = "--extra"; source.Packages.emplace_back(std::move(packageWithBoth)); PackageCollection::Package packageWithNeither{ LocIndString{ "test.withNeither"sv }, Version{ "4.0" }, Channel{ "" } }; @@ -323,16 +323,16 @@ TEST_CASE("PackageCollection_Read_OverrideAndCustomSwitches_2_0", "[PackageColle "Packages": [ { "PackageIdentifier": "test.withOverride", - "OverrideArguments": "/silent /norestart" + "InitialOverrideArguments": "/silent /norestart" }, { "PackageIdentifier": "test.withCustom", - "CustomSwitches": "--no-telemetry" + "InitialCustomSwitches": "--no-telemetry" }, { "PackageIdentifier": "test.withBoth", - "OverrideArguments": "/override", - "CustomSwitches": "--extra" + "InitialOverrideArguments": "/override", + "InitialCustomSwitches": "--extra" }, { "PackageIdentifier": "test.withNeither" @@ -357,9 +357,20 @@ TEST_CASE("PackageCollection_Read_OverrideAndCustomSwitches_2_0", "[PackageColle REQUIRE(packages.size() == 4); REQUIRE(packages[0].Id == "test.withOverride"); + REQUIRE(packages[0].InitialOverrideArgs == "/silent /norestart"); + REQUIRE(packages[0].InitialCustomSwitches.empty()); + REQUIRE(packages[1].Id == "test.withCustom"); + REQUIRE(packages[1].InitialOverrideArgs.empty()); + REQUIRE(packages[1].InitialCustomSwitches == "--no-telemetry"); + REQUIRE(packages[2].Id == "test.withBoth"); + REQUIRE(packages[2].InitialOverrideArgs == "/override"); + REQUIRE(packages[2].InitialCustomSwitches == "--extra"); + REQUIRE(packages[3].Id == "test.withNeither"); + REQUIRE(packages[3].InitialOverrideArgs.empty()); + REQUIRE(packages[3].InitialCustomSwitches.empty()); } TEST_CASE("PackageCollection_WriteRead_OverrideAndCustomSwitches", "[PackageCollection]") @@ -371,16 +382,16 @@ TEST_CASE("PackageCollection_WriteRead_OverrideAndCustomSwitches", "[PackageColl source.Details.Identifier = "TestSourceId"; PackageCollection::Package packageWithOverride{ LocIndString{ "test.withOverride"sv }, Version{ "1.0" }, Channel{ "" } }; - packageWithOverride.OverrideArgs = "/silent /norestart"; + packageWithOverride.InitialOverrideArgs = "/silent /norestart"; source.Packages.emplace_back(std::move(packageWithOverride)); PackageCollection::Package packageWithCustom{ LocIndString{ "test.withCustom"sv }, Version{ "2.0" }, Channel{ "" } }; - packageWithCustom.CustomSwitches = "--no-telemetry"; + packageWithCustom.InitialCustomSwitches = "--no-telemetry"; source.Packages.emplace_back(std::move(packageWithCustom)); PackageCollection::Package packageWithBoth{ LocIndString{ "test.withBoth"sv }, Version{ "3.0" }, Channel{ "" } }; - packageWithBoth.OverrideArgs = "/override"; - packageWithBoth.CustomSwitches = "--extra"; + packageWithBoth.InitialOverrideArgs = "/override"; + packageWithBoth.InitialCustomSwitches = "--extra"; source.Packages.emplace_back(std::move(packageWithBoth)); PackageCollection::Package packageWithNeither{ LocIndString{ "test.withNeither"sv }, Version{ "4.0" }, Channel{ "" } }; diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index bdda9cd367..9a0b5bfed7 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -331,7 +331,7 @@ namespace AppInstaller::Repository auto trackingMetadata = m_trackingPackageVersion->GetMetadata(); for (auto metadataItem : { PackageVersionMetadata::InstalledArchitecture, PackageVersionMetadata::InstalledLocale, PackageVersionMetadata::UserIntentArchitecture, PackageVersionMetadata::UserIntentLocale, PackageVersionMetadata::PinnedState, - PackageVersionMetadata::UserOverrideArguments, PackageVersionMetadata::UserCustomSwitches }) + PackageVersionMetadata::InitialOverrideArguments, PackageVersionMetadata::InitialCustomSwitches }) { auto itr = trackingMetadata.find(metadataItem); auto existingItr = result.find(metadataItem); diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index c26bab770d..676063ab02 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -213,10 +213,10 @@ namespace AppInstaller::Repository NoModify, // No Repair flag NoRepair, - // The --override arguments provided by the user when installing the package - UserOverrideArguments, - // The --custom switches provided by the user when installing the package - UserCustomSwitches, + // The --override arguments provided by the user when initially installing the package; preserved on upgrade + InitialOverrideArguments, + // The --custom switches provided by the user when initially installing the package; preserved on upgrade + InitialCustomSwitches, }; // Convert a PackageVersionMetadata to a string. diff --git a/src/AppInstallerRepositoryCore/RepositorySearch.cpp b/src/AppInstallerRepositoryCore/RepositorySearch.cpp index 72c76ca00a..faa100dbfb 100644 --- a/src/AppInstallerRepositoryCore/RepositorySearch.cpp +++ b/src/AppInstallerRepositoryCore/RepositorySearch.cpp @@ -88,8 +88,8 @@ namespace AppInstaller::Repository case PackageVersionMetadata::PinnedState: return "PinnedState"sv; case PackageVersionMetadata::UserIntentArchitecture: return "UserIntentArchitecture"sv; case PackageVersionMetadata::UserIntentLocale: return "UserIntentLocale"sv; - case PackageVersionMetadata::UserOverrideArguments: return "UserOverrideArguments"sv; - case PackageVersionMetadata::UserCustomSwitches: return "UserCustomSwitches"sv; + case PackageVersionMetadata::InitialOverrideArguments: return "InitialOverrideArguments"sv; + case PackageVersionMetadata::InitialCustomSwitches: return "InitialCustomSwitches"sv; default: return "Unknown"sv; } } From 3c85821c73aa5e1e9a0b5ba6538cd5c81c4e6397 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Tue, 31 Mar 2026 07:27:08 -0500 Subject: [PATCH 7/7] Fix test files --- .../TestData/ImportFile-Good-WithCustomSwitches.json | 2 +- .../TestData/ImportFile-Good-WithOverrideArgs.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json index 2e1f2768af..5271e795be 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithCustomSwitches.json @@ -6,7 +6,7 @@ "Packages": [ { "PackageIdentifier": "TestExeInstallerWithNothingInstalled", - "CustomSwitches": "/customSwitches" + "InitialCustomSwitches": "/customSwitches" } ], "SourceDetails": { diff --git a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json index 8c4a99b9fe..6f5309d556 100644 --- a/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json +++ b/src/AppInstallerCLITests/TestData/ImportFile-Good-WithOverrideArgs.json @@ -6,7 +6,7 @@ "Packages": [ { "PackageIdentifier": "TestExeInstallerWithNothingInstalled", - "OverrideArguments": "/overrideArgs" + "InitialOverrideArguments": "/overrideArgs" } ], "SourceDetails": {