diff --git a/aeqw89.tools.Publish/Program.cs b/aeqw89.tools.Publish/Program.cs index 6dec9d3..046afa2 100644 --- a/aeqw89.tools.Publish/Program.cs +++ b/aeqw89.tools.Publish/Program.cs @@ -1,5 +1,6 @@ using System.Collections; using System.Diagnostics; +using System.Text; using Renci.SshNet; using Spectre.Console; using aeqw89.xml.ProjectFile; @@ -26,6 +27,8 @@ public static class Program { public static Dictionary Flags { get; set; } public static bool Verbose { get; set; } = false; + public static List RestoreActions { get; set; } = []; + public static void ReadArgs(string[] args) { if (args.Length < 1) { ShowError(Exceptions.missing_mode.EscapeMarkup()); @@ -108,362 +111,442 @@ public static class Program { public static async Task Main(string[] args) { ReadArgs(args); + Console.CancelKeyPress += (sender, eventArgs) => { + RestoreActions.ForEach(x => x()); + }; + string packageId = ""; string version = ""; + int destinationsProcessed = 0; - var result = AnsiConsole.Status() - .Spinner(Spinner.Known.Dots) - .Start("Preparing project", ctx => { - ctx.Status = "Locating project file"; - if (!ProjectFile.TryLoad(Environment.CurrentDirectory, out var projectFile, out var error)) { - ShowError(error.EscapeMarkup()); - return false; - } - - packageId = projectFile.GetPackageId(); - - try { - projectFile.Backup(); - Console.CancelKeyPress += (sender, eventArgs) => { - projectFile.Restore(); - AnsiConsole.MarkupLine("[yellow]Restored project file from backup.[/]"); - }; - - if (Verbose) - AnsiConsole.WriteLine( - $"Created project file backup at {projectFile.GetDefaultBackupLocation()}"); - - ctx.Status = "Repairing project file"; - if (!Flags.ContainsKey("--skip-repair")) - if (!projectFile.TryRepair(out error)) { - ShowError(error.EscapeMarkup()); - projectFile.Restore(); - return false; - } - - if (Mode == Mode.Increment && !Flags.ContainsKey("--simulate")) { - int delta = 1; - if (Flags.TryGetValue("--delta", out var deltaStrings)) { - if (deltaStrings.Length != 1) { - ShowError(Exceptions.flag_parameter_length_incorrect.EscapeMarkup(), "--delta", 1, - deltaStrings.Length); - projectFile.Restore(); - ShowHelp(); - return false; - } - - if (!int.TryParse(deltaStrings[0], out delta)) { - ShowError(Exceptions.flag_parameter_type_incorrect.EscapeMarkup(), "--delta", 0, nameof(Int32), - deltaStrings[0]); - projectFile.Restore(); - ShowHelp(); - return false; - } - } - - ctx.Status = "Updating version"; - var version = projectFile.GetVersion(); - version = ChangeVersion(version, - Target == IncrementTarget.Patch ? delta : -1, - Target == IncrementTarget.Minor ? delta : -1, - Target == IncrementTarget.Major ? delta : -1, - (x, y) => y < 0 ? x : x + y); - - projectFile.SetVersion(version); + try { + var result = AnsiConsole.Status() + .Spinner(Spinner.Known.Dots) + .Start("Preparing project", ctx => { + ctx.Status = "Locating project file"; + if (!ProjectFile.TryLoad(Environment.CurrentDirectory, out var projectFile, out var error)) { + ShowError(error.EscapeMarkup()); + return false; } - } - catch (Exception e) { - ShowError(Exceptions.generic_error.EscapeMarkup(), e.ToString().EscapeMarkup()); - projectFile.Restore(); - return false; - } - version = projectFile.GetVersion(); + packageId = projectFile.GetPackageId(); - if (!Flags.ContainsKey("--simulate")) { try { - var packageReferences = projectFile.GetPackageReferences(); - foreach (var reference in packageReferences.Where(x => !projectFile.IsTransitive(x))) - projectFile.SetPrivateAssets(reference, PrivateAssetsValue.All); - foreach (var reference in packageReferences.Where(x => projectFile.IsTransitive(x))) - projectFile.RemovePackage(reference); + projectFile.Backup(); + RestoreActions.Add(() => { + projectFile.Restore(); + AnsiConsole.MarkupLine("[yellow]Restored project file from backup.[/]"); + }); - HashSet visited = []; - var projectReferences = new Queue(projectFile.GetProjectReferences().Cast()); - while (projectReferences.Count != 0) { - var reference = projectReferences.Dequeue(); - visited.Add(reference.Include); - if (Verbose) - AnsiConsole.WriteLine($"Processing project reference {reference.Include} out of {visited.Count} so far"); - - projectFile.SetPrivateAssets(reference, PrivateAssetsValue.All); - string pathToReferencedProjectFile = projectFile.GetAbsoluteIncludePath(reference); - if (!ProjectFile.TryLoad(pathToReferencedProjectFile, out var referencedProjectFile, - out error)) { + if (Verbose) + AnsiConsole.WriteLine( + $"Created project file backup at {projectFile.GetDefaultBackupLocation()}"); + + ctx.Status = "Repairing project file"; + if (!Flags.ContainsKey("--skip-repair")) + if (!projectFile.TryRepair(out error)) { ShowError(error.EscapeMarkup()); projectFile.Restore(); return false; } - var referencedPackageReferences = referencedProjectFile.GetPackageReferences(); - foreach (var package in referencedPackageReferences) { - if (Verbose) - AnsiConsole.WriteLine($"Hoisting package {package.Include} from {pathToReferencedProjectFile}"); - var hoisted = projectFile.AddPackage(package); - projectFile.SetTransitive(hoisted, true); - projectFile.SetPrivateAssets(hoisted, PrivateAssetsValue.None); - referencedProjectFile.SetPrivateAssets(package, PrivateAssetsValue.All); + if (Mode == Mode.Increment && !Flags.ContainsKey("--simulate")) { + int delta = 1; + if (Flags.TryGetValue("--delta", out var deltaStrings)) { + if (deltaStrings.Length != 1) { + ShowError(Exceptions.flag_parameter_length_incorrect.EscapeMarkup(), "--delta", 1, + deltaStrings.Length); + projectFile.Restore(); + ShowHelp(); + return false; + } + + if (!int.TryParse(deltaStrings[0], out delta)) { + ShowError(Exceptions.flag_parameter_type_incorrect.EscapeMarkup(), "--delta", 0, + nameof(Int32), + deltaStrings[0]); + projectFile.Restore(); + ShowHelp(); + return false; + } } - var referencedProjectReferences = referencedProjectFile.GetProjectReferences(); - foreach (var project in referencedProjectReferences) { - if (!visited.Contains(project.Include)) - projectReferences.Enqueue(project); - } + ctx.Status = "Updating version"; + var version = projectFile.GetVersion(); + version = ChangeVersion(version, delta, Target ?? IncrementTarget.Patch); + + projectFile.SetVersion(version); } - } catch (Exception e) { + } + catch (Exception e) { ShowError(Exceptions.generic_error.EscapeMarkup(), e.ToString().EscapeMarkup()); - projectFile.Restore(); + RestoreActions.ForEach(x => x()); return false; } + + version = projectFile.GetVersion(); + + if (!Flags.ContainsKey("--simulate")) { + try { + var packageReferences = projectFile.GetPackageReferences(); + foreach (var reference in packageReferences.Where(x => !projectFile.IsTransitive(x))) + projectFile.SetPrivateAssets(reference, PrivateAssetsValue.All); + foreach (var reference in packageReferences.Where(x => projectFile.IsTransitive(x))) + projectFile.RemovePackage(reference); + + HashSet visited = []; + var projectReferences = new Queue(projectFile.GetProjectReferences().Cast()); + while (projectReferences.Count != 0) { + var reference = projectReferences.Dequeue(); + visited.Add(reference.Include); + + if (Verbose) + AnsiConsole.WriteLine( + $"Processing project reference {reference.Include} out of {visited.Count} so far"); + + projectFile.SetPrivateAssets(reference, PrivateAssetsValue.All); + string pathToReferencedProjectFile = projectFile.GetAbsoluteIncludePath(reference); + if (!ProjectFile.TryLoad(pathToReferencedProjectFile, out var referencedProjectFile, + out error)) { + ShowError(error.EscapeMarkup()); + RestoreActions.ForEach(x => x()); + return false; + } + + var referencedPackageReferences = referencedProjectFile.GetPackageReferences(); + foreach (var package in referencedPackageReferences) { + if (Verbose) + AnsiConsole.WriteLine( + $"Hoisting package {package.Include} from {pathToReferencedProjectFile}"); + var hoisted = projectFile.AddPackage(package); + projectFile.SetTransitive(hoisted, true); + projectFile.SetPrivateAssets(hoisted, PrivateAssetsValue.None); + referencedProjectFile.SetPrivateAssets(package, PrivateAssetsValue.All); + } + + var referencedProjectReferences = referencedProjectFile.GetProjectReferences(); + foreach (var project in referencedProjectReferences) { + if (!visited.Contains(project.Include)) + projectReferences.Enqueue(project); + } + } + } + catch (Exception e) { + ShowError(Exceptions.generic_error.EscapeMarkup(), e.ToString().EscapeMarkup()); + RestoreActions.ForEach(x => x()); + return false; + } + } + + projectFile.Save(); + return true; + }); + + if (!result) { + return; + } + + var outDir = Path.GetRandomFileName(); + RestoreActions.Add(() => { + try { + if (!Directory.Exists(outDir)) return; + Directory.Delete(outDir, true); + AnsiConsole.MarkupLine("[yellow]Cleaned up temporary directory[/]"); + } + catch (Exception e) { + ShowError(string.Format(Exceptions.failed_to_clean_up.EscapeMarkup(), outDir.EscapeMarkup(), + e.ToString().EscapeMarkup())); } - - projectFile.Save(); - return true; }); - if (!result) { - return; - } - - var outDir = Path.GetRandomFileName(); - Console.CancelKeyPress += (sender, eventArgs) => { - try { - Directory.Delete(outDir, true); - AnsiConsole.MarkupLine("[yellow]Cleaned up temporary directory[/]"); - } - catch (Exception e) { - ShowError(string.Format(Exceptions.failed_to_clean_up.EscapeMarkup(), outDir.EscapeMarkup(), - e.ToString().EscapeMarkup())); - } - }; - - string processError = ""; - var exitCode = await AnsiConsole.Status() - .Spinner(Spinner.Known.Dots) - .StartAsync("Creating package with 'dotnet pack' ", async ctx => { - var p = Process.Start(new ProcessStartInfo() { - FileName = "dotnet", - Arguments = $"pack -o {outDir}", - WorkingDirectory = Environment.CurrentDirectory, - UseShellExecute = Verbose, - RedirectStandardOutput = !Verbose, - RedirectStandardError = !Verbose - }); - - CancellationTokenSource cts = new CancellationTokenSource(); - p?.ErrorDataReceived += (sender, eventArgs) => { - cts.Cancel(); - }; - p?.OutputDataReceived += (sender, eventArgs) => { - if (eventArgs.Data?.ToLower().Contains("press any key") == true) - cts.Cancel(); - }; - - await (p?.WaitForExitAsync(cts.Token) ?? Task.CompletedTask); - processError = p?.StandardError?.ReadToEnd() ?? ""; - return p?.ExitCode ?? -1; - }); - - if (exitCode != 0) { - ShowError(processError.EscapeMarkup()); - ShowError(Exceptions.dotnet_pack_failure.EscapeMarkup(), exitCode); - return; - } - - var package = Directory.GetFiles(outDir, "*.nupkg").FirstOrDefault(); - if (package == null) { - ShowError(Exceptions.generic_error.EscapeMarkup()); - return; - } - - var inMemory = await File.ReadAllBytesAsync(package); - var size = new FileInfo(package).Length; - const long bufferSize = 80 * 1024; // 80 KB - try { - await AnsiConsole.Progress() - .AutoClear(true) - .HideCompleted(false) - .Columns(new ProgressColumn[] { - new TaskDescriptionColumn(), - new ProgressBarColumn() - .RemainingStyle(Style.Parse("dim gray slowblink")) - .CompletedStyle(Style.Parse("green strikethrough")) - .FinishedStyle("green strikethrough"), - new DownloadedColumn(), - new RemainingTimeColumn(), - new TransferSpeedColumn(), - }) - .StartAsync(async ctx => { - await Parallel.ForEachAsync(Destinations, new ParallelOptions() { - MaxDegreeOfParallelism = Environment.ProcessorCount, - }, async (dest, ct) => { - using var reader = new MemoryStream(inMemory); - var task = ctx.AddTask(dest, new ProgressTaskSettings() { - MaxValue = size - }); - - if (dest.StartsWith("local-")) { - var name = dest[("local-".Length)..]; - var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), - name, Path.GetFileName(package)); - if (!Directory.Exists(Path.GetDirectoryName(path))) - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - await using var writer = File.OpenWrite(path); - var buffer = new byte[bufferSize]; - int read; - do { - read = await reader.ReadAsync(buffer, ct); - writer.Write(buffer, 0, read); - task.Increment(read); - } while (read > 0); - } - - else if (dest.StartsWith("cloud-")) { - var name = dest[("cloud-".Length)..]; - var connectionTask = ctx.AddTaskBefore($"Preparing cloud-{name}", new ProgressTaskSettings() { - MaxValue = 100 - }, task); - - if (!SshHosts.TryGetHost(name, out var host)) { - ShowError(Exceptions.cloud_host_not_found.EscapeMarkup(), name); - return; - } - - var connectionInfo = SshHosts.GetConnection(name); - using var sshClient = new SshClient(connectionInfo); - if (!sshClient.IsConnected) - await sshClient.ConnectAsync(ct); - connectionTask.Increment(33); - - var winC = sshClient.RunCommand("cmd /c ver"); - var othC = sshClient.RunCommand("uname -s"); - - var os = (winC.ExitStatus, othC.ExitStatus) switch { - (0, _) => "windows", - (_, 0) => "linux", - _ => "unknown" - }; - - string remoteDirectory; - string packageFileDirectory; - if (os == "windows") { - var userDirC = sshClient.RunCommand("cmd /c echo %USERPROFILE%"); - if (userDirC.ExitStatus != 0) { - ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, userDirC.Result); - return; - } - - var userDir = userDirC.Result.Trim(); - remoteDirectory = RemotePath.Combine(RemoteOs.Windows,userDir, "dotnet-packages"); - packageFileDirectory = RemotePath.Combine(RemoteOs.Windows, remoteDirectory, Path.GetFileName(package)); - - var mkdirC = sshClient.RunCommand($"cmd /c if not exist \"{remoteDirectory}\" mkdir \"{remoteDirectory}\""); - if (mkdirC.ExitStatus != 0) { - ShowError(Exceptions.failed_to_prepare_server_directory, remoteDirectory, name, os, mkdirC.Result); - return; - } - } - else if (os == "linux") { - var homeDirC = sshClient.RunCommand("printf %s \"$HOME\""); - if (homeDirC.ExitStatus != 0) { - ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, homeDirC.Result); - return; - } - var homeDir = homeDirC.Result.Trim(); // no CRLF on unix, but Trim() is safest - remoteDirectory = RemotePath.Combine(RemoteOs.Unix, homeDir, ".dotnet-packages"); - packageFileDirectory = RemotePath.Combine(RemoteOs.Unix, remoteDirectory, Path.GetFileName(package)); - - // Use -p and single quotes to handle spaces safely - var mkdirC = sshClient.RunCommand($"mkdir -p '{remoteDirectory}'"); - if (mkdirC.ExitStatus != 0) { - ShowError(Exceptions.failed_to_prepare_server_directory, remoteDirectory, name, os, mkdirC.Result); - return; - } - } - else { - ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, "Unsupported OS"); - return; - } - connectionTask.Increment(33); - - sshClient.Disconnect(); - - using var client = new SftpClient(connectionInfo); - if (!client.IsConnected) - await client.ConnectAsync(ct); - connectionTask.Increment(33); - connectionTask.StopTask(); - - await using var writer = client.OpenWrite(packageFileDirectory); - byte[] buffer = new byte[bufferSize]; - int read; - do { - read = await reader.ReadAsync(buffer, ct); - writer.Write(buffer, 0, read); - task.Increment(read); - } while (read > 0); - } - - else if (dest == "github") { - var p = Process.Start(new ProcessStartInfo() { - FileName = "dotnet", - Arguments = $"nuget push {package} --source github", - WorkingDirectory = Environment.CurrentDirectory, - UseShellExecute = false, - RedirectStandardOutput = !Verbose, - RedirectStandardError = !Verbose - }); - - var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); - p?.ErrorDataReceived += (sender, eventArgs) => { - cts.Cancel(); - }; - p?.OutputDataReceived += (sender, eventArgs) => { - if (eventArgs.Data?.ToLower().Contains("press any key") == true) - cts.Cancel(); - }; - - if (p == null) { - ShowError(Exceptions.generic_error.EscapeMarkup()); - } - - task.Increment(size / 2); - if (p != null) - await p.WaitForExitAsync(cts.Token); - processError += p?.StandardError?.ReadToEnd() ?? ""; - if (p?.ExitCode != 0) { - ShowError(processError.EscapeMarkup()); - ShowError(Exceptions.dotnet_nuget_push_failure, p?.ExitCode ?? -1); - } - task.Increment(size / 2); - } - - task.StopTask(); + string processError = ""; + var exitCode = await AnsiConsole.Status() + .Spinner(Spinner.Known.Dots) + .StartAsync("Creating package with 'dotnet pack' ", async ctx => { + var p = Process.Start(new ProcessStartInfo() { + FileName = "dotnet", + Arguments = $"pack -o {outDir}", + WorkingDirectory = Environment.CurrentDirectory, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true }); + + CancellationTokenSource cts = new CancellationTokenSource(); + StringBuilder errorLines = new(); + p?.ErrorDataReceived += (sender, eventArgs) => { + cts.Cancel(); + if (Verbose && eventArgs.Data != null) + AnsiConsole.WriteLine(eventArgs.Data); + }; + bool success = false; + p?.OutputDataReceived += (sender, eventArgs) => { + if (eventArgs.Data?.ToLower().Contains("press any key") == true) + cts.Cancel(); + if (Verbose && eventArgs.Data != null) + AnsiConsole.WriteLine(eventArgs.Data); + // Successfully created package 'C:\Users\qwsdc\source\repos\Beam\aeqw89.Beam\tozsxqaj.alp\Beam.1.0.0.nupkg'. + if (eventArgs.Data?.ToLower() + .Contains($"successfully created package '{Path.GetFullPath(outDir)}") == true) { + AnsiConsole.MarkupLine($"[bold]{eventArgs.Data}[/]"); + success = true; + } + }; + + p?.BeginOutputReadLine(); + p?.BeginErrorReadLine(); + + try { + await (p?.WaitForExitAsync(cts.Token) ?? Task.CompletedTask); + } + catch (TaskCanceledException) { + p?.Kill(); + } + + processError = errorLines.ToString().EscapeMarkup(); + return success == true ? 0 : p?.ExitCode ?? -1; }); - } - finally { + + if (exitCode != 0) { + ShowError(processError.EscapeMarkup()); + ShowError(Exceptions.dotnet_pack_failure.EscapeMarkup(), exitCode); + RestoreActions.ForEach(x => x()); + return; + } + + if (Verbose) + AnsiConsole.MarkupLine("Successfully created package with exit code [green]{0}[/]. Processing destinations.", exitCode); + + var package = Directory.GetFiles(outDir, "*.nupkg").FirstOrDefault(); + if (package == null) { + ShowError(Exceptions.generic_error.EscapeMarkup()); + RestoreActions.ForEach(x => x()); + return; + } + + var inMemory = await File.ReadAllBytesAsync(package); + var size = new FileInfo(package).Length; + const long bufferSize = 80 * 1024; // 80 KB try { - Directory.Delete(outDir, true); + await AnsiConsole.Progress() + .AutoClear(true) + .HideCompleted(false) + .Columns(new ProgressColumn[] { + new TaskDescriptionColumn(), + new ProgressBarColumn() + .RemainingStyle(Style.Parse("dim gray slowblink")) + .CompletedStyle(Style.Parse("green strikethrough")) + .FinishedStyle("green strikethrough"), + new DownloadedColumn(), + new RemainingTimeColumn(), + new TransferSpeedColumn(), + }) + .StartAsync(async ctx => { + await Parallel.ForEachAsync(Destinations, new ParallelOptions() { + MaxDegreeOfParallelism = Environment.ProcessorCount, + }, async (dest, ct) => { + using var reader = new MemoryStream(inMemory); + var task = ctx.AddTask(dest, new ProgressTaskSettings() { + MaxValue = size + }); + + if (dest.StartsWith("local-")) { + var name = dest[("local-".Length)..]; + var path = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), + name, Path.GetFileName(package)); + if (!Directory.Exists(Path.GetDirectoryName(path))) + Directory.CreateDirectory(Path.GetDirectoryName(path)!); + await using var writer = File.OpenWrite(path); + var buffer = new byte[bufferSize]; + int read; + do { + read = await reader.ReadAsync(buffer, ct); + writer.Write(buffer, 0, read); + task.Increment(read); + } while (read > 0); + } + + else if (dest.StartsWith("cloud-")) { + var name = dest[("cloud-".Length)..]; + var connectionTask = ctx.AddTaskBefore($"Preparing cloud-{name}", + new ProgressTaskSettings() { + MaxValue = 100 + }, task); + + if (!SshHosts.TryGetHost(name, out var host)) { + ShowError(Exceptions.cloud_host_not_found.EscapeMarkup(), name); + return; + } + + var connectionInfo = SshHosts.GetConnection(name); + using var sshClient = new SshClient(connectionInfo); + if (!sshClient.IsConnected) + await sshClient.ConnectAsync(ct); + connectionTask.Increment(33); + + var winC = sshClient.RunCommand("cmd /c ver"); + var othC = sshClient.RunCommand("uname -s"); + + var os = (winC.ExitStatus, othC.ExitStatus) switch { + (0, _) => "windows", + (_, 0) => "linux", + _ => "unknown" + }; + + string remoteDirectory; + string packageFileDirectory; + if (os == "windows") { + var userDirC = sshClient.RunCommand("cmd /c echo %USERPROFILE%"); + if (userDirC.ExitStatus != 0) { + ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, + userDirC.Result); + return; + } + + var userDir = userDirC.Result.Trim(); + remoteDirectory = RemotePath.Combine(RemoteOs.Windows, userDir, "dotnet-packages"); + packageFileDirectory = RemotePath.Combine(RemoteOs.Windows, remoteDirectory, + Path.GetFileName(package)); + + var mkdirC = sshClient.RunCommand( + $"cmd /c if not exist \"{remoteDirectory}\" mkdir \"{remoteDirectory}\""); + if (mkdirC.ExitStatus != 0) { + ShowError(Exceptions.failed_to_prepare_server_directory, remoteDirectory, name, + os, mkdirC.Result); + return; + } + } + else if (os == "linux") { + var homeDirC = sshClient.RunCommand("printf %s \"$HOME\""); + if (homeDirC.ExitStatus != 0) { + ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, + homeDirC.Result); + return; + } + + var homeDir = homeDirC.Result.Trim(); // no CRLF on unix, but Trim() is safest + remoteDirectory = RemotePath.Combine(RemoteOs.Unix, homeDir, ".dotnet-packages"); + packageFileDirectory = RemotePath.Combine(RemoteOs.Unix, remoteDirectory, + Path.GetFileName(package)); + + // Use -p and single quotes to handle spaces safely + var mkdirC = sshClient.RunCommand($"mkdir -p '{remoteDirectory}'"); + if (mkdirC.ExitStatus != 0) { + ShowError(Exceptions.failed_to_prepare_server_directory, remoteDirectory, name, + os, mkdirC.Result); + return; + } + } + else { + ShowError(Exceptions.failed_to_prepare_server_directory, "n/a", name, os, + "Unsupported OS"); + return; + } + + connectionTask.Increment(33); + + sshClient.Disconnect(); + + using var client = new SftpClient(connectionInfo); + if (!client.IsConnected) + await client.ConnectAsync(ct); + connectionTask.Increment(33); + connectionTask.StopTask(); + + await using var writer = client.OpenWrite(packageFileDirectory); + byte[] buffer = new byte[bufferSize]; + int read; + do { + read = await reader.ReadAsync(buffer, ct); + writer.Write(buffer, 0, read); + task.Increment(read); + } while (read > 0); + } + + else if (dest == "github") { + var p = Process.Start(new ProcessStartInfo() { + FileName = "dotnet", + Arguments = $"nuget push {package} --source github", + WorkingDirectory = Environment.CurrentDirectory, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true + }); + + var cts = CancellationTokenSource.CreateLinkedTokenSource(ct); + StringBuilder errorLines = new(); + p?.ErrorDataReceived += (sender, eventArgs) => { + cts.Cancel(); + if (Verbose && eventArgs.Data != null) + AnsiConsole.WriteLine(eventArgs.Data); + errorLines.Append(eventArgs.Data); + }; + p?.OutputDataReceived += (sender, eventArgs) => { + if (eventArgs.Data?.ToLower().Contains("press any key") == true) + cts.Cancel(); + if (Verbose && eventArgs.Data != null) + AnsiConsole.WriteLine(eventArgs.Data); + }; + + p?.BeginOutputReadLine(); + p?.BeginErrorReadLine(); + + if (p == null) { + ShowError(Exceptions.generic_error.EscapeMarkup()); + } + + task.Increment(size / 2); + if (p != null) + try { + await (p?.WaitForExitAsync(cts.Token) ?? Task.CompletedTask); + } + catch (TaskCanceledException) { + p?.Kill(); + } + + if (p?.ExitCode != 0) { + ShowError(errorLines.ToString().EscapeMarkup()); + ShowError(Exceptions.dotnet_nuget_push_failure, p?.ExitCode ?? -1); + task.StopTask(); + return; + } + + task.Increment(size / 2); + } + + Interlocked.Increment(ref destinationsProcessed); + task.StopTask(); + }); + }); } - catch (Exception e) { - ShowError(string.Format(Exceptions.failed_to_clean_up.EscapeMarkup(), outDir.EscapeMarkup(), e.ToString().EscapeMarkup())); + finally { + try { + Directory.Delete(outDir, true); + } + catch (Exception e) { + ShowError(string.Format(Exceptions.failed_to_clean_up.EscapeMarkup(), outDir.EscapeMarkup(), + e.ToString().EscapeMarkup())); + } } + + } + catch(Exception e) { + ShowError(Exceptions.generic_error.EscapeMarkup(), e.ToString().EscapeMarkup());; + RestoreActions.ForEach(x => x()); + } + + if (destinationsProcessed == 0) { + AnsiConsole.MarkupLine("[bold red]No destinations were processed. Reverting changes to project file.[/]"); + RestoreActions.ForEach(x => x()); + } + else { + AnsiConsole.MarkupLine("Completed processing of all destinations."); + AnsiConsole.MarkupLine( + "Example usage:\n\t ".EscapeMarkup(), packageId, + version); } - AnsiConsole.MarkupLine("Completed processing of all destinations."); - AnsiConsole.MarkupLine("Example usage:\n\t ".EscapeMarkup(), packageId, version); } /// @@ -476,8 +559,7 @@ public static class Program { /// A function that defines the adjustment operation to be performed on each version component. /// A new version string with the updated major, minor, and patch components, preserving any existing tag. /// Thrown if the version string is not in the correct format. - private static string ChangeVersion(string version, int patch, int minor, int major, - Func operation) { + private static string ChangeVersion(string version, int delta, IncrementTarget target) { string[] split = version.Split('.'); if (split.Length != 3) { throw new Exception(string.Format(Exceptions.version_string_not_formatted_correctly, version)); @@ -494,9 +576,23 @@ public static class Program { throw new Exception(string.Format(Exceptions.version_string_not_formatted_correctly, version)); int[] parsedVersion = split.Select(int.Parse).ToArray(); + switch (target) { + case IncrementTarget.Major: + parsedVersion[0] += delta; + parsedVersion[1] = 0; + parsedVersion[2] = 0; + break; + case IncrementTarget.Minor: + parsedVersion[1] += delta; + parsedVersion[2] = 0; + break; + case IncrementTarget.Patch: + parsedVersion[2] += delta; + break; + } return - $"{operation(parsedVersion[0], major)}.{operation(parsedVersion[1], minor)}.{operation(parsedVersion[2], patch)}{tag}"; + $"{parsedVersion[0]}.{parsedVersion[1]}.{parsedVersion[2]}{tag}"; } private static void ShowError(string message, params object[] args) { diff --git a/aeqw89.tools.Publish/ProjectFile.cs b/aeqw89.tools.Publish/ProjectFile.cs index 5b98a30..46a4ec0 100644 --- a/aeqw89.tools.Publish/ProjectFile.cs +++ b/aeqw89.tools.Publish/ProjectFile.cs @@ -106,6 +106,7 @@ internal class ProjectFile { } set("Version", "1.0.0"); + set("PackageVersion", "1.0.0"); set("Title", System.IO.Path.GetFileNameWithoutExtension(Path)); set("Authors", ""); set("Company", ""); @@ -213,5 +214,8 @@ internal class ProjectFile { public string GetVersion() => MainPropertyGroup.GetProperty("Version"); - public void SetVersion(string version) => MainPropertyGroup.SetProperty("Version", version); + public void SetVersion(string version) { + MainPropertyGroup.SetProperty("Version", version); + MainPropertyGroup.SetProperty("PackageVersion", version); + } }