diff --git a/src/Cake.Common.Tests/Fixtures/Tools/OctopusDeployPackerFixture.cs b/src/Cake.Common.Tests/Fixtures/Tools/OctopusDeployPackerFixture.cs new file mode 100644 index 0000000000..e31fd4f8bb --- /dev/null +++ b/src/Cake.Common.Tests/Fixtures/Tools/OctopusDeployPackerFixture.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Common.Tools.OctopusDeploy; +using Cake.Testing.Fixtures; + +namespace Cake.Common.Tests.Fixtures.Tools +{ + internal sealed class OctopusDeployPackerFixture : ToolFixture + { + public string Id { get; set; } + + public OctopusDeployPackerFixture() + : base("octo.exe") + { + } + + protected override void RunTool() + { + var tool = new OctopusDeployPacker(FileSystem, Environment, ProcessRunner, Tools); + tool.Pack(Id, Settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.Common.Tests/Unit/Tools/OctopusDeploy/OctoPackTests.cs b/src/Cake.Common.Tests/Unit/Tools/OctopusDeploy/OctoPackTests.cs new file mode 100644 index 0000000000..3cfdc9a10b --- /dev/null +++ b/src/Cake.Common.Tests/Unit/Tools/OctopusDeploy/OctoPackTests.cs @@ -0,0 +1,196 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Common.Tests.Fixtures.Tools; +using Xunit; + +namespace Cake.Common.Tests.Unit.Tools.OctopusDeploy +{ + public sealed class OctoPackTests + { + public sealed class ThePackMethod + { + [Fact] + public void Should_Throw_If_Id_Is_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + + // When + var result = Record.Exception(() => fixture.Run()); + + // Then + Assert.IsArgumentNullException(result, "id"); + } + + [Fact] + public void Should_Add_Id_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage", result.Args); + } + + [Fact] + public void Should_Add_Version_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Version = "1.2.3"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --version 1.2.3", result.Args); + } + + [Fact] + public void Should_Add_OutFolder_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.OutFolder = "out"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --outFolder \"/Working/out\"", result.Args); + } + + [Fact] + public void Should_Add_BasePath_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.BasePath = "base"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --basePath \"/Working/base\"", result.Args); + } + + [Fact] + public void Should_Add_Author_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Author = "author"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --author \"author\"", result.Args); + } + + [Fact] + public void Should_Add_Title_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Title = "title"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --title \"title\"", result.Args); + } + + [Fact] + public void Should_Add_Description_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Description = "description"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --description \"description\"", result.Args); + } + + [Fact] + public void Should_Add_ReleaseNotes_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.ReleaseNotes = "releasenotes"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --releaseNotes \"releasenotes\"", result.Args); + } + + [Fact] + public void Should_Add_ReleaseNotesFile_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.ReleaseNotesFile = "releasenotes.md"; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --releaseNotesFile \"/Working/releasenotes.md\"", result.Args); + } + + [Fact] + public void Should_Add_Include_To_Arguments_If_Not_Null() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Include = new[] + { + "bin/*.dll", + "bin/*.pdb" + }; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --include \"bin/*.dll\" --include \"bin/*.pdb\"", result.Args); + } + + [Fact] + public void Should_Add_Overwrite_To_Arguments_If_True() + { + // Given + var fixture = new OctopusDeployPackerFixture(); + fixture.Id = "MyPackage"; + fixture.Settings.Overwrite = true; + + // When + var result = fixture.Run(); + + // Then + Assert.Equal("pack --id MyPackage --overwrite", result.Args); + } + } + } +} diff --git a/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployAliases.cs b/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployAliases.cs index f6980959b4..9b9c002f29 100644 --- a/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployAliases.cs +++ b/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployAliases.cs @@ -1,125 +1,160 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections.Generic; -using System.Linq; -using Cake.Core; -using Cake.Core.Annotations; -using Cake.Core.IO; - -namespace Cake.Common.Tools.OctopusDeploy -{ - /// - /// Contains functionality related to Octopus Deploy. - /// - /// In order to use the commands for this alias, include the following in your build.cake file to download and - /// install from NuGet.org, or specify the ToolPath within the appropriate settings class: - /// - /// #tool "nuget:?package=OctopusTools" - /// - /// - /// - [CakeAliasCategory("Octopus Deploy")] - public static class OctopusDeployAliases - { - /// - /// Creates a release for the specified Octopus Deploy Project. - /// - /// The cake context. - /// The name of the project. - /// The settings. - /// - /// - /// // Minimum required - /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { - /// Server = "http://octopus-deploy.example", - /// ApiKey = "API-XXXXXXXXXXXXXXXXXXXX" - /// }); - /// - /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { - /// Server = "http://octopus-deploy.example", - /// Username = "DeployUser", - /// Password = "a-very-secure-password" - /// }); - /// - /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { - /// ConfigurationFile = @"C:\OctopusDeploy.config" - /// }); - /// - /// // Additional Options - /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { - /// ToolPath = "./tools/OctopusTools/Octo.exe" - /// EnableDebugLogging = true, - /// IgnoreSslErrors = true, - /// EnableServiceMessages = true, // Enables teamcity services messages when logging - /// ReleaseNumber = "1.8.2", - /// DefaultPackageVersion = "1.0.0.0", // All packages in the release should be 1.0.0.0 - /// Packages = new Dictionary<string, string> - /// { - /// { "PackageOne", "1.0.2.3" }, - /// { "PackageTwo", "5.2.3" } - /// }, - /// PackagesFolder = @"C:\MyOtherNugetFeed", - /// - /// // One or the other - /// ReleaseNotes = "Version 2.0 \n What a milestone we have ...", - /// ReleaseNotesFile = "./ReleaseNotes.md", - /// - /// IgnoreExisting = true // if this release number already exists, ignore it - /// }); - /// - /// - [CakeMethodAlias] - public static void OctoCreateRelease(this ICakeContext context, string projectName, CreateReleaseSettings settings) - { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - var packer = new OctopusDeployReleaseCreator(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); - packer.CreateRelease(projectName, settings); - } - - /// - /// Pushes the specified package to the Octopus Deploy repository - /// - /// The cake context - /// /// The Octopus server URL - /// The user's API key - /// Path to the package - /// The settings - [CakeMethodAlias] - public static void OctoPush(this ICakeContext context, string server, string apiKey, FilePath packagePath, OctopusPushSettings settings) - { - OctoPush(context, server, apiKey, new[] { packagePath }, settings); - } - - /// - /// Pushes the specified packages to the Octopus Deploy repository - /// - /// The cake context - /// The Octopus server URL - /// The user's API key - /// Paths to the packages - /// The settings - [CakeMethodAlias] - public static void OctoPush(this ICakeContext context, string server, string apiKey, IEnumerable packagePaths, OctopusPushSettings settings) - { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (packagePaths == null) - { - throw new ArgumentNullException(nameof(packagePaths)); - } - - var pusher = new OctopusDeployPusher(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); - pusher.PushPackage(server, apiKey, packagePaths.ToArray(), settings); - } - } +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using Cake.Common.Tools.MSBuild; +using Cake.Core; +using Cake.Core.Annotations; +using Cake.Core.IO; + +namespace Cake.Common.Tools.OctopusDeploy +{ + /// + /// Contains functionality related to Octopus Deploy. + /// + /// In order to use the commands for this alias, include the following in your build.cake file to download and + /// install from NuGet.org, or specify the ToolPath within the appropriate settings class: + /// + /// #tool "nuget:?package=OctopusTools" + /// + /// + /// + [CakeAliasCategory("Octopus Deploy")] + public static class OctopusDeployAliases + { + /// + /// Creates a release for the specified Octopus Deploy Project. + /// + /// The cake context. + /// The name of the project. + /// The settings. + /// + /// + /// // Minimum required + /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { + /// Server = "http://octopus-deploy.example", + /// ApiKey = "API-XXXXXXXXXXXXXXXXXXXX" + /// }); + /// + /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { + /// Server = "http://octopus-deploy.example", + /// Username = "DeployUser", + /// Password = "a-very-secure-password" + /// }); + /// + /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { + /// ConfigurationFile = @"C:\OctopusDeploy.config" + /// }); + /// + /// // Additional Options + /// OctoCreateRelease(projectNameOnServer, new CreateReleaseSettings { + /// ToolPath = "./tools/OctopusTools/Octo.exe" + /// EnableDebugLogging = true, + /// IgnoreSslErrors = true, + /// EnableServiceMessages = true, // Enables teamcity services messages when logging + /// ReleaseNumber = "1.8.2", + /// DefaultPackageVersion = "1.0.0.0", // All packages in the release should be 1.0.0.0 + /// Packages = new Dictionary<string, string> + /// { + /// { "PackageOne", "1.0.2.3" }, + /// { "PackageTwo", "5.2.3" } + /// }, + /// PackagesFolder = @"C:\MyOtherNugetFeed", + /// + /// // One or the other + /// ReleaseNotes = "Version 2.0 \n What a milestone we have ...", + /// ReleaseNotesFile = "./ReleaseNotes.md", + /// + /// IgnoreExisting = true // if this release number already exists, ignore it + /// }); + /// + /// + [CakeMethodAlias] + public static void OctoCreateRelease(this ICakeContext context, string projectName, CreateReleaseSettings settings) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var packer = new OctopusDeployReleaseCreator(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + packer.CreateRelease(projectName, settings); + } + + /// + /// Pushes the specified package to the Octopus Deploy repository + /// + /// The cake context + /// /// The Octopus server URL + /// The user's API key + /// Path to the package + /// The settings + [CakeMethodAlias] + public static void OctoPush(this ICakeContext context, string server, string apiKey, FilePath packagePath, OctopusPushSettings settings) + { + OctoPush(context, server, apiKey, new[] { packagePath }, settings); + } + + /// + /// Pushes the specified packages to the Octopus Deploy repository + /// + /// The cake context + /// The Octopus server URL + /// The user's API key + /// Paths to the packages + /// The settings + [CakeMethodAlias] + public static void OctoPush(this ICakeContext context, string server, string apiKey, IEnumerable packagePaths, OctopusPushSettings settings) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (packagePaths == null) + { + throw new ArgumentNullException(nameof(packagePaths)); + } + + var pusher = new OctopusDeployPusher(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + pusher.PushPackage(server, apiKey, packagePaths.ToArray(), settings); + } + + /// + /// Packs the specified folder into an Octopus Deploy package. + /// + /// The cake context + /// The package ID. + [CakeMethodAlias] + public static void OctoPack(this ICakeContext context, string id) + { + OctoPack(context, id, null); + } + + /// + /// Packs the specified folder into an Octopus Deploy package. + /// + /// The cake context + /// The package ID. + /// The settings + [CakeMethodAlias] + public static void OctoPack(this ICakeContext context, string id, OctopusPackSettings settings = null) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + var packer = new OctopusDeployPacker(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + packer.Pack(id, settings); + } + } } \ No newline at end of file diff --git a/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployPacker.cs b/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployPacker.cs new file mode 100644 index 0000000000..cc011ac1af --- /dev/null +++ b/src/Cake.Common/Tools/OctopusDeploy/OctopusDeployPacker.cs @@ -0,0 +1,67 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using Cake.Core; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.OctopusDeploy +{ + /// + /// The Octopus deploy package packer + /// + public sealed class OctopusDeployPacker : Tool + { + private readonly ICakeEnvironment _environment; + + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The tool locator. + public OctopusDeployPacker(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IToolLocator tools) + : base(fileSystem, environment, processRunner, tools) + { + _environment = environment; + } + + /// + /// Gets the name of the tool. + /// + /// The name of the tool. + protected override string GetToolName() + { + return "Octo"; + } + + /// + /// Gets the possible names of the tool executable. + /// + /// The tool executable name. + protected override IEnumerable GetToolExecutableNames() + { + return new[] { "octo.exe" }; + } + + /// + /// Creates an Octopus deploy package with the specified ID. + /// + /// The package ID. + /// The settings. + public void Pack(string id, OctopusPackSettings settings) + { + if (id == null) + { + throw new ArgumentNullException(nameof(id)); + } + + var builder = new OctopusPackArgumentBuilder(id, _environment, settings); + Run(settings, builder.Get()); + } + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/OctopusDeploy/OctopusPackArgumentBuilder.cs b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackArgumentBuilder.cs new file mode 100644 index 0000000000..a6b181ed4f --- /dev/null +++ b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackArgumentBuilder.cs @@ -0,0 +1,94 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Cake.Core; +using Cake.Core.IO; + +namespace Cake.Common.Tools.OctopusDeploy +{ + internal sealed class OctopusPackArgumentBuilder + { + private readonly string _id; + private readonly ICakeEnvironment _environment; + private readonly OctopusPackSettings _settings; + private readonly ProcessArgumentBuilder _builder; + + public OctopusPackArgumentBuilder(string id, ICakeEnvironment environment, OctopusPackSettings settings = null) + { + _id = id; + _environment = environment; + _settings = settings; + _builder = new ProcessArgumentBuilder(); + } + + public ProcessArgumentBuilder Get() + { + _builder.Append("pack"); + _builder.Append($"--id {_id}"); + AppendBasicOptions(); + AppendNuGetOptions(); + AppendAdvancedOptions(); + return _builder; + } + + private void AppendBasicOptions() + { + if (_settings == null) + { + return; + } + if (!string.IsNullOrWhiteSpace(_settings.Version)) + { + _builder.AppendSwitch("--version", _settings.Version); + } + if (_settings.OutFolder != null) + { + _builder.AppendSwitchQuoted("--outFolder", _settings.OutFolder.MakeAbsolute(_environment).FullPath); + } + if (_settings.BasePath != null) + { + _builder.AppendSwitchQuoted("--basePath", _settings.BasePath.MakeAbsolute(_environment).FullPath); + } + } + + private void AppendNuGetOptions() + { + if (!string.IsNullOrWhiteSpace(_settings.Author)) + { + _builder.AppendSwitchQuoted("--author", _settings.Author); + } + if (!string.IsNullOrWhiteSpace(_settings.Title)) + { + _builder.AppendSwitchQuoted("--title", _settings.Title); + } + if (!string.IsNullOrWhiteSpace(_settings.Description)) + { + _builder.AppendSwitchQuoted("--description", _settings.Description); + } + if (!string.IsNullOrWhiteSpace(_settings.ReleaseNotes)) + { + _builder.AppendSwitchQuoted("--releaseNotes", _settings.ReleaseNotes); + } + if (_settings.ReleaseNotesFile != null) + { + _builder.AppendSwitchQuoted("--releaseNotesFile", _settings.ReleaseNotesFile.MakeAbsolute(_environment).FullPath); + } + } + + private void AppendAdvancedOptions() + { + if (_settings.Include != null) + { + foreach (var include in _settings.Include) + { + _builder.AppendSwitchQuoted("--include", include); + } + } + if (_settings.Overwrite) + { + _builder.Append("--overwrite"); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/OctopusDeploy/OctopusPackFormat.cs b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackFormat.cs new file mode 100644 index 0000000000..f8485a4e71 --- /dev/null +++ b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackFormat.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Cake.Common.Tools.OctopusDeploy +{ + /// + /// Represents the format of an Octopus package. + /// + public enum OctopusPackFormat + { + /// + /// NuGet package + /// + NuPkg, + + /// + /// Zip package + /// + Zip + } +} \ No newline at end of file diff --git a/src/Cake.Common/Tools/OctopusDeploy/OctopusPackSettings.cs b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackSettings.cs new file mode 100644 index 0000000000..25793744f0 --- /dev/null +++ b/src/Cake.Common/Tools/OctopusDeploy/OctopusPackSettings.cs @@ -0,0 +1,71 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Common.Tools.OctopusDeploy +{ + /// + /// Contains the settings used by OctoPack. + /// + public sealed class OctopusPackSettings : ToolSettings + { + /// + /// Gets or sets the version. + /// + public string Version { get; set; } + + /// + /// Gets or sets the package format. + /// + public OctopusPackFormat Format { get; set; } + + /// + /// Gets or sets the folder into which the package will be written. Defaults to the current folder. + /// + public DirectoryPath OutFolder { get; set; } + + /// + /// Gets or sets the root folder containing files and folders to pack. Defaults to the current folder. + /// + public DirectoryPath BasePath { get; set; } + + /// + /// Gets or sets the author. Only applies to NuGet packages. + /// + public string Author { get; set; } + + /// + /// Gets or sets the title. Only applies to NuGet packages. + /// + public string Title { get; set; } + + /// + /// Gets or sets the description. Only applies to NuGet packages. + /// + public string Description { get; set; } + + /// + /// Gets or sets the release notes. Only applies to NuGet packages. + /// + public string ReleaseNotes { get; set; } + + /// + /// Gets or sets the release notes file. Only applies to NuGet packages. + /// + public FilePath ReleaseNotesFile { get; set; } + + /// + /// Gets or sets the file patterns to include. If none are specified, defaults to **. + /// + public ICollection Include { get; set; } + + /// + /// Gets or sets a value indicating whether to allow an existing package with the same ID/version to be overwriten. + /// + public bool Overwrite { get; set; } + } +} \ No newline at end of file