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