Deploy Rails using Capistrano without access to Subversion

Posted by dave

I have been doing some work for a company recently who wanted to deploy their rails application using capistrano but had a problem in that their remote servers were firewalled from their development network, which included their Subversion server.

The solution still seemed absolutely ripe for automation, it just meant we had to dive into writing our own capistrano task to basically do the following:

  • Check out latest version of code from subversion (svn export)
  • Zip exported source up into a file
  • Transfer the zipped source to the deployment server(s)
  • Unzip into the relevant releases folder on the deployment server(s)

So, I spent a few hours on Sunday morning tidying up what we did and making it a little more generic and I thought I would share it on here in case anyone else would find this useful.

The capistrano task is as follows:

desc "Export source from SVN, Upload it to an SFTP location, Download it to the release path" 
task :export_svn_deploy, :roles => :app do
  tmp_path = "/tmp" # A path to a *local* temporary directory
  sftp_server = "your.sftp.server" # The SFTP/SSH server for storage of the zipped source
  sftp_user = "sftp_user" # The username for the storage server
  sftp_path = "deploy_tmp" # The remote path on the storage server
  tmp_tgz = "exported_src.tar.gz" # Temporary file name for the zipped source

  # Export from SVN, zip and upload to the remote storage server
  system "svn export -q --force #{repository} #{tmp_path}/src/" 
  system "cd #{tmp_path}/src/ && tar czf #{tmp_path}/#{tmp_tgz} ./" 
  Net::SFTP.start(sftp_server, sftp_user) do |sftp|
    sftp.put_file("#{tmp_path}/#{tmp_tgz}", "#{sftp_path}/#{tmp_tgz}")
  end
  system "rm -rf #{tmp_path}/src && rm -f #{tmp_path}/#{tmp_tgz}" 

  # On each server, scp the file from the remote location and unzip it to the release_path
  run "mkdir #{release_path}" 
  run "scp -q #{sftp_user}@#{sftp_server}:#{sftp_path}/#{tmp_tgz} #{release_path}/#{tmp_tgz}" 
  run "cd #{release_path} && tar xzf #{tmp_tgz} && rm #{tmp_tgz}" 
end

Notes

  • The remote_path on the SFTP storage server must exist or the SFTP commands will fail.
  • It is assumed that you are using ssh keys and no pass phrase to connect to the SFTP server from both the client machine and also all of the remote servers, this way both the Net::SFTP and the scp commands will not need passwords to be sent.
  • The mkdir #{release_path} before the scp should not be needed if this is being used in a real deployment, let the regular capistrano tasks take care of that
  • I chose to use Net::SFTP on the client to upload the file because this script was originally developed for a client using Windows and Net::SFTP works whereas shelling out to do an scp, wouldn’t. The rest of this cleaned up task however would need some modification to work on Windows as it stands as the tar commands would fail.
  • This blog post needs less notes ;)

Next Steps

I am pretty sure that the best way to implement this would be as a custom source control plugin for capistrano so by doing:

set :scm, :svn_via_sftp

and have some extra options to specify SFTP server name and user account then this process could be completely seamless with a regular cap deploy.

I would be really interested to see if this is a common scenario, I suspect it will be with larger clients who do not want their SCC systems exposed to the web. Is anyone in this situation?

Comments

Leave a response