From b45290e6ae3acce465ab5b7b8a82b7ad73a022e3 Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Sat, 24 May 2008 21:18:42 -0600 Subject: [PATCH] Enhance sudo helper to return command. Also make run helper use the sudo password detection callback, so that sudo can be used in more complex scenarios. --- CHANGELOG | 2 + .../configuration/actions/invocation.rb | 46 ++++++++++++------- test/configuration/actions/invocation_test.rb | 8 ++-- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index eb9e0bf96..baa6b8eed 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,7 @@ *unreleased* +* Enhance the sudo helper so it can be used to return the command, instead of executing it [Jamis Buck] + * Revert "make sudo helper play nicely with complex command chains", since it broke stuff [Jamis Buck] * Make set(:default_shell, false) work for not using a shell on a per-command basis [Ryan McGeary] diff --git a/lib/capistrano/configuration/actions/invocation.rb b/lib/capistrano/configuration/actions/invocation.rb index dd6914413..b17345312 100644 --- a/lib/capistrano/configuration/actions/invocation.rb +++ b/lib/capistrano/configuration/actions/invocation.rb @@ -50,31 +50,45 @@ def run(cmd, options={}, &block) options = add_default_command_options(options) + if cmd.include?(sudo) + block = sudo_behavior_callback(block) + end + execute_on_servers(options) do |servers| targets = servers.map { |s| sessions[s] } Command.process(cmd, targets, options.merge(:logger => logger), &block) end end - # Like #run, but executes the command via sudo. This assumes - # that the sudo password (if required) is the same as the password for - # logging in to the server. + # Returns the command string used by capistrano to invoke a comamnd via + # sudo. + # + # run "#{sudo :as => 'bob'} mkdir /path/to/dir" # - # Also, this module accepts a :sudo configuration variable, + # 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" - def sudo(command, options={}, &block) - block ||= self.class.default_io_proc - - options = options.dup - as = options.delete(:as) - - user = as && "-u #{as}" - command = [fetch(:sudo, "sudo"), "-p '#{sudo_prompt}'", user, command].compact.join(" ") - - run(command, options, &sudo_behavior_callback(block)) + def sudo(*parameters, &block) + options = parameters.last.is_a?(Hash) ? parameters.pop.dup : {} + command = parameters.first + user = options[:as] && "-u #{options.delete(:as)}" + + sudo_command = [fetch(:sudo, "sudo"), "-p '#{sudo_prompt}'", user].compact.join(" ") + + if command + command = sudo_command + " " + command + run(command, options, &block) + else + return sudo_command + end end # Returns a Proc object that defines the behavior of the sudo @@ -90,13 +104,13 @@ def sudo_behavior_callback(fallback) #:nodoc: Proc.new do |ch, stream, out| if out =~ /^#{Regexp.escape(sudo_prompt)}/ ch.send_data "#{self[:password]}\n" - elsif out =~ /try again/ + 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 - else + elsif fallback fallback.call(ch, stream, out) end end diff --git a/test/configuration/actions/invocation_test.rb b/test/configuration/actions/invocation_test.rb index 9400d44f5..13171f38c 100644 --- a/test/configuration/actions/invocation_test.rb +++ b/test/configuration/actions/invocation_test.rb @@ -149,7 +149,7 @@ def test_sudo_behavior_callback_with_incorrect_password_on_first_prompt ch.stubs(:[]).with(:host).returns("capistrano") ch.stubs(:[]).with(:server).returns(server("capistrano")) @config.expects(:reset!).with(:password) - @config.sudo_behavior_callback(nil)[ch, nil, "blah blah try again blah blah"] + @config.sudo_behavior_callback(nil)[ch, nil, "Sorry, try again."] end def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts @@ -164,9 +164,9 @@ def test_sudo_behavior_callback_with_incorrect_password_on_subsequent_prompts @config.expects(:reset!).with(:password).times(2) - callback[ch, nil, "blah blah try again blah blah"] - callback[ch2, nil, "blah blah try again blah blah"] # shouldn't call reset! - callback[ch, nil, "blah blah try again blah blah"] + callback[ch, nil, "Sorry, try again."] + callback[ch2, nil, "Sorry, try again."] # shouldn't call reset! + callback[ch, nil, "Sorry, try again."] end def test_sudo_behavior_callback_should_defer_to_fallback_for_other_output