diff --git a/src/Cake.Common.Tests/Cake.Common.Tests.csproj b/src/Cake.Common.Tests/Cake.Common.Tests.csproj index 6454989d79..083370e3ab 100644 --- a/src/Cake.Common.Tests/Cake.Common.Tests.csproj +++ b/src/Cake.Common.Tests/Cake.Common.Tests.csproj @@ -94,6 +94,7 @@ + @@ -196,6 +197,7 @@ + diff --git a/src/Cake.Common.Tests/Fixtures/Tools/GitLinkFixture.cs b/src/Cake.Common.Tests/Fixtures/Tools/GitLinkFixture.cs new file mode 100644 index 0000000000..d2ff6c96db --- /dev/null +++ b/src/Cake.Common.Tests/Fixtures/Tools/GitLinkFixture.cs @@ -0,0 +1,29 @@ +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Testing.Fixtures; +using NSubstitute; + +namespace Cake.Common.Tests.Fixtures.Tools +{ + using Cake.Common.Tools.GitLink; + internal sealed class GitLinkFixture : ToolFixture + { + private readonly ICakeLog Log; + + public DirectoryPath SolutionPath { get; set; } + + public GitLinkFixture() + : base("gitlink.exe") + { + SolutionPath = new DirectoryPath("c:/temp"); + + Log = Substitute.For(); + } + + protected override void RunTool() + { + var tool = new GitLinkRunner(FileSystem, Environment, ProcessRunner, Globber, Log); + tool.Run(SolutionPath, Settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.Common.Tests/Unit/Tools/GitLink/GitlinkRunnerTests.cs b/src/Cake.Common.Tests/Unit/Tools/GitLink/GitlinkRunnerTests.cs new file mode 100644 index 0000000000..acda0adb3c --- /dev/null +++ b/src/Cake.Common.Tests/Unit/Tools/GitLink/GitlinkRunnerTests.cs @@ -0,0 +1,241 @@ +using Cake.Common.Tests.Fixtures.Tools; +using Cake.Core; +using Cake.Core.IO; +using Cake.Testing; +using Xunit; + +namespace Cake.Common.Tests.Unit.Tools.GitLink +{ + using System; + + public sealed class GitlinkRunnerTests + { + public sealed class TheRunMethod + { + [Fact] + public void Should_Throw_If_SolutionPath_Is_Null() + { + // Given + var fixture = new GitLinkFixture(); + fixture.SolutionPath = null; + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + Assert.IsArgumentNullException(result, "solutionPath"); + } + + [Fact] + public void Should_Find_GitLink_Runner() + { + // Given + var fixture = new GitLinkFixture(); + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("/Working/tools/gitlink.exe", result.Path.FullPath); + } + + [Fact] + public void Should_Throw_If_Process_Was_Not_Started() + { + // Given + var fixture = new GitLinkFixture(); + fixture.GivenProcessCannotStart(); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + Assert.IsType(result); + Assert.Equal("GitLink: Process was not started.", result.Message); + } + + [Fact] + public void Should_Throw_If_Has_A_Non_Zero_Exit_Code() + { + // Given + var fixture = new GitLinkFixture(); + fixture.GivenProcessExitsWithCode(1); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + Assert.IsType(result); + Assert.Equal("GitLink: Process returned an error.", result.Message); + } + + [Fact] + public void Should_Use_Provided_SolutionPath_In_Process_Arguments() + { + // Given + var fixture = new GitLinkFixture(); + fixture.SolutionPath = "source"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("\"/Working/source\"", result.Args); + } + + [Fact] + public void Should_Set_RepositoryUrl() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.RepositoryUrl = new Uri("http://mydomain.com"); + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -u \"http://mydomain.com/\"", result.Args); + } + + [Fact] + public void Should_Set_SolutionFileName() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.SolutionFileName = "solution.sln"; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -f \"solution.sln\"", result.Args); + } + + [Fact] + public void Should_Set_Configuration() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.Configuration = "Release"; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -c \"Release\"", result.Args); + } + + [Fact] + public void Should_Set_Platform() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.Platform = "AnyCPU"; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -p \"AnyCPU\"", result.Args); + } + + [Fact] + public void Should_Set_Branch() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.Branch = "master"; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -b \"master\"", result.Args); + } + + [Fact] + public void Should_Set_LogFilePath() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.LogFilePath = @"/temp/log.txt"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -l \"/temp/log.txt\"", result.Args); + } + + [Fact] + public void Should_Set_ShaHash() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.ShaHash = "abcdef"; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -s \"abcdef\"", result.Args); + } + + [Fact] + public void Should_Set_PdbDirectory() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.PdbDirectoryPath = DirectoryPath.FromString("pdb/"); + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -d \"/Working/pdb\"", result.Args); + } + + [Fact] + public void Should_Set_PowerShell_Switch() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.UsePowerShellCommand = true; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -powershell", result.Args); + } + + [Fact] + public void Should_Set_SkipVerify_Switch() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.SkipVerify = true; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -skipverify", result.Args); + } + + [Fact] + public void Should_Set_Debug_Switch() + { + // Given + var fixture = new GitLinkFixture(); + fixture.Settings.Debug = true; + + // Then + var result = fixture.Run(); + + // Then + Assert.Equal("\"c:/temp\" -debug", result.Args); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Common/Cake.Common.csproj b/src/Cake.Common/Cake.Common.csproj index 447aaaf4b6..eaf8f6f1eb 100644 --- a/src/Cake.Common/Cake.Common.csproj +++ b/src/Cake.Common/Cake.Common.csproj @@ -163,6 +163,9 @@ + + + @@ -345,4 +348,4 @@ --> - + \ No newline at end of file diff --git a/src/Cake.Common/Tools/GitLink/GitLinkAliases.cs b/src/Cake.Common/Tools/GitLink/GitLinkAliases.cs new file mode 100644 index 0000000000..c26d1f62c4 --- /dev/null +++ b/src/Cake.Common/Tools/GitLink/GitLinkAliases.cs @@ -0,0 +1,61 @@ +using System; +using Cake.Core; +using Cake.Core.Annotations; +using Cake.Core.IO; + +namespace Cake.Common.Tools.GitLink +{ + /// + /// Contains functionality for working with GitLink. + /// + [CakeAliasCategory("GitTools")] + public static class GitLinkAliases + { + /// + /// Update pdb files to link all sources. + /// This will allow anyone to step through the source code while debugging without a symbol source server. + /// + /// The context. + /// The Solution File to analyze. + /// + /// + /// GitLink("C:/temp/solution"); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("GitLink")] + public static void GitLink(this ICakeContext context, DirectoryPath solutionPath) + { + GitLink(context, solutionPath, new GitLinkSettings()); + } + + /// + /// Update pdb files to link all sources, using specified settings. + /// This will allow anyone to step through the source code while debugging without a symbol source server. + /// + /// The context. + /// The Solution File to analyze. + /// The settings. + /// + /// + /// GitLink("C:/temp/solution", new GitLinkSettings { + /// RepositoryUrl = new Uri("http://mydomain.com"), + /// Branch = "master", + /// ShaHash = "abcdef", + /// }); + /// + /// + [CakeMethodAlias] + [CakeAliasCategory("GitLink")] + public static void GitLink(this ICakeContext context, DirectoryPath solutionPath, GitLinkSettings settings) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + var runner = new GitLinkRunner(context.FileSystem, context.Environment, context.ProcessRunner, context.Globber, context.Log); + runner.Run(solutionPath, settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/GitLink/GitLinkRunner.cs b/src/Cake.Common/Tools/GitLink/GitLinkRunner.cs new file mode 100644 index 0000000000..9d2fd74a0d --- /dev/null +++ b/src/Cake.Common/Tools/GitLink/GitLinkRunner.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.GitLink +{ + /// + /// GitLink runner + /// + public sealed class GitLinkRunner : Tool + { + private readonly IFileSystem _fileSystem; + private readonly ICakeEnvironment _environment; + private readonly ICakeLog _log; + + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The globber. + /// The logger. + public GitLinkRunner(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IGlobber globber, ICakeLog log) + : base(fileSystem, environment, processRunner, globber) + { + _fileSystem = fileSystem; + _environment = environment; + _log = log; + } + + /// + /// Update pdb files to link all sources. + /// + /// The directory containing the solution with the pdb files. + /// The settings. + public void Run(DirectoryPath solutionPath, GitLinkSettings settings) + { + if (settings == null) + { + throw new ArgumentNullException("settings"); + } + + if (solutionPath == null) + { + throw new ArgumentNullException("solutionPath"); + } + + Run(settings, GetArguments(solutionPath, settings)); + } + + private ProcessArgumentBuilder GetArguments(DirectoryPath solutionPath, GitLinkSettings settings) + { + var builder = new ProcessArgumentBuilder(); + + builder.AppendQuoted(solutionPath.MakeAbsolute(_environment).FullPath); + + if (settings.RepositoryUrl != null) + { + builder.Append("-u"); + builder.AppendQuoted(settings.RepositoryUrl.ToString()); + } + + if (!string.IsNullOrWhiteSpace(settings.SolutionFileName)) + { + builder.Append("-f"); + builder.AppendQuoted(settings.SolutionFileName); + } + + if (!string.IsNullOrWhiteSpace(settings.Configuration)) + { + builder.Append("-c"); + builder.AppendQuoted(settings.Configuration); + } + + if (!string.IsNullOrWhiteSpace(settings.Platform)) + { + builder.Append("-p"); + builder.AppendQuoted(settings.Platform); + } + + if (!string.IsNullOrWhiteSpace(settings.Branch)) + { + builder.Append("-b"); + builder.AppendQuoted(settings.Branch); + } + + if (settings.LogFilePath != null) + { + builder.Append("-l"); + builder.AppendQuoted(settings.LogFilePath.MakeAbsolute(_environment).FullPath); + } + + if (!string.IsNullOrWhiteSpace(settings.ShaHash)) + { + builder.Append("-s"); + builder.AppendQuoted(settings.ShaHash); + } + + if (settings.PdbDirectoryPath != null) + { + builder.Append("-d"); + builder.AppendQuoted(settings.PdbDirectoryPath.MakeAbsolute(_environment).FullPath); + } + + if (settings.UsePowerShellCommand) + { + builder.Append("-powershell"); + } + + if (settings.ErrorsAsWarnings) + { + builder.Append("-errorsaswarnings"); + } + + if (settings.SkipVerify) + { + builder.Append("-skipverify"); + } + + if (settings.Debug) + { + builder.Append("-debug"); + } + + return builder; + } + + /// + /// Gets the name of the tool. + /// + /// The name of the tool. + protected override string GetToolName() + { + return "GitLink"; + } + + /// + /// Gets the possible names of the tool executable. + /// + /// The tool executable name. + protected override IEnumerable GetToolExecutableNames() + { + return new[] { "gitlink.exe" }; + } + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/GitLink/GitLinkSettings.cs b/src/Cake.Common/Tools/GitLink/GitLinkSettings.cs new file mode 100644 index 0000000000..e564389a24 --- /dev/null +++ b/src/Cake.Common/Tools/GitLink/GitLinkSettings.cs @@ -0,0 +1,74 @@ +using System; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.GitLink +{ + /// + /// Contains settings used by . + /// + public sealed class GitLinkSettings : ToolSettings + { + /// + /// Gets or sets the Url to remote git repository. + /// + public Uri RepositoryUrl { get; set; } + + /// + /// Gets or sets the Solution file name. + /// + public string SolutionFileName { get; set; } + + /// + /// Gets or sets the name of the configuration. + /// + /// Default is Release + public string Configuration { get; set; } + + /// + /// Gets or sets the name of the platform. + /// + /// Default is AnyCPU + public string Platform { get; set; } + + /// + /// Gets or sets the name of the branch to use on the remote repository. + /// + public string Branch { get; set; } + + /// + /// Gets or sets the path to the GitLink log file. + /// + public FilePath LogFilePath { get; set; } + + /// + /// Gets or sets the SHA-1 hash of the git commit to be used. + /// + public string ShaHash { get; set; } + + /// + /// Gets or sets the directory where the PDB files are located. + /// + public DirectoryPath PdbDirectoryPath { get; set; } + + /// + /// Gets or sets a value indicating whether the Use PowerShell Command option should be enabled. + /// + public bool UsePowerShellCommand { get; set; } + + /// + /// Gets or sets a value indicating whether the ErrorsAsWarnings option should be enabled. + /// + public bool ErrorsAsWarnings { get; set; } + + /// + /// Gets or sets a value indicating whether the Skip Verify option should be enabled. + /// + public bool SkipVerify { get; set; } + + /// + /// Gets or sets a value indicating whether the debug output should be enabled. + /// + public bool Debug { get; set; } + } +} \ No newline at end of file