Print capistrano execution time per server

Are all servers slow or just one ?

Output
Example logging all times > 3 seconds.

executing 'ruby -e "r = rand(10); puts %{sleep #{r}}; sleep r"'
 ** [out :: dbadmin1] sleep 6
 ** [out :: app1] sleep 4
 ** [out :: work1] sleep 2
 ** [out :: mirror1] sleep 5
 ** Server dbadmin1 finished command in 4 seconds
 ** Server app1 finished command in 7 seconds

Code

module PerServerTime
  # set via Capistrano::Command.per_server_time_threshold = 111
  def self.included(base)
    class << base
      attr_accessor :per_server_time_threshold
    end
  end

  # whenever a command finishes on 1 server print how long it ran on this server
  def process_iteration
    return super if @channels.size < 2 # do not need per-server logging for 1 server

    @start_time ||= Time.now.to_f
    @closed_channels ||= []

    @channels.each do |channel|
      if channel[:closed] && !@closed_channels.include?(channel)
        @closed_channels < (self.class.per_server_time_threshold || 30)
          host = channel.connection.instance_variable_get(:@xserver).host
          logger.info("Server #{host} finished command in #{time} seconds")
        end
      end
    end

    super
  end
end

Capistrano::Command.send(:include, PerServerTime)

Logging and showing colorful bash output

Deploy using the deploy user and also log who deploys using the original user.
Retaining the color was tricky but script fakes tty so we can keep all the color glory and with sed we strip colors before logging them.

# /usr/bin/capsu
function log {
  old_IFS=$IFS
  IFS='' # do not split on newline when reading stdin
  newline=$'\n'
  line=""

  while read -d '' -n1 c # read every character
  do
    # print every character as it comes in for cap shell and password prompts
    printf "%s" "$c"

    # amend complete line with current user (but without color codes) to log
    # so multiple people can run capsu in parallel
    if [ "$c" = $newline ]; then
      echo "$SUDO_USER: $line" | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g" >> $1
      line=""
    else
      line+=$c
    fi
  done
  IFS=$old_IFS
}

rvmsudo -u deploy script /dev/null -c "bundle exec cap $@" 2>&1 | log deploy cap.log

Invoke Any Rake Task via Capistrano

Just run a rake task without having to setup a special capistrano task.

Usage

cap rake_task:invoke COMMAND="db:migrate" #yes, its a silly example...

Setup

namespace :rake_task do
  task :invoke do
    if ENV['COMMAND'].to_s.strip == ''
      puts "USAGE:   cap rake:invoke COMMAND='db:migrate'"
    else
      run "cd #{current_path} && RAILS_ENV=production sudo rake #{ENV['COMMAND']}"
    end
  end
end