├── .gitignore ├── Gruntfile.js ├── LICENSE.txt ├── README.md ├── VERIFICATION.txt ├── WAWSDeploy.sln ├── WAWSDeploy ├── App.config ├── DeploymentArgs.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── PublishSettings.cs ├── WAWSDeploy.csproj ├── WAWSDeploy.nuspec └── WebDeployHelper.cs └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | *.user 4 | *.suo 5 | *.nupkg 6 | App_Data 7 | *.results.xml 8 | /packages 9 | *.GhostDoc.xml 10 | .vs/ 11 | 12 | #node 13 | lib-cov 14 | lcov.info 15 | *.seed 16 | *.log 17 | *.csv 18 | *.dat 19 | *.out 20 | *.pid 21 | *.gz 22 | 23 | pids 24 | logs 25 | results 26 | build 27 | .grunt 28 | 29 | node_modules 30 | 31 | #build 32 | dist 33 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | clean: ["dist"], 7 | shell: { 8 | makeDir: { 9 | command: 'mkdir dist' 10 | } 11 | }, 12 | nugetpack: { 13 | dist: { 14 | src: 'WAWSDeploy/*.nuspec', 15 | dest: 'dist/' 16 | } 17 | }, 18 | nugetpush: { 19 | dist: { 20 | src: 'dist/*.nupkg', 21 | options: { 22 | source: "http://chocolatey.org/" 23 | } 24 | } 25 | } 26 | }); 27 | 28 | grunt.loadNpmTasks('grunt-contrib-clean'); 29 | 30 | // load the shell plugin for cmd goodies 31 | grunt.loadNpmTasks('grunt-shell'); 32 | 33 | // Load the plugin that provides the "nuget" task. 34 | grunt.loadNpmTasks('grunt-nuget'); 35 | 36 | // Default task(s). 37 | grunt.registerTask('default', ['clean', 'shell', 'nugetpack']); 38 | // Build and publish to nuget task 39 | grunt.registerTask('publish', ['clean', 'shell', 'nugetpack', 'nugetpush']); 40 | }; -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | From: http://www.apache.org/licenses/LICENSE-2.0 2 | 3 | LICENSE 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | WAWSDeploy 2 | ========== 3 | 4 | Simple command line tool to deploy a folder or a zip file to an Azure Website using WebDeploy. 5 | See this [post](http://blog.davidebbo.com/2014/03/WAWSDeploy.html) for details. 6 | 7 | **Note**: the `.PublishSettings` file discussed here is the one you can download for a specific Azure Web App via the portal. 8 | 9 | Sample use: 10 | ```PowerShell 11 | WAWSDeploy c:\somefolder mysite.PublishSettings 12 | WAWSDeploy c:\somefile.zip mysite.PublishSettings 13 | ``` 14 | 15 | With optional password argument 16 | ```PowerShell 17 | WAWSDeploy c:\somefolder mysite.PublishSettings /p mypubsettingspassword 18 | WAWSDeploy c:\somefolder mysite.PublishSettings /password mypubsettingspassword 19 | ``` 20 | 21 | Allowing untrusted cert 22 | ```PowerShell 23 | WAWSDeploy c:\somefolder mysite.PublishSettings /au 24 | ``` 25 | 26 | Delete files on the remote host 27 | ```PowerShell 28 | WAWSDeploy c:\somefolder mysite.PublishSettings /d 29 | ``` 30 | 31 | Use checksums instead of timestamps 32 | ```PowerShell 33 | WAWSDeploy c:\somefolder mysite.PublishSettings /c 34 | ``` 35 | 36 | Verbose Logging 37 | ```PowerShell 38 | WAWSDeploy c:\somefolder mysite.PublishSettings /v 39 | ``` 40 | 41 | [What If deployment](http://www.asp.net/web-forms/tutorials/deployment/advanced-enterprise-web-deployment/performing-a-what-if-deployment) 42 | ```PowerShell 43 | WAWSDeploy c:\somefolder mysite.PublishSettings /w 44 | ``` 45 | 46 | Target Path - The virtual directory to deploy to 47 | ```PowerShell 48 | WAWSDeploy C:\somefolder mysite.PublishSettings /t someVirtualDirectoryName 49 | ``` 50 | 51 | Target Path - Using a physical target folder 52 | ```PowerShell 53 | WAWSDeploy C:\somefolder mysite.PublishSettings /t d:\home\site\blah 54 | ``` 55 | 56 | App offline - Attempt to turn ASP.Net application off before deployment. 57 | ```PowerShell 58 | WAWSDeploy C:\somefolder mysite.PublishSettings /o 59 | ``` 60 | 61 | Setup a higher number of retries in case of deployment failure (WebDeployment default is 5) 62 | ```PowerShell 63 | WAWSDeploy C:\somefolder mysite.PublishSettings /r 10 64 | ``` 65 | 66 | Setup the interval (in milliseconds) between each deployment attempts in case of deployment failure (WebDeployment default is 1000ms) 67 | ```PowerShell 68 | WAWSDeploy C:\somefolder mysite.Publishsettings /i 6000 69 | ``` 70 | 71 | Skip the app_data directory during the deployment. 72 | ```PowerShell 73 | WAWSDeploy C:\somefolder mysite.PublishSettings /sa 74 | ``` 75 | 76 | User defined folders regular expressions that should be skipped during deployment. 77 | ```PowerShell 78 | WAWSDeploy C:\somefolder mysite.PublishSettings /sf .*my_tools .*local_cache 79 | ``` 80 | 81 | 82 | 83 | ## To build this project 84 | 85 | npm install -g grunt-cli 86 | npm install 87 | grunt 88 | 89 | ## History 90 | 91 | ### 7/10/2019: v1.10 92 | 93 | Add /SkipAppData and /SkipFoldersRegexps switches 94 | 95 | ### 7/5/2019: v1.9 96 | 97 | Add /RetryAttempts and /RetryInterval switches 98 | 99 | ### 2/4/2017: v1.8 100 | 101 | Add /AppOffline switch 102 | 103 | ### 5/25/2016: v1.7 104 | 105 | Add /TargetPath switch 106 | 107 | ### 1/9/2015: v1.6 108 | 109 | Add /TargetPath switch 110 | 111 | ### 11/12/2014: v1.5 112 | 113 | Add -t switch to allow targeted deployment to a specific virtual directory. 114 | 115 | ### 5/1/2014: v1.4 116 | 117 | Add -WhatIf and -deleteexistingfiles switches 118 | 119 | ### 3/26/2014: v1.3 120 | 121 | Add support for untrusted certs. 122 | 123 | ### 3/6/2014: v1.2 124 | 125 | Support optionally passing password 126 | 127 | ### 3/5/2014: v1.1 128 | 129 | Support publishing from zip files 130 | 131 | ### 3/5/2014: v1.0 132 | 133 | Original version 134 | -------------------------------------------------------------------------------- /VERIFICATION.txt: -------------------------------------------------------------------------------- 1 | VERIFICATION 2 | Verification is intended to assist the Chocolatey moderators and community 3 | in verifying that this package's contents are trustworthy. 4 | 5 | This package is published by the WAWSDeploy Project itself. -------------------------------------------------------------------------------- /WAWSDeploy.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28922.388 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WAWSDeploy", "WAWSDeploy\WAWSDeploy.csproj", "{2C29AEA8-CD1D-407A-989F-0E56ED4CA353}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{95E63752-B477-412E-8B47-E5551B0ACDE1}" 9 | ProjectSection(SolutionItems) = preProject 10 | Gruntfile.js = Gruntfile.js 11 | README.md = README.md 12 | EndProjectSection 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {2C29AEA8-CD1D-407A-989F-0E56ED4CA353}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {2C29AEA8-CD1D-407A-989F-0E56ED4CA353}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {2C29AEA8-CD1D-407A-989F-0E56ED4CA353}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {2C29AEA8-CD1D-407A-989F-0E56ED4CA353}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {17258024-056F-45CC-9D97-917323A60790} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /WAWSDeploy/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /WAWSDeploy/DeploymentArgs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Args; 3 | using System.ComponentModel; 4 | using System.Diagnostics; 5 | 6 | namespace WAWSDeploy 7 | { 8 | /// 9 | /// Arguments used for publishing 10 | /// 11 | public class DeploymentArgs 12 | { 13 | /// 14 | /// Gets or sets the folder. 15 | /// 16 | /// The folder. 17 | [ArgsMemberSwitch(0)] 18 | public string Folder { get; set; } 19 | 20 | /// 21 | /// Gets or sets the publish settings file. 22 | /// 23 | /// The publish settings file. 24 | [ArgsMemberSwitch(1)] 25 | public string PublishSettingsFile { get; set; } 26 | 27 | /// 28 | /// Gets or sets the password. 29 | /// 30 | /// The password. 31 | [ArgsMemberSwitch("p", "password", "pw"), 32 | Description("provide the password if it's not in the profile (default: use from the publish settings).")] 33 | public string Password { get; set; } 34 | 35 | /// 36 | /// Gets or sets the allowUntrusted flag 37 | /// 38 | /// true or false 39 | [ArgsMemberSwitch("u", "allowuntrusted", "au"), 40 | Description("skip cert verification (default: false).")] 41 | public bool AllowUntrusted { get; set; } 42 | 43 | [ArgsMemberSwitch("d", "deleteexistingfiles", "def"), 44 | Description("delete target files that don't exist at the source (default: false).")] 45 | public bool DeleteExistingFiles { get; set; } 46 | 47 | [ArgsMemberSwitch("v", "verbose", "vb"), 48 | Description("Verbose mode (default: false)")] 49 | public bool Verbose { get; set; } 50 | 51 | [ArgsMemberSwitch("w", "whatif", "wi"), 52 | Description("don't actually perform the publishing (default: false).")] 53 | public bool WhatIf { get; set; } 54 | 55 | [ArgsMemberSwitch("t", "targetpath", "tp"), 56 | Description("the virtual or physical directory to deploy to.")] 57 | public string TargetPath { get; set; } 58 | 59 | [ArgsMemberSwitch("c", "usechecksum", "cs"), 60 | Description("use checksum (default: false).")] 61 | public bool UseChecksum { get; set; } 62 | 63 | [ArgsMemberSwitch("o", "appoffline", "off"), 64 | Description( 65 | "attempt to automatically take an ASP.Net application offline before publishing to it (default: false).")] 66 | public bool AppOffline { get; set; } 67 | 68 | [ArgsMemberSwitch("r", "retryattempts", "ra"), 69 | Description("number of deployment retry attempts in case of failure (default: 5).")] 70 | public int? RetryAttempts { get; set; } 71 | 72 | [ArgsMemberSwitch("i", "retryinterval", "ri"), 73 | Description("retry interval in ms between attempts in case of failure (default: 1000).")] 74 | public int? RetryInterval { get; set; } 75 | 76 | [ArgsMemberSwitch("a", "skipappdata", "sa"), 77 | Description("Skip the application data during the deployment... (default: false).")] 78 | public bool SkipAppData { get; set; } 79 | 80 | [ArgsMemberSwitch("s", "skipfoldersregexps", "sf"), 81 | Description( 82 | "Skip the folders matching the given regular expressions during the deployment... (default: null).")] 83 | public IEnumerable SkipFoldersRegexps { get; set; } 84 | 85 | public TraceLevel TraceLevel 86 | { 87 | get 88 | { 89 | if (Verbose) 90 | return TraceLevel.Verbose; 91 | 92 | return TraceLevel.Off; 93 | } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /WAWSDeploy/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Args.Help; 4 | using Args.Help.Formatters; 5 | using Microsoft.Web.Deployment; 6 | 7 | namespace WAWSDeploy 8 | { 9 | class Program 10 | { 11 | static void Main(string[] args) 12 | { 13 | var model = Args.Configuration.Configure(); 14 | if (args.Length < 2) 15 | { 16 | WriteLine(@"WAWSDeploy version {0}", typeof(Program).Assembly.GetName().Version); 17 | var help = new HelpProvider(); 18 | new ConsoleHelpFormatter().WriteHelp(help.GenerateModelHelp(model), Console.Out); 19 | return; 20 | } 21 | 22 | // parse the command line args 23 | var command = model.CreateAndBind(args); 24 | 25 | try 26 | { 27 | var webDeployHelper = new WebDeployHelper(); 28 | 29 | webDeployHelper.DeploymentTraceEventHandler += Trace; 30 | 31 | WriteLine("Starting deployment..."); 32 | DeploymentChangeSummary changeSummary = webDeployHelper.DeployContentToOneSite( 33 | command.Folder, 34 | command.PublishSettingsFile, 35 | command.Password, 36 | command.AllowUntrusted, 37 | !command.DeleteExistingFiles, 38 | command.TraceLevel, 39 | command.WhatIf, 40 | command.TargetPath, 41 | command.UseChecksum, 42 | command.AppOffline, 43 | command.RetryAttempts, 44 | command.RetryInterval, 45 | command.SkipAppData, 46 | command.SkipFoldersRegexps 47 | ); 48 | 49 | WriteLine("BytesCopied: {0}", changeSummary.BytesCopied); 50 | WriteLine("Added: {0}", changeSummary.ObjectsAdded); 51 | WriteLine("Updated: {0}", changeSummary.ObjectsUpdated); 52 | WriteLine("Deleted: {0}", changeSummary.ObjectsDeleted); 53 | WriteLine("Errors: {0}", changeSummary.Errors); 54 | WriteLine("Warnings: {0}", changeSummary.Warnings); 55 | WriteLine("Total changes: {0}", changeSummary.TotalChanges); 56 | } 57 | catch (Exception e) 58 | { 59 | WriteLine("Deployment failed: {0}", e.Message); 60 | Environment.ExitCode = 1; 61 | } 62 | } 63 | 64 | static void Trace(object sender, DeploymentTraceEventArgs e) 65 | { 66 | Console.WriteLine(e.Message); 67 | } 68 | 69 | static void WriteLine(string message, params object[] args) 70 | { 71 | Console.WriteLine(String.Format(message, args)); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /WAWSDeploy/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WAWSDeploy")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WAWSDeploy")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("47dfd509-a1b7-46bc-ac78-c9a16bde9533")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.10.0.0")] 36 | [assembly: AssemblyFileVersion("1.10.0.0")] 37 | -------------------------------------------------------------------------------- /WAWSDeploy/PublishSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Diagnostics; 5 | using System.Text; 6 | using System.Xml; 7 | using System.Xml.XPath; 8 | 9 | namespace WAWSDeploy 10 | { 11 | public class PublishSettings 12 | { 13 | private const string ProfileRootNode = "publishData"; 14 | private const string ProfileNode = "publishProfile"; 15 | private const string PublishMethod = "publishMethod"; 16 | private const string MSDeployHandler = "msdeploy.axd"; 17 | private const string DefaultPort = ":8172"; 18 | 19 | private string _publishUrlRaw = string.Empty; 20 | private string _computerName = string.Empty; 21 | private string _siteName = string.Empty; 22 | private string _userName = string.Empty; 23 | private string _password = string.Empty; 24 | private string _destinationAppUrl = string.Empty; 25 | private string _sqlDbConnectionString = string.Empty; 26 | private string _mysqlDbConnectionString = string.Empty; 27 | private bool _allowUntrusted; 28 | private bool? _useNTLM = null; 29 | private IDictionary _databases; 30 | private PublishSettingsRemoteAgent _agentType = PublishSettingsRemoteAgent.None; 31 | private NameValueCollection _otherAttributes; 32 | 33 | public PublishSettings(string filePath) 34 | { 35 | XmlDocument doc = new XmlDocument(); 36 | doc.Load(filePath); 37 | Load(doc.CreateNavigator()); 38 | } 39 | 40 | /// 41 | /// Used for unit testing 42 | /// 43 | internal PublishSettings() 44 | { 45 | } 46 | 47 | internal void Load(XPathNavigator nav) 48 | { 49 | Debug.Assert(nav != null, "nav should not be null"); 50 | bool foundPublishSettings = false; 51 | string name = string.Empty; 52 | string value = string.Empty; 53 | 54 | nav.MoveToFirstChild(); 55 | 56 | if (nav.Name != ProfileRootNode) 57 | { 58 | throw new XmlException(string.Format("Expected root node to be '{0}'", ProfileRootNode)); 59 | } 60 | 61 | bool fContinue = nav.MoveToFirstChild(); 62 | 63 | if (nav.Name != ProfileNode) 64 | { 65 | throw new XmlException(string.Format("Expected first child node to be '{0}'", ProfileNode)); 66 | } 67 | 68 | while (fContinue) 69 | { 70 | string publishMethod = nav.GetAttribute(PublishMethod, string.Empty); 71 | 72 | if (string.Equals(publishMethod, "MSDeploy", StringComparison.OrdinalIgnoreCase)) 73 | { 74 | bool hasMoreAttributes = nav.MoveToFirstAttribute(); 75 | 76 | while (hasMoreAttributes) 77 | { 78 | if (string.Equals(nav.Name, "publishUrl", StringComparison.OrdinalIgnoreCase)) 79 | { 80 | _publishUrlRaw = nav.Value; 81 | } 82 | else if (string.Equals(nav.Name, "msdeploySite", StringComparison.OrdinalIgnoreCase)) 83 | { 84 | _siteName = nav.Value; 85 | } 86 | else if (string.Equals(nav.Name, "userName", StringComparison.OrdinalIgnoreCase)) 87 | { 88 | _userName = nav.Value; 89 | } 90 | else if (string.Equals(nav.Name, "userPWD", StringComparison.OrdinalIgnoreCase)) 91 | { 92 | _password = nav.Value; 93 | } 94 | else if (string.Equals(nav.Name, "destinationAppUrl", StringComparison.OrdinalIgnoreCase)) 95 | { 96 | _destinationAppUrl = nav.Value; 97 | } 98 | else if (string.Equals(nav.Name, "agentType", StringComparison.OrdinalIgnoreCase)) 99 | { 100 | string agentType = nav.Value; 101 | try 102 | { 103 | _agentType = (PublishSettingsRemoteAgent)Enum.Parse(typeof(PublishSettingsRemoteAgent), agentType, true); 104 | } 105 | catch (ArgumentException ex) 106 | { 107 | throw new XmlException( 108 | string.Format("Invalid agent type. Valid options are '{0}'", 109 | string.Join(", ", Enum.GetNames(typeof(PublishSettingsRemoteAgent)))), 110 | ex); 111 | } 112 | } 113 | else if (string.Equals(nav.Name, "SQLServerDBConnectionString", StringComparison.OrdinalIgnoreCase)) 114 | { 115 | _sqlDbConnectionString = nav.Value; 116 | } 117 | else if (string.Equals(nav.Name, "mySQLDBConnectionString", StringComparison.OrdinalIgnoreCase)) 118 | { 119 | _mysqlDbConnectionString = nav.Value; 120 | } 121 | else if (string.Equals(nav.Name, "msdeployAllowUntrustedCertificate", StringComparison.OrdinalIgnoreCase)) 122 | { 123 | string allowUntrustedValue = nav.Value; 124 | _allowUntrusted = string.Equals(allowUntrustedValue, "true", StringComparison.OrdinalIgnoreCase) ? true : false; 125 | } 126 | else if (string.Equals(nav.Name, "useNTLM", StringComparison.OrdinalIgnoreCase)) 127 | { 128 | string useNTLM = nav.Value; 129 | if (!string.IsNullOrEmpty(useNTLM)) 130 | { 131 | _useNTLM = Convert.ToBoolean(useNTLM); 132 | } 133 | 134 | // User didn't specify a value so we'll automatically figure it out a little later based on agent type. 135 | } 136 | else if (!string.Equals(nav.Name, PublishMethod, StringComparison.OrdinalIgnoreCase)) 137 | { 138 | OtherAttributes.Add(nav.Name, nav.Value); 139 | } 140 | 141 | hasMoreAttributes = nav.MoveToNextAttribute(); 142 | } 143 | 144 | // Move to the publishProfile node 145 | nav.MoveToParent(); 146 | if (nav.MoveToFirstChild()) 147 | { 148 | do 149 | { 150 | if (string.Equals(nav.Name, "databases", StringComparison.OrdinalIgnoreCase)) 151 | { 152 | AddDatabases(Databases, nav); 153 | } 154 | } while (nav.MoveToNext()); 155 | 156 | // Move to publishProfile node 157 | nav.MoveToParent(); 158 | } 159 | 160 | foundPublishSettings = true; 161 | break; 162 | } 163 | 164 | //move to next publishprofile node 165 | fContinue = nav.MoveToNext(); 166 | } 167 | 168 | if (!foundPublishSettings) 169 | { 170 | throw new XmlException("Could not find MSDeploy publish settings"); 171 | } 172 | } 173 | 174 | internal static void AddDatabases( 175 | IDictionary databases, 176 | XPathNavigator nav) 177 | { 178 | if (nav.MoveToFirstChild()) 179 | { 180 | do 181 | { 182 | if (string.Equals(nav.Name, "add", StringComparison.OrdinalIgnoreCase)) 183 | { 184 | PublishSettingsDatabase database = new PublishSettingsDatabase() 185 | { 186 | Name = nav.GetAttribute("name", string.Empty), 187 | ConnectionString = nav.GetAttribute("connectionString", string.Empty), 188 | ProviderName = nav.GetAttribute("providerName", string.Empty), 189 | Type = nav.GetAttribute("type", string.Empty), 190 | TargetDatabase = nav.GetAttribute("targetDatabaseEngineType", string.Empty), 191 | TargetServerVersion = nav.GetAttribute("targetServerVersion", string.Empty) 192 | }; 193 | 194 | if (string.IsNullOrEmpty(database.Name)) 195 | { 196 | throw new XmlException("Database 'add' element must contain a 'Name' attribute"); 197 | } 198 | 199 | databases.Add(database.Name, database); 200 | } 201 | } 202 | while (nav.MoveToNext()); 203 | 204 | // Move back to the databases node 205 | nav.MoveToParent(); 206 | } 207 | } 208 | 209 | public string ComputerName 210 | { 211 | get 212 | { 213 | if (string.IsNullOrEmpty(_computerName) && !string.IsNullOrEmpty(_publishUrlRaw)) 214 | { 215 | if (AgentType == PublishSettingsRemoteAgent.WMSvc || 216 | AgentType == PublishSettingsRemoteAgent.None) 217 | { 218 | _computerName = GetWmsvcUrl(_publishUrlRaw, _siteName); 219 | } 220 | else 221 | { 222 | _computerName = _publishUrlRaw; 223 | } 224 | } 225 | 226 | return _computerName; 227 | } 228 | 229 | internal set 230 | { 231 | _computerName = value; 232 | } 233 | } 234 | 235 | public string PublishUrlRaw 236 | { 237 | get 238 | { 239 | return _publishUrlRaw; 240 | } 241 | 242 | internal set 243 | { 244 | _publishUrlRaw = value; 245 | } 246 | } 247 | 248 | public bool AllowUntrusted 249 | { 250 | get 251 | { 252 | return _allowUntrusted; 253 | } 254 | 255 | internal set 256 | { 257 | _allowUntrusted = value; 258 | } 259 | } 260 | 261 | public string SiteName 262 | { 263 | get 264 | { 265 | return _siteName; 266 | } 267 | 268 | internal set 269 | { 270 | _siteName = value; 271 | } 272 | } 273 | 274 | public string DestinationAppUrl 275 | { 276 | get 277 | { 278 | return _destinationAppUrl; 279 | } 280 | 281 | internal set 282 | { 283 | _destinationAppUrl = value; 284 | } 285 | } 286 | 287 | public string Username 288 | { 289 | get 290 | { 291 | return _userName; 292 | } 293 | 294 | internal set 295 | { 296 | _userName = value; 297 | } 298 | } 299 | 300 | public string Password 301 | { 302 | get 303 | { 304 | return _password; 305 | } 306 | 307 | internal set 308 | { 309 | _password = value; 310 | } 311 | } 312 | 313 | public string MySqlDBConnectionString 314 | { 315 | get 316 | { 317 | return _mysqlDbConnectionString; 318 | } 319 | 320 | internal set 321 | { 322 | _mysqlDbConnectionString = value; 323 | } 324 | } 325 | 326 | public string SqlDBConnectionString 327 | { 328 | get 329 | { 330 | return _sqlDbConnectionString; 331 | } 332 | 333 | internal set 334 | { 335 | _sqlDbConnectionString = value; 336 | } 337 | } 338 | 339 | public IDictionary Databases 340 | { 341 | get 342 | { 343 | if (_databases == null) 344 | { 345 | _databases = 346 | new Dictionary(StringComparer.OrdinalIgnoreCase); 347 | } 348 | 349 | return _databases; 350 | } 351 | 352 | internal set 353 | { 354 | _databases = value; 355 | } 356 | } 357 | 358 | public PublishSettingsRemoteAgent AgentType 359 | { 360 | get 361 | { 362 | return _agentType; 363 | } 364 | 365 | internal set 366 | { 367 | _agentType = value; 368 | } 369 | } 370 | 371 | public bool UseNTLM 372 | { 373 | get 374 | { 375 | 376 | if (!_useNTLM.HasValue) 377 | { 378 | if (_agentType == PublishSettingsRemoteAgent.WMSvc || 379 | _agentType == PublishSettingsRemoteAgent.None) 380 | { 381 | _useNTLM = false; 382 | } 383 | else 384 | { 385 | _useNTLM = true; 386 | } 387 | } 388 | 389 | return _useNTLM.Value; 390 | } 391 | 392 | internal set 393 | { 394 | _useNTLM = value; 395 | } 396 | } 397 | 398 | public NameValueCollection OtherAttributes 399 | { 400 | get 401 | { 402 | if (_otherAttributes == null) 403 | { 404 | _otherAttributes = new NameValueCollection(); 405 | } 406 | 407 | return _otherAttributes; 408 | } 409 | 410 | internal set 411 | { 412 | _otherAttributes = value; 413 | } 414 | } 415 | 416 | internal static string GetWmsvcUrl(string publishUrl, string siteName) 417 | { 418 | string computerName = publishUrl; 419 | 420 | if (!computerName.StartsWith("http", StringComparison.OrdinalIgnoreCase)) 421 | { 422 | // Some examples of what we might expect here: 423 | // foo.com:443/MSDeploy/msdeploy.axd 424 | // foo.com/MSDeploy/msdeploy.axd 425 | // foo.com:443 426 | // foo.com 427 | 428 | computerName = InsertPortIfNotSpecified(computerName); 429 | computerName = AppendHandlerIfNotSpecified(computerName); 430 | 431 | if (!string.IsNullOrEmpty(siteName)) 432 | { 433 | computerName = string.Format("https://{0}?site={1}", computerName, siteName); 434 | } 435 | else 436 | { 437 | computerName = string.Format("https://{0}", computerName); 438 | } 439 | } 440 | 441 | return computerName; 442 | } 443 | 444 | internal static string AppendHandlerIfNotSpecified(string publishUrl) 445 | { 446 | if (!publishUrl.EndsWith(MSDeployHandler, StringComparison.OrdinalIgnoreCase)) 447 | { 448 | if (publishUrl.EndsWith("/")) 449 | { 450 | publishUrl = publishUrl + MSDeployHandler; 451 | } 452 | else 453 | { 454 | publishUrl = publishUrl + "/" + MSDeployHandler; 455 | } 456 | } 457 | 458 | return publishUrl; 459 | } 460 | 461 | internal static string InsertPortIfNotSpecified(string publishUrl) 462 | { 463 | string[] colonParts = publishUrl.Split(new char[] { ':' }); 464 | 465 | if (colonParts.Length == 1) 466 | { 467 | // No port was specified so we need to add it in 468 | int slashIndex = publishUrl.IndexOf('/'); 469 | if (slashIndex > -1) 470 | { 471 | //publishUrl = InsertPortBeforeSlash(publishUrl, slashIndex); 472 | publishUrl = publishUrl.Insert(slashIndex, DefaultPort); 473 | } 474 | else 475 | { 476 | publishUrl = publishUrl + DefaultPort; 477 | } 478 | } 479 | 480 | if (colonParts.Length > 1) 481 | { 482 | // It's possible that a port was specified, but we're not sure. Apps like Monaco do weird 483 | // things like put colon characters in the path and who knows what might happen in the future. 484 | // We're being extra careful here to make sure that we only look for ports after the hostname. 485 | // This means right after a colon, but never following ANY '/' characters. 486 | 487 | // Examples of colonParts[0] might be 488 | // test.com 489 | // foo.com/bar 490 | int slashIndex = colonParts[0].IndexOf('/'); 491 | if (slashIndex > -1) 492 | { 493 | // Since a slash was found before the first colon, we know that the first colon was 494 | // not used for the port. Therefore we need to inject the default port before the first slash 495 | colonParts[0] = colonParts[0].Insert(slashIndex, DefaultPort); 496 | publishUrl = string.Join(":", colonParts); 497 | } 498 | } 499 | 500 | return publishUrl; 501 | } 502 | } 503 | 504 | public enum PublishSettingsRemoteAgent : int 505 | { 506 | WMSvc = 0, 507 | MSDepSvc, 508 | TempAgent, 509 | None, 510 | } 511 | 512 | public class PublishSettingsDatabase 513 | { 514 | public string Name { get; set; } 515 | public string ConnectionString { get; set; } 516 | public string ProviderName { get; set; } 517 | public string Type { get; set; } 518 | public string TargetDatabase { get; set; } 519 | public string TargetServerVersion { get; set; } 520 | } 521 | 522 | } 523 | -------------------------------------------------------------------------------- /WAWSDeploy/WAWSDeploy.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2C29AEA8-CD1D-407A-989F-0E56ED4CA353} 8 | Exe 9 | Properties 10 | WAWSDeploy 11 | WAWSDeploy 12 | v4.6.1 13 | 512 14 | true 15 | 16 | 17 | 18 | AnyCPU 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | 38 | False 39 | ..\..\..\..\Program Files\IIS\Microsoft Web Deploy V3\Microsoft.Web.Deployment.dll 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 1.2.1 64 | 65 | 66 | 67 | 74 | -------------------------------------------------------------------------------- /WAWSDeploy/WAWSDeploy.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WAWSDeploy 5 | WAWSDeploy 6 | 1.10.0 7 | David Ebbo 8 | A simple tool to publish a folder to an Azure Web Site 9 | A simple tool to publish a folder to an Azure Web Site 10 | https://github.com/davidebbo/WAWSDeploy 11 | azure websites webdeploy msdeploy 12 | http://www.apache.org/licenses/LICENSE-2.0.html 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /WAWSDeploy/WebDeployHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Microsoft.Web.Deployment; 5 | using System.Net; 6 | using System.Security.Cryptography.X509Certificates; 7 | using System.Net.Security; 8 | using System.Diagnostics; 9 | 10 | namespace WAWSDeploy 11 | { 12 | public class WebDeployHelper 13 | { 14 | public event EventHandler DeploymentTraceEventHandler; 15 | 16 | /// 17 | /// Deploys the content to one site. 18 | /// 19 | /// The content path. 20 | /// The publish settings file. 21 | /// The password (default is nul). 22 | /// Deploy even if destination certificate is untrusted (default is false) 23 | /// Do not delete target files that don't exist at the source if true (default is true) 24 | /// TraceLevel to be used for log verbosity. Default is TraceLevel.Off 25 | /// Don't perform the actual publishing if true (default is false). 26 | /// The virtual or physical directory to deploy to (defaults to null). 27 | /// Use checksum during deployment. (default is false). 28 | /// Attempts to automatically take the application offline before synching, defaults to false. 29 | /// Allows to set the number of sync retry attempts (default is null and therefore 5 attempts). 30 | /// Allows to set the time interval (in ms.) between sync retry attempts (default is null and therefore 1000 ms). 31 | /// Allows to skip the app_data folder during deployment (default is false). 32 | /// Regular expressions of folders that should be ignored during deployment. 33 | /// DeploymentChangeSummary. 34 | public DeploymentChangeSummary DeployContentToOneSite(string sourcePath, 35 | string publishSettingsFile, 36 | string password = null, 37 | bool allowUntrusted = false, 38 | bool doNotDelete = true, 39 | TraceLevel traceLevel = TraceLevel.Off, 40 | bool whatIf = false, 41 | string targetPath = null, 42 | bool useChecksum = false, 43 | bool appOfflineEnabled = false, 44 | int? retryAttempts = null, 45 | int? retryInterval = null, 46 | bool skipAppData = false, 47 | IEnumerable skipFoldersRegexps = null) 48 | { 49 | sourcePath = Path.GetFullPath(sourcePath); 50 | 51 | var sourceBaseOptions = new DeploymentBaseOptions(); 52 | 53 | string destinationPath = SetBaseOptions(publishSettingsFile, out var destBaseOptions, allowUntrusted); 54 | 55 | destBaseOptions.TraceLevel = traceLevel; 56 | destBaseOptions.Trace += destBaseOptions_Trace; 57 | 58 | // use the password from the command line args if provided 59 | if (!string.IsNullOrEmpty(password)) 60 | destBaseOptions.Password = password; 61 | 62 | if (retryAttempts != null) 63 | destBaseOptions.RetryAttempts = (int) retryAttempts; 64 | 65 | if (retryInterval != null) 66 | destBaseOptions.RetryInterval = (int) retryInterval; 67 | 68 | var sourceProvider = DeploymentWellKnownProvider.ContentPath; 69 | var targetProvider = DeploymentWellKnownProvider.ContentPath; 70 | 71 | // If a target path was specified, it could be virtual or physical 72 | if (!string.IsNullOrEmpty(targetPath)) 73 | { 74 | if (Path.IsPathRooted(targetPath)) 75 | { 76 | // If it's rooted (e.g. d:\home\site\foo), use DirPath 77 | sourceProvider = targetProvider = DeploymentWellKnownProvider.DirPath; 78 | 79 | destinationPath = targetPath; 80 | } 81 | else 82 | { 83 | // It's virtual, so append it to what we got from the publish profile 84 | destinationPath += "/" + targetPath; 85 | } 86 | } 87 | 88 | // If the content path is a zip file, use the Package provider 89 | if (Path.GetExtension(sourcePath).Equals(".zip", StringComparison.OrdinalIgnoreCase)) 90 | { 91 | // For some reason, we can't combine a zip with a physical target path 92 | // Maybe there is some way to make it work? 93 | if (targetProvider == DeploymentWellKnownProvider.DirPath) 94 | { 95 | throw new Exception("A source zip file can't be used with a physical target path"); 96 | } 97 | 98 | sourceProvider = DeploymentWellKnownProvider.Package; 99 | } 100 | 101 | var syncOptions = new DeploymentSyncOptions 102 | { 103 | DoNotDelete = doNotDelete, 104 | WhatIf = whatIf, 105 | UseChecksum = useChecksum 106 | }; 107 | if (appOfflineEnabled) AddDeploymentRule(syncOptions, "AppOffline"); 108 | 109 | var skipRegexps = new List(); 110 | if (skipAppData) 111 | { 112 | skipRegexps.Add(".*app_data"); 113 | } 114 | 115 | if (skipFoldersRegexps != null) 116 | { 117 | skipRegexps.AddRange(skipFoldersRegexps); 118 | } 119 | AddSkippedFoldersToWebDeploy(skipRegexps, sourceBaseOptions, destBaseOptions); 120 | 121 | // Publish the content to the remote site 122 | using (var deploymentObject = DeploymentManager.CreateObject(sourceProvider, sourcePath, sourceBaseOptions)) 123 | { 124 | // Note: would be nice to have an async flavor of this API... 125 | 126 | return deploymentObject.SyncTo(targetProvider, destinationPath, destBaseOptions, syncOptions); 127 | } 128 | } 129 | 130 | private void AddDeploymentRule(DeploymentSyncOptions syncOptions, string name) 131 | { 132 | var rules = DeploymentSyncOptions.GetAvailableRules(); 133 | if (rules.TryGetValue(name, out var newRule)) 134 | { 135 | syncOptions.Rules.Add(newRule); 136 | } 137 | } 138 | 139 | private void AddSkippedFoldersToWebDeploy(IEnumerable skippedFoldersRegexps, 140 | DeploymentBaseOptions sourceBaseOptions, DeploymentBaseOptions destinationBaseOptions) 141 | { 142 | foreach (var skippedFoldersRegexp in skippedFoldersRegexps) 143 | { 144 | var skippedDirective = new DeploymentSkipDirective($"Skip_{skippedFoldersRegexp}", 145 | $"objectName=dirPath,absolutePath={skippedFoldersRegexp}", true); 146 | sourceBaseOptions.SkipDirectives.Add(skippedDirective); 147 | destinationBaseOptions.SkipDirectives.Add(skippedDirective); 148 | } 149 | } 150 | 151 | void destBaseOptions_Trace(object sender, DeploymentTraceEventArgs e) 152 | { 153 | DeploymentTraceEventHandler?.Invoke(sender, e); 154 | } 155 | 156 | private string SetBaseOptions(string publishSettingsPath, out DeploymentBaseOptions deploymentBaseOptions, 157 | bool allowUntrusted) 158 | { 159 | PublishSettings publishSettings = new PublishSettings(publishSettingsPath); 160 | 161 | deploymentBaseOptions = new DeploymentBaseOptions 162 | { 163 | ComputerName = publishSettings.ComputerName, 164 | UserName = publishSettings.Username, 165 | Password = publishSettings.Password, 166 | AuthenticationType = publishSettings.UseNTLM ? "ntlm" : "basic" 167 | }; 168 | 169 | if (allowUntrusted || publishSettings.AllowUntrusted) 170 | { 171 | ServicePointManager.ServerCertificateValidationCallback = AllowCertificateCallback; 172 | } 173 | 174 | return publishSettings.SiteName; 175 | } 176 | 177 | private static bool AllowCertificateCallback(object sender, X509Certificate cert, X509Chain chain, 178 | SslPolicyErrors errors) 179 | { 180 | return true; 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "WAWSDeploy", 3 | "version": "1.5.0", 4 | "homepage": "https://github.com/davidebbo/WAWSDeploy", 5 | "keywords": [ 6 | "msdeploy", 7 | "webdeploy", 8 | "waws" 9 | ], 10 | "dependencies": {}, 11 | "optionalDependencies": {}, 12 | "devDependencies": { 13 | "grunt": "~0.4.2", 14 | "grunt-contrib-clean": "~0.5.0", 15 | "grunt-contrib-copy": "~0.4.1", 16 | "grunt-shell": "~0.6.4", 17 | "grunt-nuget": "~0.1.1" 18 | } 19 | } 20 | --------------------------------------------------------------------------------