lib/capistrano-extensions/deploy.rb in capistrano-extensions-0.1.5 vs lib/capistrano-extensions/deploy.rb in capistrano-extensions-0.1.8
- old
+ new
@@ -1,11 +1,10 @@
require 'capistrano-extensions/geminstaller_dependency'
-require 'capistrano/server_definition'
# Overrides the majority of recipes from Capistrano's deploy recipe set.
Capistrano::Configuration.instance(:must_exist).load do
- # Add sls_recipes to the load path
+ # Add us to the load path
@load_paths << File.expand_path(File.dirname(__FILE__))
# ========================================================================
# These variables MUST be set in the client capfiles. If they are not set,
# the deploy will fail with an error.
@@ -25,14 +24,14 @@
# These variables should NOT be changed unless you are very confident in
# what you are doing. Make sure you understand all the implications of your
# changes if you do decide to muck with these!
# =========================================================================
- set(:use_sudo, false) # we don't want to use sudo-- we don't have to!
- set(:deploy_via, :export) # we don't want our .svn folders on the server!
- _cset(:deploy_to) { "/var/www/vhosts/#{application}" }
- _cset(:deployable_environments, [:production])
+ set(:use_sudo, false) # we don't want to use sudo-- we don't have to!
+ set(:deploy_via, :copy) # no need to have subversion on the production server
+ _cset(:deploy_to) { "/var/vhosts/#{application}" }
+ _cset(:deployable_environments, [:staging])
_cset(:rails_config_path) { File.join(latest_release, 'config') }
_cset(:db_conf) {
fetch(:config_structure, :rails).to_sym == :sls ?
File.join(rails_config_path, rails_env, 'database.yml') :
@@ -43,15 +42,40 @@
_cset(:content_dir, "content")
_cset(:content_path) { File.join(shared_path, content_dir) }
_cset(:public_path) { File.join(latest_release, 'public') }
_cset(:log_path) { "/var/log/#{application}" }
+ # Local Properties
+ _cset(:tmp_dir, "tmp/cap")
+ # when local:syncing, should we keep backups just in case of failure?
+ _cset(:store_dev_backups, false)
+ # how long to allow remote backups to be valid (at both cache levels)
+ _cset(:remote_backup_expires, 172800) # 2 days in seconds.
+ # when remote:syncing, should we keep backups just in case of failure?
+ _cset(:store_remote_backups, true)
+ # paths to exclude during deployment
+ _cset(:exclude_paths, [])
+
+ _cset(:copy_cache) { File.expand_path("~/.capistrano/#{application}") }
+ set(:copy_exclude) {
+ # don't deploy the other environment directories
+ envs = fetch(:deployable_environments).dup
+ envs.delete_if { |env| rails_env.to_sym == env.to_sym }
+ envs.map! { |env| File.join("config", "#{env}") }
+
+ envs + fetch(:exclude_paths)
+ }
+
+ _cset(:zip, "gzip")
+ _cset(:unzip, "gunzip")
+ _cset(:zip_ext, "gz")
+
# Allow recipes to ask for a certain local environment
def local_db_conf(env = nil)
env ||= fetch(:rails_env)
fetch(:config_structure, :rails).to_sym == :sls ?
- File.join('config', env, 'database.yml') :
+ File.join('config', env.to_s, 'database.yml') :
File.join('config', 'database.yml')
end
# Read from the local machine-- BE CAREFUL!!!
set(:db) { YAML.load_file(local_db_conf)[rails_env] }
@@ -77,21 +101,20 @@
end
# Now, let's actually include our common recipes!
namespace :deploy do
desc <<-DESC
- [capistrano-extensions] Creates shared directories and symbolic links to them by the
- :content_directories and :shared_content properties. See the README for
- further explanation.
+ [capistrano-extensions] Creates shared directories and symbolic links to them by reading the
+ :content_directories and :shared_content properties. See the README for further explanation.
DESC
task :create_shared_file_column_dirs, :roles => :app, :except => { :no_release => true } do
mappings = content_directories.inject(shared_content) { |hsh, dir| hsh.merge({"content/#{dir}" => "public/#{dir}"}) }
mappings.each_pair do |remote, local|
run <<-CMD
+ umask 0022 &&
mkdir -p #{shared_path}/#{remote} &&
- ln -sf #{shared_path}/#{remote} #{latest_release}/#{local} &&
- chmod 755 -R #{shared_path}/#{remote}
+ ln -sf #{shared_path}/#{remote} #{latest_release}/#{local}
CMD
end
end
desc <<-DESC
@@ -113,93 +136,22 @@
[capistrano-extensions]: Tarballs deployable environment's rails logfile (identified by
RAILS_ENV environment variable, which defaults to 'production') and copies it to the local
filesystem
DESC
task :pull do
- tmp_location = "#{shared_path}/#{rails_env}.log.gz"
- run "cp #{log_path}/#{rails_env}.log #{shared_path}/ && gzip #{shared_path}/#{rails_env}.log"
- get "#{tmp_location}", "#{application}-#{rails_env}.log.gz"
+ tmp_location = "#{shared_path}/#{rails_env}.log.#{zip_ext}"
+ run "cp #{log_path}/#{rails_env}.log #{shared_path}/ && #{zip} #{shared_path}/#{rails_env}.log"
+ get "#{tmp_location}", "#{application}-#{rails_env}.log.#{zip_ext}"
run "rm #{tmp_location}"
end
end
+ load 'recipes/db_sync'
+ load 'recipes/content_sync'
+
namespace :remote do
desc <<-DESC
- [capistrano-extensions] Uploads the backup file downloaded from local:backup_db (specified via the FROM env variable),
- copies it to the remove environment specified by RAILS_ENV, and imports (via mysql command line tool) it back into the
- remote database.
- DESC
- task :restore_db, :roles => :db do
- env = ENV['FROM'] || 'production'
-
- puts "\033[1;41m Restoring database backup to #{rails_env} environment \033[0m"
- if deployable_environments.include?(rails_env.to_sym)
- # remote environment
- local_backup_file = "#{application}-#{env}-db.sql.gz"
- remote_file = "#{shared_path}/restore_db.sql"
- if !File.exists?(local_backup_file)
- puts "Could not find backup file: #{local_backup_file}"
- exit 1
- end
- upload(local_backup_file, "#{remote_file}.gz")
-
- pass_str = pluck_pass_str(db)
- run "gunzip -f #{remote_file}.gz"
- run "mysql -u#{db['username']} #{pass_str} #{db['database']} < #{remote_file}"
- run "rm -f #{remote_file}"
- end
- end
-
- desc <<-DESC
- [capistrano-extensions]: Backs up target deployable environment's database (identified
- by the FROM environment variable, which defaults to 'production') and restores it to
- the remote database identified by the TO environment variable, which defaults to "staging."
- DESC
- task :sync_db do
- system("capistrano-extensions-sync-db #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
- end
-
- desc <<-DESC
- [capistrano-extensions]: Uploads the backup file downloaded from local:backup_content (specified via the
- FROM env variable), copies it to the remote environment specified by RAILS_ENV, and unpacks it into the
- shared/ directory.
- DESC
- task :restore_content do
- from = ENV['FROM'] || 'production'
-
- if deployable_environments.include?(rails_env.to_sym)
- local_backup_file = "#{application}-#{from}-content_backup.tar.gz"
- remote_file = "#{shared_path}/content_backup.tar.gz"
-
- if !File.exists?(local_backup_file)
- puts "Could not find backup file: #{local_backup_file}"
- exit 1
- end
-
- upload(local_backup_file, "#{remote_file}")
- remote_dirs = ["content"] + shared_content.keys
-
- run("cd #{shared_path} && rm -rf #{remote_dirs.join(' ')} && tar xzf #{remote_file} -C #{shared_path}/")
- end
- end
-
- desc <<-DESC
- [capistrano-extensions]: Backs up target deployable environment's shared content (identified by the FROM environment
- variable, which defaults to 'production') and restores it to the remote environment identified
- by the TO envrionment variable, which defaults to "staging."
-
- Because multiple capistrano configurations must be loaded, an external executable
- (capistrano-extensions-sync_content) is invoked, which independently calls capistrano. See the
- executable at $GEM_HOME/capistrano-extensions-0.1.2/bin/capistrano-extensions-sync_content
-
- $> cap remote:sync_content FROM=production TO=staging
- DESC
- task :sync_content do
- system("capistrano-extensions-sync-content #{ENV['FROM'] || 'production'} #{ENV['TO'] || 'staging'}")
- end
-
- desc <<-DESC
[capistrano-extensions]: Wrapper fro remote:sync_db and remote:sync_content.
$> cap remote:sync FROM=production TO=staging
DESC
task :sync do
sync_db
@@ -207,124 +159,88 @@
end
end
namespace :local do
desc <<-DESC
- [capistrano-extensions]: Backs up deployable environment's database (identified by the
- RAILS_ENV environment variable, which defaults to 'production') and copies it to the local machine
+ [capistrano-extensions]: Wrapper for local:sync_db and local:sync_content
+ $> cap local:sync RAILS_ENV=production RESTORE_ENV=development
DESC
- task :backup_db, :roles => :db do
- pass_str = pluck_pass_str(db)
-
- run "mysqldump -u#{db['username']} #{pass_str} #{db['database']} > #{shared_path}/db_backup.sql"
- run "gzip #{shared_path}/db_backup.sql"
- get "#{shared_path}/db_backup.sql.gz", "#{application}-#{rails_env}-db.sql.gz"
- run "rm -f #{shared_path}/db_backup.sql.gz #{shared_path}/db_backup.sql"
+ task :sync do
+ sync_db
+ sync_content
end
+ end
+
+ namespace :util do
- desc <<-DESC
- [capistrano-extensions] Untars the backup file downloaded from local:backup_db (specified via the FROM env
- variable, which defalts to RAILS_ENV), and imports (via mysql command line tool) it back into the database
- defined in the RAILS_ENV env variable.
-
- ToDo: implement proper rollback: currently, if the mysql import succeeds, but the rm fails,
- the database won't be rolled back. Not sure this is even all that important or necessary, since
- it's a local database that doesn't demand integrity (in other words, you're still going to have to
- fix it, but it's not mission critical).
- DESC
- task :restore_db, :roles => :db do
- on_rollback { "gzip #{application}-#{from}-db.sql"}
-
- from = ENV['FROM'] || rails_env
-
- env = ENV['RESTORE_ENV'] || 'development'
- y = YAML.load_file(local_db_conf(env))[env]
- db, user = y['database'], (y['username'] || 'root') # update me!
-
- pass_str = pluck_pass_str(y)
-
- puts "\033[1;41m Restoring database backup to #{env} environment \033[0m"
- # local
- system <<-CMD
- gunzip #{application}-#{from}-db.sql.gz &&
- mysql -u#{user} #{pass_str} #{db} < #{application}-#{from}-db.sql
- CMD
- end
-
- desc <<-DESC
- [capistrano-extensions]: Downloads a tarball of uploaded content (that lives in public/
- directory, as specified by the :content_directories property) from the production site
- back to the local filesystem
- DESC
- task :backup_content do
- folders = ["content"] + shared_content.keys
-
- run "cd #{shared_path} && tar czf #{shared_path}/content_backup.tar.gz #{folders.join(' ')}"
-
- #run "cd #{content_path} && tar czf #{shared_path}/content_backup.tar.gz *"
- download("#{shared_path}/content_backup.tar.gz", "#{application}-#{rails_env}-content_backup.tar.gz")
- run "rm -f #{shared_path}/content_backup.tar.gz"
- end
-
- desc <<-DESC
- [capistrano-extensions]: Restores the backed up content (evn var FROM specifies which environment
- was backed up, defaults to RAILS_ENV) to the local development environment app
- DESC
- task :restore_content do
- from = ENV['FROM'] || rails_env
-
- system "mkdir -p tmp/content-#{from}"
- system "tar xzf #{application}-#{from}-content_backup.tar.gz -C tmp/content-#{from}"
- system "rm -f #{application}-#{from}-content_backup.tar.gz"
-
- shared_content.each_pair do |remote, local|
- system "rm -rf #{local} && mv tmp/content-#{from}/#{remote} #{local}"
+ namespace :tmp do
+ desc "[capistrano-extensions]: Displays warning if :tmp_dir has more than 10 files or is greater than 50MB"
+ task :check do
+ #[ 5 -le "`ls -1 tmp/cap | wc -l`" ] && echo "Display Me"
+ cmd = %Q{ [ 10 -le "`ls -1 #{tmp_dir} | wc -l`" ] || [ 50 -le "`du -sh #{tmp_dir} | awk '{print int($1)}'`" ] && printf "\033[1;41m Clean up #{tmp_dir} directory \033[0m\n" && du -sh #{tmp_dir}/* }
+ system(cmd)
end
- content_directories.each do |public_dir|
- system "rm -rf public/#{public_dir}"
- system "mv tmp/content-#{from}/content/#{public_dir} public/"
+ desc "[capistrano-extensions]: Remove the current remote env's backups from :tmp_dir"
+ task :clean_remote do
+ system("rm -f #{tmp_dir}/#{application}-#{rails_env}*")
end
-
- end
- desc <<-DESC
- [capistrano-extensions]: Wrapper for local:backup_db and local:restore_db.
- $> cap local:sync_db RAILS_ENV=production RESTORE_ENV=development
- DESC
- task :sync_db do
- transaction do
- backup_db
- ENV['FROM'] = rails_env
- restore_db
- end
+ # desc "Removes all but a single backup from :tmp_dir"
+ # task :clean do
+ #
+ # end
+ #
+ # desc "Removes all tmp files from :tmp_dir"
+ # task :remove do
+ #
+ # end
end
-
- desc <<-DESC
- [capistrano-extensions]: Wrapper for local:backup_content and local:restore_content
- $> cap local:sync_content RAILS_ENV=production RESTORE_ENV=development
- DESC
- task :sync_content do
- transaction do
- backup_content
- restore_content
- end
- end
-
- desc <<-DESC
- [capistrano-extensions]: Wrapper for local:sync_db and local:sync_content
- $> cap local:sync RAILS_ENV=production RESTORE_ENV=development
- DESC
- task :sync do
- sync_db
- sync_content
- end
end
+
end
def pluck_pass_str(db_config)
pass_str = db_config['password']
if !pass_str.nil?
pass_str = "-p#{pass_str.gsub('$', '\$')}"
end
pass_str || ''
-end
+end
+
+module LocalUtils
+ def current_timestamp
+ @current_timestamp ||= Time.now.to_i
+ end
+
+ def local_db_backup_file(args = {})
+ env = args[:env] || rails_env
+ timestamp = args[:timestamp] || current_timestamp
+ "#{tmp_dir}/#{application}-#{env}-db-#{timestamp}.sql"
+ end
+
+ def local_content_backup_dir(args={})
+ env = args[:env] || rails_env
+ timestamp = args[:timestamp] || current_timestamp
+ "#{tmp_dir}/#{application}-#{env}-content-#{timestamp}"
+ end
+
+ def retrieve_local_files(env, type)
+ `ls -r #{tmp_dir} | awk -F"-" '{ if ($2 ~ /#{env}/ && $3 ~ /#{type}/) { print $4; } }'`.split(' ')
+ end
+
+ def most_recent_local_backup(env, type)
+ retrieve_local_files(env, type).first.to_i
+ end
+end
+
+module RemoteUtils
+ def last_mod_time(path)
+ capture("stat -c%Y #{path}").to_i
+ end
+
+ def server_cache_valid?(path)
+ capture("[ -f #{path} ] || echo '1'").empty? && ((Time.now.to_i - last_mod_time(path)) <= remote_backup_expires) # two days in seconds
+ end
+end
+
+include LocalUtils, RemoteUtils
\ No newline at end of file