Module: Capistrano::Configuration::Actions::Invocation

Included in:
Capistrano::Configuration
Defined in:
lib/capistrano/configuration/actions/invocation.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object

:nodoc:



7
8
9
10
11
12
13
14
15
16
17
# File 'lib/capistrano/configuration/actions/invocation.rb', line 7

def self.included(base) #:nodoc:
  base.extend(ClassMethods)

  base.send :alias_method, :initialize_without_invocation, :initialize
  base.send :alias_method, :initialize, :initialize_with_invocation

  base.default_io_proc = Proc.new do |ch, stream, out|
    level = stream == :err ? :important : :info
    ch[:options][:logger].send(level, out, "#{stream} :: #{ch[:server]}")
  end
end

Instance Method Details

#add_default_command_options(options) ⇒ Object

Merges the various default command options into the options hash and returns the result. The default command options that are understand are:

  • :default_environment: If the :env key already exists, the :env key is merged into default_environment and then added back into options.

  • :default_shell: if the :shell key already exists, it will be used. Otherwise, if the :default_shell key exists in the configuration, it will be used. Otherwise, no :shell key is added.



152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/capistrano/configuration/actions/invocation.rb', line 152

def add_default_command_options(options)
  defaults = self[:default_run_options]
  options = defaults.merge(options)

  env = self[:default_environment]
  env = env.merge(options[:env]) if options[:env]
  options[:env] = env unless env.empty?

  shell = options[:shell] || self[:default_shell]
  options[:shell] = shell unless shell.nil?

  options
end

#continue_execution(tree) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/capistrano/configuration/actions/invocation.rb', line 171

def continue_execution(tree)
  if tree.branches.length == 1
    continue_execution_for_branch(tree.branches.first)
  else
    tree.each { |branch| branch.skip! unless continue_execution_for_branch(branch) }
    tree.any? { |branch| !branch.skip? }
  end
end

#continue_execution_for_branch(branch) ⇒ Object



180
181
182
183
184
185
186
187
188
189
# File 'lib/capistrano/configuration/actions/invocation.rb', line 180

def continue_execution_for_branch(branch)
  case Capistrano::CLI.debug_prompt(branch)
    when "y"
      true
    when "n"
      false
    when "a"
      exit(-1)
  end
end

#initialize_with_invocation(*args) ⇒ Object

:nodoc:



23
24
25
26
27
# File 'lib/capistrano/configuration/actions/invocation.rb', line 23

def initialize_with_invocation(*args) #:nodoc:
  initialize_without_invocation(*args)
  set :default_environment, {}
  set :default_run_options, {}
end

#invoke_command(cmd, options = {}, &block) ⇒ Object

Invokes the given command. If a via key is given, it will be used to determine what method to use to invoke the command. It defaults to :run, but may be :sudo, or any other method that conforms to the same interface as run and sudo.



39
40
41
42
43
# File 'lib/capistrano/configuration/actions/invocation.rb', line 39

def invoke_command(cmd, options={}, &block)
  options = options.dup
  via = options.delete(:via) || :run
  send(via, cmd, options, &block)
end

#parallel(options = {}) ⇒ Object

Raises:

  • (ArgumentError)


29
30
31
32
33
# File 'lib/capistrano/configuration/actions/invocation.rb', line 29

def parallel(options={})
  raise ArgumentError, "parallel() requires a block" unless block_given?
  tree = Command::Tree.new(self) { |t| yield t }
  run_tree(tree)
end

#run(cmd, options = {}, &block) ⇒ Object

Execute the given command on all servers that are the target of the current task. If a block is given, it is invoked for all output generated by the command, and should accept three parameters: the SSH channel (which may be used to send data back to the remote process), the stream identifier (:err for stderr, and :out for stdout), and the data that was received.



51
52
53
54
55
# File 'lib/capistrano/configuration/actions/invocation.rb', line 51

def run(cmd, options={}, &block)
  block ||= self.class.default_io_proc
  tree = Command::Tree.new(self) { |t| t.else(cmd, &block) }
  run_tree(tree, options)
end

#run_tree(tree, options = {}) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/capistrano/configuration/actions/invocation.rb', line 57

def run_tree(tree, options={})
  if tree.branches.empty? && tree.fallback
    logger.debug "executing #{tree.fallback}"
  elsif tree.branches.any?
    logger.debug "executing multiple commands in parallel"
    tree.each do |branch|
      logger.trace "-> #{branch}"
    end
  else
    raise ArgumentError, "attempt to execute without specifying a command"
  end

  return if dry_run || (debug && continue_execution(tree) == false)

  options = add_default_command_options(options)

  tree.each do |branch|
    if branch.command.include?(sudo)
      branch.callback = sudo_behavior_callback(branch.callback)
    end
  end

  execute_on_servers(options) do |servers|
    targets = servers.map { |s| sessions[s] }
    Command.process(tree, targets, options.merge(:logger => logger))
  end
end

#sudo(*parameters, &block) ⇒ Object

Returns the command string used by capistrano to invoke a comamnd via sudo.

run "#{sudo :as => 'bob'} mkdir /path/to/dir"

It can also be invoked like #run, but executing the command via sudo. This assumes that the sudo password (if required) is the same as the password for logging in to the server.

sudo "mkdir /path/to/dir"

Also, this method understands a :sudo configuration variable, which (if specified) will be used as the full path to the sudo executable on the remote machine:

set :sudo, "/opt/local/bin/sudo"


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/capistrano/configuration/actions/invocation.rb', line 101

def sudo(*parameters, &block)
  options = parameters.last.is_a?(Hash) ? parameters.pop.dup : {}
  command = parameters.first
  user = options[:as] && "-u #{options.delete(:as)}"

  sudo_prompt_option = "-p '#{sudo_prompt}'" unless sudo_prompt.empty?
  sudo_command = [fetch(:sudo, "sudo"), sudo_prompt_option, user].compact.join(" ")

  if command
    command = sudo_command + " " + command
    run(command, options, &block)
  else
    return sudo_command
  end
end

#sudo_behavior_callback(fallback) ⇒ Object

Returns a Proc object that defines the behavior of the sudo callback. The returned Proc will defer to the fallback argument (which should also be a Proc) for any output it does not explicitly handle.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/capistrano/configuration/actions/invocation.rb', line 121

def sudo_behavior_callback(fallback) #:nodoc:
  # in order to prevent _each host_ from prompting when the password
  # was wrong, let's track which host prompted first and only allow
  # subsequent prompts from that host.
  prompt_host = nil

  Proc.new do |ch, stream, out|
    if out =~ /^#{Regexp.escape(sudo_prompt)}/
      ch.send_data "#{self[:password]}\n"
    elsif out =~ /^Sorry, try again/
      if prompt_host.nil? || prompt_host == ch[:server]
        prompt_host = ch[:server]
        logger.important out, "#{stream} :: #{ch[:server]}"
        reset! :password
      end
    elsif fallback
      fallback.call(ch, stream, out)
    end
  end
end

#sudo_promptObject

Returns the prompt text to use with sudo



167
168
169
# File 'lib/capistrano/configuration/actions/invocation.rb', line 167

def sudo_prompt
  fetch(:sudo_prompt, "sudo password: ")
end