The amazing adventures of Doug Hughes

I received the following question in email this morning and I thought it would create a pretty good blog entry:

Hi Doug,

Thanks for the meetup presentation for Ant. I’m getting into SVN and ANT at the moment and was looking for some advice on my setup.

I have a dev server with an SVN repo, a staging server and a live server. What is the best way to upload changes from dev to staging. It would be individual files and folders rather than the whole application. Do you think FTP from the repo to staging, if thats possible?

Many Thanks

This is a variation on a fairly common question I get about how we deploy applications at Alagad. It seems that a lot of people have applications that are made up of lots of files. I get the impression that they’re used to working in an environment where, when deploying their application, they only deploy the files that have been modified.

I vaguely remember this approach from before I fully embraced source code control, but honestly it’s such a foreign concept to me that I almost want to simply ignore it.

I assure you that at Alagad we have worked on some very large applications made up of thousands of files. And yes, it can take time to check the application out of Subversion into staging or production. However, usually those three servers are on the same network, or at least are on networks that have significant connections to the Internet. This generally means it will only take a few minutes to get the files for the deployment out of subversion.

The other point is that I’d rather feel really confident that my entire application is deployed correctly. Deploying only a few files would make me nervous that something subtle was missed. By doing a blanket update of everything I know I have the latest of everything.

So, a simple Ant4CF build script to do what I suggest would look like these two files.

First, this is the local build.xml file. This file has two targets, BuildStaging (the default) and BuildProduction. For the same of simplicity I’ve assumed that these are two different servers that are setup exactly the same. This means I can use the same build.remote.xml file for both servers.

In the real world there’s a good chance that you will have some configuration differences between staging and production and my either execute different targets in the build.remote.xml file or would have two completely separate build scripts.

build.xml

<project default="Build" name="Build Script">

    <target name="Build">
        <!-- if we don't specify a target when we run this script the default target will ask us which one we want. The default will be BuildStaging -->

        <input addproperty="target" defaultvalue="BuildStaging" message="Which target do you wish to run?" validargs="BuildStaging,BuildProduction"/>
        <!-- call the target chosen -->

        <antcall target="${target}"/>
    </target>

    <target name="BuildStaging">
        <antcall target="RunBuild">
            <!-- this property sets which server the build script will be run on (staging) -->

            <param name="ant4cfUrl" value="http://staging.yoursite.com/CFIDE/ant4cf"/>
        </antcall>
    </target>

    <target name="BuildProduction">
        <antcall target="RunBuild">
            <!-- this property sets which server the build script will be run on (production) -->

            <param name="ant4cfUrl" value="http://production.yoursite.com/CFIDE/ant4cf"/>
        </antcall>
    </target>

    <target name="RunBuild">

        <!-- if the ant4cfUrl property is not set, then fail this script -->

        <fail message="Please run the BiildProduction or BuildStaging target." unless="ant4cfUrl"/>
        <!-- output which url we're using to build -->

        <echo message="Building via URL: ${ant4cfUrl}"/>

        <!-- Gather usernames and passwords for the cfadmin and svn -->

        <input addproperty="cfAdminPassword" message="Please enter CFAdmin password:"/>
        <input addproperty="svnUsername" message="Please enter SVN username:"/>
        <input addproperty="svnPassword" message="Please enter SVN password:"/>

        <!-- define the tasks for ant4cf -->

        <taskdef classname="com.alagad.ant4cf.Configure" classpath="antlib/ant4cf-java-2.0-SNAPSHOT-jar-with-dependencies.jar" name="configure"/>
        <taskdef classname="com.alagad.ant4cf.RemoteAnt" classpath="antlib/ant4cf-java-2.0-SNAPSHOT-jar-with-dependencies.jar" name="remoteant"/>

        <!-- configure ant4cf to deploy to your staging server -->

        <configure adminPassword="${cfAdminPassword}" ant4cfUrl="${ant4cfUrl}"/>
        <!-- run the remote ant script on staging -->

        <remoteant antfile="build.remote.xml" properties="cfAdminPassword,svnUsername,svnPassword,dsnPassword" timeout="240"/>

    </target>
</project>

remote.build.xml

<project default="BuildRemote" name="Remote Build Script">

    <target name="BuildRemote">

        <!-- get the app from subversion -->

        <echo message="Export App out of SVN"/>
        <exec executable="svn" failifexecutionfails="true">
            <arg line="export http://svn.cfu09ant.com:81/svn/Presentations/Trunk/cfunited09/DeployingAppsWithAnt/exampleApp/c:pathtoyoursite --username ${svnUsername} --password ${svnPassword} --force"/>
        </exec>

	</target>
</project>

This example is somewhat different from a lot of the other examples I’ve shown mainly because the build script lets you choose which site to build, staging or production. This means you can run the script by simply running the command “ant”, or you can manually choose a target with “ant BuildStaging” or “ant BuildProduction”.

The remote.build.xml file is, for the sake of this example, extremely simple. All it does is check out the entire application from SVN and dump it into the webroot. We’re not using any Ant4CF features remotely so I don’t even define those tasks. In a typical script I like to insure that the application and server is configured correctly, but that wasn’t the question.

So, that’s how I’d deploy to staging or production. But, what if you really only want to deploy files that have changed in subversion? That’s a more of a challenge. And honestly, not one I’ve taken on before.

I spent much of the day today trying to write a service in CF that would export only the files which had changed between specified revision numbers in SVN. After about 150 lines of code I decided to throw in the towel on this.

My goal had been to create a new service that would take the SVN url, revision range, and other related information. Then, using the “svn log” command get a lits of the files that had changed. I could then loop over the files and export each one individually.

There were a few problems in this plan though. First off, the log output shows the output for each revision and therefore the same file could be in there more than once. The other problem was that it’s not as easy to tell if something in Subversion is a file or a directory. In other words if I may know that this path “/trunk/www/admin/capabilities/views/shared” was modified in my last commit. However, I don’t know if “shared” is a file without an extension or a directory.

This mean that I have to perform a fair amount of work to manage the data from the log to not only remove duplicates, but avoid exporting a directory followed by exporting each file in that directory. Overall, it’s a mess I’d rather not get into.

So, my recommendation is to simply use “svn export” and dump everything. It’s faster and easier and more accurate.

Comments on: "How To Deploy From SVN to Staging and Production Using Ant4CF" (3)

  1. I agree with Doug – as soon as you start trying to implement only deploying certain files – you are opening yourself up to the same mistakes you make during manual deployments.

    With the ‘all-or-nothing’ approach – you are always 100% certain that all the files are going to the server, and don’t have to worry about changing scripts because now you need to deploy Q,R,S and not X,Y,Z.

    Like

  2. Couldn’t this be done a whole lot easier with cfexecute? Just kick off a svn export or svn update and you’re done right?

    Like

  3. One idea I’ve had, but have yet to implement is to just use an rsync task to deploy from my build area to both testing/staging and production. I hope that this will have the effect of not missing anything (since I’m doing a full rsync) and make the overall process a little more snappy as I won’t need to pull down all the files again on each server.

    Another slight advantage with rsync is that I should be able to delete files that no longer exist in the repository. This has not been automatic for me in the past.

    @Doug – thanks again for a great meetup presentation.

    Like

Comments are closed.

Tag Cloud

<span>%d</span> bloggers like this: