diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c249179 --- /dev/null +++ b/.gitignore @@ -0,0 +1,235 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ +testing/ +src/tools \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..95f2cc3 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Miha Markič + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9f0bbf --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Cake.Pscp + +A Cake AddIn that extends Cake with [Putty](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html/) command tools. + +[![cakebuild.net](https://img.shields.io/badge/WWW-cakebuild.net-blue.svg)](http://cakebuild.net/) +[![NuGet](https://img.shields.io/nuget/v/Cake.Putty.svg)](https://www.nuget.org/packages/Cake.Putty) + +## Supported tools + +- Pscp v1.0.0 + +## Including addin +Including addin in cake script is easy. +``` +#addin "Cake.Putty" +``` + +## Usage + +To use the addin just add it to Cake call the aliases and configure any settings you want. + +```csharp +#addin "Cake.Putty" + +... + +Task("Default") + .Does(() => + { + Pscp("FILENAME", "USERNAME@YOURSERVER:FILENAME"); + }); + +Task("WithSettings") + .Does(() => + { + Pscp("FILENAME", "YOURSERVER:FILENAME", new PscpSettings{ SshVersion = SshVersion.V2, User="USERNAME" }); + }); +``` + +# General Notes +**This is an initial version and not tested thoroughly**. +Contributions welcome. + +Tested only on Windows at this time. Ensure that Putty command line tools (pspc, plink) can be located using the PATH (e.g. check that it can be found with `which pscp`). + +[![Follow @mihamarkic](https://img.shields.io/badge/Twitter-Follow%20%40mihamarkic-blue.svg)](https://twitter.com/intent/follow?screen_name=mihamarkic) diff --git a/ReleaseNotes.md b/ReleaseNotes.md new file mode 100644 index 0000000..407d343 --- /dev/null +++ b/ReleaseNotes.md @@ -0,0 +1,2 @@ +# Cake.Putty 1.0.0 +Pscp implemented. \ No newline at end of file diff --git a/src/Cake.Putty.nuspec b/src/Cake.Putty.nuspec new file mode 100644 index 0000000..0ef94b6 --- /dev/null +++ b/src/Cake.Putty.nuspec @@ -0,0 +1,28 @@ + + + + Cake.Pscp + $version$ + Righthand + Righthand + Cake.Putty + Cake Build addin for Putty. + Putty command line addin for cake build. + https://github.com/MihaMarkic/Cake.Putty/blob/master/LICENSE + https://github.com/MihaMarkic/Cake.Putty + See https://github.com/MihaMarkic/Cake.Putty/blob/master/ReleaseNotes.md + + false + Copyright (c) Righthand 2016 + Cake Script Build Putty + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cake.Putty.sln b/src/Cake.Putty.sln new file mode 100644 index 0000000..3c5c9f2 --- /dev/null +++ b/src/Cake.Putty.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25123.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.Putty", "Cake.Putty\Cake.Putty.csproj", "{7DE3FF26-12A9-436C-9291-FED348AEACD7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{73141119-9964-4C31-9406-A24AAF6AB92F}" + ProjectSection(SolutionItems) = preProject + build.cake = build.cake + Cake.Putty.nuspec = Cake.Putty.nuspec + ..\README.md = ..\README.md + ..\ReleaseNotes.md = ..\ReleaseNotes.md + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cake.PuttyTests", "Cake.PuttyTests\Cake.PuttyTests.csproj", "{7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7DE3FF26-12A9-436C-9291-FED348AEACD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DE3FF26-12A9-436C-9291-FED348AEACD7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DE3FF26-12A9-436C-9291-FED348AEACD7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DE3FF26-12A9-436C-9291-FED348AEACD7}.Release|Any CPU.Build.0 = Release|Any CPU + {7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/Cake.Putty/ArgumentsBuilderExtension.cs b/src/Cake.Putty/ArgumentsBuilderExtension.cs new file mode 100644 index 0000000..5222804 --- /dev/null +++ b/src/Cake.Putty/ArgumentsBuilderExtension.cs @@ -0,0 +1,235 @@ +using Cake.Core; +using Cake.Core.IO; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Linq; + +namespace Cake.Putty +{ + /// + /// Arguments builder + /// + public static class ArgumentsBuilderExtension + { + /// + /// Appends all arguments from and . + /// + /// + /// + /// + /// + /// + public static void AppendAll(this ProcessArgumentBuilder builder, string command, TSettings settings, IList arguments) + where TSettings: AutoToolSettings, new() + { + if (builder == null) + { + throw new ArgumentNullException("builder"); + } + if (arguments == null) + { + throw new ArgumentNullException("arguments"); + } + if (settings == null) + { + settings = new TSettings(); + } + if (!string.IsNullOrEmpty(command)) + { + builder.Append(command); + } + foreach (var property in typeof(TSettings).GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + foreach (string argument in GetArgumentFromProperty(property, settings)) + { + if (!string.IsNullOrEmpty(argument)) + { + builder.Append(argument); + } + } + } + if (arguments != null) + { + foreach (string argument in arguments) + { + builder.Append(argument); + } + } + } + + /// + /// Checks whether a type is nullable. + /// + /// + /// + public static bool IsNullableType(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + /// + /// Gets and processes value from . + /// + /// + /// + /// + /// + public static IEnumerable GetArgumentFromProperty(PropertyInfo property, TSettings settings) + where TSettings : AutoToolSettings, new() + { + if (property.PropertyType == typeof(bool)) + { + if (IsNullableType(property.PropertyType)) + { + yield return GetArgumentFromNullableBoolProperty(property, (bool?)property.GetValue(settings)); + } + else + { + yield return GetArgumentFromBoolProperty(property, (bool)property.GetValue(settings)); + } + } + else if (property.PropertyType == typeof(int?)) + { + yield return GetArgumentFromNullableIntProperty(property, (int?)property.GetValue(settings)); + } + else if (property.PropertyType.IsEnum) + { + yield return GetArgumentFromEnumProperty(property, property.GetValue(settings)); + } + else if (property.PropertyType == typeof(string)) + { + yield return GetArgumentFromStringProperty(property, (string)property.GetValue(settings)); + } + else if (property.PropertyType == typeof(FilePath)) + { + var filePath = (FilePath)property.GetValue(settings); + string value = filePath?.FullPath; + yield return GetArgumentFromStringProperty(property, value); + } + else if (property.PropertyType == typeof(string[])) + { + foreach (string arg in GetArgumentFromStringArrayProperty(property, (string[])property.GetValue(settings))) + { + yield return arg; + } + } + } + /// + /// + /// + /// + /// + /// + public static string GetArgumentFromBoolProperty(PropertyInfo property, bool value) + { + return value ? $"-{GetPropertyName(property)}" : null; + } + /// + /// + /// + /// + /// + /// + public static string GetArgumentFromNullableBoolProperty(PropertyInfo property, bool? value) + { + if (value.HasValue) + { + BoolParameterAttribute attribute = property.GetCustomAttribute(); + if (attribute == null) + { + throw new Exception($"{property.Name} isn't attributed with BoolParameterAttribute"); + } + string parameterValue = value.Value ? attribute.OnTrue: attribute.OnFalse; + return "-" + parameterValue; + } + else + { + return null; + } + } + + /// + /// Get arguments for a (nullable) enum. + /// + /// + /// + /// + public static string GetArgumentFromEnumProperty(PropertyInfo property, object value) + { + return value != null ? $"-{GetEnumName(property.PropertyType, value)}" : null; + } + + /// + /// + /// + /// + /// + /// + public static string GetArgumentFromNullableIntProperty(PropertyInfo property, int? value) + { + return value.HasValue ? $"-{GetPropertyName(property)} {value.Value}" : null; + } + + /// + /// + /// + /// + /// + /// + public static IEnumerable GetArgumentFromStringArrayProperty(PropertyInfo property, string[] values) + { + if (values != null) + { + foreach (string value in values) + { + yield return GetArgumentFromStringProperty(property, value); + } + } + } + + /// + /// + /// + /// + /// + /// + public static string GetArgumentFromStringProperty(PropertyInfo property, string value) + { + return !string.IsNullOrEmpty(value) ? $"-{GetPropertyName(property)} {value}" : null; + } + + /// + /// Retrieves property name from . + /// + /// + /// + public static string GetPropertyName(PropertyInfo property) + { + ParameterAttribute attribute = property.GetCustomAttribute(); + if (attribute == null) + { + throw new Exception($"{property.Name} isn't attributed with ParameterAttribute"); + } + return attribute.Name; + } + /// + /// Retrieves enum name from . + /// + /// + /// + /// + public static string GetEnumName(Type sourceType, object value) + { + Type enumType = IsNullableType(sourceType) ? sourceType.GenericTypeArguments[0] : sourceType; + var member = enumType.GetMember(Convert.ToString(value)).Single(); + + ParameterAttribute attribute = member.GetCustomAttribute(); + if (attribute == null) + { + throw new Exception($"{member.Name} isn't attributed with ParameterAttribute"); + } + return attribute.Name; + } + } +} diff --git a/src/Cake.Putty/AutoToolSettings.cs b/src/Cake.Putty/AutoToolSettings.cs new file mode 100644 index 0000000..cb988ff --- /dev/null +++ b/src/Cake.Putty/AutoToolSettings.cs @@ -0,0 +1,11 @@ +using Cake.Core.Tooling; + +namespace Cake.Putty +{ + /// + /// Base class for tooling that is used for autogeneration of command line arguments. + /// + public abstract class AutoToolSettings: ToolSettings + { + } +} diff --git a/src/Cake.Putty/BoolParameter.cs b/src/Cake.Putty/BoolParameter.cs new file mode 100644 index 0000000..2faba74 --- /dev/null +++ b/src/Cake.Putty/BoolParameter.cs @@ -0,0 +1,30 @@ +using System; + +namespace Cake.Putty +{ + /// + /// Decorates a bool property with parameter names. + /// + [AttributeUsage(AttributeTargets.Property)] + public class BoolParameterAttribute: Attribute + { + /// + /// Value on true. + /// + public string OnTrue { get; set; } + /// + /// Value on false. + /// + public string OnFalse { get; set; } + /// + /// Constructor. + /// + /// + /// + public BoolParameterAttribute(string onTrue, string onFalse) + { + OnTrue = onTrue; + OnFalse = onFalse; + } + } +} diff --git a/src/Cake.Putty/Cake.Putty.csproj b/src/Cake.Putty/Cake.Putty.csproj new file mode 100644 index 0000000..cde47f9 --- /dev/null +++ b/src/Cake.Putty/Cake.Putty.csproj @@ -0,0 +1,77 @@ + + + + + Debug + AnyCPU + {7DE3FF26-12A9-436C-9291-FED348AEACD7} + Library + Properties + Cake.Putty + Cake.Putty + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + bin\Release\Cake.Putty.xml + + + + ..\packages\Cake.Core.0.10.1\lib\net45\Cake.Core.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Cake.Putty/IpVersion.cs b/src/Cake.Putty/IpVersion.cs new file mode 100644 index 0000000..4e74720 --- /dev/null +++ b/src/Cake.Putty/IpVersion.cs @@ -0,0 +1,21 @@ +using Cake.Putty; + +namespace Cake.Putty +{ + /// + /// IP version + /// + public enum IpVersion + { + /// + /// IPv4 + /// + [ParameterAttribute("4")] + V4, + /// + /// IPv6 + /// + [ParameterAttribute("6")] + V6 + } +} diff --git a/src/Cake.Putty/ParameterAttribute.cs b/src/Cake.Putty/ParameterAttribute.cs new file mode 100644 index 0000000..8f7509d --- /dev/null +++ b/src/Cake.Putty/ParameterAttribute.cs @@ -0,0 +1,24 @@ +using System; + +namespace Cake.Putty +{ + /// + /// Decorates with parameter name. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public class ParameterAttribute: Attribute + { + /// + /// Parameter name. + /// + public string Name { get; set; } + /// + /// Constructor. + /// + /// + public ParameterAttribute(string name) + { + Name = name; + } + } +} diff --git a/src/Cake.Putty/Properties/AssemblyInfo.cs b/src/Cake.Putty/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a45bdc7 --- /dev/null +++ b/src/Cake.Putty/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Cake.Pscp")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Cake.Pscp")] +[assembly: AssemblyCopyright("Copyright © Righthand 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7de3ff26-12a9-436c-9291-fed348aeacd7")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Cake.Putty/Protocol.cs b/src/Cake.Putty/Protocol.cs new file mode 100644 index 0000000..5a6f0db --- /dev/null +++ b/src/Cake.Putty/Protocol.cs @@ -0,0 +1,19 @@ +namespace Cake.Putty +{ + /// + /// Transfer protocol. + /// + public enum Protocol + { + /// + /// SFTP + /// + [ParameterAttribute("sftp")] + Sftp, + /// + /// SCP + /// + [ParameterAttribute("scp")] + Scp + } +} diff --git a/src/Cake.Putty/Pscp/GenericPscpRunner.cs b/src/Cake.Putty/Pscp/GenericPscpRunner.cs new file mode 100644 index 0000000..c4ee94b --- /dev/null +++ b/src/Cake.Putty/Pscp/GenericPscpRunner.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using Cake.Core; +using Cake.Core.IO; + +namespace Cake.Putty +{ + /// + /// + /// + /// + public class GenericPscpRunner : PscpTool + where TSettings: AutoToolSettings, new() + { + /// + /// + /// + /// + /// + /// + /// + public GenericPscpRunner(IFileSystem fileSystem, ICakeEnvironment environment, IProcessRunner processRunner, IGlobber globber) + : base(fileSystem, environment, processRunner, globber) + { + } + + /// + /// Runs given using given and . + /// + /// + /// + /// + public void Run(string command, TSettings settings, string[] additional) + { + if (string.IsNullOrEmpty(command)) + { + throw new ArgumentNullException(nameof(command)); + } + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + if (additional == null || additional.Length == 0) + { + throw new ArgumentNullException(nameof(additional)); + } + Run(settings, GetArguments(command, settings, additional)); + } + /// + /// Runs using given and . + /// + /// + /// + public void Run(TSettings settings, IList additional) + { + if (settings == null) + { + throw new ArgumentNullException(nameof(settings)); + } + if (additional == null || additional.Count == 0) + { + throw new ArgumentNullException(nameof(additional)); + } + Run(settings, GetArguments(null, settings, additional)); + } + + private ProcessArgumentBuilder GetArguments(string command, TSettings settings, IList additional) + { + var builder = new ProcessArgumentBuilder(); + builder.AppendAll(command, settings, additional); + return builder; + } + + /// + /// Runs a command and returns a result based on processed output. + /// + /// + /// + /// + /// + /// + /// + public T[] RunWithResult(string command, TSettings settings, + Func, T[]> processOutput, + params string[] arguments) + { + if (settings == null) + { + throw new ArgumentNullException("settings"); + } + if (processOutput == null) + { + throw new ArgumentNullException("processOutput"); + } + T[] result = new T[0]; + Run(settings, GetArguments(command, settings, arguments), + new ProcessSettings { RedirectStandardOutput = true }, + proc => { + result = processOutput(proc.GetStandardOutput()); + }); + return result; + } + } +} diff --git a/src/Cake.Putty/Pscp/PscpResolver.cs b/src/Cake.Putty/Pscp/PscpResolver.cs new file mode 100644 index 0000000..9f69a0c --- /dev/null +++ b/src/Cake.Putty/Pscp/PscpResolver.cs @@ -0,0 +1,62 @@ +using Cake.Core; +using Cake.Core.IO; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Cake.Putty +{ + internal static class PscpResolver + { + public static FilePath GetPscpPath(IFileSystem fileSystem, ICakeEnvironment environment) + { + if (fileSystem == null) + { + throw new ArgumentNullException("fileSystem"); + } + + if (environment == null) + { + throw new ArgumentNullException("environment"); + } + + // Cake already searches the PATH for the executable tool names. + // Check for other known locations. + return !environment.IsUnix() + ? CheckCommonWindowsPaths(fileSystem) + : null; + } + + /// + /// Check common Pscp client locations. + /// + /// + /// + private static FilePath CheckCommonWindowsPaths(IFileSystem fileSystem) + { + return GetDefaultWindowsPaths(fileSystem) + .Select(path => path.CombineWithFilePath("pscp.exe")) + .FirstOrDefault(pscpExecutable => fileSystem.GetFile(pscpExecutable).Exists); + } + + /// + /// Get default paths for common Pscp client installations. + /// + /// + /// + private static DirectoryPath[] GetDefaultWindowsPaths(IFileSystem fileSystem) + { + var paths = new List(); + + DirectoryPath programFiles; + programFiles = new DirectoryPath(Environment.GetEnvironmentVariable("ProgramFiles(x86)")); + var defaultPuttyPath = programFiles.Combine("PuTTY"); + if (fileSystem.GetDirectory(defaultPuttyPath).Exists) + { + paths.Add(defaultPuttyPath); + } + + return paths.ToArray(); + } + } +} diff --git a/src/Cake.Putty/Pscp/PscpSettings.cs b/src/Cake.Putty/Pscp/PscpSettings.cs new file mode 100644 index 0000000..3d7e52e --- /dev/null +++ b/src/Cake.Putty/Pscp/PscpSettings.cs @@ -0,0 +1,83 @@ +using Cake.Core.IO; + +namespace Cake.Putty +{ + /// + /// Settings for Pscp build. + /// + public sealed class PscpSettings: AutoToolSettings + { + /// + /// Preserve file attributes. + /// + [Parameter("p")] + public bool PreserveFiltAttributes { get; set; } + /// + /// Copy directories recursively. + /// + [Parameter("r")] + public bool CopyDirectoriesRecursively { get; set; } + /// + /// Load settings from saved session. + /// + [Parameter("load")] + public string LoadSettingsFromSavedSession { get; set; } + /// + /// Connect to specified port. + /// + [Parameter("P")] + public int Port { get; set; } + /// + /// Connect with specified username. + /// + [Parameter("l")] + public string User { get; set; } + /// + /// Login with specified password. + /// + [Parameter("pw")] + public string Password { get; set; } + /// + /// Force use of particular SSH protocol version. + /// + public SshVersion? SshVersion { get; set; } + /// + /// Force use of IPv4 or IPv6. + /// + public IpVersion IpVersion { get; set; } + /// + /// Enable compression. + /// + [Parameter("C")] + public bool Compression { get; set; } + /// + /// Private key file for user authentication. + /// + [Parameter("i")] + public FilePath KeyFileForUserAuthentication { get; set; } + /// + /// Enables or disabled use of Pageant. + /// + [BoolParameterAttribute("agent", "noagent")] + public bool? EnablePageant { get; set; } + /// + /// Manually specify a host key (may be repeated). + /// + [Parameter("hostkey")] + public string Hostkey { get; set; } + /// + /// Disable all interactive prompts. + /// + [Parameter("batch")] + public bool Batch { get; set; } + /// + /// Allow server-side wildcards (DANGEROUS). + /// + [Parameter("unsafe")] + public bool Unsafe { get; set; } + /// + /// Force use of SFTP or SCP protocol. + /// + public Protocol? Protocol { get; set; } + } +} diff --git a/src/Cake.Putty/Pscp/PscpTool.cs b/src/Cake.Putty/Pscp/PscpTool.cs new file mode 100644 index 0000000..b77d2cc --- /dev/null +++ b/src/Cake.Putty/Pscp/PscpTool.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Linq; +using Cake.Core; +using Cake.Core.IO; +using Cake.Core.Tooling; + +namespace Cake.Putty +{ + /// + /// Base class for all Pscp related tools. + /// + /// The settings type. + public abstract class PscpTool: Tool + where TSettings: ToolSettings + { + private readonly ICakeEnvironment _environment; + private readonly IFileSystem _fileSystem; + + /// + /// Initializes a new instance of the class. + /// + /// The file system. + /// The environment. + /// The process runner. + /// The globber. + protected PscpTool( + IFileSystem fileSystem, + ICakeEnvironment environment, + IProcessRunner processRunner, + IGlobber globber) + : base(fileSystem, environment, processRunner, globber) + { + _fileSystem = fileSystem; + _environment = environment; + } + + /// + /// Gets the name of the tool. + /// + /// The name of the tool. + protected override string GetToolName() + { + return "Pscp"; + } + + /// + /// Gets the possible names of the tool executable. + /// + /// The tool executable name. + protected override IEnumerable GetToolExecutableNames() + { + return new[] { "pscp.exe", "pscp" }; + } + + /// + /// + /// + /// + /// + protected override IEnumerable GetAlternativeToolPaths(TSettings settings) + { + var path = PscpResolver.GetPscpPath(_fileSystem, _environment); + return path != null + ? new[] { path } + : Enumerable.Empty(); + } + } +} diff --git a/src/Cake.Putty/Pscp/PuttyAliases.Pscp.cs b/src/Cake.Putty/Pscp/PuttyAliases.Pscp.cs new file mode 100644 index 0000000..70d3110 --- /dev/null +++ b/src/Cake.Putty/Pscp/PuttyAliases.Pscp.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Cake.Core; +using Cake.Core.Annotations; + +namespace Cake.Putty +{ + /// + /// Alias for PSCP + /// + [CakeAliasCategory("File Operations")] + public static partial class PuttyAliases + { + /// + /// Invokes Pscp with a single from argument. + /// + /// + /// + /// + /// + [CakeMethodAlias] + public static void Pscp(this ICakeContext context, string from, string to, PscpSettings settings) + { + context.Pscp(new string[] { from }, to, settings); + } + /// + /// Invokes Pscp with a single from argument and settings. + /// + /// + /// + /// + [CakeMethodAlias] + public static void Pscp(this ICakeContext context, string from, string to) + { + context.Pscp(new string[] { from }, to); + } + /// + /// Invokes Pscp with array of from arguments. + /// + /// + /// + /// + [CakeMethodAlias] + public static void Pscp(this ICakeContext context, string[] from, string to) + { + context.Pscp(from, to, null); + } + /// + /// Invokes Pscp with array of from arguments. + /// + /// + /// + /// + /// + [CakeMethodAlias] + public static void Pscp(this ICakeContext context, string[] from, string to, PscpSettings settings) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + if (from?.Length < 1) + { + throw new ArgumentNullException("from", "from has to contain at least one entry"); + } + if (string.IsNullOrEmpty(to)) + { + throw new ArgumentNullException("to"); + } + var runner = new GenericPscpRunner(context.FileSystem, context.Environment, context.ProcessRunner, context.Globber); + List additional = new List(from); + additional.Add(to); + runner.Run(settings ?? new PscpSettings(), additional); + } + } +} diff --git a/src/Cake.Putty/PuttyAliases.cs b/src/Cake.Putty/PuttyAliases.cs new file mode 100644 index 0000000..f0f5557 --- /dev/null +++ b/src/Cake.Putty/PuttyAliases.cs @@ -0,0 +1,13 @@ +using Cake.Core.Annotations; + +namespace Cake.Putty +{ + /// + /// Alias for PSCP + /// + [CakeAliasCategory("File Operations")] + public static partial class PuttyAliases + { + + } +} diff --git a/src/Cake.Putty/SshVersion.cs b/src/Cake.Putty/SshVersion.cs new file mode 100644 index 0000000..4d5f1df --- /dev/null +++ b/src/Cake.Putty/SshVersion.cs @@ -0,0 +1,21 @@ +using Cake.Putty; + +namespace Cake.Putty +{ + /// + /// SSH version. + /// + public enum SshVersion + { + /// + /// SSH v1 + /// + [ParameterAttribute("1")] + V1, + /// + /// SSH v2 + /// + [ParameterAttribute("2")] + V2 + } +} diff --git a/src/Cake.Putty/packages.config b/src/Cake.Putty/packages.config new file mode 100644 index 0000000..8c559e8 --- /dev/null +++ b/src/Cake.Putty/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/Cake.Putty/pscp-arguments.txt b/src/Cake.Putty/pscp-arguments.txt new file mode 100644 index 0000000..73b1eff --- /dev/null +++ b/src/Cake.Putty/pscp-arguments.txt @@ -0,0 +1,28 @@ +PuTTY Secure Copy client +Release 0.65 +Usage: pscp [options] [user@]host:source target + pscp [options] source [source...] [user@]host:target + pscp [options] -ls [user@]host:filespec +Options: + -V print version information and exit + -pgpfp print PGP key fingerprints and exit + -p preserve file attributes + -q quiet, don't show statistics + -r copy directories recursively + -v show verbose messages + -load sessname Load settings from saved session + -P port connect to specified port + -l user connect with specified username + -pw passw login with specified password + -1 -2 force use of particular SSH protocol version + -4 -6 force use of IPv4 or IPv6 + -C enable compression + -i key private key file for user authentication + -noagent disable use of Pageant + -agent enable use of Pageant + -hostkey aa:bb:cc:... + manually specify a host key (may be repeated) + -batch disable all interactive prompts + -unsafe allow server-side wildcards (DANGEROUS) + -sftp force use of SFTP protocol + -scp force use of SCP protocol diff --git a/src/Cake.PuttyTests/ArgumentsBuilderExtensionTest.cs b/src/Cake.PuttyTests/ArgumentsBuilderExtensionTest.cs new file mode 100644 index 0000000..22feb1a --- /dev/null +++ b/src/Cake.PuttyTests/ArgumentsBuilderExtensionTest.cs @@ -0,0 +1,204 @@ +using System.Linq; +using System.Reflection; +using Cake.Core.IO; +using NUnit.Framework; +using Cake.Putty; + +namespace Cake.Putty.Tests +{ + public class ArgumentsBuilderExtensionTest + { + public static PropertyInfo StringProperty => GetProperty(nameof(TestSettings.String)); + public static PropertyInfo StringsProperty => GetProperty(nameof(TestSettings.Strings)); + public static PropertyInfo NullableIntProperty => GetProperty(nameof(TestSettings.NullableInt)); + public static PropertyInfo BoolProperty => GetProperty(nameof(TestSettings.Bool)); + public static PropertyInfo AttributedBoolProperty => GetProperty(nameof(TestSettings.AttributedBool)); + public static PropertyInfo EnumProperty => GetProperty(nameof(TestSettings.Enum)); + public static PropertyInfo GetProperty(string name) + { + return typeof(TestSettings).GetProperty(name, BindingFlags.Public | BindingFlags.Instance); + } + [TestFixture] + public class GetArgumentFromBoolProperty + { + [Test] + public void WhenTrue_FormatsProperly() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromBoolProperty(BoolProperty, true); + + Assert.That(actual, Is.EqualTo("-b")); + } + [Test] + public void WhenFalse_NullIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromBoolProperty(BoolProperty, false); + + Assert.That(actual, Is.Null); + } + } + [TestFixture] + public class GetArgumentFromAttributedBoolProperty + { + [Test] + public void WhenAttributedIsNull_NullIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromNullableBoolProperty(AttributedBoolProperty, null); + + Assert.That(actual, Is.Null); + } + [Test] + public void WhenAttributedIsTrue_OnTrueIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromNullableBoolProperty(AttributedBoolProperty, true); + + Assert.That(actual, Is.EqualTo("-OnTrue")); + } + [Test] + public void WhenAttributedIsFalse_OnFalseIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromNullableBoolProperty(AttributedBoolProperty, false); + + Assert.That(actual, Is.EqualTo("-OnFalse")); + } + } + [TestFixture] + public class GetArgumentFromStringProperty + { + [Test] + public void WhenGivenStringProperty_FormatsProperly() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromStringProperty(StringProperty, "tubo"); + + Assert.That(actual, Is.EqualTo("-s tubo")); + } + [Test] + public void WhenGivenNull_NullIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromStringProperty(StringProperty, null); + + Assert.That(actual, Is.Null); + } + } + [TestFixture] + public class GetArgumentFromStringArrayProperty + { + [Test] + public void WhenGivenStringArrayProperty_FormatsProperly() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromStringArrayProperty(StringsProperty, new string[] { "tubo1", "tubo2" }); + + Assert.AreEqual(actual.ToArray(), new string[] { "-strs tubo1", "-strs tubo2" }); + } + [Test] + public void WhenGivenNull_EmptyArrayReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromStringArrayProperty(StringsProperty, null); + + Assert.That(actual, Is.Empty); + } + } + [TestFixture] + public class GetArgumentFromNullableIntProperty + { + [Test] + public void WhenGivenValue_FormatsProperly() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromNullableIntProperty(NullableIntProperty, 5); + + Assert.That(actual, Is.EqualTo("-i 5")); + } + + [Test] + public void WhenGivenNull_NullIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromNullableIntProperty(NullableIntProperty, null); + + Assert.That(actual, Is.Null); + } + } + + [TestFixture] + public class GetArgumentFromEnumProperty + { + [Test] + public void WhenGivenValue_FormatsProperly() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromEnumProperty(EnumProperty, TestEnum.One); + + Assert.That(actual, Is.EqualTo("-one")); + } + [Test] + public void WhenGivenNull_NullIsReturned() + { + var actual = ArgumentsBuilderExtension.GetArgumentFromEnumProperty(EnumProperty, null); + + Assert.That(actual, Is.Null); + } + } + + [TestFixture] + public class GetPropertyName + { + [Test] + public void WhenInput_ReturnsCorrectlyFormatted() + { + string actual = ArgumentsBuilderExtension.GetPropertyName(StringProperty); + + Assert.That(actual, Is.EqualTo("s")); + } + } + + [TestFixture] + public class AppendAll + { + [Test] + public void WhenStringInput_AddsAsArgument() + { + TestSettings input = new TestSettings { String = "tubo" }; + + ProcessArgumentBuilder builder = new ProcessArgumentBuilder(); + builder.AppendAll("cmd", input, new string[] { "arg1" }); + var actual = builder.Render(); + + Assert.That(actual, Is.EqualTo("cmd -s tubo arg1")); + } + + } + + [TestFixture] + public class GetEnumName + { + [Test] + public void WhenPassedMember_ExtractName() + { + TestEnum source = TestEnum.One; + + string actual = ArgumentsBuilderExtension.GetEnumName(typeof(TestEnum), source); + + Assert.That(actual, Is.EqualTo("one")); + } + } + } + + public class TestSettings: AutoToolSettings + { + [Parameter("s")] + public string String { get; set; } + [Parameter("strs")] + public string[] Strings { get; set; } + [Parameter("i")] + public int? NullableInt { get; set; } + [Parameter("b")] + public bool Bool { get; set; } + [BoolParameter("OnTrue", "OnFalse")] + public bool? AttributedBool { get; set; } + public TestEnum? Enum { get; set; } + } + + public enum TestEnum + { + [Parameter("one")] + One, + Two + } +} diff --git a/src/Cake.PuttyTests/Cake.PuttyTests.csproj b/src/Cake.PuttyTests/Cake.PuttyTests.csproj new file mode 100644 index 0000000..1829940 --- /dev/null +++ b/src/Cake.PuttyTests/Cake.PuttyTests.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {7B0DDA5A-0A44-4288-8ABA-D7C08A11A63E} + Library + Properties + Cake.Putty.Tests + Cake.Putty.Tests + v4.5.2 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Cake.Core.0.10.1\lib\net45\Cake.Core.dll + + + ..\packages\NUnit.3.2.1\lib\net45\nunit.framework.dll + + + + + + + + + + + + + + + + + {7de3ff26-12a9-436c-9291-fed348aeacd7} + Cake.Docker + + + + + + + + \ No newline at end of file diff --git a/src/Cake.PuttyTests/Properties/AssemblyInfo.cs b/src/Cake.PuttyTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d696640 --- /dev/null +++ b/src/Cake.PuttyTests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Cake.Pscp.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Cake.Pscp.Tests")] +[assembly: AssemblyCopyright("Copyright © 2016")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7b0dda5a-0a44-4288-8aba-d7c08a11a63e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Cake.PuttyTests/packages.config b/src/Cake.PuttyTests/packages.config new file mode 100644 index 0000000..5d243b1 --- /dev/null +++ b/src/Cake.PuttyTests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/build.cake b/src/build.cake new file mode 100644 index 0000000..c727e52 --- /dev/null +++ b/src/build.cake @@ -0,0 +1,56 @@ +#addin "Cake.FileHelpers" + +var Project = Directory("./Cake.Putty/"); +var TestProject = Directory("./Cake.Putty.Tests/"); +var CakePuttyProj = Project + File("Cake.Putty.csproj"); +var CakeTestPuttyProj = TestProject + File("Cake.Putty.Test.csproj"); +var CakeTestPuttyAssembly = TestProject + Directory("bin/Release") + File("Cake.Putty.Tests.dll"); +var AssemblyInfo = Project + File("Properties/AssemblyInfo.cs"); +var CakePuttySln = File("./Cake.Putty.sln"); +var CakePuttyNuspec = File("./Cake.Putty.nuspec"); +var Nupkg = Directory("./nupkg"); + +var target = Argument("target", "Default"); +var version = ""; + +Task("Default") + .Does (() => + { + NuGetRestore (CakePuttySln); + DotNetBuild (CakePuttySln, c => { + c.Configuration = "Release"; + c.Verbosity = Verbosity.Minimal; + }); +}); + +Task("UnitTest") + .IsDependentOn("Default") + .Does(() => + { + NUnit3(CakeTestPuttyAssembly); + }); + +Task("NuGetPack") + .IsDependentOn("GetVersion") + .IsDependentOn("Default") + .IsDependentOn("UnitTest") + .Does (() => +{ + CreateDirectory(Nupkg); + NuGetPack (CakePuttyNuspec, new NuGetPackSettings { + Version = version, + Verbosity = NuGetVerbosity.Detailed, + OutputDirectory = Nupkg, + BasePath = "./", + }); +}); + +Task("GetVersion") + .Does(() => { + var assemblyInfo = ParseAssemblyInfo(AssemblyInfo); + var semVersion = string.Join(".", assemblyInfo.AssemblyVersion.Split('.').Take(3)); + Information("Version {0}", semVersion); + version = semVersion; + }); + +RunTarget (target); diff --git a/src/build.ps1 b/src/build.ps1 new file mode 100644 index 0000000..9c61f56 --- /dev/null +++ b/src/build.ps1 @@ -0,0 +1,140 @@ +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER Experimental +Tells Cake to use the latest Roslyn release. +.PARAMETER WhatIf +Performs a dry run of the build script. +No tasks will be executed. +.PARAMETER Mono +Tells Cake to use the Mono scripting engine. + +.LINK +http://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target = "Default", + [string]$Configuration = "Release", + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity = "Verbose", + [switch]$Experimental, + [Alias("DryRun","Noop")] + [switch]$WhatIf, + [switch]$Mono, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +Write-Host "Preparing to run build script..." + +$PS_SCRIPT_ROOT = split-path -parent $MyInvocation.MyCommand.Definition; +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$NUGET_URL = "http://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" + +# Should we use mono? +$UseMono = ""; +if($Mono.IsPresent) { + Write-Verbose -Message "Using the Mono based scripting engine." + $UseMono = "-mono" +} + +# Should we use the new Roslyn? +$UseExperimental = ""; +if($Experimental.IsPresent -and !($Mono.IsPresent)) { + Write-Verbose -Message "Using experimental version of Roslyn." + $UseExperimental = "-experimental" +} + +# Is this a dry run? +$UseDryRun = ""; +if($WhatIf.IsPresent) { + $UseDryRun = "-dryrun" +} + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { Invoke-WebRequest -Uri http://cakebuild.net/bootstrapper/packages -OutFile $PACKAGES_CONFIG } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { Test-Path $_ } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + (New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) +{ + # Restore packages from NuGet. + Push-Location + Set-Location $TOOLS_DIR + + Write-Verbose -Message "Restoring tools from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location + if ($LASTEXITCODE -ne 0) + { + exit $LASTEXITCODE + } +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + +# Start Cake +Write-Host "Running build script..." +Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs" +exit $LASTEXITCODE