Using FTP Task in CruiseControl.NET for Publishing Websites

Since version 1.5 CruiseControl.NET includes an FTP Task which can be used for uploading build results to a remote FTP server. Its most typical use case is for publishing websites to a remote server. Configuring this correctly requires some thought and at the moment there are very few helpful resources available. Below are some suggestions for the most simple case when the files to be uploaded only have to be retrieved from source control and there is no additional build process involved.

The core of the configuration is of course the FTP task. Its configuration is pretty straightforward. You only need to supply it with login details and the path to the local and the remote folder. This also means that there are almost no configuration options available – you can only choose between uploading the files recursively or not.

The problem with the lack of options is that most source control systems put additional files in the local folder which you don't want to upload. In the case of Subversion this is a .svn folder in every local folder. Because the FTP task can't be configured to skip or ignore certain files, these files have to be deleted before the FTP task is executed. The best way to do that is by executing a batch file:

FOR /F "tokens=*" %%G IN ('DIR /B /AD /S *.svn*') DO RMDIR /S /Q "%%G"

If you do this directly in the working directory where the files are retrieved from Subversion, then the next retrieval from source control will fail unless you set the cleanCopy configuration element to true and retrieve all of the files from Subversion every time. Doing this has two downsides:

  • If the project is large, this will significantly decrease performance. Downloading lots of files from Subversion takes time.
  • The file timestamps will be reset every time. This information could potentially be useful for the FTP task (but isn't yet as I explain at the end of the post).

To avoid this you should first copy the retrieved files to a different folder. The best way to do this is by using the build publisher. Just don't forget to set the alwaysPublish configuration element to true. Otherwise it won't copy anything because you are using it in the tasks section where the build is not yet successful.

To sum it all up, your tasks section should something look like this (I've used preprocessor constants for most of the values to increase readability):

<tasks>
    <buildpublisher>
        <sourceDir>$(WorkingDir)</sourceDir>
        <publishDir>$(PublishDir)</publishDir>
        <cleanPublishDirPriorToCopy>true</cleanPublishDirPriorToCopy>
        <useLabelSubDirectory>false</useLabelSubDirectory>
        <alwaysPublish>true</alwaysPublish>
    </buildpublisher>
    <exec>
        <executable>$(ClearSvnFilesBatch)</executable>
        <baseDirectory>$(PublishDir)</baseDirectory>
    </exec>
    <ftp>
        <serverName>$(FtpServerName)</serverName>
        <userName>$(FtpUserName)</userName>
        <password>$(FtpPassword)</password>
        <action>UploadFolder</action>
        <ftpFolderName>$(FtpRemoteFolder)</ftpFolderName>
        <localFolderName>$(PublishDir)</localFolderName>
        <recursiveCopy>true</recursiveCopy>
    </ftp>
</tasks>

I'll conclude this post with bad news. Unfortunately there is no support in the FTP task to only upload the files that have changed since the previous build. For a small website this might not be an issue but for larger ones this is quite a problem. You really don't want to upload tens or even hundreds of megabytes every time. Let's just hope that the issue will be fixed soon.

Copyright
Creative Commons License