net-ssh-cli 0.2.0 → 0.3.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 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