sshkit-interactive 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +31 -0
- data/CHANGELOG.md +7 -0
- data/README.md +33 -9
- data/Rakefile +3 -1
- data/lib/sshkit/interactive.rb +2 -1
- data/lib/sshkit/interactive/backend.rb +38 -7
- data/lib/sshkit/interactive/command.rb +68 -40
- data/lib/sshkit/interactive/dsl.rb +22 -0
- data/lib/sshkit/interactive/version.rb +1 -1
- data/spec/backend_spec.rb +47 -4
- data/spec/command_spec.rb +69 -37
- data/spec/dsl_spec.rb +25 -0
- data/spec/gemfiles/net-ssh2_8.gemfile +7 -0
- data/spec/gemfiles/net-ssh2_9.gemfile +7 -0
- data/spec/gemfiles/net-ssh3_0.gemfile +7 -0
- data/spec/gemfiles/net-ssh3_1.gemfile +7 -0
- data/spec/gemfiles/net-ssh3_2.gemfile +7 -0
- data/spec/spec_helper.rb +9 -0
- data/sshkit-interactive.gemspec +10 -10
- metadata +26 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b9f9fe3a0df465a8ce537cde0a15dd59a8c757a
|
4
|
+
data.tar.gz: 8d66b61a1c46fec1547fd4aeaa0bd99396daba00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7c4f24a202860ae9b4543fd05c16531b64daea2718a39a3ac8e906bc5470271de288e09badaf8ee6d34310ebed65388f67f138bd75c86a1f7bc9ae07e5ffa70
|
7
|
+
data.tar.gz: 1c344d7c66e3fe33ebfe57ad387d073238c1d6f621595dda8fea50b7c85dd5e6d53eb122274628d367065816fcbf3bed2a96e1570af2c08ced4d94285573c97b
|
data/.travis.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
sudo: false
|
2
|
+
|
3
|
+
language: ruby
|
4
|
+
|
5
|
+
rvm:
|
6
|
+
- ruby-2.2.5
|
7
|
+
- ruby-2.3.1
|
8
|
+
- rbx-2.5.8
|
9
|
+
- jruby-1.7
|
10
|
+
- jruby-9.1.2.0
|
11
|
+
|
12
|
+
gemfile:
|
13
|
+
- spec/gemfiles/net-ssh2_8.gemfile
|
14
|
+
- spec/gemfiles/net-ssh2_9.gemfile
|
15
|
+
- spec/gemfiles/net-ssh3_0.gemfile
|
16
|
+
- spec/gemfiles/net-ssh3_1.gemfile
|
17
|
+
- spec/gemfiles/net-ssh3_2.gemfile
|
18
|
+
|
19
|
+
matrix:
|
20
|
+
exclude:
|
21
|
+
- rvm: jruby-1.7
|
22
|
+
gemfile: spec/gemfiles/net-ssh3_0.gemfile
|
23
|
+
- rvm: jruby-1.7
|
24
|
+
gemfile: spec/gemfiles/net-ssh3_1.gemfile
|
25
|
+
- rvm: jruby-1.7
|
26
|
+
gemfile: spec/gemfiles/net-ssh3_2.gemfile
|
27
|
+
|
28
|
+
before_install:
|
29
|
+
- gem install bundler -v '~> 1.13'
|
30
|
+
|
31
|
+
script: bundle exec rake spec
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# SSHKit::Interactive
|
2
2
|
|
3
|
+
[](http://badge.fury.io/rb/sshkit-interactive) [](https://travis-ci.org/afeld/sshkit-interactive)
|
4
|
+
|
3
5
|
An [SSHKit](https://github.com/capistrano/sshkit) [backend](https://github.com/capistrano/sshkit/tree/master/test/unit/backends) that allows you to execute interactive commands on your servers. Remote commands that you might want to use this for:
|
4
6
|
|
5
7
|
* A Rails console
|
@@ -27,30 +29,52 @@ require 'sshkit/interactive'
|
|
27
29
|
|
28
30
|
## Usage
|
29
31
|
|
30
|
-
|
32
|
+
Running interactive commands will make a system call to `ssh` under the hood.
|
33
|
+
|
34
|
+
### DSL
|
35
|
+
|
36
|
+
Using the DSL, simply put the command within the `run_interactively` block. In Capistrano, it might look something like this:
|
31
37
|
|
32
38
|
```ruby
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
39
|
+
namespace :rails do
|
40
|
+
desc "Run Rails console"
|
41
|
+
task :console do
|
42
|
+
run_interactively primary(:app) do
|
43
|
+
execute(:rails, :console)
|
44
|
+
end
|
45
|
+
end
|
37
46
|
end
|
38
47
|
```
|
39
48
|
|
40
|
-
|
49
|
+
It is also possible to set directory and user the Capistrano way:
|
41
50
|
|
42
51
|
```ruby
|
43
52
|
namespace :rails do
|
44
53
|
desc "Run Rails console"
|
45
54
|
task :console do
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
run_interactively primary(:app) do
|
56
|
+
within current_path do
|
57
|
+
as user: :foobar do
|
58
|
+
execute(:rails, :console)
|
59
|
+
end
|
60
|
+
end
|
49
61
|
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
```
|
53
65
|
|
66
|
+
### Manually setting the backend
|
67
|
+
|
68
|
+
Use the [interactive backend](lib/sshkit/interactive/backend.rb) and execute commands as normal:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
SSHKit.config.backend = SSHKit::Interactive::Backend
|
72
|
+
hosts = %w{my.server.com}
|
73
|
+
on hosts do |host|
|
74
|
+
execute(:vim)
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
54
78
|
## Contributing
|
55
79
|
|
56
80
|
1. [Fork it](https://github.com/afeld/sshkit-interactive/fork)
|
data/Rakefile
CHANGED
data/lib/sshkit/interactive.rb
CHANGED
@@ -3,9 +3,10 @@ require 'sshkit'
|
|
3
3
|
require_relative 'interactive/version'
|
4
4
|
require_relative 'interactive/command'
|
5
5
|
require_relative 'interactive/backend'
|
6
|
+
require_relative 'interactive/dsl'
|
6
7
|
|
7
8
|
module SSHKit
|
8
9
|
module Interactive
|
9
|
-
|
10
|
+
Unsupported = Class.new(SSHKit::StandardError)
|
10
11
|
end
|
11
12
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# based on https://github.com/jetthoughts/j-cap-recipes/blob/be9dffe279b7bee816c9bafcb3633109096b20d5/lib/sshkit/backends/ssh_command.rb
|
2
1
|
module SSHKit
|
3
2
|
module Interactive
|
4
3
|
class Backend < SSHKit::Backend::Printer
|
@@ -6,18 +5,50 @@ module SSHKit
|
|
6
5
|
instance_exec(host, &@block)
|
7
6
|
end
|
8
7
|
|
9
|
-
def within(directory, &
|
10
|
-
(@pwd ||= []).push
|
8
|
+
def within(directory, &_block)
|
9
|
+
(@pwd ||= []).push(directory.to_s)
|
10
|
+
|
11
11
|
yield
|
12
12
|
ensure
|
13
13
|
@pwd.pop
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def as(who, &_block)
|
17
|
+
if who.is_a?(Hash)
|
18
|
+
@user = who[:user] || who['user']
|
19
|
+
@group = who[:group] || who['group']
|
20
|
+
else
|
21
|
+
@user = who
|
22
|
+
@group = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
raise SSHKit::Interactive::Unsupported, 'Setting group (through `as`) is currently not supported' unless @group.nil?
|
26
|
+
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
remove_instance_variable(:@user)
|
30
|
+
remove_instance_variable(:@group)
|
20
31
|
end
|
32
|
+
|
33
|
+
def execute(*args)
|
34
|
+
super
|
35
|
+
|
36
|
+
options = args.extract_options!
|
37
|
+
cmd = Command.new(host, command(args, options))
|
38
|
+
|
39
|
+
debug(cmd.to_s)
|
40
|
+
|
41
|
+
cmd.execute
|
42
|
+
end
|
43
|
+
|
44
|
+
def _unsupported_operation(*args)
|
45
|
+
raise SSHKit::Backend::MethodUnavailableError, 'SSHKit::Interactive does not support this operation.'
|
46
|
+
end
|
47
|
+
|
48
|
+
alias :upload! :_unsupported_operation
|
49
|
+
alias :download! :_unsupported_operation
|
50
|
+
alias :test :_unsupported_operation
|
51
|
+
alias :capture :_unsupported_operation
|
21
52
|
end
|
22
53
|
end
|
23
54
|
end
|
@@ -3,66 +3,94 @@ module SSHKit
|
|
3
3
|
class Command
|
4
4
|
attr_reader :host, :remote_command
|
5
5
|
|
6
|
-
#
|
7
|
-
|
8
|
-
|
6
|
+
# Instantiate new interactive SSHKit command wrapper.
|
7
|
+
#
|
8
|
+
# @param host [SSHKit::Host] the host to run `remote_command` on.
|
9
|
+
# @param remote_command [SSHKit::Command] the command to run on `host`.
|
10
|
+
def initialize(host, remote_command = nil)
|
11
|
+
@host = host
|
9
12
|
@remote_command = remote_command
|
10
13
|
end
|
11
14
|
|
15
|
+
# Run the command on the remote host via SSH binary.
|
16
|
+
def execute
|
17
|
+
system(to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
# SSH command arguments
|
21
|
+
def ssh_cmd_args
|
22
|
+
args = []
|
23
|
+
|
24
|
+
args << '-t'
|
25
|
+
args << '-A' if forward_agent?
|
26
|
+
args << "-p #{port}" if port
|
27
|
+
args << "-l #{user}" if user
|
28
|
+
args << %Q{-o "PreferredAuthentications #{auth_methods_str}"} if auth_methods.count > 0
|
29
|
+
args << %Q{-o "ProxyCommand #{proxy_command}"} if proxy
|
30
|
+
args << %Q{-o "HostName #{host_name}"} if host_name
|
31
|
+
|
32
|
+
keys.each do |key|
|
33
|
+
args << "-i #{key}"
|
34
|
+
end
|
35
|
+
|
36
|
+
args
|
37
|
+
end
|
38
|
+
|
39
|
+
# Complete command
|
40
|
+
def to_s
|
41
|
+
[
|
42
|
+
:ssh,
|
43
|
+
*ssh_cmd_args,
|
44
|
+
host.hostname,
|
45
|
+
"\"#{remote_command_str}\""
|
46
|
+
].reject(&:empty?).join(' ')
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# available options: http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start
|
12
52
|
def netssh_options
|
13
|
-
|
53
|
+
host.netssh_options
|
14
54
|
end
|
15
55
|
|
16
56
|
def user
|
17
|
-
|
57
|
+
host.user
|
18
58
|
end
|
19
59
|
|
20
|
-
def
|
21
|
-
|
60
|
+
def port
|
61
|
+
netssh_options[:port]
|
22
62
|
end
|
23
63
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
27
|
-
if netssh_options[:keys]
|
28
|
-
netssh_options[:keys].each do |k|
|
29
|
-
opts << "-i #{k}"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
opts << "-l #{user}" if user
|
33
|
-
opts << %{-o "PreferredAuthentications #{netssh_options[:auth_methods].join(',')}"} if netssh_options[:auth_methods]
|
34
|
-
opts << %{-o "ProxyCommand #{netssh_options[:proxy].command_line_template}"} if netssh_options[:proxy]
|
35
|
-
opts << "-p #{netssh_options[:port]}" if netssh_options[:port]
|
36
|
-
opts << '-t' if self.remote_command
|
64
|
+
def forward_agent?
|
65
|
+
!!netssh_options[:forward_agent]
|
66
|
+
end
|
37
67
|
|
38
|
-
|
68
|
+
def host_name
|
69
|
+
netssh_options[:host_name]
|
39
70
|
end
|
40
71
|
|
41
|
-
def
|
42
|
-
|
72
|
+
def keys
|
73
|
+
netssh_options[:keys] || []
|
43
74
|
end
|
44
75
|
|
45
|
-
def
|
46
|
-
|
47
|
-
%{"#{self.remote_command}"}
|
48
|
-
else
|
49
|
-
''
|
50
|
-
end
|
76
|
+
def auth_methods
|
77
|
+
netssh_options[:auth_methods] || []
|
51
78
|
end
|
52
79
|
|
53
|
-
def
|
54
|
-
|
55
|
-
'ssh',
|
56
|
-
self.options_str,
|
57
|
-
self.hostname,
|
58
|
-
self.remote_command_str
|
59
|
-
]
|
60
|
-
|
61
|
-
parts.reject(&:empty?).join(' ')
|
80
|
+
def auth_methods_str
|
81
|
+
auth_methods.join(',')
|
62
82
|
end
|
63
83
|
|
64
|
-
def
|
65
|
-
|
84
|
+
def proxy
|
85
|
+
netssh_options[:proxy]
|
86
|
+
end
|
87
|
+
|
88
|
+
def proxy_command
|
89
|
+
proxy.command_line_template
|
90
|
+
end
|
91
|
+
|
92
|
+
def remote_command_str
|
93
|
+
%Q{\\$SHELL -l -c \\"#{remote_command.to_command}\\"}
|
66
94
|
end
|
67
95
|
end
|
68
96
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module SSHKit
|
2
|
+
module Interactive
|
3
|
+
module DSL
|
4
|
+
# run commands interactively
|
5
|
+
def run_interactively(host, &block)
|
6
|
+
Thread.current[:run_interactively] = true
|
7
|
+
|
8
|
+
SSHKit::Interactive::Backend.new(host, &block).run
|
9
|
+
ensure
|
10
|
+
Thread.current[:run_interactively] = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def on(*args)
|
14
|
+
raise SSHKit::Interactive::Unsupported, 'Switching host in interactive mode is not possible' if Thread.current[:run_interactively]
|
15
|
+
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
include SSHKit::Interactive::DSL
|
data/spec/backend_spec.rb
CHANGED
@@ -1,10 +1,53 @@
|
|
1
1
|
describe SSHKit::Interactive::Backend do
|
2
2
|
describe '#execute' do
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
let(:host) { SSHKit::Host.new('example.com') }
|
4
|
+
let(:backend) { SSHKit::Interactive::Backend.new(host) }
|
5
|
+
|
6
|
+
it 'does a system call with the SSH command' do
|
7
|
+
expect_system_call('ssh -t -A example.com "\\$SHELL -l -c \\"/usr/bin/env ls\\""')
|
7
8
|
backend.execute('ls')
|
8
9
|
end
|
10
|
+
|
11
|
+
it 'respects the specified directory' do
|
12
|
+
backend.within('/var/log') do
|
13
|
+
expect_system_call('ssh -t -A example.com "\\$SHELL -l -c \\"cd /var/log && /usr/bin/env ls\\""')
|
14
|
+
backend.execute('ls')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'respects the specified user' do
|
19
|
+
backend.as('deployer') do
|
20
|
+
expect_system_call('ssh -t -A example.com "\\$SHELL -l -c \\"sudo -u deployer -- sh -c \'/usr/bin/env ls\'\\""')
|
21
|
+
backend.execute('ls')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'respects the specified group' do
|
26
|
+
expect {
|
27
|
+
backend.as(user: :user, group: :group) do
|
28
|
+
backend.execute('ls')
|
29
|
+
end
|
30
|
+
}.to raise_error(SSHKit::Interactive::Unsupported)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'respects the specified env'
|
34
|
+
|
35
|
+
describe 'prevents calling unsupported operations' do
|
36
|
+
it '#upload!' do
|
37
|
+
expect { backend.upload!(:a, :b) }.to raise_error(::SSHKit::Backend::MethodUnavailableError)
|
38
|
+
end
|
39
|
+
|
40
|
+
it '#download!' do
|
41
|
+
expect { backend.download!(:a, :b) }.to raise_error(::SSHKit::Backend::MethodUnavailableError)
|
42
|
+
end
|
43
|
+
|
44
|
+
it '#test' do
|
45
|
+
expect { backend.test(:true) }.to raise_error(::SSHKit::Backend::MethodUnavailableError)
|
46
|
+
end
|
47
|
+
|
48
|
+
it '#capture' do
|
49
|
+
expect { backend.capture(:ls) }.to raise_error(::SSHKit::Backend::MethodUnavailableError)
|
50
|
+
end
|
51
|
+
end
|
9
52
|
end
|
10
53
|
end
|
data/spec/command_spec.rb
CHANGED
@@ -1,72 +1,104 @@
|
|
1
1
|
describe SSHKit::Interactive::Command do
|
2
|
-
describe '#
|
3
|
-
def
|
2
|
+
describe '#ssh_cmd_args' do
|
3
|
+
def command_args(host)
|
4
4
|
command = SSHKit::Interactive::Command.new(host)
|
5
|
-
command.
|
5
|
+
command.ssh_cmd_args
|
6
6
|
end
|
7
7
|
|
8
|
-
it
|
8
|
+
it 'handles a simple hostname' do
|
9
9
|
host = SSHKit::Host.new('example.com')
|
10
|
-
|
10
|
+
|
11
|
+
expect(command_args(host)).to include('-A')
|
11
12
|
end
|
12
13
|
|
13
|
-
it
|
14
|
+
it 'handles a username and port' do
|
14
15
|
host = SSHKit::Host.new('someuser@example.com:2222')
|
15
|
-
|
16
|
+
|
17
|
+
expect(command_args(host)).to include('-A')
|
18
|
+
expect(command_args(host)).to include('-l someuser')
|
19
|
+
expect(command_args(host)).to include('-p 2222')
|
16
20
|
end
|
17
21
|
|
18
|
-
it
|
22
|
+
it 'handles a proxy' do
|
19
23
|
host = SSHKit::Host.new('someuser@example.com:2222')
|
20
24
|
host.ssh_options = {
|
21
25
|
proxy: Net::SSH::Proxy::Command.new('ssh mygateway.com -W %h:%p')
|
22
26
|
}
|
23
27
|
|
24
|
-
expect(
|
28
|
+
expect(command_args(host)).to include('-A')
|
29
|
+
expect(command_args(host)).to include('-l someuser')
|
30
|
+
expect(command_args(host)).to include('-p 2222')
|
31
|
+
expect(command_args(host)).to include('-o "ProxyCommand ssh mygateway.com -W %h:%p"')
|
25
32
|
end
|
26
33
|
|
27
|
-
it
|
34
|
+
it 'handles keys option' do
|
28
35
|
host = SSHKit::Host.new('example.com')
|
29
36
|
host.ssh_options = { keys: %w(/home/user/.ssh/id_rsa) }
|
30
37
|
|
31
|
-
expect(
|
38
|
+
expect(command_args(host)).to include('-A')
|
39
|
+
expect(command_args(host)).to include('-i /home/user/.ssh/id_rsa')
|
32
40
|
end
|
33
41
|
|
34
|
-
|
35
|
-
|
36
|
-
host =
|
37
|
-
|
38
|
-
host.
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
it 'handles keys on host' do
|
43
|
+
host = SSHKit::Host.new('example.com')
|
44
|
+
host.keys = ['~/.ssh/some_key_here']
|
45
|
+
|
46
|
+
expect(command_args(host)).to include('-A')
|
47
|
+
expect(command_args(host)).to include('-i ~/.ssh/some_key_here')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'handles a host_name' do
|
51
|
+
host = SSHKit::Host.new('example.com')
|
52
|
+
host.ssh_options = { host_name: 'foo.bar' }
|
53
|
+
|
54
|
+
expect(command_args(host)).to include('-A')
|
55
|
+
expect(command_args(host)).to include('-o "HostName foo.bar"')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'handles disabled forward agent' do
|
59
|
+
host = SSHKit::Host.new('example.com')
|
60
|
+
host.ssh_options = { forward_agent: false }
|
44
61
|
|
45
|
-
expect(
|
62
|
+
expect(command_args(host)).not_to include('-A')
|
46
63
|
end
|
47
64
|
|
48
|
-
it
|
65
|
+
it 'handles auth methods' do
|
66
|
+
host = SSHKit::Host.new('example.com:2222')
|
67
|
+
host.ssh_options = { auth_methods: %w(publickey password) }
|
68
|
+
|
69
|
+
expect(command_args(host)).to include('-o "PreferredAuthentications publickey,password"')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'handles port option' do
|
73
|
+
host = SSHKit::Host.new('example.com:2222')
|
74
|
+
host.ssh_options = { port: 3232 }
|
75
|
+
|
76
|
+
expect(command_args(host)).to include('-p 3232')
|
77
|
+
end
|
49
78
|
end
|
50
79
|
|
51
80
|
describe '#to_s' do
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
81
|
+
let(:host) { SSHKit::Host.new('example.com') }
|
82
|
+
let(:command) { SSHKit::Command.new(:ls) }
|
83
|
+
|
84
|
+
it 'includes options' do
|
85
|
+
cmd = SSHKit::Interactive::Command.new(host, command)
|
86
|
+
|
87
|
+
expect(cmd).to receive(:ssh_cmd_args).and_return(%w(-A -B -C))
|
88
|
+
expect(cmd.to_s).to eq('ssh -A -B -C example.com "\\$SHELL -l -c \\"/usr/bin/env ls\\""')
|
57
89
|
end
|
58
90
|
|
59
|
-
it
|
60
|
-
|
61
|
-
|
62
|
-
expect(
|
63
|
-
expect(
|
91
|
+
it 'excludes options if they\'re blank' do
|
92
|
+
cmd = SSHKit::Interactive::Command.new(host, command)
|
93
|
+
|
94
|
+
expect(cmd).to receive(:ssh_cmd_args).and_return([])
|
95
|
+
expect(cmd.to_s).to eq('ssh example.com "\\$SHELL -l -c \\"/usr/bin/env ls\\""')
|
64
96
|
end
|
65
97
|
|
66
|
-
it
|
67
|
-
|
68
|
-
|
69
|
-
expect(
|
98
|
+
it 'accepts a remote command' do
|
99
|
+
cmd = SSHKit::Interactive::Command.new(host, command)
|
100
|
+
|
101
|
+
expect(cmd.to_s).to eq('ssh -t -A example.com "\\$SHELL -l -c \\"/usr/bin/env ls\\""')
|
70
102
|
end
|
71
103
|
end
|
72
104
|
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
describe SSHKit::Interactive::DSL do
|
2
|
+
describe '#run_interactively' do
|
3
|
+
include SSHKit::Interactive::DSL
|
4
|
+
|
5
|
+
let(:host) { SSHKit::Host.new('example.com') }
|
6
|
+
|
7
|
+
it 'will execute interactively' do
|
8
|
+
expect_system_call('ssh -t -A example.com "\\$SHELL -l -c \\"/usr/bin/env ls\\""')
|
9
|
+
|
10
|
+
run_interactively host do
|
11
|
+
execute(:ls)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'does not support switching hosts' do
|
16
|
+
expect {
|
17
|
+
run_interactively host do
|
18
|
+
on host do
|
19
|
+
execute(:ls)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
}.to raise_error(SSHKit::Interactive::Unsupported)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -92,4 +92,13 @@ RSpec.configure do |config|
|
|
92
92
|
# as the one that triggered the failure.
|
93
93
|
Kernel.srand config.seed
|
94
94
|
=end
|
95
|
+
|
96
|
+
config.before(:each) do
|
97
|
+
# block all system calls
|
98
|
+
allow_any_instance_of(SSHKit::Interactive::Command).to receive(:system)
|
99
|
+
|
100
|
+
def expect_system_call(command)
|
101
|
+
expect_any_instance_of(SSHKit::Interactive::Command).to receive(:system).with(command)
|
102
|
+
end
|
103
|
+
end
|
95
104
|
end
|
data/sshkit-interactive.gemspec
CHANGED
@@ -4,22 +4,22 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'sshkit/interactive/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'sshkit-interactive'
|
8
8
|
spec.version = SSHKit::Interactive::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Aidan Feldman']
|
10
|
+
spec.email = ['aidan.feldman@gmail.com']
|
11
11
|
spec.summary = %q{An SSHKit backend that allows you to execute interactive commands on your servers. }
|
12
|
-
spec.homepage =
|
13
|
-
spec.license =
|
12
|
+
spec.homepage = 'https://github.com/afeld/sshkit-interactive'
|
13
|
+
spec.license = 'MIT'
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
16
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
-
spec.require_paths = [
|
18
|
+
spec.require_paths = ['lib']
|
19
19
|
|
20
|
-
spec.add_dependency
|
20
|
+
spec.add_dependency 'sshkit', '~> 1.9'
|
21
21
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
22
|
+
spec.add_development_dependency 'bundler', '~> 1.13'
|
23
|
+
spec.add_development_dependency 'rake', '~> 11.0'
|
24
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
25
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sshkit-interactive
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aidan Feldman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-09-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sshkit
|
@@ -16,56 +16,56 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.9'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.9'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '1.
|
33
|
+
version: '1.13'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
40
|
+
version: '1.13'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '11.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '11.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
61
|
+
version: '3.5'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
68
|
+
version: '3.5'
|
69
69
|
description:
|
70
70
|
email:
|
71
71
|
- aidan.feldman@gmail.com
|
@@ -75,6 +75,8 @@ extra_rdoc_files: []
|
|
75
75
|
files:
|
76
76
|
- ".gitignore"
|
77
77
|
- ".rspec"
|
78
|
+
- ".travis.yml"
|
79
|
+
- CHANGELOG.md
|
78
80
|
- Gemfile
|
79
81
|
- LICENSE.txt
|
80
82
|
- README.md
|
@@ -82,9 +84,16 @@ files:
|
|
82
84
|
- lib/sshkit/interactive.rb
|
83
85
|
- lib/sshkit/interactive/backend.rb
|
84
86
|
- lib/sshkit/interactive/command.rb
|
87
|
+
- lib/sshkit/interactive/dsl.rb
|
85
88
|
- lib/sshkit/interactive/version.rb
|
86
89
|
- spec/backend_spec.rb
|
87
90
|
- spec/command_spec.rb
|
91
|
+
- spec/dsl_spec.rb
|
92
|
+
- spec/gemfiles/net-ssh2_8.gemfile
|
93
|
+
- spec/gemfiles/net-ssh2_9.gemfile
|
94
|
+
- spec/gemfiles/net-ssh3_0.gemfile
|
95
|
+
- spec/gemfiles/net-ssh3_1.gemfile
|
96
|
+
- spec/gemfiles/net-ssh3_2.gemfile
|
88
97
|
- spec/spec_helper.rb
|
89
98
|
- sshkit-interactive.gemspec
|
90
99
|
homepage: https://github.com/afeld/sshkit-interactive
|
@@ -107,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
116
|
version: '0'
|
108
117
|
requirements: []
|
109
118
|
rubyforge_project:
|
110
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.5.1
|
111
120
|
signing_key:
|
112
121
|
specification_version: 4
|
113
122
|
summary: An SSHKit backend that allows you to execute interactive commands on your
|
@@ -115,4 +124,10 @@ summary: An SSHKit backend that allows you to execute interactive commands on yo
|
|
115
124
|
test_files:
|
116
125
|
- spec/backend_spec.rb
|
117
126
|
- spec/command_spec.rb
|
127
|
+
- spec/dsl_spec.rb
|
128
|
+
- spec/gemfiles/net-ssh2_8.gemfile
|
129
|
+
- spec/gemfiles/net-ssh2_9.gemfile
|
130
|
+
- spec/gemfiles/net-ssh3_0.gemfile
|
131
|
+
- spec/gemfiles/net-ssh3_1.gemfile
|
132
|
+
- spec/gemfiles/net-ssh3_2.gemfile
|
118
133
|
- spec/spec_helper.rb
|