Skip to content

Commit

Permalink
Fix incorrect hash for ZIP installers in interactive update (#528)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdanish-kh authored Apr 3, 2024
1 parent 9be3d9f commit c804449
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 9 deletions.
4 changes: 3 additions & 1 deletion src/WingetCreateCLI/Commands/UpdateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,7 @@ private async Task UpdateSingleInstallerInteractively(Installer installer)
string url = Prompt.Input<string>(Resources.NewInstallerUrl_Message, null, null, new[] { FieldValidation.ValidateProperty(newInstaller, nameof(Installer.InstallerUrl)) });

string packageFile = await DownloadPackageFile(url);
string archivePath = null;

if (string.IsNullOrEmpty(packageFile))
{
Expand All @@ -872,6 +873,7 @@ private async Task UpdateSingleInstallerInteractively(Installer installer)

if (packageFile.IsZipFile())
{
archivePath = packageFile;
string extractDirectory = ExtractArchiveAndRetrieveDirectoryPath(packageFile);
bool isRelativePathNull = false;

Expand All @@ -894,7 +896,7 @@ private async Task UpdateSingleInstallerInteractively(Installer installer)
packageFile = Path.Combine(extractDirectory, installer.NestedInstallerFiles.First().RelativeFilePath);
}

if (!PackageParser.ParsePackageAndUpdateInstallerNode(installer, packageFile, url))
if (!PackageParser.ParsePackageAndUpdateInstallerNode(installer, packageFile, url, archivePath))
{
Logger.ErrorLocalized(nameof(Resources.PackageParsing_Error), url);
Console.WriteLine();
Expand Down
21 changes: 14 additions & 7 deletions src/WingetCreateCore/Common/PackageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,22 @@ public static void UpdateInstallerNodesAsync(List<InstallerMetadata> installerMe
/// Parses the package for relevant metadata and and updates the metadata of the provided installer node.
/// </summary>
/// <param name="installer">Installer node.</param>
/// <param name="path">Path to package file.</param>
/// <param name="filePath">Path to package file.</param>
/// <param name="url">Installer url.</param>
/// <param name="archivePath">Path to archive file containing the installer. Required if the installer type is Zip.</param>
/// <returns>Boolean indicating whether the package parse was successful.</returns>
public static bool ParsePackageAndUpdateInstallerNode(Installer installer, string path, string url)
public static bool ParsePackageAndUpdateInstallerNode(Installer installer, string filePath, string url, string archivePath = null)
{
// Guard clause to ensure that the archivePath is provided if the installer type is Zip.
if (installer.InstallerType == InstallerType.Zip && string.IsNullOrEmpty(archivePath))
{
return false;
}

List<Installer> newInstallers = new List<Installer>();
bool parseResult = ParseExeInstallerType(path, installer, newInstallers) ||
ParseMsix(path, installer, null, newInstallers) ||
ParseMsi(path, installer, null, newInstallers);
bool parseResult = ParseExeInstallerType(filePath, installer, newInstallers) ||
ParseMsix(filePath, installer, null, newInstallers) ||
ParseMsi(filePath, installer, null, newInstallers);

if (!parseResult || !newInstallers.Any())
{
Expand All @@ -302,11 +309,11 @@ public static bool ParsePackageAndUpdateInstallerNode(Installer installer, strin
else
{
// For a single installer, detect the architecture. If no architecture is detected, default to architecture from existing manifest.
newInstaller.Architecture = GetArchFromUrl(url) ?? GetMachineType(path)?.ToString().ToEnumOrDefault<Architecture>() ?? installer.Architecture;
newInstaller.Architecture = GetArchFromUrl(url) ?? GetMachineType(filePath)?.ToString().ToEnumOrDefault<Architecture>() ?? installer.Architecture;
}

newInstaller.InstallerUrl = url;
newInstaller.InstallerSha256 = GetFileHash(path);
newInstaller.InstallerSha256 = string.IsNullOrEmpty(archivePath) ? GetFileHash(filePath) : GetFileHash(archivePath);
UpdateInstallerMetadata(installer, newInstallers.First());
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

namespace Microsoft.WingetCreateUnitTests
{
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using AutoMapper;
using Microsoft.WingetCreateCore;
Expand Down Expand Up @@ -157,13 +159,47 @@ public void ParseAndUpdateInstaller()
WingetCreateCore.Models.Singleton.Installer initialInstaller = initialManifests.SingletonManifest.Installers.First();
Installer installer = ConvertSingletonInstaller(initialInstaller);

PackageParser.ParsePackageAndUpdateInstallerNode(installer, testMsiInstallerPath, installer.InstallerUrl);
bool result = PackageParser.ParsePackageAndUpdateInstallerNode(installer, testMsiInstallerPath, installer.InstallerUrl);
ClassicAssert.IsTrue(result, "ParsePackageAndUpdateInstallerNode should return true.");
ClassicAssert.AreEqual(InstallerType.Msi, installer.InstallerType, "InstallerType should be updated.");
ClassicAssert.AreEqual(initialInstaller.Architecture.ToEnumAttributeValue(), installer.Architecture.ToEnumAttributeValue(), "Architecture should not change.");
ClassicAssert.AreNotEqual(initialInstaller.InstallerSha256, installer.InstallerSha256, "InstallerSha256 should be updated.");
ClassicAssert.AreEqual("{E2650EFC-DCD3-4FAA-BBAC-FD1812B03A61}", installer.ProductCode, "ProductCode should be updated");
}

/// <summary>
/// Validates that the ParsePackageAndUpdateInstallerNode function works as expected for a zip installer.
/// </summary>
[Test]
public void ParseAndUpdateZipInstaller()
{
var testZipInstaller = TestUtils.MockDownloadFile(TestConstants.TestZipInstaller);
Assert.That(testZipInstaller, Is.Not.Null.And.Not.Empty);
string extractDirectory = Path.Combine(PackageParser.InstallerDownloadPath, Path.GetFileNameWithoutExtension(testZipInstaller));

try
{
ZipFile.ExtractToDirectory(testZipInstaller, extractDirectory, true);
}
catch (Exception e)
{
ClassicAssert.Fail($"Failed to extract the zip file: {e.Message}");
}

List<string> initialManifestContent = TestUtils.GetInitialManifestContent($"TestPublisher.ZipWithExe.yaml");
Manifests initialManifests = Serialization.DeserializeManifestContents(initialManifestContent);
WingetCreateCore.Models.Singleton.Installer initialInstaller = initialManifests.SingletonManifest.Installers.First();
Installer installer = ConvertSingletonInstaller(initialInstaller);
string nestedInstallerPath = Path.Combine(extractDirectory, installer.NestedInstallerFiles.First().RelativeFilePath);

bool result = PackageParser.ParsePackageAndUpdateInstallerNode(installer, nestedInstallerPath, installer.InstallerUrl, testZipInstaller);
ClassicAssert.IsTrue(result, "ParsePackageAndUpdateInstallerNode should return true.");
ClassicAssert.AreEqual(InstallerType.Zip, installer.InstallerType, "InstallerType should not change");
ClassicAssert.AreEqual(initialInstaller.Architecture.ToEnumAttributeValue(), installer.Architecture.ToEnumAttributeValue(), "Architecture should not change.");
ClassicAssert.AreNotEqual(initialInstaller.InstallerSha256, installer.InstallerSha256, "InstallerSha256 should be updated");
ClassicAssert.AreEqual(installer.InstallerSha256, PackageParser.GetFileHash(testZipInstaller), "InstallSha256 should match the hash of the zip file");
}

/// <summary>
/// Converts the SingletonManifest Installer object model to the InstallerManifest Installer object model.
/// </summary>
Expand All @@ -175,6 +211,7 @@ private static Installer ConvertSingletonInstaller(WingetCreateCore.Models.Singl
{
cfg.AllowNullCollections = true;
cfg.CreateMap<WingetCreateCore.Models.Singleton.Dependencies, WingetCreateCore.Models.Installer.Dependencies>();
cfg.CreateMap<WingetCreateCore.Models.Singleton.NestedInstallerFile, WingetCreateCore.Models.Installer.NestedInstallerFile>();
cfg.CreateMap<WingetCreateCore.Models.Singleton.Installer, WingetCreateCore.Models.Installer.Installer>();
cfg.CreateMap<WingetCreateCore.Models.Singleton.InstallerSwitches, WingetCreateCore.Models.Installer.InstallerSwitches>();
});
Expand Down

0 comments on commit c804449

Please sign in to comment.