Created
July 29, 2009 09:34
-
-
Save mrrooijen/157958 to your computer and use it in GitHub Desktop.
a "base" Capistrano Rails Deployment Recipe. Use it to deploy your Rails application. It is also easily expandable. So feel free to grab this Recipe and add your own tasks/customization!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Guide | |
# Configure the essential configurations below and do the following: | |
# | |
# Repository Creation: | |
# cap deploy:repository:create | |
# git add . | |
# git commit -am "initial commit" | |
# git push origin master | |
# | |
# Initial Deployment: | |
# cap deploy:setup | |
# cap deploy | |
# cap deploy:gems:install | |
# cap deploy:db:create | |
# cap deploy:db:migrate | |
# cap deploy:passenger:restart | |
# | |
# * or run "cap deploy:initial" do run all of these | |
# | |
# Then For Every Update Just Do: | |
# git add . | |
# git commit -am "some other commit" | |
# git push origin master | |
# cap deploy | |
# | |
# For Apache2 Users | |
# cap deploy:apache:create | |
# cap deploy:apache:destroy | |
# cap deploy:apache:restart | |
# cap deploy:destroy_all:apache | |
# | |
# For NginX Users | |
# cap deploy:nginx:create | |
# cap deploy:nginx:destroy | |
# cap deploy:nginx:restart | |
# cap deploy:destroy_all:nginx | |
# | |
# For a Full List of Commands | |
# cap -T | |
# =================================== # | |
# START CONFIGURATION # | |
# =================================== # | |
# This configuration is *essential* | |
set :ip, "123.45.678.910" | |
set :domain, "domain.com" | |
set :subdomain, false # unless domain is a subdomain, e.g. subdomain.domain.com | |
set :user, "root" | |
# This configuration is *conventional* | |
set :application, ip | |
set :deploy_to, "/var/rails/#{domain}" | |
set :repository_path, "/var/git/#{domain}.git" | |
set :repository, "ssh://#{user}@#{application}#{repository_path}" | |
# The following configuration *optional* | |
set :scm, "git" | |
set :branch, "master" | |
set :use_sudo, true | |
role :web, application | |
role :app, application | |
role :db, application | |
default_run_options[:pty] = true | |
# Setup Shared Folders | |
# that should be created inside the shared_path | |
directory_configuration = %w(db config system) | |
# Setup Symlinks | |
# that should be created after each deployment | |
symlink_configuration = [ | |
%w(config/database.yml config/database.yml), | |
%w(db/production.sqlite3 db/production.sqlite3), | |
%w(system public/system) | |
] | |
# Application Specific Tasks | |
# that should be performed at the end of each deployment | |
def application_specific_tasks | |
# system 'cap deploy:whenever:update_crontab' | |
# system 'cap deploy:delayed_job:stop' | |
# system 'cap deploy:delayed_job:start n=1' | |
# system 'cap deploy:run_command command="ls -la"' | |
end | |
# =================================== # | |
# END CONFIGURATION # | |
# DON'T EDIT THE CONFIGURATION BELOW # | |
# =================================== # | |
# | |
# Helper Methods | |
# | |
def create_tmp_file(contents) | |
system 'mkdir tmp' | |
file = File.new("tmp/#{domain}", "w") | |
file << contents | |
file.close | |
end | |
# | |
# Capistrano Recipe | |
# | |
namespace :deploy do | |
# Tasks that run after every deployment (cap deploy) | |
desc "Initializes a bunch of tasks in order after the last deployment process." | |
task :restart do | |
puts "\n\n=== Running Custom Processes! ===\n\n" | |
create_production_log | |
setup_symlinks | |
application_specific_tasks | |
set_permissions | |
system 'cap deploy:passenger:restart' | |
end | |
# Deployment Tasks | |
desc "Executes the initial procedures for deploying a Ruby on Rails Application." | |
task :initial do | |
system "cap deploy:setup" | |
system "cap deploy" | |
system "cap deploy:gems:install" | |
system "cap deploy:db:create" | |
system "cap deploy:db:migrate" | |
system "cap deploy:passenger:restart" | |
end | |
namespace :destroy_all do | |
desc "Destroys Git Repository, Rails Environment and Apache2 Configuration." | |
task :apache do | |
system "cap deploy:repository:destroy" | |
run "rm -rf #{deploy_to}" | |
system "cap deploy:apache:destroy" | |
end | |
desc "Destroys Git Repository, Rails Environment and Nginx Configuration." | |
task :nginx do | |
system "cap deploy:repository:destroy" | |
run "rm -rf #{deploy_to}" | |
system "cap deploy:nginx:destroy" | |
end | |
end | |
namespace :passenger do | |
desc "Restarts Passenger" | |
task :restart do | |
puts "\n\n=== Restarting Passenger! ===\n\n" | |
run "touch #{current_path}/tmp/restart.txt" | |
end | |
end | |
desc "Sets permissions for Rails Application" | |
task :set_permissions do | |
puts "\n\n=== Setting Permissions! ===\n\n" | |
run "chown -R www-data:www-data #{deploy_to}" | |
end | |
desc "Creates the production log if it does not exist" | |
task :create_production_log do | |
unless File.exist?(File.join(shared_path, 'log', 'production.log')) | |
puts "\n\n=== Creating Production Log! ===\n\n" | |
run "touch #{File.join(shared_path, 'log', 'production.log')}" | |
end | |
end | |
desc "Creates symbolic links from shared folder" | |
task :setup_symlinks do | |
puts "\n\n=== Setting up Symbolic Links! ===\n\n" | |
symlink_configuration.each do |config| | |
run "ln -nfs #{File.join(shared_path, config[0])} #{File.join(release_path, config[1])}" | |
end | |
end | |
# Manual Tasks | |
namespace :db do | |
desc "Syncs the database.yml file from the local machine to the remote machine" | |
task :sync_yaml do | |
puts "\n\n=== Syncing database yaml to the production server! ===\n\n" | |
unless File.exist?("config/database.yml") | |
puts "There is no config/database.yml.\n " | |
exit | |
end | |
system "rsync -vr --exclude='.DS_Store' config/database.yml #{user}@#{application}:#{shared_path}/config/" | |
end | |
desc "Create Production Database" | |
task :create do | |
puts "\n\n=== Creating the Production Database! ===\n\n" | |
run "cd #{current_path}; rake db:create RAILS_ENV=production" | |
system "cap deploy:set_permissions" | |
end | |
desc "Migrate Production Database" | |
task :migrate do | |
puts "\n\n=== Migrating the Production Database! ===\n\n" | |
run "cd #{current_path}; rake db:migrate RAILS_ENV=production" | |
system "cap deploy:set_permissions" | |
end | |
desc "Resets the Production Database" | |
task :migrate_reset do | |
puts "\n\n=== Resetting the Production Database! ===\n\n" | |
run "cd #{current_path}; rake db:migrate:reset RAILS_ENV=production" | |
end | |
desc "Destroys Production Database" | |
task :drop do | |
puts "\n\n=== Destroying the Production Database! ===\n\n" | |
run "cd #{current_path}; rake db:drop RAILS_ENV=production" | |
system "cap deploy:set_permissions" | |
end | |
desc "Moves the SQLite3 Production Database to the shared path" | |
task :move_to_shared do | |
puts "\n\n=== Moving the SQLite3 Production Database to the shared path! ===\n\n" | |
run "mv #{current_path}/db/production.sqlite3 #{shared_path}/db/production.sqlite3" | |
system "cap deploy:setup_symlinks" | |
system "cap deploy:set_permissions" | |
end | |
desc "Populates the Production Database" | |
task :seed do | |
puts "\n\n=== Populating the Production Database! ===\n\n" | |
run "cd #{current_path}; rake db:seed RAILS_ENV=production" | |
end | |
end | |
namespace :gems do | |
desc "Installs any 'not-yet-installed' gems on the production server or a single gem when the gem= is specified." | |
task :install do | |
if ENV['gem'] | |
puts "\n\n=== Installing #{ENV['gem']}! ===\n\n" | |
run "gem install #{ENV['gem']}" | |
else | |
puts "\n\n=== Installing required RubyGems! ===\n\n" | |
run "cd #{current_path}; rake gems:install RAILS_ENV=production" | |
end | |
end | |
end | |
namespace :repository do | |
desc "Creates the remote Git repository." | |
task :create do | |
puts "\n\n=== Creating remote Git repository! ===\n\n" | |
run "mkdir -p #{repository_path}" | |
run "cd #{repository_path} && git --bare init" | |
system "git remote rm origin" | |
system "git remote add origin #{repository[:repository]}" | |
p "#{repository[:repository]} was added to your git repository as origin/master." | |
end | |
desc "Creates the remote Git repository." | |
task :destroy do | |
puts "\n\n=== destroying remote Git repository! ===\n\n" | |
run "rm -rf #{repository_path}" | |
system "git remote rm origin" | |
p "#{repository[:repository]} (origin/master) was removed from your git repository." | |
end | |
desc "Resets the remote Git repository." | |
task :reset do | |
puts "\n\n=== Resetting remove Git repository! ===\n\n" | |
system "cap deploy:repository:destroy" | |
system "cap deploy:repository:create" | |
end | |
desc "Reinitializes Origin/Master." | |
task :reinitialize do | |
system "git remote rm origin" | |
system "git remote add origin #{repository[:repository]}" | |
p "#{repository[:repository]} (origin/master) was added to your git repository." | |
end | |
end | |
namespace :environment do | |
desc "Creates the production environment" | |
task :create do | |
system "cap deploy:setup" | |
end | |
desc "Destroys the production environment" | |
task :destroy do | |
run "rm -rf #{deploy_to}" | |
end | |
desc "Resets the production environment" | |
task :reset do | |
run "rm -rf #{deploy_to}" | |
system "cap deploy:setup" | |
end | |
end | |
namespace :whenever do | |
desc "Update the crontab file for the Whenever Gem." | |
task :update_crontab, :roles => :db do | |
puts "\n\n=== Updating the Crontab! ===\n\n" | |
run "cd #{release_path} && whenever --update-crontab #{domain}" | |
end | |
end | |
namespace :delayed_job do | |
desc "Starts the Delayed Job Daemon(s)." | |
task :start do | |
puts "\n\n=== Starting #{(ENV['n'] + ' ') if ENV['n']}Delayed Job Daemon(s)! ===\n\n" | |
run "RAILS_ENV=production #{current_path}/script/delayed_job #{"-n #{ENV['n']} " if ENV['n']}start" | |
end | |
desc "Stops the Delayed Job Daemon(s)." | |
task :stop do | |
puts "\n\n=== Stopping Delayed Job Daemon(s)! ===\n\n" | |
run "RAILS_ENV=production #{current_path}/script/delayed_job stop" | |
end | |
end | |
namespace :apache do | |
desc "Adds Apache2 configuration and enables it." | |
task :create do | |
puts "\n\n=== Adding Apache2 Virtual Host for #{domain}! ===\n\n" | |
config = <<-CONFIG | |
<VirtualHost *:80> | |
ServerName #{domain} | |
#{unless subdomain then "ServerAlias www.#{domain}" end} | |
DocumentRoot #{File.join(deploy_to, 'current', 'public')} | |
</VirtualHost> | |
CONFIG | |
system 'mkdir tmp' | |
file = File.new("tmp/#{domain}", "w") | |
file << config | |
file.close | |
system "rsync -vr tmp/#{domain} #{user}@#{application}:/etc/apache2/sites-available/#{domain}" | |
File.delete("tmp/#{domain}") | |
run "sudo a2ensite #{domain}" | |
run "sudo /etc/init.d/apache2 restart" | |
end | |
desc "Restarts Apache2." | |
task :restart do | |
run "sudo /etc/init.d/apache2 restart" | |
end | |
desc "Removes Apache2 configuration and disables it." | |
task :destroy do | |
puts "\n\n=== Removing Apache2 Virtual Host for #{domain}! ===\n\n" | |
begin run("a2dissite #{domain}"); rescue; end | |
begin run("sudo rm /etc/apache2/sites-available/#{domain}"); rescue; end | |
run("sudo /etc/init.d/apache2 restart") | |
end | |
end | |
namespace :nginx do | |
desc "Adds NginX configuration and enables it." | |
task :create do | |
puts "\n\n=== Adding NginX Virtual Host for #{domain}! ===\n\n" | |
config = <<-CONFIG | |
server { | |
listen 80; | |
server_name #{unless subdomain then "www.#{domain} #{domain}" else domain end}; | |
root #{File.join(deploy_to, 'current', 'public')}; | |
passenger_enabled on; | |
} | |
CONFIG | |
create_tmp_file(config) | |
run "mkdir -p /opt/nginx/conf/sites-enabled" | |
system "rsync -vr tmp/#{domain} #{user}@#{application}:/opt/nginx/conf/sites-enabled/#{domain}" | |
File.delete("tmp/#{domain}") | |
system 'cap deploy:nginx:restart' | |
end | |
desc "Restarts NginX." | |
task :restart do | |
Net::SSH.start(application, user) {|ssh| ssh.exec "/etc/init.d/nginx stop"} | |
Net::SSH.start(application, user) {|ssh| ssh.exec "/etc/init.d/nginx start"} | |
end | |
desc "Removes NginX configuration and disables it." | |
task :destroy do | |
puts "\n\n=== Removing NginX Virtual Host for #{domain}! ===\n\n" | |
begin | |
run("rm /opt/nginx/conf/sites-enabled/#{domain}") | |
ensure | |
system 'cap deploy:nginx:restart' | |
end | |
end | |
end | |
desc "Run a command on the remote server. Specify command='my_command'." | |
task :run_command do | |
run "cd #{current_path}; #{ENV['command']}" | |
end | |
# Tasks that run after the (cap deploy:setup) | |
desc "Sets up the shared path" | |
task :setup_shared_path do | |
puts "\n\n=== Setting up the shared path! ===\n\n" | |
directory_configuration.each do |directory| | |
run "mkdir -p #{shared_path}/#{directory}" | |
end | |
system "cap deploy:db:sync_yaml" | |
end | |
end | |
# Callbacks | |
after 'deploy:setup', 'deploy:setup_shared_path' |
woah
Thanks dude - nice work.
This is really comprehensive and usable. Much thanks!
Best deploy.rb! Thanks!
When i tried deploy to a remote server with ssh running on different port, it's bug. My solution:
system "rsync -vr --exclude='.DS_Store' --rsh='ssh -p#{ssh_options[:port]}' config/database.yml #{user}@#{application}:#{shared_path}/config/"
btw, thank you very much.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
best capistrano gist, respect !