mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Re-implemented msi installer based on Clowd.Squirrel
This re-implements the msi installer that was present inside of Clowd.Squirrel. This is hidden behind some hidden options.
This commit is contained in:
@@ -15,4 +15,10 @@
|
||||
<PackageReference Include="System.Formats.Asn1" Version="9.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Deployment.WindowsInstaller">
|
||||
<HintPath>..\..\vendor\wix\Microsoft.Deployment.WindowsInstaller.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Deployment.WindowsInstaller;
|
||||
using Microsoft.Win32;
|
||||
using NuGet.Packaging;
|
||||
using Velopack.Compression;
|
||||
using Velopack.Core;
|
||||
using Velopack.Packaging.Commands;
|
||||
using Velopack.Packaging.Exceptions;
|
||||
using Velopack.Packaging.Windows.Commands;
|
||||
using Velopack.Util;
|
||||
using Velopack.Vpk;
|
||||
using Velopack.Vpk.Logging;
|
||||
using Velopack.Windows;
|
||||
using static Azure.Core.HttpHeader;
|
||||
|
||||
namespace Velopack.Packaging.Tests;
|
||||
|
||||
@@ -26,7 +27,7 @@ public class WindowsPackTests
|
||||
_output = output;
|
||||
}
|
||||
|
||||
private WindowsPackCommandRunner GetPackRunner(ILogger logger)
|
||||
private static WindowsPackCommandRunner GetPackRunner(ILogger logger)
|
||||
{
|
||||
var console = new BasicConsole(logger, new VelopackDefaults(false));
|
||||
return new WindowsPackCommandRunner(logger, console);
|
||||
@@ -199,6 +200,7 @@ public class WindowsPackTests
|
||||
TargetRuntime = RID.Parse("win-x64"),
|
||||
PackDirectory = tmpOutput,
|
||||
Shortcuts = "Desktop,StartMenuRoot",
|
||||
NoPortable = true
|
||||
};
|
||||
|
||||
var runner = GetPackRunner(logger);
|
||||
@@ -207,7 +209,7 @@ public class WindowsPackTests
|
||||
var setupPath1 = Path.Combine(tmpReleaseDir, $"{id}-win-Setup.exe");
|
||||
Assert.True(File.Exists(setupPath1));
|
||||
|
||||
RunNoCoverage(setupPath1, new[] { "--silent", "--installto", tmpInstallDir }, Environment.CurrentDirectory, logger);
|
||||
RunNoCoverage(setupPath1, ["--silent", "--installto", tmpInstallDir], Environment.CurrentDirectory, logger);
|
||||
|
||||
var updatePath = Path.Combine(tmpInstallDir, "Update.exe");
|
||||
Assert.True(File.Exists(updatePath));
|
||||
@@ -244,17 +246,16 @@ public class WindowsPackTests
|
||||
var date = DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture);
|
||||
Assert.Equal(date, installDate.Trim('\0'));
|
||||
|
||||
var uninstOutput = RunNoCoverage(updatePath, new string[] { "--silent", "--uninstall" }, Environment.CurrentDirectory, logger);
|
||||
var uninstOutput = RunNoCoverage(updatePath, ["--silent", "--uninstall"], Environment.CurrentDirectory, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "Y", uninstOutput); // this checks that the self-delete succeeded
|
||||
|
||||
Assert.False(File.Exists(startLnk));
|
||||
Assert.False(File.Exists(desktopLnk));
|
||||
Assert.False(File.Exists(appPath));
|
||||
|
||||
using (var key2 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.OpenSubKey(uninstallRegSubKey + "\\" + id, RegistryKeyPermissionCheck.ReadSubTree)) {
|
||||
Assert.Null(key2);
|
||||
}
|
||||
using var key2 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Default)
|
||||
.OpenSubKey(uninstallRegSubKey + "\\" + id, RegistryKeyPermissionCheck.ReadSubTree);
|
||||
Assert.Null(key2);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
@@ -274,7 +275,7 @@ public class WindowsPackTests
|
||||
var setupPath1 = Path.Combine(releaseDir, $"{id}-win-Setup.exe");
|
||||
RunNoCoverage(
|
||||
setupPath1,
|
||||
new string[] { "--silent", "--installto", installDir },
|
||||
["--silent", "--installto", installDir],
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
logger);
|
||||
|
||||
@@ -287,19 +288,19 @@ public class WindowsPackTests
|
||||
var mvTo = Path.Combine(installDir, "packages", fileName);
|
||||
File.Copy(mvFrom, mvTo);
|
||||
|
||||
RunCoveredDotnet(appPath, new string[] { "--autoupdate" }, installDir, logger, exitCode: null);
|
||||
RunCoveredDotnet(appPath, ["--autoupdate"], installDir, logger, exitCode: null);
|
||||
|
||||
Thread.Sleep(3000); // update.exe runs in separate process
|
||||
|
||||
var chk1version = RunCoveredDotnet(appPath, new string[] { "version" }, installDir, logger);
|
||||
var chk1version = RunCoveredDotnet(appPath, ["version"], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "2.0.0", chk1version);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public void TestPackGeneratesValidDelta()
|
||||
{
|
||||
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||
string id = "SquirrelDeltaTest";
|
||||
PackTestApp(id, "1.0.0", "version 1 test", releaseDir, logger);
|
||||
@@ -338,7 +339,7 @@ public class WindowsPackTests
|
||||
new DeltaPatchOptions {
|
||||
BasePackage = Path.Combine(releaseDir, $"{id}-1.0.0-full.nupkg"),
|
||||
OutputFile = output,
|
||||
PatchFiles = new[] { new FileInfo(deltaPath) },
|
||||
PatchFiles = [new FileInfo(deltaPath)],
|
||||
}).GetAwaiterResult();
|
||||
|
||||
// are the packages the same?
|
||||
@@ -437,7 +438,7 @@ public class WindowsPackTests
|
||||
var setupPath1 = Path.Combine(releaseDir, $"{id}-win-Setup.exe");
|
||||
RunNoCoverage(
|
||||
setupPath1,
|
||||
new string[] { "--silent", "--installto", installDir },
|
||||
["--silent", "--installto", installDir],
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
|
||||
logger);
|
||||
|
||||
@@ -451,11 +452,11 @@ public class WindowsPackTests
|
||||
logger.Info("TEST: v1 installed");
|
||||
|
||||
// check app output
|
||||
var chk1test = RunCoveredDotnet(appPath, new string[] { "test" }, installDir, logger);
|
||||
var chk1test = RunCoveredDotnet(appPath, ["test"], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "version 1 test", chk1test);
|
||||
var chk1version = RunCoveredDotnet(appPath, new string[] { "version" }, installDir, logger);
|
||||
var chk1version = RunCoveredDotnet(appPath, ["version"], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "1.0.0", chk1version);
|
||||
var chk1check = RunCoveredDotnet(appPath, new string[] { "check", releaseDir }, installDir, logger);
|
||||
var chk1check = RunCoveredDotnet(appPath, ["check", releaseDir], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "no updates", chk1check);
|
||||
logger.Info("TEST: v1 output verified");
|
||||
|
||||
@@ -463,7 +464,7 @@ public class WindowsPackTests
|
||||
PackTestApp(id, "2.0.0", "version 2 test", releaseDir, logger);
|
||||
|
||||
// check can find v2 update
|
||||
var chk2check = RunCoveredDotnet(appPath, new string[] { "check", releaseDir }, installDir, logger);
|
||||
var chk2check = RunCoveredDotnet(appPath, ["check", releaseDir], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "update: 2.0.0", chk2check);
|
||||
logger.Info("TEST: found v2 update");
|
||||
|
||||
@@ -476,17 +477,17 @@ public class WindowsPackTests
|
||||
|
||||
// perform full update, check that we get v3
|
||||
// apply should fail if there's not an update downloaded
|
||||
RunCoveredDotnet(appPath, new string[] { "apply", releaseDir }, installDir, logger, exitCode: -1);
|
||||
RunCoveredDotnet(appPath, new string[] { "download", releaseDir }, installDir, logger);
|
||||
RunCoveredDotnet(appPath, new string[] { "apply", releaseDir }, installDir, logger, exitCode: null);
|
||||
RunCoveredDotnet(appPath, ["apply", releaseDir], installDir, logger, exitCode: -1);
|
||||
RunCoveredDotnet(appPath, ["download", releaseDir], installDir, logger);
|
||||
RunCoveredDotnet(appPath, ["apply", releaseDir], installDir, logger, exitCode: null);
|
||||
logger.Info("TEST: v3 applied");
|
||||
|
||||
// check app output
|
||||
var chk3test = RunCoveredDotnet(appPath, new string[] { "test" }, installDir, logger);
|
||||
var chk3test = RunCoveredDotnet(appPath, ["test"], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "version 3 test", chk3test);
|
||||
var chk3version = RunCoveredDotnet(appPath, new string[] { "version" }, installDir, logger);
|
||||
var chk3version = RunCoveredDotnet(appPath, ["version"], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "3.0.0", chk3version);
|
||||
var ch3check2 = RunCoveredDotnet(appPath, new string[] { "check", releaseDir }, installDir, logger);
|
||||
var ch3check2 = RunCoveredDotnet(appPath, ["check", releaseDir], installDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "no updates", ch3check2);
|
||||
logger.Info("TEST: v3 output verified");
|
||||
|
||||
@@ -504,7 +505,7 @@ public class WindowsPackTests
|
||||
|
||||
// uninstall
|
||||
var updatePath = Path.Combine(installDir, "Update.exe");
|
||||
RunNoCoverage(updatePath, new string[] { "--silent", "--uninstall" }, Environment.CurrentDirectory, logger);
|
||||
RunNoCoverage(updatePath, ["--silent", "--uninstall"], Environment.CurrentDirectory, logger);
|
||||
logger.Info("TEST: uninstalled / complete");
|
||||
}
|
||||
|
||||
@@ -543,8 +544,8 @@ public class WindowsPackTests
|
||||
using var _1 = TempUtil.GetTempDirectory(out var releaseDir);
|
||||
PackTestApp("LegacyTestApp", "2.0.0", "hello!", releaseDir, logger);
|
||||
|
||||
RunNoCoverage(appExe, new string[] { "download", releaseDir }, currentDir, logger, exitCode: 0);
|
||||
RunNoCoverage(appExe, new string[] { "apply", releaseDir }, currentDir, logger, exitCode: null);
|
||||
RunNoCoverage(appExe, ["download", releaseDir], currentDir, logger, exitCode: 0);
|
||||
RunNoCoverage(appExe, ["apply", releaseDir], currentDir, logger, exitCode: null);
|
||||
|
||||
logger.Info("TEST: " + DateTime.Now.ToLongTimeString());
|
||||
|
||||
@@ -555,7 +556,7 @@ public class WindowsPackTests
|
||||
logger.Info("TEST: " + DateTime.Now.ToLongTimeString());
|
||||
|
||||
if (origDirName != "current") {
|
||||
Assert.True(!Directory.Exists(currentDir));
|
||||
Assert.False(Directory.Exists(currentDir));
|
||||
currentDir = Path.Combine(rootDir, "current");
|
||||
}
|
||||
|
||||
@@ -569,10 +570,90 @@ public class WindowsPackTests
|
||||
// this is the file written by TestApp when it's detected the squirrel restart. if this is here, everything went smoothly.
|
||||
Assert.True(File.Exists(Path.Combine(rootDir, "restarted")));
|
||||
|
||||
var chk3version = RunNoCoverage(appExe, new string[] { "version" }, currentDir, logger);
|
||||
var chk3version = RunNoCoverage(appExe, ["version"], currentDir, logger);
|
||||
Assert.EndsWith(Environment.NewLine + "2.0.0", chk3version);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task TestPackGeneratesMsi()
|
||||
{
|
||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||
|
||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||
|
||||
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||
|
||||
var exe = "testapp.exe";
|
||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||
var id = "Test.Squirrel-App";
|
||||
var version = "1.2.3";
|
||||
|
||||
PathHelper.CopyRustAssetTo(exe, tmpOutput);
|
||||
PathHelper.CopyRustAssetTo(pdb, tmpOutput);
|
||||
|
||||
var options = new WindowsPackOptions {
|
||||
EntryExecutableName = exe,
|
||||
ReleaseDir = new DirectoryInfo(tmpReleaseDir),
|
||||
PackId = id,
|
||||
PackVersion = version,
|
||||
TargetRuntime = RID.Parse("win-x64"),
|
||||
PackDirectory = tmpOutput,
|
||||
Shortcuts = "Desktop,StartMenuRoot",
|
||||
BuildMsi = true
|
||||
};
|
||||
|
||||
var runner = GetPackRunner(logger);
|
||||
await runner.Run(options);
|
||||
|
||||
string msiPath = Path.Combine(tmpReleaseDir, $"{id}-win-DeploymentTool.msi");
|
||||
Assert.True(File.Exists(msiPath));
|
||||
using Database db = new Database(msiPath);
|
||||
var msiVersion = db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'") as string;
|
||||
Assert.Equal("1.2.3.0", msiVersion);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task TestPackGeneratesMsiWithSpecifiedVersion()
|
||||
{
|
||||
Skip.IfNot(VelopackRuntimeInfo.IsWindows);
|
||||
|
||||
using var logger = _output.BuildLoggerFor<WindowsPackTests>();
|
||||
|
||||
using var _1 = TempUtil.GetTempDirectory(out var tmpOutput);
|
||||
using var _2 = TempUtil.GetTempDirectory(out var tmpReleaseDir);
|
||||
|
||||
var exe = "testapp.exe";
|
||||
var pdb = Path.ChangeExtension(exe, ".pdb");
|
||||
var id = "Test.Squirrel-App";
|
||||
var version = "1.0.0";
|
||||
|
||||
PathHelper.CopyRustAssetTo(exe, tmpOutput);
|
||||
PathHelper.CopyRustAssetTo(pdb, tmpOutput);
|
||||
|
||||
var options = new WindowsPackOptions {
|
||||
EntryExecutableName = exe,
|
||||
ReleaseDir = new DirectoryInfo(tmpReleaseDir),
|
||||
PackId = id,
|
||||
PackVersion = version,
|
||||
TargetRuntime = RID.Parse("win-x64"),
|
||||
PackDirectory = tmpOutput,
|
||||
Shortcuts = "Desktop,StartMenuRoot",
|
||||
BuildMsi = true,
|
||||
MsiVersionOverride = "4.5.6.1"
|
||||
};
|
||||
|
||||
var runner = GetPackRunner(logger);
|
||||
await runner.Run(options);
|
||||
|
||||
string msiPath = Path.Combine(tmpReleaseDir, $"{id}-win-DeploymentTool.msi");
|
||||
Assert.True(File.Exists(msiPath));
|
||||
|
||||
using Database db = new Database(msiPath);
|
||||
var msiVersion = db.ExecuteScalar("SELECT `Value` FROM `Property` WHERE `Property` = 'ProductVersion'") as string;
|
||||
Assert.Equal("4.5.6.1", msiVersion);
|
||||
}
|
||||
|
||||
private static string ReadFileWithRetry(string path, ILogger logger)
|
||||
{
|
||||
return IoUtil.Retry(
|
||||
@@ -610,7 +691,7 @@ public class WindowsPackTests
|
||||
// return RunImpl(psi, logger, exitCode);
|
||||
//}
|
||||
|
||||
private string RunImpl(ProcessStartInfo psi, ILogger logger, int? exitCode = 0)
|
||||
private static string RunImpl(ProcessStartInfo psi, ILogger logger, int? exitCode = 0)
|
||||
{
|
||||
//logger.Info($"TEST: Running {psi.FileName} {psi.ArgumentList.Aggregate((a, b) => $"{a} {b}")}");
|
||||
//using var p = Process.Start(psi);
|
||||
@@ -679,7 +760,7 @@ public class WindowsPackTests
|
||||
}
|
||||
}
|
||||
|
||||
private string RunCoveredDotnet(string exe, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
|
||||
private static string RunCoveredDotnet(string exe, string[] args, string workingDir, ILogger logger, int? exitCode = 0)
|
||||
{
|
||||
var outputfile = PathHelper.GetTestRootPath($"coverage.rundotnet.{RandomString(8)}.xml");
|
||||
|
||||
@@ -703,7 +784,7 @@ public class WindowsPackTests
|
||||
return RunImpl(psi, logger, exitCode);
|
||||
}
|
||||
|
||||
private static Random _random = new Random();
|
||||
private static readonly Random _random = Random.Shared;
|
||||
|
||||
private static string RandomString(int length)
|
||||
{
|
||||
@@ -727,7 +808,7 @@ public class WindowsPackTests
|
||||
return RunImpl(psi, logger, exitCode);
|
||||
}
|
||||
|
||||
private void PackTestApp(string id, string version, string testString, string releaseDir, ILogger logger, bool addNewFile = false)
|
||||
private static void PackTestApp(string id, string version, string testString, string releaseDir, ILogger logger, bool addNewFile = false)
|
||||
{
|
||||
var projDir = PathHelper.GetTestRootPath("TestApp");
|
||||
var testStringFile = Path.Combine(projDir, "Const.cs");
|
||||
|
||||
Reference in New Issue
Block a user