Archive

Posts Tagged ‘deployment’

Automatic Web Site Deployment with SVN and Ruby

September 18th, 2008

I was setting up a new test server and production server for our web sites at work. I had finally persuaded my manager that we needed to mandate the use of a SCM system for the site source. I wanted to make deploying the sites drop dead simple as well. The last thing I wanted was to force people to check changes into Subversion, then have them upload those changes to the test server, then when the changes are approved, upload them to the production server. Too much shifting, and too much opportunity for files to get out of sync.
I created a couple Ruby scripts that sit on the test server and production server. On the test server the script checks out the latest revision of the test web sites. The script is run via a cron job, so very shortly after changes are checked in to the repository they are exported out to the version of the site running on the test server.

#!/usr/bin/env ruby
 
# The path to the directory where your site folders sit
web_root = "/path/to/document/root/"
 
# A hash of sites. Folder name and SVN repository name
websites = { "site_folder" => "repo_name",
  "site2_folder" => "repo2_name"}
 
websites.each do |site, repo|
  # First we check for a file named "revision" in the site
  # folder. If that's not there we quit. This is a just in
  # case safety measure, you have to create the revision
  # file for the script to work. Initially it can be a file
  # with just a single "0" in it (no quotes).
  if File.exist?(web_root + site + "/revision")
    local_revision = File.open(web_root + site + "/revision").gets.chomp
  else
    exit
  end
  # Get the current revision in subversion
  svn_revision = `/usr/bin/svn info http://svn.server.com/#{repo}/ | grep '^Revision' | sed -e 's/Revision: *//'`.chomp
  # The the Subversion revision is greater than the number in the revision file
  # then we need to update the site.
  if svn_revision.to_i > local_revision.to_i then
    `/usr/bin/svn export --revision #{svn_revision} --force --username USERNAME --password PASSWORD http://svn.server.com/#{repo}/trunk #{web_root}#{site}`
    # After the export make sure the update the revision number in the revision file
    `echo "#{svn_revision}" > #{web_root}#{site}/revision`
  end
end

In order to get a site on the test server we need to create a folder for it in the web_root, add the revision file to the empty folder, set up the virtual host, and finally add the information to the websites hash in the script. I have this script running every thirty minutes on the test server. Once the site script runs the site is there. It will always be up to date, and the developer does not have to touch the test server. The most important rule you need if you are going to do things this way is don’t check broken code into Subversion. If you do then your test site will very shortly break as well.

The production server is pretty much the same, except I have a second file called deploy_revision that tells the script exactly what revision to check out. In production we may not always want the freshest code. Other than checking the deploy_revision file, the script on the production site is pretty much the same as the script on the test server.

#!/usr/bin/env ruby
 
# The path to the directory where your site folders sit
web_root = "/path/to/document/root/"
 
# A hash of sites. Folder name and SVN repository name
websites = { "site_folder" => "repo_name",
  "site2_folder" => "repo2_name"}
 
websites.each do |site, repo|
  # If deploy_revision file isn't there just quit.
  if !File.exist?(web_root + site + "/deploy_revision")
    exit
  else
    deploy_revision = File.open(web_root + site + "/deploy_revision").gets.chomp  
    # Check for the revision file, it's okay if it's not there, in that
    # case just set the revision to 0
    if File.exist?(web_root + site + "/revision")
      local_revision = File.open(web_root + site + "/revision").gets.chomp
    else
      local_revision = 0
    end
    # Check the SVN revision
    svn_revision = `/usr/bin/svn info http://svn.server.com/#{repo}/ | grep '^Revision' | sed -e 's/Revision: *//'`.chomp
    # if the SVN revision is less than the deployed revision quite, that's not right...
    if svn_revision.to_i < deploy_revision.to_i
      exit
    else
      # If the deploy_revision is greater than the local then we need to update
      if deploy_revision.to_i > local_revision.to_i then
        `/usr/bin/svn export --revision #{deploy_revision} --force --username USERNAME --password PASSWORD http://svn.server.com/#{repo}/trunk #{web_root}#{site}`
        `echo "#{svn_revision}" > #{web_root}#{site}/revision`
      end
    end
  end
end

I don’t have the production script running under a cron job just because I want to be really careful about what gets deployed. You could easily do that thought.
We’ve only got half a dozen sites getting updated this way right now, but eventually as changes are made to sites they are being added into Subversion and rolled into this setup.

Development , , , , , ,