diff --git a/src/WingetCreateCore/Common/PackageParser.cs b/src/WingetCreateCore/Common/PackageParser.cs
index 80773d67..85998312 100644
--- a/src/WingetCreateCore/Common/PackageParser.cs
+++ b/src/WingetCreateCore/Common/PackageParser.cs
@@ -501,6 +501,14 @@ private static bool ParsePackageAndGenerateInstallerNodes(InstallerMetadata inst
foreach (NestedInstallerFile nestedInstallerFile in installerMetadata.NestedInstallerFiles)
{
+ // Skip adding duplicate NestedInstallerFile object.
+ if (baseInstaller.NestedInstallerFiles.Any(i =>
+ i.RelativeFilePath == nestedInstallerFile.RelativeFilePath &&
+ i.PortableCommandAlias == nestedInstallerFile.PortableCommandAlias))
+ {
+ continue;
+ }
+
baseInstaller.NestedInstallerFiles.Add(new NestedInstallerFile
{
RelativeFilePath = nestedInstallerFile.RelativeFilePath,
diff --git a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.ZipMultipleInstallers.yaml b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.ZipMultipleInstallers.yaml
index f3767c1c..f4ede0f4 100644
--- a/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.ZipMultipleInstallers.yaml
+++ b/src/WingetCreateTests/WingetCreateTests/Resources/TestPublisher.ZipMultipleInstallers.yaml
@@ -12,6 +12,7 @@ Installers:
NestedInstallerType: exe
NestedInstallerFiles:
- RelativeFilePath: WingetCreateTestExeInstaller.exe
+ PortableCommandAlias: TestPortableCommandAlias
- Architecture: x86
InstallerType: zip
InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip
@@ -19,6 +20,15 @@ Installers:
NestedInstallerType: exe
NestedInstallerFiles:
- RelativeFilePath: WingetCreateTestExeInstaller.exe
+ PortableCommandAlias: TestPortableCommandAlias
+- Architecture: arm64
+ InstallerType: zip
+ InstallerUrl: https://fakedomain.com/WingetCreateTestZipInstaller.zip
+ InstallerSha256: 8A052767127A6E2058BAAE03B551A807777BB1B726650E2C7E92C3E92C8DF80D
+ NestedInstallerType: exe
+ NestedInstallerFiles:
+ - RelativeFilePath: WingetCreateTestExeInstaller.exe
+ PortableCommandAlias: TestPortableCommandAlias
PackageLocale: en-US
ManifestType: singleton
ManifestVersion: 1.4.0
\ No newline at end of file
diff --git a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs
index a82082d2..2db1b306 100644
--- a/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs
+++ b/src/WingetCreateTests/WingetCreateTests/UnitTests/UpdateCommandTests.cs
@@ -823,6 +823,44 @@ public async Task UpdateZipWithMsix()
Assert.IsTrue(initialSecondInstaller.InstallerSha256 != updatedSecondInstaller.InstallerSha256, "InstallerSha256 should be updated");
}
+ ///
+ /// Verifies that updating a zip package with multiple zip installers works as expected.
+ ///
+ /// A representing the asynchronous unit test.
+ [Test]
+ public async Task UpdateMultipleZipInstallers()
+ {
+ TestUtils.InitializeMockDownloads(TestConstants.TestZipInstaller);
+
+ string installerUrl = $"https://fakedomain.com/{TestConstants.TestZipInstaller}";
+ (UpdateCommand command, var initialManifestContent) = GetUpdateCommandAndManifestData("TestPublisher.ZipMultipleInstallers", null, this.tempPath, new[] { $"{installerUrl}|x64", $"{installerUrl}|x86", $"{installerUrl}|arm64" });
+
+ var updatedManifests = await RunUpdateCommand(command, initialManifestContent);
+ Assert.IsNotNull(updatedManifests, "Command should have succeeded");
+
+ var initialManifests = Serialization.DeserializeManifestContents(initialManifestContent);
+ var initialInstallers = initialManifests.SingletonManifest.Installers;
+ var initialFirstInstaller = initialInstallers[0];
+ var initialSecondInstaller = initialInstallers[1];
+ var initialThirdInstaller = initialInstallers[2];
+
+ var updatedInstallerManifest = updatedManifests.InstallerManifest;
+ var updatedFirstInstaller = updatedInstallerManifest.Installers[0];
+ var updatedSecondInstaller = updatedInstallerManifest.Installers[1];
+ var updatedThirdInstaller = updatedInstallerManifest.Installers[2];
+
+ Assert.IsTrue(updatedInstallerManifest.InstallerType == InstallerType.Zip, "InstallerType should be ZIP");
+ Assert.IsTrue(updatedInstallerManifest.NestedInstallerType == NestedInstallerType.Exe, "NestedInstallerType should be EXE");
+ Assert.IsTrue(updatedInstallerManifest.NestedInstallerFiles.Count == 1, "NestedInstallerFiles list should contain only one member");
+
+ Assert.IsTrue(initialFirstInstaller.NestedInstallerFiles[0].RelativeFilePath == updatedInstallerManifest.NestedInstallerFiles[0].RelativeFilePath, "RelativeFilePath should be preserved.");
+ Assert.IsTrue(initialFirstInstaller.NestedInstallerFiles[0].PortableCommandAlias == updatedInstallerManifest.NestedInstallerFiles[0].PortableCommandAlias, "PortableCommandAlias should be preserved.");
+
+ Assert.IsTrue(initialFirstInstaller.InstallerSha256 != updatedFirstInstaller.InstallerSha256, "InstallerSha256 should be updated");
+ Assert.IsTrue(initialSecondInstaller.InstallerSha256 != updatedSecondInstaller.InstallerSha256, "InstallerSha256 should be updated");
+ Assert.IsTrue(initialThirdInstaller.InstallerSha256 != updatedThirdInstaller.InstallerSha256, "InstallerSha256 should be updated");
+ }
+
///
/// Verifies that moving common installer fields to the root of the manifest works as expected.
///