sftp capistrano deployment strategy
// capistrano deployment strategy used when you have sftp access only (no ssh access)
require 'capistrano/recipes/deploy/strategy/base' require 'fileutils' require 'tempfile' # Dir.tmpdir require 'net/ssh' require 'net/sftp' require 'find' module Capistrano module Deploy module Strategy # Special Strategy for a special price! # This strategy is created in favor of the sftp only apps, in lack of ssh login support # # * exports the repository to a local directory # * uploads all the files to the remote server using an sftp script # * renames current directory to a directory with a timestamp of 1 hour ago # * renames the uploaded directory to current class SftpCopy < Base 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.debug "Connecting to sftp user = #{configuration[:user]}" Net::SSH.start(configuration[:host], configuration[:user], configuration[:password]) do |ssh| ssh.sftp.connect do |sftp| logger.debug "Creating directory: #{remote_dir}" sftp.mkdir remote_dir, :permissions => 0755 logger.debug "Uploading files from #{destination} to #{remote_dir}" logger.debug "Why don't you grab a cup of coffee while you wait, i might be busy for some time...\nJust sit back and enjoy the show..." Find.find(destination) do |file| if File.stat(file).directory? remote_directory = remote_dir + file.sub(destination, '') begin sftp.stat(remote_directory) rescue Net::SFTP::Operations::StatusException => e raise "BOEM" unless e.code == 2 sftp.mkdir(remote_directory, :permissions => 0755) end else remote_file = remote_dir + file.sub(destination, '') sftp.put_file file, remote_file sftp.setstat(remote_file, :permissions => 0644) end end logger.debug "RENAMING DIRECTORIES" sftp.rename "#{configuration[:copy_remote_dir]}/current", "#{configuration[:copy_remote_dir]}/#{(Time.now - 1.hour).utc.strftime("%Y%m%d%H%M%S")}" sftp.rename "#{remote_dir}", "#{configuration[:copy_remote_dir]}/current" end end ensure logger.debug "Remove local export" FileUtils.rm_rf destination rescue nil end def check! super.check do |d| 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 # Returns the name of the file that the source code will be # compressed to. def filename @filename ||= File.join(tmpdir, "#{File.basename(destination)}.#{compression_extension}") 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 to which the archive should be # copied def remote_dir @remote_dir ||= "#{configuration[:copy_remote_dir]}/#{File.basename(configuration[:release_path])}" || "/tmp" end end end end end