@madpilot makes

WGET: The poor man’s SVN – Using Capistrano on a host with out subversion

I have a client for whom I created a CakePHP-based website for over a year ago. He has since come back to me and asked for a number of changes. I thought I would take the opportunity to use capistrano, because there are a number of steps I always had to perform when updating his site and I hate having to do them manually.

I went about checking all the necessary requirements on his host:

  1. SSH access – check! The host his site was on allows an SSH connection which is required by capistrano
  2. Apache follows symbolic links – check! Because capistrano uses a symbolic link from the document root to the latest version of the site, Apache needs to be able to follow them (i.e the site’s apache configuration needs FollowSymLinks enabled)
  3. Has svn installed – fail! This could be a problem. Capistrano by default checks out the HEAD revision from the defined repository – if it can’t use SVN, it can’t download the latest version of the site.

So close! If only I could download the HEAD revision of a site using a common command line system. I thought about writing a SVN-to-web interface, that would check out the latest version and post them as a website, but then I remembered SVN does that out of the box using the SVN apache module. Thankfully, when I was building my development machine, I made I installed the SVN module – it was now time to use it!

First I needed to tell Apache serve up the a copy of the SVN repository. Dropping the following into the Apache config file did the trick:

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> DAV svn
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> SVNPath /path/to/svn/repository
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> AuthType Basic
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> AuthName &#8220;My Secret SVN Repository&#8221;
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> AuthUserFile /path/to/a/.htpassword/file
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> Require valid-user<span class="sc3"></span><span class="re1"><span class="re2" /></span>
  </div>
</li>

<li class="li1">
  </Location>
</li>

For those playing at home, replace /url/you/would/like/to/access with a nice easy path – this is the URL you will access to download the files, replace /path/to/svn/repository with the actual physical path to your repository and create a .htpassword file so you can limit access to the repository by using the htpasswd2 command: htpasswd2 -c /path/to/a/.htpassword/file username would work in this case (After substituting a username and real path, of course)

If you point you browser to the URL you just setup, you should see the root directory of the repository, after you enter the username and password you setup. Congratulations! You are basically there. Now you just need to reconfigure your capistrano to use wget instead of svn. I do this by overriding the deploy method – because I’m not using rails for this project, the paths and shard folders are different anyway. If you are using rails, you might need to have a look at the original recipe file and replace the svn command with the one below.

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> <ins class="in"> </ins> <ins class="in"> </ins> <ins class="in"> </ins> run <span class="st0">&#8220;wget &#8211;user=#{wget_user} &#8211;password=#{wget_pass} -m &#8211;cut-dirs=4 -nH -P #{release_path} -q -R index.html #{repository}&#8221;</span>
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> <ins class="in"> </ins> run <span class="st0">&#8220;ln -nfs #{release_path} #{current_path}&#8221;</span>
  </div>
</li>

<li class="li1">
  <div class="de1">
  </div>
</li>

<li class="li1">
  <div class="de1">
    <ins class="in"> </ins> <ins class="in"> </ins> <ins class="in"> </ins> <ins class="in"> </ins> run <span class="st0">&#8220;rm -rf #{release_path}/app/webroot/files&#8221;</span>
  </div>
</li>

<li class="li1">
  <div class="de1">
    <span class="kw1">end</span>
  </div>
</li>

The only modification to that line is the number after the –cut-dirs switch – it should be equal to the number of directories in the URL. In our example the URL is /url/you/would/like/to/access so –cut-dirs it needs to be equal to 6.

The last thing to do is to setup the wget_user and wget_pass variables to be equal to the username and password you created using htpasswd2.

That should do it! You can now deploy to a server that is sans SVN!

Caveats: Because of the way the SVN module and WGET work, I’ve had to not include he downloading of index.html (Basically WGET treats the directory listing as a page, and will output it as index.html) so this technique will not work if you have any pages called index.html in your structure. Work around: Rename all instances of index.html to index.htm

You might get some weird results if some one checks in code at the same time as you do a deploy – unless you have a bucket load of developers working on your system and you have no communication between developers, this is pretty unlikely.

(Names have been changed to protect the innocent)