Having a good Continuous Integration setup can be one of the highlights of any developers daily grind. Regardless, it can be seen as almost pointless if your automated deployment setup still needs a physical person to upload the files to your server if it is offsite. Adding FTP/SFTP to your CI process is the solution to this.
This is the first post in a series on Continuous Integration Tips & Tricks in follow up to a post i made about setting up Continuous Integration Automated Deployment with Web Deployment Projects & Team City.
A solution for FTP deployment is below, but for FTP there are two solutions – one using simple MSBUILD and external software, and the other using an additional MSBUILD task pack.
Solution #1 Simple directory upload
The first solution i will draw your attention to is for people simply wanting to copy all the build output to a remote host. In this instance, to keep things simple, i recommend using the MSBUILD community tasks pack. The reason for this is that it has a task called FtpUploadDirectoryContent that allows you to push a folder in one task. This can be great if you simply want to push a folders contents up to an FTP site and overwrite the content already there.
Download the MSBUILD Community tasks pack from here.
Install the above on both your local development machine (so that you can debug the build) and the build server.
Add the following task to your MSBUILD file. If you are using Web Deployment projects as per my original tutorial, simply make sure the task name is set to AfterBuild so that it fires after the build is complete. In this example i also use the default path $(OutputPath) that is part of the web deployment project – if you are using a simple MSBUILD script, then you will need to replace this with the path to your build output.
<Import Project="$(MSBuildExtensionsPath32)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/> <Target Name="AfterBuild"> <PropertyGroup> <ftpHost>ftp.yourwebsiteurl.com</ftpHost> <ftpUser>YourFtpUsername</ftpUser> <ftpPass>YourFtpPassword</ftpPass> </PropertyGroup> <FtpUploadDirectoryContent ServerHost="$(ftpHost)" Port="41" Username="$(ftpUser)" Password="$(ftpPass)" LocalDirectory="$(OutputPath)" RemoteDirectory="" Recursive="true" /> </Target>
Solution #2 for More customised or complex deployments
Another solution that i would probably recommend more because it makes complicated deployments really easy is to use an MSBUILD EXEC task to fire a command line ftp client.
In my example i am going to use WinSCP as i find it the easiest to work with, and it supports all the main protocols ftp, ftps, sftp. The fact that it is FREE helps too :-) The reason i am such a fan is because it allows for scripting, allowing for complex deployments where many actions can take place. I will show you a simple deployment, but set it up in a way that makes adding extra automation really easy.
Download WinSCP form here.
Install it on both your build server and local development machine (to test the deployment).
Create a new file in your project and name it FtpDeployment.config and inside it put the following example list of commands (the WinSCP scripting documentation can be found here).
option batch abort option confirm off open ftp://myUsername:myPassword@ftp.myftpsite.com put %1%\* rm FtpDeployment.config exit
This basically tells the FTP client to:
- Turn batch support on
- Not ask for confirmation to overwrite files
- Open a connection to our FTP server
- Put the entire build folder’s contents
- Delete the file FtpDeployment.config from the destination (we don’t want our FTP access details sitting on the server for review by anyone)
Now add the following post build event to your MSBUILD/Web deployment project file.
<Target Name="AfterBuild"> <!-- Set the path to your FTP program (winscp) --> <PropertyGroup> <PathToWinSCP>"C:\Program Files (x86)\WinSCP\winscp.exe"</PathToWinSCP> </PropertyGroup> <!-- Get the date as a string for our log filename--> <GetDate Format="yyyyMMdd"> <Output PropertyName="DateString" TaskParameter="Date"/> </GetDate> <!-- Convert the path to an absolute path --> <ConvertToAbsolutePath Paths="$(OutputPath)"> <Output TaskParameter="AbsolutePaths" PropertyName="OutputPath"/> </ConvertToAbsolutePath> <!-- Fire WinSCP and give it your script files name as well as passing it the parameter to this build --> <Exec Command="$(PathToWinSCP) /script=$(OutputPath)Deployment\FtpDeployment.config /parameter $(OutputPath) /log=$(OutputPath)FtpLog-$(DateString).txt" /> </Target>
This will fire WinSCP and pass it all the parameters it will need:
- The script file to run (the one we created above)
- The path that the current build’s output is
- The log file FtpLog-YYYYMMDD.txt to write the output of the session to for later review
Things to consider now you have a sweet deployment strategy
Now that we have an automated system to deploy our sites by FTP, other things that you can consider doing by changing the FTP config script are:
- Make sure that you are only building the Trunk of your source control, and do all development in a branch. This way when you successfully merge a range of upgrades that you’ve successfully tested, you only deploy tested changes.
- Automatically push up an app_offline.htm file before you push the site up (blog post to come) and then delete it after your finished.
- Potentially download any site side assets for archiving/backup (user uploaded photos)