Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ appear at the top.

* Add your entries below here, remember to credit yourself however you want
to be credited!
* Introduced `test_and_capture` method on backends
[PR #274](https://github.com/capistrano/sshkit/pull/274)
@betesh
* Export environment variables and execute command in a subshell.
[PR #273](https://github.com/capistrano/sshkit/pull/273)
@kuon
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ parallel.

#### Running commands

All backends support the `execute(*args)`, `test(*args)` & `capture(*args)` methods
All backends support the `execute(*args)`, `test(*args)`, `capture(*args)` & `test_and_capture(*args)` methods
for executing a command. You can call any of these methods in the context of an `on()`
block.

**Note: In SSHKit, the first parameter of the `execute` / `test` / `capture` methods
**Note: In SSHKit, the first parameter of the `execute` / `test` / `capture` / `test_and_capture` methods
has a special significance. If the first parameter isn't a Symbol,
SSHKit assumes that you want to execute the raw command and the
`as` / `within` / `with` methods, `SSHKit.config.umask` and [the comand map](#the-command-map)
Expand All @@ -55,10 +55,14 @@ on '1.example.com'
execute(:cp, 'somefile.txt', 'somewhere_else.txt')
end
ls_output = capture(:ls, '-l')
rm_output = test_and_capture(:rm, "/a/file")
unless rm_output.success?
raise StandardError, rm_output.stderr + rm_output.stdout
end
end
```

By default the `capture` methods strips whitespace. If you need to preserve whitespace
By default the `capture` & `test_and_capture` methods strip whitespace. If you need to preserve whitespace
you can pass the `strip: false` option: `capture(:ls, '-l', strip: false)`

#### Transferring files
Expand Down Expand Up @@ -267,7 +271,7 @@ Wherever possible, you should call commands in a way that doesn't require intera
(eg by specifying all options as command arguments).

However in some cases, you may want to programmatically drive interaction with a command
and this can be achieved by specifying an `:interaction_handler` option when you `execute`, `capture` or `test` a command.
and this can be achieved by specifying an `:interaction_handler` option when you `execute`, `capture`, `test`, or `test_and_capture` a command.

**It is not necessary, or desirable to enable `Netssh.config.pty` to use the `interaction_handler` option.
Only enable `Netssh.config.pty` if the command you are calling won't work without a pty.**
Expand Down Expand Up @@ -450,7 +454,7 @@ SSHKit.config.format = :foobar

## Output Verbosity

By default calls to `capture()` and `test()` are not logged, they are used
By default calls to `test()`, `capture()` and `test_and_capture()` are not logged, they are used
*so* frequently by backend tasks to check environmental settings that it
produces a large amount of noise. They are tagged with a verbosity option on
the `Command` instances of `Logger::DEBUG`. The default configuration for
Expand Down
22 changes: 22 additions & 0 deletions lib/sshkit/backends/abstract.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
module SSHKit

module Backend
class CapturedResult
attr_reader :stdout, :stderr

def initialize(success, stdout, stderr)
@success, @stdout, @stderr = success, stdout, stderr
end

def success?
@success
end
end

MethodUnavailableError = Class.new(SSHKit::StandardError)

Expand Down Expand Up @@ -40,6 +51,17 @@ def capture(*args)
options[:strip] ? result.strip : result
end

def test_and_capture(*args)
options = { verbosity: Logger::DEBUG, strip: true, raise_on_non_zero_exit: false }.merge(args.extract_options!)
cmd = create_command_and_execute(args, options)
stdout, stderr = cmd.full_stdout, cmd.full_stderr
if options[:strip]
stdout = stdout.strip
stderr = stderr.strip
end
CapturedResult.new(cmd.success?, stdout, stderr)
end

def background(*args)
SSHKit.config.deprecation_logger.log(
'The background method is deprecated. Blame badly behaved pseudo-daemons!'
Expand Down
19 changes: 19 additions & 0 deletions test/unit/backends/test_abstract.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ def test_capture_creates_and_executes_command_and_returns_stripped_output
assert_equal 'Some stdout', output
end

def test_test_and_capture
output = nil
backend = ExampleBackend.new do
output = test_and_capture :cat, '/a/file'
end
backend.full_stdout = "Some stdout\n "
backend.full_stderr = "Some stderr\n "

backend.run

assert_equal '/usr/bin/env cat /a/file', backend.executed_command.to_command
assert_equal false, output.success?
assert_equal 'Some stdout', output.stdout
assert_equal 'Some stderr', output.stderr
assert_equal false, backend.executed_command.options[:raise_on_non_zero_exit], 'raise_on_non_zero_exit option'
end

def test_capture_supports_disabling_strip
output = nil
backend = ExampleBackend.new do
Expand Down Expand Up @@ -121,6 +138,7 @@ def test_invoke_raises_no_method_error

# Use a concrete ExampleBackend rather than a mock for improved assertion granularity
class ExampleBackend < Abstract
attr_writer :full_stderr
attr_writer :full_stdout
attr_reader :executed_command

Expand All @@ -132,6 +150,7 @@ def initialize(&block)
def execute_command(command)
@executed_command = command
command.on_stdout(nil, @full_stdout) unless @full_stdout.nil?
command.on_stderr(nil, @full_stderr) unless @full_stderr.nil?
end

def ExampleBackend.example_host
Expand Down