net-ssh-cli 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acbf06cc3c79c68f798c7eaa7ee15a904fe56679d115c6ba9a3de3663d9b9198
4
- data.tar.gz: 3e5da2f3f30cb58ea964faf25ff66da3f52221735bb6b4f953ac6def4d5d3dbc
3
+ metadata.gz: 36080ea8a1e4e289bc73d79d9a665a0848925799db74242e5705814a8c6081c9
4
+ data.tar.gz: 85cbb73ba6ff4925b39955c7416934a5c9124b8e4e5cbf0eeafc67af1bb2193f
5
5
  SHA512:
6
- metadata.gz: f27705802860566503cc779c4e34a82270176bcb057d3b01545503673f4b0fb0244e4b2888afe85a09c0fe9f1e2510b7656d4ecfaaead6b751b98485046c1de2
7
- data.tar.gz: c9d9f68b922206b848687164c5523682d5d349eed7195772c55b3f1f8e7e25a14adbd5c2201729f5c6effd4cea6a1ea2c8eda3ef71e469c86bd49fa1aecfaba7
6
+ metadata.gz: 0e3a0db15b0bb3d14e9cc2f7a1bd8c5e3379c8e2829e09fc8e3f63a445514f6b5a467cc3de76be5da34ec33632e8545bf8eea10ea2898eade24496f6fca222cb
7
+ data.tar.gz: 9b15edce5dc418753069b56004480bd9836104bfd9508b3e2dc89c31acdf3433253b76717bde7959dc6e4dcc53fcbbdedea1f8dee79f95359f2615feb37f402f
data/README.md CHANGED
@@ -29,7 +29,7 @@ Or install it yourself as:
29
29
 
30
30
  ```ruby
31
31
  Net::SSH.start('host', 'user', password: "password") do |ssh|
32
- cli = ssh.open_cli_channel(default_prompt: /(\nuser@host):/m)
32
+ cli = ssh.cli(default_prompt: /(\nuser@host):/m)
33
33
  cli.cmd ""
34
34
  # => "Last login: \nuser@host:"
35
35
 
@@ -40,18 +40,18 @@ end
40
40
 
41
41
  ```ruby
42
42
  net_ssh = Net::SSH.start('host', 'user', password: "password")
43
- cli = Net::SSH::CLI::Channel.new(net_ssh: net_ssh)
43
+ cli = Net::SSH::CLI::Session.new(net_ssh: net_ssh)
44
44
  cli.cmd ""
45
45
  ```
46
46
 
47
47
  ```ruby
48
- cli = Net::SSH::CLI::Channel.new(net_ssh_options: {host: 'host', user: 'user', password: 'password'})
48
+ cli = Net::SSH::CLI::Session.new(net_ssh_options: {host: 'host', user: 'user', password: 'password'})
49
49
  cli.cmd ""
50
50
  ```
51
51
 
52
52
  ### #cmd
53
53
  ```ruby
54
- cli = ssh.open_cli_channel(default_prompt: /(\nuser@host):/m)
54
+ cli = ssh.cli(default_prompt: /(\nuser@host):/m)
55
55
  cli.cmd "echo 'bananas'"
56
56
  # => "echo 'bananas'\nbananas\nuser@host:"
57
57
  cli.cmd "echo 'bananas'", rm_command: true
@@ -64,7 +64,7 @@ end
64
64
 
65
65
  Remove the command and the prompt for #cmd & #dialog by default
66
66
  ```ruby
67
- cli = ssh.open_cli_channel(default_prompt: /(\nuser@host):/m, cmd_rm_command: true, cmd_rm_prompt: true)
67
+ cli = ssh.cli(default_prompt: /(\nuser@host):/m, cmd_rm_command: true, cmd_rm_prompt: true)
68
68
  cli.cmd "echo 'bananas'"
69
69
  # => "bananas"
70
70
  ```
@@ -76,6 +76,50 @@ Remove the command and the prompt for #cmd & #dialog by default
76
76
  cli.cmd "yes"
