diff --git a/src/Velopack.Packaging/Exe.cs b/src/Velopack.Packaging/Exe.cs index 1d711d00..8cbc7e26 100644 --- a/src/Velopack.Packaging/Exe.cs +++ b/src/Velopack.Packaging/Exe.cs @@ -54,11 +54,14 @@ public static class Exe FileName = fileName, Arguments = args, UseShellExecute = false, - WorkingDirectory = workDir, + WorkingDirectory = workDir ?? "", CreateNoWindow = true, }; using var process = Process.Start(psi); + if (process == null) + throw new Exception("Failed to start process"); + process.WaitForExit(); var stdout = Utility.Retry(() => File.ReadAllText(outputFile).Trim(), 10, 1000); @@ -67,6 +70,32 @@ public static class Exe return result.Item2; } + public static void RunHostedCommandNoWait(string command, string workDir = null) + { + using var _1 = Utility.GetTempFileName(out var outputFile); + File.Create(outputFile).Close(); + + var fileName = "cmd.exe"; + var args = $"/S /C \"{command} >> \"{outputFile}\" 2>&1\""; + + if (!VelopackRuntimeInfo.IsWindows) { + fileName = "/bin/bash"; + string escapedCommand = command.Replace("'", "'\\''"); + args = $"-c '{escapedCommand} >> \"{outputFile}\" 2>&1'"; + } + + var psi = new ProcessStartInfo { + FileName = fileName, + Arguments = args, + UseShellExecute = false, + WorkingDirectory = workDir ?? "", + CreateNoWindow = true, + }; + + if (Process.Start(psi) == null) + throw new Exception("Failed to start process"); + } + public static string InvokeAndThrowIfNonZero(string exePath, IEnumerable args, string workingDir, IDictionary envVar = null) { var result = InvokeProcess(exePath, args, workingDir, CancellationToken.None, envVar); @@ -76,39 +105,37 @@ public static class Exe public static (int ExitCode, string StdOutput) InvokeProcess(ProcessStartInfo psi, CancellationToken ct) { - var pi = Process.Start(psi); - var process = new Process(); process.StartInfo = psi; var sOut = new StringBuilder(); var sErr = new StringBuilder(); - pi.OutputDataReceived += (sender, e) => { + process.OutputDataReceived += (sender, e) => { if (e.Data != null) sOut.AppendLine(e.Data); }; - pi.ErrorDataReceived += (sender, e) => { + process.ErrorDataReceived += (sender, e) => { if (e.Data != null) sErr.AppendLine(e.Data); }; - if (!pi.Start()) + if (!process.Start()) throw new Exception("Failed to start process"); - pi.BeginOutputReadLine(); - pi.BeginErrorReadLine(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); while (!ct.IsCancellationRequested) { - if (pi.WaitForExit(500)) break; + if (process.WaitForExit(500)) break; } - if (ct.IsCancellationRequested && !pi.HasExited) { - pi.Kill(); + if (ct.IsCancellationRequested && !process.HasExited) { + process.Kill(); ct.ThrowIfCancellationRequested(); } var all = (sOut.ToString().Trim()) + Environment.NewLine + (sOut.ToString().Trim()); - return (pi.ExitCode, all.Trim()); + return (process.ExitCode, all.Trim()); } public static (int ExitCode, string StdOutput, string Command) InvokeProcess(string fileName, IEnumerable args, string workingDirectory, CancellationToken ct = default, diff --git a/test/Velopack.Packaging.Tests/CrossCompile.cs b/test/Velopack.Packaging.Tests/CrossCompile.cs index ef792c90..165d79c6 100644 --- a/test/Velopack.Packaging.Tests/CrossCompile.cs +++ b/test/Velopack.Packaging.Tests/CrossCompile.cs @@ -46,8 +46,9 @@ public class CrossCompile public void RunCrossAppLinux(string artifactId) { using var logger = _output.BuildLoggerFor(); - Skip.If(String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("VELOPACK_CROSS_ARTIFACTS")), - "VELOPACK_CROSS_ARTIFACTS not set"); + Skip.If( + String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("VELOPACK_CROSS_ARTIFACTS")), + "VELOPACK_CROSS_ARTIFACTS not set"); Skip.IfNot(VelopackRuntimeInfo.IsLinux, "AppImage's can only run on Linux"); var artifactsDir = PathHelper.GetTestRootPath("artifacts"); @@ -68,7 +69,8 @@ public class CrossCompile public void RunCrossAppWindows(string artifactId) { using var logger = _output.BuildLoggerFor(); - Skip.If(String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("VELOPACK_CROSS_ARTIFACTS")), + Skip.If( + String.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("VELOPACK_CROSS_ARTIFACTS")), "VELOPACK_CROSS_ARTIFACTS not set"); Skip.IfNot(VelopackRuntimeInfo.IsWindows, "PE files can only run on Windows"); @@ -85,7 +87,8 @@ public class CrossCompile Utility.DeleteFileOrDirectoryHard(appRoot); Assert.False(File.Exists(appExe)); - var installOutput = Exe.InvokeAndThrowIfNonZero(artifactPath, new[] { "--silent" }, null); + + var installOutput = Exe.InvokeAndThrowIfNonZero(artifactPath, new[] { "--silent", "--nocolor" }, null); logger.LogInformation(installOutput); Assert.True(File.Exists(appExe)); @@ -94,7 +97,11 @@ public class CrossCompile logger.LogInformation(output); Assert.EndsWith(artifactId, output.Trim()); - Exe.RunHostedCommand($"\"{appUpdate}\" --uninstall --silent"); + var uninstallOutput = Exe.RunHostedCommand($"\"{appUpdate}\" --uninstall --silent --nocolor"); + logger.LogInformation(uninstallOutput); + Assert.False(File.Exists(appExe)); + Assert.True(File.Exists(Path.Combine(appRoot, ".dead"))); + Utility.DeleteFileOrDirectoryHard(appRoot); } -} +} \ No newline at end of file