From e8fd65a392e2c77946574adbd4ca4e010921ae5d Mon Sep 17 00:00:00 2001 From: Pascal Berger Date: Mon, 2 Aug 2021 11:42:21 +0200 Subject: [PATCH] (GH-183) Add option to use Git CLI --- .../BuildServers/BaseBuildServer.cs | 7 +- .../Context/Parameters/IssuesParameters.cs | 6 + .../Parameters/RepositoryInfoProviderType.cs | 20 ++++ .../Context/State/IssuesState.cs | 36 +++++- .../RepositoryInfo/IRepositoryInfoProvider.cs | 107 ++++++++++++++++++ 5 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/RepositoryInfoProviderType.cs create mode 100644 Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/IRepositoryInfoProvider.cs diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/BuildServers/BaseBuildServer.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/BuildServers/BaseBuildServer.cs index a62445fd..24ea8829 100644 --- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/BuildServers/BaseBuildServer.cs +++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/BuildServers/BaseBuildServer.cs @@ -1,8 +1,6 @@ using Cake.Core.IO; -using Cake.Git; using Cake.Issues; using System; -using System.Linq; namespace Cake.Frosting.Issues.Recipe { @@ -19,8 +17,7 @@ public virtual Uri DetermineRepositoryRemoteUrl( context.NotNull(nameof(context)); repositoryRootDirectory.NotNull(nameof(repositoryRootDirectory)); - var currentBranch = context.GitBranchCurrent(repositoryRootDirectory); - return new Uri(currentBranch.Remotes.Single(x => x.Name == "origin").Url); + return context.State.RepositoryInfo.GetRepositoryRemoteUrl(context, repositoryRootDirectory); } /// @@ -31,7 +28,7 @@ public virtual string DetermineCommitId( context.NotNull(nameof(context)); repositoryRootDirectory.NotNull(nameof(repositoryRootDirectory)); - return context.GitLogTip(repositoryRootDirectory).Sha; + return context.State.RepositoryInfo.GetCommitId(context, repositoryRootDirectory); } /// diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/IssuesParameters.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/IssuesParameters.cs index 97381b7a..0f441905 100644 --- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/IssuesParameters.cs +++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/IssuesParameters.cs @@ -7,6 +7,12 @@ namespace Cake.Frosting.Issues.Recipe /// public class IssuesParameters { + /// + /// Gets or sets how information about the Git repository should be determined. + /// Default value is RepositoryInfoProviderType.CakeGit. + /// + public RepositoryInfoProviderType RepositoryInfoProvider { get; set; } = RepositoryInfoProviderType.CakeGit; + /// /// Gets or sets the path to the output directory. /// A relative path will be relative to the current working directory. diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/RepositoryInfoProviderType.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/RepositoryInfoProviderType.cs new file mode 100644 index 00000000..ad91a8e1 --- /dev/null +++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/Parameters/RepositoryInfoProviderType.cs @@ -0,0 +1,20 @@ +namespace Cake.Frosting.Issues.Recipe +{ + /// + /// Supported ways to read repository information. + /// + public enum RepositoryInfoProviderType + { + /// + /// Read repository information using Cake.Git addin. + /// Requires system to be compatible with Cake.Git addin. + /// + CakeGit, + + /// + /// Read repository information using Git CLI. + /// Requires Git CLI to be available in path. + /// + Cli + } +} \ No newline at end of file diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs index 7f8ef378..b86ed33a 100644 --- a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs +++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/Context/State/IssuesState.cs @@ -3,7 +3,7 @@ using Cake.Common.Diagnostics; using Cake.Common.IO; using Cake.Core.IO; -using Cake.Git; +using Cake.Frosting.Issues.Recipe.RepositoryInfo; using Cake.Issues; using System; using System.Collections.Generic; @@ -47,6 +47,11 @@ public class IssuesState /// public FilePath SummaryIssuesReport { get; set; } + /// + /// Gets the provider to read information about the Git repository. + /// + public IRepositoryInfoProvider RepositoryInfo { get; } + /// /// Gets the build server under which the build is running. /// Returns null if running locally or on an unsupported build server. @@ -78,7 +83,9 @@ public IssuesState(IssuesContext context) this.BuildRootDirectory = context.MakeAbsolute(context.Directory("./")); context.Information("Build script root directory: {0}", this.BuildRootDirectory); - this.RepositoryRootDirectory = context.GitFindRootFromPath(this.BuildRootDirectory); + this.RepositoryInfo = DetermineRepositoryInfoProvider(context); + + this.RepositoryRootDirectory = this.RepositoryInfo.GetRepositoryRootDirectory(context, this.BuildRootDirectory); context.Information("Repository root directory: {0}", this.RepositoryRootDirectory); this.BuildServer = DetermineBuildServer(context); @@ -127,6 +134,31 @@ public void AddIssues(IEnumerable issues) this.issues.AddRange(issues); } + /// + /// Determines the repository info provider to use. + /// + /// The Cake context. + /// The repository info provider which should be used. + private static IRepositoryInfoProvider DetermineRepositoryInfoProvider(IssuesContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + switch (context.Parameters.RepositoryInfoProvider) + { + case RepositoryInfoProviderType.CakeGit: + context.Information("Using Cake.Git for providing repository information"); + return new CliRepositoryInfoProvider(); + case RepositoryInfoProviderType.Cli: + context.Information("Using Git CLI for providing repository information"); + return new CliRepositoryInfoProvider(); + default: + throw new NotImplementedException("Unsupported repository info provider"); + } + } + /// /// Determines the build server on which the build is running. /// diff --git a/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/IRepositoryInfoProvider.cs b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/IRepositoryInfoProvider.cs new file mode 100644 index 00000000..e753fc21 --- /dev/null +++ b/Cake.Frosting.Issues.Recipe/Cake.Frosting.Issues.Recipe/RepositoryInfo/IRepositoryInfoProvider.cs @@ -0,0 +1,107 @@ +using Cake.Common; +using Cake.Core; +using Cake.Core.IO; +using Cake.Git; +using Cake.Issues; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Cake.Frosting.Issues.Recipe.RepositoryInfo +{ + public interface IRepositoryInfoProvider + { + DirectoryPath GetRepositoryRootDirectory(IssuesContext context, DirectoryPath buildRootDirectory); + Uri GetRepositoryRemoteUrl(IssuesContext context, DirectoryPath repositoryRootDirectory); + string GetCommitId(IssuesContext context, DirectoryPath repositoryRootDirectory); + } + + public class CakeGitRepositoryInfoProvider : IRepositoryInfoProvider + { + /// + public DirectoryPath GetRepositoryRootDirectory(IssuesContext context, DirectoryPath buildRootDirectory) + { + context.NotNull(nameof(context)); + buildRootDirectory.NotNull(nameof(buildRootDirectory)); + + return context.GitFindRootFromPath(buildRootDirectory); + } + + /// + public Uri GetRepositoryRemoteUrl(IssuesContext context, DirectoryPath repositoryRootDirectory) + { + context.NotNull(nameof(context)); + repositoryRootDirectory.NotNull(nameof(repositoryRootDirectory)); + + var currentBranch = context.GitBranchCurrent(repositoryRootDirectory); + return new Uri(currentBranch.Remotes.Single(x => x.Name == "origin").Url); + } + + /// + public string GetCommitId(IssuesContext context, DirectoryPath repositoryRootDirectory) + { + context.NotNull(nameof(context)); + repositoryRootDirectory.NotNull(nameof(repositoryRootDirectory)); + + return context.GitLogTip(repositoryRootDirectory).Sha; + } + } + + public class CliRepositoryInfoProvider : IRepositoryInfoProvider + { + /// + public DirectoryPath GetRepositoryRootDirectory(IssuesContext context, DirectoryPath buildRootDirectory) + { + var result = this.GitCommand(context, buildRootDirectory, "rev-parse", "--show-toplevel"); + return new DirectoryPath(result.Single()); + } + + /// + public Uri GetRepositoryRemoteUrl(IssuesContext context, DirectoryPath repositoryRootDirectory) + { + return null; + } + + /// + public string GetCommitId(IssuesContext context, DirectoryPath repositoryRootDirectory) + { + return string.Empty; + } + + private IEnumerable GitCommand( + ICakeContext context, + DirectoryPath repositoryRootFolder, + params string[] arguments) + { + if (!arguments.Any()) + { + throw new ArgumentOutOfRangeException(nameof(arguments)); + } + + var gitArguments = string.Join(" ", arguments); + + var exitCode = context.StartProcess( + "git", + new ProcessSettings + { + Arguments = gitArguments, + WorkingDirectory = repositoryRootFolder.FullPath, + RedirectStandardOutput = true, + RedirectStandardError = true + }, + out var redirectedStandardOutput, + out var redirectedErrorOutput + ); + + if (exitCode != 0) + { + throw new Exception( + $"Git command failed with arguments {gitArguments}. Exit code: {exitCode}. Error output: {string.Join(Environment.NewLine, redirectedErrorOutput)}" + ); + } + + return redirectedStandardOutput; + + } + } +} \ No newline at end of file