77
77
  ```
78
78
 
79
+ ### #read & #write
80
+
81
+ ```ruby
82
+ cli.write "echo 'hello'\n"
83
+ # => "echo 'hello'\n"
84
+ cli.read
85
+ # => "echo 'hello'\nhello\nuser@host:"
86
+ ```
87
+
88
+ ### #write_n
89
+ ```ruby
90
+ cli.write_n "echo 'hello'"
91
+ # => "echo 'hello'\n"
92
+ ```
93
+
94
+ ### #read_till
95
+ keep on processing till the stdout matches to given|default prompt and then read the whole stdin.
96
+ ```ruby
97
+ cli.write "\n"
98
+ # => "echo 'hello'\n"
99
+ cli.read_till
100
+ # => "echo 'hello'\nhello\nuser@host:"
101
+ ```
102
+
103
+ This method is used by #cmd
104
+
105
+ ## Configuration
106
+
107
+ Have a deep look at ``Net::SSH::CLI::OPTIONS`` at ``lib/net/ssh/cli.rb``
108
+
109
+ ### Callbacks
110
+
111
+ The following callbacks are available
112
+ - #before_open_channel
113
+ - #after_open_channel
114
+ - #before_on_data
115
+ - #before_on_data
116
+
117
+ ```ruby
118
+ cli.before_open_channel do
119
+ puts "The channel will open soon"
120
+ end
121
+ ```
122
+
79
123
  ## Development
80
124
 
81
125
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/bin/localhost CHANGED
@@ -3,35 +3,29 @@
3
3
 
4
4
  require 'bundler/setup'
5
5
  require 'net/ssh/cli'
6
-
7
- # You can add fixtures and/or initialization code here to make experimenting
8
- # with your gem easier. You can also use a different console, if you like.
9
-
10
- # (If you use this, don't forget to add pry to your Gemfile!)
11
- # require "pry"
12
- # Pry.start
13
-
14
6
  require 'irb'
15
7
 
16
- def reload!
17
- load __FILE__
18
- end
19
-
20
- $CLI = nil
21
8
  begin
22
- $CLI = Net::SSH::CLI::Channel.new(host: 'localhost', user: ENV['USER'], default_prompt: '@')
9
+ $NET_SSH = Net::SSH.start("localhost")
10
+ $CLI = Net::SSH::CLI::Session.new(net_ssh: $NET_SSH, default_prompt: "@")
23
11
  $CLI.open_channel
24
- puts "\nPUTS #{$CLI.read}"
25
- sleep 0.3
26
- puts "\nPUTS #{$CLI.read}"
27
- puts "\nPUTS #{$CLI.write "\n"}"
28
- puts "\nPUTS TILL #{$CLI.read_till}"
29
- puts "try $CLI.cmd"
30
- puts File.read(__FILE__)
12
+ puts "assuming your prompt contains '@'"
13
+ puts $CLI.cmd "echo 'hello world'"
31
14
  rescue StandardError => error
32
- puts error.class
33
- puts error.message
15
+ puts "#{error.class} #{error.message}"
34
16
  puts error.backtrace
35
17
  ensure
18
+ puts ""
19
+ puts File.read(__FILE__).lines.map {|line| "[bin/localhost] " + line}
20
+ puts ""
36
21
  IRB.start(__FILE__)
37
22
  end
23
+
24
+ ## Try one of those
25
+ # $CLI.cmd "echo 'hello world'"
26
+ # $CLI.detect_prompt
27
+ # $CLI.default_prompt
28
+ # $CLI.cmd "cat /etc/passwd"
29
+ # $CLI.write "cat /etc/passwd"
30
+ # $CLI.read
31
+ # $CLI.cmd "echo 'hello world'"
@@ -1,7 +1,7 @@
1
1
  module Net
2
2
  module SSH
3
3
  module CLI
4
- VERSION = "0.2.0"
4
+ VERSION = "0.3.0"
5
5
  end
6
6
  end
7
7
  end
data/lib/net/ssh/cli.rb CHANGED
@@ -16,42 +16,50 @@ module Net
16
16
  class UndefinedMatch < Error; end
17
17
  class OpenChannelTimeout < Error; end
18
18
  class ReadTillTimeout < Error; end
19
+ class PromptDetection < Error; end
20
+ end
21
+
22
+ # Example
23
+ # net_ssh = Net::SSH.start("localhost")
24
+ # net_ssh_cli = Net::SSH::CLI.start(net_ssh: net_ssh)
25
+ # net_ssh_cli.cmd "cat /etc/passwd"
26
+ # => "root:x:0:0:root:/root:/bin/bash\n..."
27
+ def self.start(**opts)
28
+ Net::SSH::CLI::Session.new(**opts)
19
29
  end
20
30
 
21
31
  def initialize(**opts)
22
32
  options.merge!(opts)
23
33
  self.net_ssh = options.delete(:net_ssh)
24
34
  self.logger = options.delete(:logger) || Logger.new(STDOUT, level: Logger::WARN)
25
- open_channel unless lazy
26
- @with_prompt = []
27
35
  end
28
36
 
29
- attr_accessor :channel, :stdout, :stderr, :net_ssh, :logger
30
-
31
- ## make everthing configurable!
32
- #
33
-
34
- DEFAULT = ActiveSupport::HashWithIndifferentAccess.new(
35
- default_prompt: /^(\S+@.*)\z/,
36
- process_time: 0.00001,
37
- read_till_timeout: nil,
38
- read_till_rm_prompt: false,
39
- cmd_rm_prompt: false,
40
- cmd_rm_command: false,
41
- lazy: true
37
+ attr_accessor :channel, :stdout, :net_ssh, :logger
38
+
39
+ OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
40
+ default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for
41
+ cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
42
+ cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
43
+ read_till_timeout: nil, # timeout for #read_till to find the match
44
+ named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
45
+ before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
46
+ after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
47
+ before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
48
+ after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
49
+ open_channel_timeout: nil, # timeout to open the channel
50
+ net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
51
+ process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
52
+ background_processing: false, # default false, whether the process method maps to the underlying net_ssh#process or the net_ssh#process happens in a separate loop
42
53
  )
43
54
 
44
- def default
45
- @default ||= DEFAULT.clone
46
- end
47
-
48
- def default!(**defaults)
49
- default.merge!(**defaults)
50
- end
51
-
52
- # don't even think about nesting hashes here
53
55
  def options
54
- @options ||= default
56
+ @options ||= begin
57
+ opts = OPTIONS.clone
58
+ opts.each do |key,value|
59
+ opts[key] = value.clone if value.is_a?(Hash)
60
+ end
61
+ opts
62
+ end
55
63
  end
56
64
 
57
65
  # don't even think about nesting hashes here
@@ -59,63 +67,28 @@ module Net
59
67
  options.merge!(opts)
60
68
  end
61
69
 
62
- # don't even think about nesting hashes here
63
70
  def options=(opts)
64
71
  @options = ActiveSupport::HashWithIndifferentAccess.new(opts)
65
72
  end
66
73
 
67
- %i[default_prompt process_time read_till_timeout cmd_rm_command cmd_rm_prompt read_till_rm_prompt lazy].each do |name|
74
+ OPTIONS.keys.each do |name|
68
75
  define_method name do
69
76
  options[name]
70
77
  end
71
78
  define_method "#{name}=" do |value|
72
79
  options[name] = value
73
80
  end
74
- end
75
-
76
- %i[process_stdout_procs process_stderr_procs named_prompts net_ssh_options open_channel_options].each do |name|
77
- define_method name do
78
- options[name] ||= ActiveSupport::HashWithIndifferentAccess.new
79
- end
80
- define_method "#{name}!" do |**opts|
81
- send(name).merge!(**opts)
82
- end
83
- define_method "#{name}=" do |value|
84
- options[name] = ActiveSupport::HashWithIndifferentAccess.new(value)
81
+ define_method "#{name}?" do
82
+ !!options[name]
85
83
  end
86
84
  end
87
85
 
88
- def formatted_net_ssh_options
89
- net_ssh_options.symbolize_keys.reject {|k,v| [:host, :ip, :user].include?(k)}
90
- end
91
-
92
- ## Net::SSH instance
93
- #
94
-
95
- def net_ssh
96
- return @net_ssh if @net_ssh
97
-
98
- logger.debug { 'Net:SSH #start' }
99
- self.net_ssh = Net::SSH.start(net_ssh_options[:ip] || net_ssh_options[:host], net_ssh_options[:user] || ENV['USER'], formatted_net_ssh_options)
100
- rescue StandardError => error
101
- self.net_ssh = nil
102
- raise
103
- end
104
- alias proxy net_ssh
105
-
106
- def host
107
- net_ssh_options[:host] || net_ssh_options[:hostname] || net_ssh_options[:ip] || @net_ssh&.host
108
- end
109
- alias to_s host
110
- alias hostname host
111
-
112
- def ip
113
- net_ssh_options[:ip]
86
+ OPTIONS.keys.select {|key| key.to_s.include? "procs"}.each do |name|
87
+ define_method name.sub("_procs","") do |&blk|
88
+ self.send(name)[SecureRandom.uuid] = Proc.new {blk.call}
89
+ end
114
90
  end
115
91
 
116
- ## channel & stderr|stdout stream handling
117
- #
118
-
119
92
  def stdout
120
93
  @stdout ||= String.new
121
94
  end
@@ -126,65 +99,15 @@ module Net
126
99
  var
127
100
  end
128
101
 
129
- def stderr
130
- @stderr ||= String.new
131
- end
132
-
133
- def stderr!
134
- var = stderr
135
- self.stderr = String.new
136
- var
137
- end
138
-
139
- def open_channel # cli_channel
140
- ::Timeout.timeout(open_channel_options[:timeout], Error::OpenChannelTimeout) do
141
- net_ssh.open_channel do |channel_|
142
- logger.debug 'channel is open'
143
- self.channel = channel_
144
- channel_.request_pty do |_ch, success|
145
- raise Error::Pty, "#{host || ip} Failed to open ssh pty" unless success
146
- end
147
- channel_.send_channel_request('shell') do |_ch, success|
148
- raise Error::RequestShell, 'Failed to open ssh shell' unless success
149
- end
150
- channel_.on_data do |_ch, data|
151
- process_stdout(data)
152
- end
153
- channel_.on_extended_data do |_ch, type, data|
154
- process_stderr(data, type)
155
- end
156
- channel_.on_close do
157
- close
158
- end
159
- end
160
- until channel do process end
161
- end
162
- logger.debug 'channel is ready, running callbacks now'
163
- read_till if open_channel_options[:after_read_till_prompt]
164
- open_channel_options[:after_proc]&.call
165
- process
166
- rescue StandardError => error
167
- close
168
- raise
169
- end
170
-
171
- def process_stdout(data)
102
+ def on_stdout(data)
103
+ before_on_stdout_procs.each { |_name, a_proc| a_proc.call }
172
104
  stdout << data
105
+ after_on_stdout_procs.each { |_name, a_proc| a_proc.call }
173
106
  process # if we receive data, we probably receive more - improves performance
174
- process_stdout_procs.each { |_name, a_proc| a_proc.call }
175
107
  stdout
176
108
  end
177
109
 
178
- def process_stderr(data, _type)
179
- stderr << data
180
- process # if we receive data, we probably receive more - improves performance
181
- process_stderr_procs.each { |_name, a_proc| a_proc.call }
182
- stderr
183
- end
184
-
185
110
  def write(content = String.new)
186
- raise Error, 'channel is not stablished or gone' unless channel
187
-
188
111
  logger.debug { "#write #{content.inspect}" }
189
112
  channel.send_data content
190
113
  process
@@ -196,7 +119,6 @@ module Net
196
119
  write content + "\n"
197
120
  end
198
121
 
199
- # returns the stdout buffer and empties it
200
122
  def read
201
123
  process
202
124
  var = stdout!
@@ -208,7 +130,7 @@ module Net
208
130
  #
209
131
 
210
132
  def current_prompt
211
- @with_prompt[-1] || default_prompt
133
+ with_prompts[-1] || default_prompt
212
134
  end
213
135
 
214
136
  def with_named_prompt(name)
@@ -219,19 +141,26 @@ module Net
219
141
  end
220
142
  end
221
143
 
222
- def detect_prompt(seconds: 5)
223
- process(seconds)
224
- self.default_prompt = read[/.*\z/]
144
+ def detect_prompt(seconds: 3)
145
+ write_n
146
+ future = Time.now + seconds
147
+ while future > Time.now
148
+ process
149
+ sleep 0.1
150
+ end
151
+ self.default_prompt = read[/\n?^.*\z/]
152
+ raise Error::PromptDetection, "couldn't detect a prompt" unless default_prompt.present?
153
+ default_prompt
225
154
  end
226
155
 
227
156
  # prove a block where the default prompt changes
228
157
  def with_prompt(prompt)
229
158
  logger.debug { "#with_prompt: #{current_prompt.inspect} => #{prompt.inspect}" }
230
- @with_prompt << prompt
159
+ with_prompts << prompt
231
160
  yield
232
161
  prompt
233
162
  ensure
234
- @with_prompt.delete_at(-1)
163
+ with_prompts.delete_at(-1)
235
164
  logger.debug { "#with_prompt: => #{current_prompt.inspect}" }
236
165
  end
237
166
 
@@ -240,59 +169,51 @@ module Net
240
169
 
241
170
  ::Timeout.timeout(timeout, Error::ReadTillTimeout.new("output did not prompt #{prompt.inspect} within #{timeout}")) do
242
171
  with_prompt(prompt) do
243
- process until stdout[current_prompt]
172
+ until stdout[current_prompt] do
173
+ process
174
+ sleep 0.1
175
+ end
244
176
  end
245
177
  end
246
178
  read
247
179
  end
248
180
 
249
181
  def read_for(seconds:)
250
- process
251
- sleep seconds
252
- process
182
+ process(seconds)
253
183
  read
254
184
  end
255
185
 
256
186
  def dialog(command, prompt, **opts)
257
- pre_read = read
258
- logger.debug { "#dialog ignores the following pre-output #{pre_read.inspect}" } if pre_read.present?
259
- write command
260
- output = read_till(prompt: prompt, **opts)
261
- rm_prompt!(output, prompt: prompt, **opts)
262
- rm_command!(output, command, prompt: prompt, **opts)
263
- output
187
+ opts = opts.clone.merge(prompt: prompt)
188
+ cmd(command, opts)
264
189
  end
265
190
 
266
191
  # 'read' first on purpuse as a feature. once you cmd you ignore what happend before. otherwise use read|write directly.
267
192
  # this should avoid many horrible state issues where the prompt is not the last prompt
268
- def cmd(command, **opts)
269
- pre_read = read
270
- logger.debug { "#cmd ignoring pre-read: #{pre_read.inspect}" } if pre_read.present?
193
+ def cmd(command, pre_read: true, rm_prompt: cmd_rm_prompt, rm_command: cmd_rm_command, prompt: current_prompt, **opts)
194
+ opts = opts.clone.merge(pre_read: pre_read, rm_prompt: rm_prompt, rm_command: rm_command, prompt: prompt)
195
+ if pre_read
196
+ pre_read_data = read
197
+ logger.debug { "#cmd ignoring pre-command output: #{pre_read_data.inspect}" } if pre_read_data.present?
198
+ end
271
199
  write_n command
272
- output = read_till(**opts)
273
- rm_prompt!(output, **opts)
274
- rm_command!(output, command, **opts)
200
+ output = read_till(opts)
201
+ rm_prompt!(output, opts)
202
+ rm_command!(output, command, opts)
275
203
  output
276
204
  end
277
205
  alias command cmd
206
+ alias exec cmd
278
207
 
279
208
  def cmds(commands, **opts)
280
209
  commands.map { |command| [command, cmd(command, **opts)] }
281
210
  end
282
211
  alias commands cmds
283
212
 
284
- def rm_command?(**opts)
285
- opts[:rm_cmd].nil? ? cmd_rm_command : opts[:rm_cmd]
286
- end
287
-
288
213
  def rm_command!(output, command, **opts)
289
214
  output[command + "\n"] = '' if rm_command?(opts) && output[command + "\n"]
290
215
  end
291
216
 
292
- def rm_prompt?(**opts)
293
- opts[:rm_prompt].nil? ? cmd_rm_prompt : opts[:rm_prompt]
294
- end
295
-
296
217
  def rm_prompt!(output, **opts)
297
218
  if rm_prompt?(opts)
298
219
  prompt = opts[:prompt] || current_prompt
@@ -302,61 +223,97 @@ module Net
302
223
  end
303
224
  end
304
225
 
226
+ def host
227
+ @net_ssh&.host
228
+ end
229
+ alias hostname host
230
+ alias to_s host
231
+
305
232
  ## NET::SSH
306
233
  #
307
234
 
235
+ def net_ssh
236
+ return @net_ssh if @net_ssh
237
+
238
+ logger.debug { 'Net:SSH #start' }
239
+ self.net_ssh = Net::SSH.start(net_ssh_options[:ip] || net_ssh_options[:host] || "localhost", net_ssh_options[:user] || ENV['USER'], formatted_net_ssh_options)
240
+ rescue StandardError => error
241
+ self.net_ssh = nil
242
+ raise
243
+ end
244
+ alias proxy net_ssh
245
+
246
+ # have a deep look at the source of Net::SSH
247
+ # session#process https://github.com/net-ssh/net-ssh/blob/dd13dd44d68b7fa82d4ca9a3bbe18e30c855f1d2/lib/net/ssh/connection/session.rb#L227
248
+ # session#loop https://github.com/net-ssh/net-ssh/blob/dd13dd44d68b7fa82d4ca9a3bbe18e30c855f1d2/lib/net/ssh/connection/session.rb#L179
249
+ # because the (cli) channel stays open, we always need to ensure that the ssh layer gets "processed" further. This can be done inside here automatically or outside in a separate event loop for the net_ssh connection.
308
250
  def process(time = process_time)
309
- net_ssh.process(time)
251
+ background_processing? ? sleep(time) : net_ssh.process(time)
310
252
  rescue IOError => error
311
253
  raise Error, error.message
312
254
  end
313
255
 
314
- def close
315
- return unless net_ssh
256
+ def open_channel # cli_channel
257
+ before_open_channel_procs.each { |_name, a_proc| a_proc.call }
258
+ ::Timeout.timeout(open_channel_timeout, Error::OpenChannelTimeout) do
259
+ net_ssh.open_channel do |new_channel|
260
+ logger.debug 'channel is open'
261
+ self.channel = new_channel
262
+ new_channel.request_pty do |_ch, success|
263
+ raise Error::Pty, "#{host || ip} Failed to open ssh pty" unless success
264
+ end
265
+ new_channel.send_channel_request('shell') do |_ch, success|
266
+ raise Error::RequestShell, 'Failed to open ssh shell' unless success
267
+ end
268
+ new_channel.on_data do |_ch, data|
269
+ on_stdout(data)
270
+ end
271
+ #new_channel.on_extended_data do |_ch, type, data| end
272
+ #new_channel.on_close do end
273
+ end
274
+ until channel do process end
275
+ end
276
+ logger.debug 'channel is ready, running callbacks now'
277
+ after_open_channel_procs.each { |_name, a_proc| a_proc.call }
278
+ process
279
+ self
280
+ end
316
281
 
317
- net_ssh.cleanup_channel(channel) if channel
282
+ def close_channel
283
+ net_ssh&.cleanup_channel(channel) if channel
318
284
  self.channel = nil
319
- # ssh.close if ssh.channels.none? # should the connection be closed if the last channel gets closed?
320
285
  end
321
286
 
322
- def connect
323
- open_channel unless channel
324
- end
287
+ private
325
288
 
326
- def reconnect
327
- disconnect
328
- connect
289
+ def with_prompts
290
+ @with_prompts ||= []
329
291
  end
330
292
 
331
- # feels wrong
332
-
333
- def disconnect
334
- close
335
- net_ssh&.close
336
- self.net_ssh = nil
293
+ def formatted_net_ssh_options
294
+ net_ssh_options.symbolize_keys.reject {|k,v| [:host, :ip, :user].include?(k)}
337
295
  end
338
296
 
339
- def shutdown!
340
- net_ssh&.shutdown!
297
+ def rm_prompt?(**opts)
298
+ opts[:rm_prompt].nil? ? cmd_rm_prompt : opts[:rm_prompt]
341
299
  end
342
300
 
343
- private
344
-
301
+ def rm_command?(**opts)
302
+ opts[:rm_cmd].nil? ? cmd_rm_command : opts[:rm_cmd]
303
+ end
345
304
  end
346
305
  end
347
306
  end
348
307
 
349
- class Net::SSH::CLI::Channel
308
+ class Net::SSH::CLI::Session
350
309
  include Net::SSH::CLI
351
- def initialize(**options)
352
- super
353
- # open_channel
354
- end
355
310
  end
356
311
 
357
312
  class Net::SSH::Connection::Session
358
313
  attr_accessor :cli_channels
359
- def open_cli_channel(**opts)
360
- Net::SSH::CLI::Channel.new({ net_ssh: self, lazy: false }.merge(opts))
314
+ def cli(**opts)
315
+ cli_session = Net::SSH::CLI::Session.new({net_ssh: self}.merge(opts))
316
+ cli_session.open_channel
317
+ cli_session
361
318
  end
362
319
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-ssh-cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fabian Stillhart
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-25 00:00:00.000000000 Z
11
+ date: 2019-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler