diff --git a/src/dotnet-releaser/DevHosting/GitHubDevHosting.cs b/src/dotnet-releaser/DevHosting/GitHubDevHosting.cs index 5e08ae3..2316f1c 100644 --- a/src/dotnet-releaser/DevHosting/GitHubDevHosting.cs +++ b/src/dotnet-releaser/DevHosting/GitHubDevHosting.cs @@ -10,6 +10,7 @@ using DotNetReleaser.Changelog; using DotNetReleaser.Configuration; using DotNetReleaser.Logging; +using DotNetReleaser.Runners; using NuGet.Versioning; using Octokit; @@ -90,18 +91,87 @@ public async Task CreateOrUpdateGist(string gistId, string fileName, string cont _log.Info($"No need to update the gist {gistId} as the content is the same."); return; } + + // Update the file + GistUpdate gistUpdate = new GistUpdate(); + //gistUpdate.Files.Add(); + gistUpdate.Files.Add(fileName, new GistFileUpdate() { NewFileName = fileName, Content = content }); + await _client.Gist.Edit(gistId, gistUpdate); } else { - _log.Warn($"Cannot update gist {gistId} as it does not contain the required file {fileName}"); - return; - } + if (!gist.GitPushUrl.StartsWith("https://")) + { + _log.Warn($"The gist URL {gist.GitPushUrl} is not a standard gist URL and cannot be updated."); + return; + } + + var uri = new Uri(gist.GitPushUrl); + var gitCloneUrl = $"git@{uri.Host}:{uri.PathAndQuery.TrimStart('/')}"; + + var gistTempDirectory = Directory.CreateTempSubdirectory("dotnet-releaser-gist"); + try + { + var result = await GitRunner.Run("clone", new[] { gitCloneUrl, gistTempDirectory.FullName }); + + if (result.HasErrors) + { + _log.Error($"Unable to clone the gist {gistId} to {gistTempDirectory.FullName}. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } + + var gistFile = Path.Combine(gistTempDirectory.FullName, fileName); + await File.WriteAllTextAsync(gistFile, content); + + result = await GitRunner.Run("config", new[] { "--local", "user.email", "action@github.com" }, gistTempDirectory.FullName); + if (result.HasErrors) + { + _log.Error($"Unable to set the user.email for the git repository. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } + + result = await GitRunner.Run("config", new[] { "--local", "user.name", "GitHub Action" }, gistTempDirectory.FullName); + if (result.HasErrors) + { + _log.Error($"Unable to set the user.name for the git repository. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } - // Update the file - GistUpdate gistUpdate = new GistUpdate(); - //gistUpdate.Files.Add(); - gistUpdate.Files.Add(fileName, new GistFileUpdate() { NewFileName = fileName, Content = content }); - await _client.Gist.Edit(gistId, gistUpdate); + result = await GitRunner.Run("add", new[] { "." }, gistTempDirectory.FullName); + if (result.HasErrors) + { + _log.Error($"Unable to add the file {fileName} to the git repository. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } + + result = await GitRunner.Run("commit", new[] { "-m", $"Upload new file {fileName}" }, gistTempDirectory.FullName); + if (result.HasErrors) + { + _log.Error($"Unable to commit the file {fileName} to the git repository. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } + + result = await GitRunner.Run("push", Array.Empty(), gistTempDirectory.FullName); + if (result.HasErrors) + { + _log.Error($"Unable to push the file {fileName} to the git repository. ExitCode: {result.CommandResult.ExitCode}, Output: {result.Output}"); + return; + } + } + finally + { + try + { + gistTempDirectory.Delete(true); + } + catch + { + // ignore + } + } + + _log.Warn($"New file uploaded to gist {gistId}"); + } } private async Task> GetAllReleaseTagsImpl(string user, string repo, string tagPrefix) diff --git a/src/dotnet-releaser/Runners/DotNetRunner.cs b/src/dotnet-releaser/Runners/DotNetRunner.cs index 13b91f3..5738ba2 100644 --- a/src/dotnet-releaser/Runners/DotNetRunner.cs +++ b/src/dotnet-releaser/Runners/DotNetRunner.cs @@ -8,7 +8,7 @@ public DotNetRunner(string command) : base(command) { } - public async Task Run() + public async Task Run() { return await RunImpl(); } diff --git a/src/dotnet-releaser/Runners/DotNetRunnerBase.cs b/src/dotnet-releaser/Runners/DotNetRunnerBase.cs index 499a7a4..edc8cb2 100644 --- a/src/dotnet-releaser/Runners/DotNetRunnerBase.cs +++ b/src/dotnet-releaser/Runners/DotNetRunnerBase.cs @@ -9,7 +9,7 @@ namespace DotNetReleaser.Runners; -public record DotNetResult(CommandResult CommandResult, string CommandLine, string Output) +public record CommandResulExtended(CommandResult CommandResult, string CommandLine, string Output) { public bool HasErrors => CommandResult.ExitCode != 0; } @@ -43,7 +43,7 @@ protected DotNetRunnerBase(string command) protected virtual IReadOnlyDictionary ComputeProperties() => Properties; - protected async Task RunImpl() + protected async Task RunImpl() { return await Run(Command, ComputeArguments(), ComputeProperties(), WorkingDirectory); } @@ -83,7 +83,7 @@ private static string GetPropertyValueAsString(object value) return value.ToString() ?? string.Empty; } - private async Task Run(string command, IEnumerable args, IReadOnlyDictionary? properties = null, string? workingDirectory = null) + private async Task Run(string command, IEnumerable args, IReadOnlyDictionary? properties = null, string? workingDirectory = null) { var stdOutAndErrorBuffer = new StringBuilder(); @@ -102,7 +102,7 @@ private async Task Run(string command, IEnumerable args, I RunAfterStart?.Invoke(); var result = await wrap.ConfigureAwait(false); - return new DotNetResult(result, $"dotnet {arguments}",stdOutAndErrorBuffer.ToString()); + return new CommandResulExtended(result, $"dotnet {arguments}",stdOutAndErrorBuffer.ToString()); } protected virtual void Dispose(bool disposing) diff --git a/src/dotnet-releaser/Runners/GitRunner.cs b/src/dotnet-releaser/Runners/GitRunner.cs new file mode 100644 index 0000000..d3c8616 --- /dev/null +++ b/src/dotnet-releaser/Runners/GitRunner.cs @@ -0,0 +1,39 @@ +// Copyright (c) Alexandre Mutel. All rights reserved. +// Licensed under the BSD-Clause 2 license. +// See license.txt file in the project root for full license information. + +using CliWrap; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using System; +using CliWrap.Builders; + +namespace DotNetReleaser.Runners; + +public static class GitRunner +{ + public static async Task Run(string command, IEnumerable args, string? workingDirectory = null) + { + var stdOutAndErrorBuffer = new StringBuilder(); + + var argsBuilder = new ArgumentsBuilder(); + argsBuilder.Add(command); + foreach (var arg in args) + { + argsBuilder.Add(arg); + } + var arguments = argsBuilder.Build(); + + var wrap = Cli.Wrap("git") + .WithArguments(arguments) + .WithWorkingDirectory(workingDirectory ?? Environment.CurrentDirectory) + .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutAndErrorBuffer)) + .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdOutAndErrorBuffer)) + .WithValidation(CommandResultValidation.None) + .ExecuteAsync(); + + var result = await wrap.ConfigureAwait(false); + return new CommandResulExtended(result, $"git {arguments}", stdOutAndErrorBuffer.ToString()); + } +} diff --git a/src/dotnet-releaser/Runners/MSBuildProgram.cs b/src/dotnet-releaser/Runners/MSBuildProgram.cs index 9198e0c..6e28a26 100644 --- a/src/dotnet-releaser/Runners/MSBuildProgram.cs +++ b/src/dotnet-releaser/Runners/MSBuildProgram.cs @@ -10,7 +10,7 @@ namespace DotNetReleaser.Runners; -public record MSBuildResult(CommandResult CommandResult, string CommandLine, string Output, Dictionary> TargetOutputs) : DotNetResult(CommandResult, CommandLine, Output); +public record MSBuildResult(CommandResult CommandResult, string CommandLine, string Output, Dictionary> TargetOutputs) : CommandResulExtended(CommandResult, CommandLine, Output); public class MSBuildRunner : DotNetRunnerBase {