This presentation describes the deployment process of flinc.org and demonstrates several features of Capistrano by giving examples extracted from flinc's deploy scripts.
parallel on multiple remote machines, via SSH. It uses a simple DSL (borrowed in part from Rake) that allows you to define tasks, which may be applied to machines in certain roles.“
"staging" # Paths set :application, "flinc.org" set :deploy_to, "/var/www/#{application}" # User set :user, "deploy" set :use_sudo, false # Repository set :scm, :git set :repository, "[email protected]:flinc/flinc.git" set :deploy_via, :remote_cache capistrano-ext gem
desc "[internal] Enables fast restarts for the current session" task :fast_restart { set(:thin_one_by_one, false) } [:start, :stop, :restart].each do |action| desc "[internal] Performs #{action} on all thin instances" task action, :roles => :web do config_path = fetch(:thin_config, "#{shared_path}/config/thin.yml") one_by_one = fetch(:thin_one_by_one, true) ? "-O" : "" bundler.run "thin #{action} -C #{config_path} #{one_by_one}" end end end before "deploy:migrations", "thin:fast_restart"
desc "Prepare assets, commit and push them." task :prepare do unless fetch(:skip_assets, false) git.ensure_clean_working_directory! git.on_branch(branch) do run_locally "bundle exec rake deploy:prepare" git.add "public/assets" git.commit "Prepared assets for deploy to #{rails_env}" git.push(git.current_branch) end end end end before "deploy:update_code", "assets:prepare" Runs command on local machine
desc "Stream logs from current environment" task :tail, :except => { :no_release => true } do stream "tail -f #{current_path}/log/#{rails_env}.log" end end „Streams the result of the command from all servers that are the target of the current task.“
primary server" task :console, :roles => :app, :primary => true do hostname = find_servers_for_task(current_task).first exec "ssh -l #{user} #{hostname} -t 'source ~/.profile && rvm use #{rvm_ruby_string} && #{current_path}/script/rails c #{rails_env}'" end end Open the rails console on the primary server „Identifies all servers that the given task should be executed on.“
:setup_known_hosts, :except => { :no_release => true } do known_hosts = '~/.ssh/known_hosts' run "mkdir -p #{File.dirname(known_hosts)}" run "if [ ! -f #{known_hosts} ]; then touch #{known_hosts}; fi" template.read("known_hosts").split(/$/).each do |line| next unless host = line.split(" ").first run <<-CMD if grep -q '#{host}' #{known_hosts}; then echo 'Host #{host} is already added.'; else echo '#{line.strip}' >> #{known_hosts}; echo 'Host #{host} added.'; fi CMD end end end after 'deploy:setup', 'ssh:setup_known_hosts'
the database.yml configuration file in shared path." task :setup, :except => { :no_release => true } do template.upload "database.yml.erb", "#{shared_path}/config/database.yml", :binding => binding end desc "[internal] Updates the symlink for database.yml file to the just deployed release." task :symlink, :except => { :no_release => true} do run "ln -nfs #{shared_path}/config/database.yml #{release_path}/config/database.yml" end end <%= rails_env %>: adapter: <%= fetch(:database_adapter, 'postgresql') %> encoding: <%= fetch(:database_encoding, 'utf8') %> reconnect: <%= fetch(:database_reconnect, 'false') %> database: <%= fetch(:database_name, "#{application}_#{rails_env}") %> pool: <%= fetch(:database_pool, 5) %> username: <%= fetch(:database_username, user) %> password: <%= Capistrano::CLI.password_prompt("Enter database password: ") %> host: <%= fetch(:database_host, '') %> port: <%= fetch(:database_port, '5432') %> Prompts the user for a password, so we don‘t need to check it into version control
ENV['rvm_path'])) require 'rvm/capistrano' else raise 'You need a RVM environment to use the deploy scripts' end # Ruby Version Manager set :rvm_ruby_string, 'ruby-1.9.2-p180' set :rvm_type, :user
do desc "Install the given version of rvm" task :install, :except => { :no_release => true } do ruby = fetch(:ruby_version, fetch(:rvm_ruby_string, '1.8.7')) old_shell = default_shell set :default_shell, "/bin/bash" run "rvm install #{ruby}" set :rvm_ruby_string, ruby set :default_shell, old_shell rvm.setup end end
up everything required to smoothly run the given ruby version" task :setup, :except => { :no_release => true } do run "rvm #{rvm_ruby_string} exec gem install bundler" run "rvm #{rvm_ruby_string} exec gem install god" run "rvm wrapper #{rvm_ruby_string} current bundle" run "rvm wrapper #{rvm_ruby_string} current god" bundle.install run "if [ -f /etc/init.d/god ]; then sudo /etc/init.d/god restart; fi;" end end
desc "Updates rvm to the latest version" task :update, :except => { :no_release => true } do run "rvm get latest" run "rvm reload" end desc "Output RVM info on all servers" task :info, :except => { :no_release => true } do run "rvm info" end desc "Output the current ruby version on all servers" task :current, :except => { :no_release => true } do run "rvm current" end desc "Output the list of installed rubies on all servers" task :list, :except => { :no_release => true } do run "rvm list" end end
do namespace :web do task :disable, :roles => :proxy do on_rollback { rm "#{shared_path}/system/maintenance.html" } deadline, reason = ENV['UNTIL'], ENV['REASON'] template.upload "maintenance.erb", "#{shared_path}/system/maintenance.html", :mode => 0644, :binding => binding end desc "Enables the application by removing the maintenance.html page" task :enable, :roles => :proxy do run "rm #{shared_path}/system/maintenance.html" end end end
:web do desc "[internal] Ensures that web is currently disabled" task :ensure, :roles => :proxy do begin run "test -f #{shared_path}/system/maintenance.html" rescue Capistrano::CommandError unless fetch(:force, false) logger.important "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" logger.important "Disable the website (deploy:web:disable) before doing this" logger.important "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" exit end end end end before 'deploy:migrations', 'deploy:web:ensure' before 'deploy:migrate', 'deploy:web:ensure'
do task :default do git.ensure_clean_working_directory! git.fetch("--tags") git.on_branch(branch) do git.tag(git.next_tag) git.tag("-d #{rails_env}-latest") git.push(":refs/tags/#{rails_env}-latest") git.tag("#{rails_env}-latest #{git.last_tag}") git.push("--tags") end end end end after "deploy:update_code", "deploy:tag"
module Deploy module Plugins module Bundler def run(command) top.run "BUNDLE_GEMFILE=#{current_path}/Gemfile bundle exec #{command}" end end end end Capistrano.plugin :bundler, Deploy::Plugins::Bundler