*** This version of Confluence is for testing only and contains a copy of content from June 29th 2026. No changes will be preserved. ***
Here is an example deploy.rb with a bunch of features specific to deploying Drupal on CUL servers. (You'll need to change all the TODO's)
require 'capistrano/ext/multistage'
set :application, "features.library.cornell.edu" # TODO - name your application
default_run_options[:shell] = '/bin/bash'
# based on:
# http://guides.beanstalkapp.com/deployments/deploy-with-capistrano.html
# http://www.58bits.com/blog/2013/03/23/deploying-drupal-with-capistrano
# https://github.com/antistatique/capdrupal
# TODO: list of modules to enable
# drush pm-list | grep Enabled | grep -o '(.\+)' | grep -o '[^()]\+' | tr '\n' ' ' && echo ""
set :enabled_modules, File.open("drupal_config/enabled_modules.txt", "r") { |file| file.read.split(" ") }
# override these in production
set :disabled_modules, File.open("drupal_config/disabled_modules.txt", "r") { |file| file.read.split(" ") }
set :default_theme, File.open("drupal_config/default_theme.txt", "r") { |file| file.read.split(" ") }
#SSH and PTY Options
default_run_options[:pty] = true
ssh_options[:forward_agent] = true
set :use_sudo, false
#set :port, 5144
#On Mac OS X
ssh_options[:compression] = "none"
#Application settings
set :user, ENV['USER'] # can also come from ~/.caprc
set :runner, ENV['USER'] # when sudo-ing use this user
set :runner_group, "lib_web_dev_role"
set :admin_runner, ENV['USER'] # when sudo-ing during :setup use this
set :php_user, "apache" # user php runs as
set :stages, ["local", "staging", "production"] # TODO - define different stages
set :default_stage, "staging"
set :keep_releases, 5
set :drush_cmd, "drush"
set :scm, :git
set :repository, "git@git.library.cornell.edu:features_test_library_cornell_edu.git" # TODO - your git repo here
set :branch, "master"
# prevent errors from missing javascripts,stylesheets,images directories
set :normalize_asset_timestamps, false
# now use the regular cap deploy instead of drupal
#before 'deploy', 'deploy:drush:site_offline', 'drupal:db:backup', 'git:push_deploy_tag'
before 'deploy' do
git.push_deploy_tag
drupal.drush.site_offline
drupal.db.backup
drupal.drush.site_online
end
#after 'deploy', 'drupal:link_filesystem', 'drupal:drush:set_files_paths', 'drupal:drush:updatedb', 'deploy:drush:site_online'
after 'deploy' do
drupal.link_filesystem
drupal.drush.site_offline
drupal.drush.set_files_paths
drupal.drush.enabler(enabled_modules)
drupal.drush.updatedb
drupal.drush.disabler(disabled_modules)
drupal.drush.revert_features
drupal.drush.set_theme(default_theme)
drupal.drush.clear_all_caches
drupal.drush.site_online
end
after 'deploy:rollback' do
drupal.link_filesystem
drupal.drush.clear_all_caches
drupal.drush.site_offline
drupal.db.reload_previous
drupal.drush.site_online
end
before 'drupal:db:grab', 'drupal:drush:site_offline', 'drupal:drush:clear_all_caches'
after 'drupal:db:grab', 'drupal:drush:site_online'
before 'drupal:db:install', 'drupal:drush:site_offline'
after 'drupal:db:install', 'drupal:drush:set_files_paths', 'drupal:drush:clear_all_caches', 'drupal:drush:site_online'
before 'drupal:db:install_latest', 'drupal:drush:site_offline'
after 'drupal:db:install_latest', 'drupal:drush:set_files_paths', 'drupal:drush:clear_all_caches', 'drupal:drush:site_online'
after 'deploy:setup', 'drupal:site_setup', 'deploy:update', 'drupal:link_filesystem', 'drupal:site_setup_permissions'
after 'drupal:post_install_configuration' do
drupal.drush.set_files_paths
drupal.drush.enabler(enabled_modules)
drupal.drush.updatedb
drupal.drush.disabler(disabled_modules)
drupal.drush.set_theme(default_theme)
end
after 'drupal:fix_undefined_index', 'drupal:drush:clear_all_caches'
namespace :deploy do
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
#Place holder for app restart - in Ruby apps this would touch restart.txt.
end
end
#Drupal application and project specific tasks.
namespace :drupal do
desc "Real list of modules to enable"
task :read_enabled_modules do
contents = File.open("config/enabled_modules.txt", "r") { |file| file.read }
return contents
end
#desc "Perform a Drupal application deploy."
# see before/after deploy above
task :default, :roles => :app, :except => { :no_release => true } do
site_offline
clear_all_caches
backupdb
deploy.default
link_filesystem
set_files_paths
updatedb
site_online
end
desc "Initial remote setup for Drupal"
task :site_setup, :roles => :app do
commands = []
commands << "mkdir -p #{deploy_to}/backup"
commands << "mkdir -p #{deploy_to}/cache"
commands << "mkdir -p #{deploy_to}/files"
commands << "mkdir -p #{deploy_to}/log"
commands << "mkdir -p #{deploy_to}/private_files"
commands << "mkdir -p #{deploy_to}/subsites"
commands << "mkdir -p #{deploy_to}/tmp"
run commands.join(' && ') if commands.any?
end
#desc "Set permissions for php to write to some directories"
task :site_setup_permissions, :roles => :app do
sudo "chown #{php_user} #{deploy_to}/backup"
sudo "chown #{php_user} #{deploy_to}/cache"
sudo "chown #{php_user} #{deploy_to}/files"
sudo "chown #{php_user} #{deploy_to}/private_files"
sudo "chown #{php_user} #{deploy_to}/tmp"
sudo "chown #{php_user} #{deploy_to}/settings.php"
end
desc "Get the user to run the Drupal install, then do final configuration"
task :post_install_configuration, :roles => :app do
servers = find_servers_for_task(current_task)
server = servers.first
default_ans = 'yes'
ans = Capistrano::CLI.ui.ask "Have you run http://#{server}/install.php? [#{default_ans}]"
ans = default_ans if ans.empty?
abort "Please install Drupal first" if ans != default_ans
end
#desc "This should not be run on its own - so comment out the description.
# "Recreate the required Drupal symlinks to static directories and clear all caches."
task :link_filesystem, :roles => :app, :except => { :no_release => true } do
commands = []
commands << "if [ ! -f #{deploy_to}/settings.php ]; then cp #{app_path}/sites/default/default.settings.php #{deploy_to}/settings.php; fi"
commands << "cat /dev/null > #{app_path}/.htaccess"
commands << "mkdir -p #{app_path}/sites/default"
commands << "ln -nfs #{share_path}/settings.php #{app_path}/sites/default/settings.php"
commands << "ln -nfs #{share_path}/files #{app_path}/sites/default/files"
commands << "ln -nfs #{share_path}/tmp #{app_path}/sites/default/tmp"
commands << "ln -nfs #{share_path}/cache #{app_path}/cache"
commands << "find #{app_path} -type d -print0 | xargs -0 chmod 755"
commands << "find #{app_path} -type f -print0 | xargs -0 chmod 644"
run commands.join(' && ') if commands.any?
end
namespace :db do
desc "returns a stage and time stamped backup file name"
def backup_file_name(stage)
backup_time = release_name # use capistrano's variable %Y%m%d%H%M%S
return make_backup_file_name(stage, backup_time)
end
def make_backup_file_name(stage, backup_time)
filename = "backup/#{stage}-snapshot-#{backup_time}.sql"
return filename
end
desc "gets the most recent database backup file with a given prefix"
def latest_backup(backup_prefix)
tmax = Time.at 0
latestfile = nil
Dir.glob("backup/#{backup_prefix}-snapshot-*") do |filename|
mt = File.mtime(filename)
if mt > tmax
tmax = mt
latestfile = filename
end
end
return latestfile
end
desc "upload and install database"
def upload_install(filename)
puts "uploading #{filename}"
top.upload("#{filename}", "#{deploy_to}/#{filename}")
puts "loading drupal with database"
drush.load_drupal("#{deploy_to}/#{filename}")
end
desc "backup the database"
task :backup, :roles => :app, :except => { :no_release => true } do
backup_file = backup_file_name(stage)
drush.dump_drupal(backup_file)
puts "#{backup_file}"
end
desc "Grab local copy of remote database"
# cap production drupal:db:grab
task :grab, :roles => :app, :except => { :no_release => true } do
backup_file = backup_file_name(stage)
drush.dump_drupal(backup_file)
puts "#{backup_file}"
download "#{deploy_to}/#{backup_file}", "#{backup_file}"
end
desc "Install latest local database snapshot"
# cap staging drupal:db:install_latest -s from=production
task :install_latest, :roles => :app, :except => { :no_release => true } do
set :source_stage, fetch(:from, '')
if source_stage.empty?
puts "useage:"
abort "cap staging drupal:db:install_latest -s from=production"
else
filename = latest_backup(source_stage)
if filename.nil?
abort "first grab a copy of the datatbase: cap #{source_stage} drupal:db:grab"
else
puts "Moving database from #{source_stage} to #{stage}"
upload_install(filename)
end
end
end
desc "Install a specific local database snapshot"
# cap staging drupal:db:install -s file=backup/staging-snapshot-2013-10-25-13-17-23.sql
task :install, :roles => :app, :except => { :no_release => true } do
set :filename, fetch(:file, '')
if filename.empty?
puts "useage:"
abort "cap staging drupal:db:install -s file=backup/production-snapshot-2013-10-25-13-17-23.sql"
else
puts "Moving database from #{filename} to #{stage}"
upload_install(filename)
end
end
desc "reload database from previous release"
task :reload_previous, :roles => :app, :except => { :no_release => true } do
previous_release_name = File.basename(previous_release)
filename = make_backup_file_name(stage, previous_release_name)
puts "reloading database from #{filename}"
drush.load_drupal("#{deploy_to}/#{filename}")
end
end
namespace :drush do
desc "Backup the database."
task :backupdb, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} sql-dump --result-file=#{deploy_to}/backup/release-drupal-db.sql"
end
#desc "Backup the database"
# we don't want to require backup_migrate
task :backupdb_bam, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} bam-backup"
end
desc "Clear all caches"
task :clear_all_caches, :roles => :app, :except => { :no_release => true } do
run "#{drush_cmd} -r #{app_path} --uri=#{drush_uri} cc all"
end
desc "error message fix - undefined index: <name, version>"
# see https://drupal.org/node/1170362
task :fix_undefined_index, :roles => :app, :except => { :no_release => true } do
run "#{drush_cmd} -r #{app_path} vset --yes install_profile \"standard\""
run "#{drush_cmd} -r #{app_path} sql-query \"UPDATE system SET status=1 WHERE name='standard';\""
end
desc "Revert all features to install the newest versions"
task :revert_features, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} features-revert-all -y"
end
desc "set the private_files & tmp path for the deploy environment"
task :set_files_paths, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} vset --yes file_public_path sites/default/files"
run "#{drush_cmd} -r #{app_path} vset --yes file_private_path #{deploy_to}/private_files"
run "#{drush_cmd} -r #{app_path} vset --yes file_temporary_path sites/default/tmp"
end
desc "Set the site offline"
task :site_offline, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} vset site_offline 1 -y"
run "#{drush_cmd} -r #{app_path} vset maintenance_mode 1 -y"
end
desc "Set the site online"
task :site_online, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} vset site_offline 0 -y"
run "#{drush_cmd} -r #{app_path} vset maintenance_mode 0 -y"
end
desc "Run Drupal database updates for new core/modules/themes if required."
task :updatedb, :on_error => :continue do
run "#{drush_cmd} -r #{app_path} updatedb -y"
end
desc "backup the Drupal database to a timestamped file"
def dump_drupal(filename)
run "#{drush_cmd} -r #{app_path} sql-dump --result-file=#{deploy_to}/#{filename}"
end
desc "load the Drupal database from an sql dump"
def load_drupal(filename)
run "`#{drush_cmd} -r #{app_path} sql-connect` < #{filename}"
end
desc "enable an array of modules"
def enabler(modules)
run "#{drush_cmd} -r #{app_path} pm-enable -y #{modules.join(' ')}"
end
desc "disable an array of modules"
def disabler(modules)
run "#{drush_cmd} -r #{app_path} pm-disable -y #{modules.join(' ')}"
end
desc "set the default theme"
def set_theme(theme)
run "#{drush_cmd} -r #{app_path} vset theme_default #{theme}"
end
end
end
namespace :git do
desc "Place release tag into Git and push it to origin server."
task :push_deploy_tag do
user = `git config --get user.name`
email = `git config --get user.email`
tag = "release_#{release_name}"
if exists?(:stage)
tag = "#{stage}_#{tag}"
end
puts `git tag #{tag} #{revision} -m "Deployed by #{user} <#{email}>"`
puts `git push origin tag #{tag}`
end
end
namespace :explore do
desc "Local variables"
task :locals, :on_error => :continue do
local_variables.each {|var| puts "#{var} fetch(:#{var})"}
puts "shared_path #{shared_path}"
puts "stage #{stage}"
puts "args #{args}"
puts "release_name #{release_name}"
puts "previous_revision #{previous_revision}"
puts "current_revision #{current_revision}"
# deploy.instance_variables.map do |var|
# puts [var, deploy.instance_variable_get(var)].join(":")
# end
end
desc "check paths for mysql and mysqldump"
task :check_mysql, :on_error => :continue do
puts "$PATH"
run "echo $PATH"
puts "Which mysql?"
run "which mysql"
puts "Which mysqldump?"
run "which mysqldump"
puts "connect statement:"
run "#{drush_cmd} -r #{app_path} sql-connect"
end
desc "test du jour"
task :testdujour, :on_error => :continue do
mysqlcmd = %x[ echo `#{drush_cmd} -r #{app_path} sql-connect` ]
puts "mysql command: #{mysqlcmd}"
puts "current_path #{current_path}"
puts "deploy_to #{deploy_to}"
end
desc "find code for a task"
task :find, :on_error => :continue do
# load all the tasks associated with the rails app
Rails.application.load_tasks
# get the source locations of actions called by a task
task_name = 'deploy:cold' # fully scoped task name
Rake.application[task_name].actions.map(&:source_location)
end
desc "try out enabler"
task :tryout do
modules = ['aaa', 'bbb', 'ccc', 'ploop']
drupal.drush.enabler(modules)
end
end