Hello!

I am using Mercurial as scm and it really bothered me to transfer  
complete copy of local repository to servers on every deploy.

So I hacked together a general (I think so) deployment strategy which  
does a local checkout (or even better: export) and copies it using  
rsync to remote cache.

As an added bonus the file contains few lines that cut the transfer  
size in half - support for Mercurial export. I really don't know why  
this isn't in official distribution, but I am newbie regarding  
Mercurial and maybe somebody will enlighten me to the potential  
problems in my code.

Also, since I am newbie regarding modifications of cap sources, I  
welcome any comments about problems with the strategy code. It works  
for me, but since capistrano helps me a lot, I thought it would be  
nice to contribute something back.

capify.org is down and #rubyonrails is full, so no patch/pastie  
coolnes. I just include the file source below. Paste it into file  
$RAILS_ROOT/capistrano/recipes/deploy/strategy/ 
copy_with_rsync_and_remote_cache.rb
and use
set :deploy_via, :copy_with_rsync_and_remote_cache
in your deploy.rb.

Regards,
izidor

#
# addition for Mercurial :export
#

require 'capistrano/recipes/deploy/scm/mercurial.rb'

module Capistrano
   module Deploy
     module SCM

       class Mercurial < Base
         #  hg archive - cuts the directory size in half (at least)  
compared to :checkout
         def export(revision, destination)
           scm :archive, verbose, "-r", revision, destination
         end
       end
     end
   end
end

#
# Copying to remote cache with rsync
#
# (Implementation shamelessly borrows code from Remote, RemoteCache  
and Copy)
#

require 'capistrano/recipes/deploy/strategy/base'
require 'fileutils'
require 'tempfile'  # Dir.tmpdir

module Capistrano
   module Deploy
     module Strategy

       # Implements the deployment strategy that keeps a cached copy of
       # the source code on each remote server. Each deploy does the  
following:
       # - makes local checkout to temporary directory,
       # - copies the local checkout using rsync to each remote server,
       # - on each remote server copies from the cached copy to the  
final deployment location.
       class CopyWithRsyncAndRemoteCache < Base
         # Obtains a copy of the source code locally (via the  
#command method),
         # copies (with rsync) that directory to cached directory on  
all target
         # servers, and then copy from cached directory to the  
deployment
         # directory.
         def deploy!
           logger.debug "getting (via #{copy_strategy}) revision # 
{revision} to #{destination}"
           system(command)
           File.open(File.join(destination, "REVISION"), "w") { |f|  
f.puts(revision) }
           logger.trace( "copying data with rsync" )
           # get all servers for our current task and copy the files  
using rsync
           # TODO Is there a better way to implement the following  
code, some canonical predefined methods we can use?
           servers = find_servers :roles => :app, :except =>  
{ :no_release => true }
           servers.each do |dest_server|
             logger.trace( "copying to #{dest_server}" )
             raise unless system( rsync_command_for(dest_server) )
           end

           logger.trace( "copying data with rsync - finished" )
           copy_repository_cache
         ensure
           FileUtils.rm_rf destination rescue nil
         end

         def check!
           super.check do |d|
             d.remote.writable(shared_path)
             d.local.command( "rsync" )
             d.remote.command( "rsync" )
           end
         end

         private

           # Returns the basename of the release_path, which will be  
used to
           # name the local copy and archive file.
           def destination
             @destination ||= File.join(tmpdir, File.basename 
(configuration[:release_path]))
           end

           # Returns the value of the :copy_strategy variable,  
defaulting to
           # :checkout if it has not been set.
           def copy_strategy
             @copy_strategy ||= configuration.fetch 
(:copy_strategy, :checkout)
           end

           # Should return the command(s) necessary to obtain the  
source code
           # locally.
           def command
             @command ||= case copy_strategy
             when :checkout
               source.checkout(revision, destination)
             when :export
               source.export(revision, destination)
             end
           end

           # The directory to which the copy should be checked out
           def tmpdir
             @tmpdir ||= configuration[:copy_dir] || Dir.tmpdir
           end

           # The directory on the remote server where cached copy  
should reside
           def repository_cache
             File.join(shared_path, configuration[:repository_cache]  
|| "cached-copy")
           end

           #
           def copy_repository_cache
             logger.trace "copying the cached version to # 
{configuration[:release_path]}"
             run "cp -RPp #{repository_cache} #{configuration 
[:release_path]} && #{mark}"
           end

           # Returns the command which will write the identifier of the
           # revision being deployed to the REVISION file on each host.
           def mark
             "(echo #{revision} > #{configuration[:release_path]}/ 
REVISION)"
           end

           #
           def rsync_command_for( server )
             "rsync -az --delete '#{destination}/' '#{configuration 
[:[EMAIL PROTECTED]:#{repository_cache}'"
           end
       end

     end
   end
end


--~--~---------~--~----~------------~-------~--~----~
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at http://groups.google.com/group/capistrano
-~----------~----~----~----~------~----~------~--~---

Reply via email to