From a29e69505db386494b784df5c1b2244bb0876dea Mon Sep 17 00:00:00 2001 From: Ben Olden-Cooligan Date: Sat, 9 Jul 2022 20:57:10 -0700 Subject: [PATCH] Exe installer verification and better console output --- NAPS2.Setup/setup.template.iss | 3 ++- NAPS2.Tools/Cli.cs | 17 +++++++------ NAPS2.Tools/Project/BuildCommand.cs | 2 +- NAPS2.Tools/Project/BuildOptions.cs | 3 +++ .../Project/Packaging/InnoSetupPackager.cs | 18 +++++++++++-- .../Project/Packaging/PackageCommand.cs | 8 +++--- NAPS2.Tools/Project/Packaging/PackageInfo.cs | 5 +++- .../Project/Packaging/PackageOptions.cs | 3 +++ .../Project/Packaging/WixToolsetPackager.cs | 10 +++++--- .../Project/Packaging/ZipArchivePackager.cs | 22 +++++++++++++--- .../{VersionHelper.cs => ProjectHelper.cs} | 13 +++++++++- NAPS2.Tools/Project/TestCommand.cs | 2 +- NAPS2.Tools/Project/TestOptions.cs | 2 ++ .../Project/Verification/AppDriverRunner.cs | 10 +++++--- .../Project/Verification/ExeSetupVerifier.cs | 25 +++++++++++++++++++ NAPS2.Tools/Project/Verification/Verifier.cs | 17 +++++++++++++ .../Project/Verification/VerifyCommand.cs | 10 +++----- .../Project/Verification/VerifyOptions.cs | 3 +++ .../Verification/ZipArchiveVerifier.cs | 10 ++++---- 19 files changed, 143 insertions(+), 40 deletions(-) rename NAPS2.Tools/Project/{VersionHelper.cs => ProjectHelper.cs} (62%) create mode 100644 NAPS2.Tools/Project/Verification/ExeSetupVerifier.cs create mode 100644 NAPS2.Tools/Project/Verification/Verifier.cs diff --git a/NAPS2.Setup/setup.template.iss b/NAPS2.Setup/setup.template.iss index 2c2c9847c..ad8edab1f 100644 --- a/NAPS2.Setup/setup.template.iss +++ b/NAPS2.Setup/setup.template.iss @@ -13,6 +13,7 @@ OutputDir=../publish/{#AppVersion} OutputBaseFilename=naps2-{#AppVersion}-{#AppPlatform} Compression=lzma SolidCompression=yes +; !arch LicenseFile=..\..\LICENSE UninstallDisplayIcon={app}\scanner-app.ico @@ -54,11 +55,11 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ ; !files ; Delete files from old locations in case of upgrade -; TODO: Delete from Program Files (x86)? [InstallDelete] Type: files; Name: "{app}\*.exe" Type: files; Name: "{app}\*.exe.config" Type: filesandordirs; Name: "{app}\lib" +; !clean32 [Icons] Name: "{group}\NAPS2"; Filename: "{app}\NAPS2.exe" diff --git a/NAPS2.Tools/Cli.cs b/NAPS2.Tools/Cli.cs index e01874308..99b8dc06f 100644 --- a/NAPS2.Tools/Cli.cs +++ b/NAPS2.Tools/Cli.cs @@ -2,7 +2,7 @@ namespace NAPS2.Tools; public static class Cli { - public static void Run(string command, string args, Dictionary? env = null) + public static void Run(string command, string args, bool verbose, Dictionary? env = null) { var startInfo = new ProcessStartInfo { @@ -29,23 +29,26 @@ public static class Cli // TODO: Maybe we forward Console.CancelKeyPress while (!proc.WaitForExit(100)) { - PrintAll(proc.StandardOutput); - PrintAll(proc.StandardError); + PrintAll(proc.StandardOutput, verbose); + PrintAll(proc.StandardError, true); } - PrintAll(proc.StandardOutput); - PrintAll(proc.StandardError); + PrintAll(proc.StandardOutput, verbose); + PrintAll(proc.StandardError, true); if (proc.ExitCode != 0) { throw new Exception($"Command failed: {command} {args}"); } } - private static void PrintAll(StreamReader stream) + private static void PrintAll(StreamReader stream, bool forwardToStdout) { string? line; while ((line = stream.ReadLine()) != null) { - Console.WriteLine(line); + if (forwardToStdout) + { + Console.WriteLine(line); + } } } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/BuildCommand.cs b/NAPS2.Tools/Project/BuildCommand.cs index c22379f8c..9d1aa22b3 100644 --- a/NAPS2.Tools/Project/BuildCommand.cs +++ b/NAPS2.Tools/Project/BuildCommand.cs @@ -7,7 +7,7 @@ public static class BuildCommand foreach (var config in GetConfigs(opts.What)) { Console.WriteLine($"---------- BUILDING CONFIGURATION: {config} ----------"); - Cli.Run("dotnet", $"build -c {config}"); + Cli.Run("dotnet", $"build -c {config}", opts.Verbose); } return 0; } diff --git a/NAPS2.Tools/Project/BuildOptions.cs b/NAPS2.Tools/Project/BuildOptions.cs index 109038203..bbc3f875a 100644 --- a/NAPS2.Tools/Project/BuildOptions.cs +++ b/NAPS2.Tools/Project/BuildOptions.cs @@ -7,4 +7,7 @@ public class BuildOptions { [Value(0, MetaName = "what", Required = true, HelpText = "all|debug|exe|msi|zip")] public string? What { get; set; } + + [Option('v', "verbose", Required = false, HelpText = "Show full output")] + public bool Verbose { get; set; } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/Packaging/InnoSetupPackager.cs b/NAPS2.Tools/Project/Packaging/InnoSetupPackager.cs index afb613e4a..c4794e297 100644 --- a/NAPS2.Tools/Project/Packaging/InnoSetupPackager.cs +++ b/NAPS2.Tools/Project/Packaging/InnoSetupPackager.cs @@ -4,13 +4,18 @@ namespace NAPS2.Tools.Project.Packaging; public static class InnoSetupPackager { - public static void PackageExe(PackageInfo packageInfo) + public static void PackageExe(PackageInfo packageInfo, bool verbose) { + var exePath = packageInfo.GetPath("exe"); + Console.WriteLine($"Packaging exe installer: {exePath}"); + var innoDefPath = GenerateInnoDef(packageInfo); // TODO: Use https://github.com/DomGries/InnoDependencyInstaller for .net dependency var iscc = Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%/Inno Setup 6/iscc.exe"); - Cli.Run(iscc, $"\"{innoDefPath}\""); + Cli.Run(iscc, $"\"{innoDefPath}\"", verbose); + + Console.WriteLine(verbose ? $"Packaged exe installer: {exePath}" : "Done."); } private static string GenerateInnoDef(PackageInfo packageInfo) @@ -22,6 +27,15 @@ public static class InnoSetupPackager defLines.AppendLine($"#define AppPlatform \"{packageInfo.Platform.PackageName()}\""); template = template.Replace("; !defs", defLines.ToString()); + if (packageInfo.Platform == Platform.Win64) + { + var arch = new StringBuilder(); + arch.AppendLine("ArchitecturesInstallIn64BitMode=x64"); + arch.AppendLine("ArchitecturesAllowed=x64"); + template = template.Replace("; !arch", arch.ToString()); + template = template.Replace("; !clean32", @"Type: filesandordirs; Name: ""{commonpf32}\NAPS2"""); + } + var fileLines = new StringBuilder(); foreach (var pkgFile in packageInfo.Files) { diff --git a/NAPS2.Tools/Project/Packaging/PackageCommand.cs b/NAPS2.Tools/Project/Packaging/PackageCommand.cs index 1e450d35d..03dde83bb 100644 --- a/NAPS2.Tools/Project/Packaging/PackageCommand.cs +++ b/NAPS2.Tools/Project/Packaging/PackageCommand.cs @@ -13,24 +13,24 @@ public static class PackageCommand // TODO: Allow customizing net version, platform, etc // TODO: The fact that we only have one project config for the app but multiple for the SDK is problematic; things will overwrite each other unless we either pull them explicitly from the right project or have a separate config or normalize things somehow to avoid needing multiple configs var pkgInfo = GetPackageInfo(platform, "InstallerEXE"); - InnoSetupPackager.PackageExe(pkgInfo); + InnoSetupPackager.PackageExe(pkgInfo, opts.Verbose); } if (opts.What == "msi" || opts.What == "all") { var pkgInfo = GetPackageInfo(platform, "InstallerMSI"); - WixToolsetPackager.PackageMsi(pkgInfo); + WixToolsetPackager.PackageMsi(pkgInfo, opts.Verbose); } if (opts.What == "zip" || opts.What == "all") { var pkgInfo = GetPackageInfo(platform, "Standalone"); - ZipArchivePackager.PackageZip(pkgInfo); + ZipArchivePackager.PackageZip(pkgInfo, opts.Verbose); } return 0; } private static PackageInfo GetPackageInfo(Platform platform, string preferredConfig) { - var pkgInfo = new PackageInfo(platform, VersionHelper.GetProjectVersion("NAPS2.App.WinForms")); + var pkgInfo = new PackageInfo(platform, ProjectHelper.GetProjectVersion("NAPS2.App.WinForms")); foreach (var project in new[] { "NAPS2.Sdk", "NAPS2.Lib.Common", "NAPS2.App.Worker", "NAPS2.App.Console", "NAPS2.App.WinForms" }) { diff --git a/NAPS2.Tools/Project/Packaging/PackageInfo.cs b/NAPS2.Tools/Project/Packaging/PackageInfo.cs index 2031fc87e..4cd6f97bd 100644 --- a/NAPS2.Tools/Project/Packaging/PackageInfo.cs +++ b/NAPS2.Tools/Project/Packaging/PackageInfo.cs @@ -15,7 +15,10 @@ public class PackageInfo public string Version { get; } - public string FileName => $"naps2-{Version}-{Platform.PackageName()}"; + public string GetPath(string ext) + { + return ProjectHelper.GetPackagePath(ext, Platform, Version); + } public IEnumerable Files => _files; diff --git a/NAPS2.Tools/Project/Packaging/PackageOptions.cs b/NAPS2.Tools/Project/Packaging/PackageOptions.cs index 3a69dc0c6..8f0462fad 100644 --- a/NAPS2.Tools/Project/Packaging/PackageOptions.cs +++ b/NAPS2.Tools/Project/Packaging/PackageOptions.cs @@ -11,6 +11,9 @@ public class PackageOptions // TODO: Allow platform combos (e.g. win32+win64) [Option('p', "platform", Required = false, HelpText = "win32|win64|mac|macarm|linux")] public string? Platform { get; set; } + + [Option('v', "verbose", Required = false, HelpText = "Show full output")] + public bool Verbose { get; set; } // TODO: Add net target (net462/net5/net5-windows etc.) } \ No newline at end of file diff --git a/NAPS2.Tools/Project/Packaging/WixToolsetPackager.cs b/NAPS2.Tools/Project/Packaging/WixToolsetPackager.cs index 78d19123d..d276db8f1 100644 --- a/NAPS2.Tools/Project/Packaging/WixToolsetPackager.cs +++ b/NAPS2.Tools/Project/Packaging/WixToolsetPackager.cs @@ -5,18 +5,20 @@ namespace NAPS2.Tools.Project.Packaging; public static class WixToolsetPackager { - public static void PackageMsi(PackageInfo pkgInfo) + public static void PackageMsi(PackageInfo pkgInfo, bool verbose) { + var msiPath = pkgInfo.GetPath("msi"); + Console.WriteLine($"Packaging msi installer: {msiPath}"); var wxsPath = GenerateWxs(pkgInfo); var candle = Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%/WiX Toolset v3.11/bin/candle.exe"); - Cli.Run(candle, $"\"{wxsPath}\" -o \"{Paths.SetupObj}/\""); + Cli.Run(candle, $"\"{wxsPath}\" -o \"{Paths.SetupObj}/\"", verbose); var wixobjPath = wxsPath.Replace(".wxs", ".wixobj"); var light = Environment.ExpandEnvironmentVariables("%PROGRAMFILES(X86)%/WiX Toolset v3.11/bin/light.exe"); - var publishFile = Path.Combine(Paths.Publish, pkgInfo.Version, $"{pkgInfo.FileName}.msi"); - Cli.Run(light, $"\"{wixobjPath}\" -spdb -ext WixUIExtension -o \"{publishFile}\""); + Cli.Run(light, $"\"{wixobjPath}\" -spdb -ext WixUIExtension -o \"{msiPath}\"", verbose); + Console.WriteLine(verbose ? $"Packaged msi installer: {msiPath}" : "Done."); } private static string GenerateWxs(PackageInfo packageInfo) diff --git a/NAPS2.Tools/Project/Packaging/ZipArchivePackager.cs b/NAPS2.Tools/Project/Packaging/ZipArchivePackager.cs index 5abd4875d..81c0a25ae 100644 --- a/NAPS2.Tools/Project/Packaging/ZipArchivePackager.cs +++ b/NAPS2.Tools/Project/Packaging/ZipArchivePackager.cs @@ -4,9 +4,10 @@ namespace NAPS2.Tools.Project.Packaging; public static class ZipArchivePackager { - public static void PackageZip(PackageInfo pkgInfo) + public static void PackageZip(PackageInfo pkgInfo, bool verbose) { - var zipPath = Path.Combine(Paths.Publish, pkgInfo.Version, $"{pkgInfo.FileName}.zip"); + var zipPath = pkgInfo.GetPath("zip"); + Console.WriteLine($"Packaging zip archive: {zipPath}"); if (File.Exists(zipPath)) { File.Delete(zipPath); @@ -22,9 +23,24 @@ public static class ZipArchivePackager using var archive = ZipFile.Open(zipPath, ZipArchiveMode.Create); foreach (var file in pkgInfo.Files) { - archive.CreateEntryFromFile(file.SourcePath, Path.Combine("App", file.DestPath)); + var destPath = Path.Combine("App", file.DestPath); + if (verbose) + { + Console.WriteLine($"Compressing {destPath}"); + } + archive.CreateEntryFromFile(file.SourcePath, destPath); + } + if (verbose) + { + Console.WriteLine($"Creating Data/"); } archive.CreateEntry("Data/"); + if (verbose) + { + Console.WriteLine($"Compressing NAPS2.Portable.exe"); + } archive.CreateEntryFromFile(portableExe, "NAPS2.Portable.exe"); + + Console.WriteLine(verbose ? $"Packaged zip archive: {zipPath}" : "Done."); } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/VersionHelper.cs b/NAPS2.Tools/Project/ProjectHelper.cs similarity index 62% rename from NAPS2.Tools/Project/VersionHelper.cs rename to NAPS2.Tools/Project/ProjectHelper.cs index 167aaa231..03a8e7fa4 100644 --- a/NAPS2.Tools/Project/VersionHelper.cs +++ b/NAPS2.Tools/Project/ProjectHelper.cs @@ -2,7 +2,7 @@ using System.Text.RegularExpressions; namespace NAPS2.Tools.Project; -public static class VersionHelper +public static class ProjectHelper { public static string GetProjectVersion(string projectName) { @@ -19,4 +19,15 @@ public static class VersionHelper } return version; } + + public static string GetDefaultProjectVersion() + { + return GetProjectVersion("NAPS2.App.WinForms"); + } + + public static string GetPackagePath(string ext, Platform platform, string? version = null) + { + version ??= GetProjectVersion("NAPS2.App.WinForms"); + return Path.Combine(Paths.Publish, version, $"naps2-{version}-{platform.PackageName()}.{ext}"); + } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/TestCommand.cs b/NAPS2.Tools/Project/TestCommand.cs index 22a64cc22..326baf473 100644 --- a/NAPS2.Tools/Project/TestCommand.cs +++ b/NAPS2.Tools/Project/TestCommand.cs @@ -5,7 +5,7 @@ public static class TestCommand public static int Run(TestOptions opts) { // TODO: Do we want to test on .net core too? - Cli.Run("dotnet", "test -f net462", new() + Cli.Run("dotnet", "test -f net462", opts.Verbose, new() { {"NAPS2_TEST_ROOT", Path.Combine(Paths.SolutionRoot, "NAPS2.App.Tests", "bin", "Debug", "net462")} }); diff --git a/NAPS2.Tools/Project/TestOptions.cs b/NAPS2.Tools/Project/TestOptions.cs index 4b3e3363b..debaba15a 100644 --- a/NAPS2.Tools/Project/TestOptions.cs +++ b/NAPS2.Tools/Project/TestOptions.cs @@ -5,4 +5,6 @@ namespace NAPS2.Tools.Project; [Verb("test", HelpText = "Runs the project tests")] public class TestOptions { + [Option('v', "verbose", Required = false, HelpText = "Show full output")] + public bool Verbose { get; set; } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/Verification/AppDriverRunner.cs b/NAPS2.Tools/Project/Verification/AppDriverRunner.cs index 391b77123..2c960f432 100644 --- a/NAPS2.Tools/Project/Verification/AppDriverRunner.cs +++ b/NAPS2.Tools/Project/Verification/AppDriverRunner.cs @@ -4,19 +4,21 @@ public class AppDriverRunner : IDisposable { private readonly Process _process; - public static AppDriverRunner Start() + public static AppDriverRunner Start(bool verbose) { - return new AppDriverRunner(); + return new AppDriverRunner(verbose); } - private AppDriverRunner() + private AppDriverRunner(bool verbose) { var path = @"C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe"; // TODO: Wait for successful starting and handle errors (e.g. if the dev doesn't have developer mode on) _process = Process.Start(new ProcessStartInfo { FileName = path, - UseShellExecute = false + UseShellExecute = false, + // TODO: Fix (use Cli.Run from a thread? Or similar) + // RedirectStandardOutput = !verbose }) ?? throw new Exception($"Could not start WinAppDriver: {path}"); } diff --git a/NAPS2.Tools/Project/Verification/ExeSetupVerifier.cs b/NAPS2.Tools/Project/Verification/ExeSetupVerifier.cs new file mode 100644 index 000000000..87855a720 --- /dev/null +++ b/NAPS2.Tools/Project/Verification/ExeSetupVerifier.cs @@ -0,0 +1,25 @@ +namespace NAPS2.Tools.Project.Verification; + +public static class ExeSetupVerifier +{ + public static void Verify(Platform platform, string version, bool verbose) + { + var exePath = ProjectHelper.GetPackagePath("exe", platform, version); + Console.WriteLine($"Starting exe installer: {exePath}"); + var process = Process.Start(new ProcessStartInfo + { + FileName = exePath, + Arguments = "/SILENT /CLOSEAPPLICATIONS" + }); + if (process == null) + { + throw new Exception($"Could not start installer: {exePath}"); + } + process.WaitForExit(); + + var pfVar = platform == Platform.Win64 ? "%PROGRAMFILES%" : "%PROGRAMFILES(X86)%"; + var pfPath = Environment.ExpandEnvironmentVariables(pfVar); + Verifier.RunVerificationTests(Path.Combine(pfPath, "NAPS2"), verbose); + Console.WriteLine(verbose ? $"Verified exe installer: {exePath}" : "Done."); + } +} \ No newline at end of file diff --git a/NAPS2.Tools/Project/Verification/Verifier.cs b/NAPS2.Tools/Project/Verification/Verifier.cs new file mode 100644 index 000000000..108a8279b --- /dev/null +++ b/NAPS2.Tools/Project/Verification/Verifier.cs @@ -0,0 +1,17 @@ +namespace NAPS2.Tools.Project.Verification; + +public static class Verifier +{ + public static void RunVerificationTests(string testRoot, bool verbose) + { + Console.WriteLine($"Running verification tests in: {testRoot}"); + Cli.Run("dotnet", "test NAPS2.App.Tests -f net462", verbose, new() + { + { "NAPS2_TEST_ROOT", testRoot } + }); + if (verbose) + { + Console.WriteLine($"Ran verification tests in: {testRoot}"); + } + } +} \ No newline at end of file diff --git a/NAPS2.Tools/Project/Verification/VerifyCommand.cs b/NAPS2.Tools/Project/Verification/VerifyCommand.cs index 86e51a6a1..d7ca70489 100644 --- a/NAPS2.Tools/Project/Verification/VerifyCommand.cs +++ b/NAPS2.Tools/Project/Verification/VerifyCommand.cs @@ -5,14 +5,12 @@ public class VerifyCommand public static int Run(VerifyOptions opts) { var platform = PlatformHelper.FromOption(opts.Platform, Platform.Win64); + var version = ProjectHelper.GetDefaultProjectVersion(); - var version = VersionHelper.GetProjectVersion("NAPS2.App.WinForms"); - var basePath = Path.Combine(Paths.Publish, version, $"naps2-{version}-{platform.PackageName()}"); - - using var appDriverRunner = AppDriverRunner.Start(); + using var appDriverRunner = AppDriverRunner.Start(opts.Verbose); if (opts.What == "exe" || opts.What == "all") { - // ExeSetupVerifier.Verify() + ExeSetupVerifier.Verify(platform, version, opts.Verbose); } if (opts.What == "msi" || opts.What == "all") { @@ -20,7 +18,7 @@ public class VerifyCommand } if (opts.What == "zip" || opts.What == "all") { - ZipArchiveVerifier.Verify(basePath + ".zip", opts.NoCleanup); + ZipArchiveVerifier.Verify(platform, version, opts.NoCleanup, opts.Verbose); } return 0; } diff --git a/NAPS2.Tools/Project/Verification/VerifyOptions.cs b/NAPS2.Tools/Project/Verification/VerifyOptions.cs index 65380ccd0..b39c4f75f 100644 --- a/NAPS2.Tools/Project/Verification/VerifyOptions.cs +++ b/NAPS2.Tools/Project/Verification/VerifyOptions.cs @@ -13,4 +13,7 @@ public class VerifyOptions [Option("nocleanup", Required = false, HelpText = "Skip cleaning up temp files")] public bool NoCleanup { get; set; } + + [Option('v', "verbose", Required = false, HelpText = "Show full output")] + public bool Verbose { get; set; } } \ No newline at end of file diff --git a/NAPS2.Tools/Project/Verification/ZipArchiveVerifier.cs b/NAPS2.Tools/Project/Verification/ZipArchiveVerifier.cs index 5853fca5b..91dd16f19 100644 --- a/NAPS2.Tools/Project/Verification/ZipArchiveVerifier.cs +++ b/NAPS2.Tools/Project/Verification/ZipArchiveVerifier.cs @@ -4,17 +4,17 @@ namespace NAPS2.Tools.Project.Verification; public class ZipArchiveVerifier { - public static void Verify(string zipPath, bool noCleanup) + public static void Verify(Platform platform, string version, bool verbose, bool noCleanup) { + var zipPath = ProjectHelper.GetPackagePath("zip", platform, version); + Console.WriteLine($"Extracting zip archive: {zipPath}"); // TODO: We probably want other commands to use unique paths too var extractPath = Path.Combine(Paths.SetupObj, Path.GetRandomFileName()); try { ZipFile.ExtractToDirectory(zipPath, extractPath); - Cli.Run("dotnet", "test NAPS2.App.Tests -f net462", new() - { - { "NAPS2_TEST_ROOT", Path.Combine(extractPath, "App") } - }); + Verifier.RunVerificationTests(Path.Combine(extractPath, "App"), verbose); + Console.WriteLine(verbose ? $"Verified zip archive: {zipPath}" : "Done."); } finally {