net-ssh-cli 1.5.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/README.md +76 -1
- data/lib/net/ssh/cli/version.rb +1 -1
- data/lib/net/ssh/cli.rb +143 -29
- data/net-ssh-cli.gemspec +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fe1c60d30e59f8fc15074b0a59f9a0c868f47472df17d1ad156c301b3582bc22
|
4
|
+
data.tar.gz: bd9f7a6dd71fb3429440f2fd6713d7f04322aba58705bd63d474156a7932cf00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c81d16a0fd072fea7563c55aaae2bbd0f17622ff27294ec080c14084cf11a106a6bbadb7aeb6516aab8cbb5e35ee8a8be501b33f45f0ed8afff5e0fbb36a08dd
|
7
|
+
data.tar.gz: 8f27e6a09b431191571d90d360486a4141c3b314bf291b948fe401737049e6deebfdd25d8aeaeb830cdeb8727074ae628880c03ea3987f988775a349958c31f8
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
3.0
|
data/README.md
CHANGED
@@ -60,6 +60,12 @@ end
|
|
60
60
|
# => "echo 'bananas'\nbananas"
|
61
61
|
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true
|
62
62
|
# => "bananas"
|
63
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, minimum_duration: 9
|
64
|
+
# => "bananas"
|
65
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, prompt: /\nuser@host:/m
|
66
|
+
# => "bananas"
|
67
|
+
cli.cmd "echo 'bananas'", rm_command: true, rm_prompt: true, timeout: 60
|
68
|
+
# => "bananas"
|
63
69
|
```
|
64
70
|
|
65
71
|
Remove the command and the prompt for #cmd & #dialog by default
|
@@ -69,9 +75,23 @@ Remove the command and the prompt for #cmd & #dialog by default
|
|
69
75
|
# => "bananas"
|
70
76
|
```
|
71
77
|
|
78
|
+
You can define a timeout for a `#cmd` in order to avoid hanging commands. The timeout gets passed into the underlying function #read_till.
|
79
|
+
This is usefull in case your prompt won't match because of an unexpected behaviour or undefined behaviour. For example some form of unexpected dialog.
|
80
|
+
The underlying implementation is using a soft timeout because `Timeout.timeout` is dangerous. In order to deal anyway with hanging low level issues, `Timeout.timeout` is used too, but with a higher value than the soft timeout.
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
cli = ssh.cli(default_prompt: /(\nuser@host):/m, read_till_timeout: 11)
|
84
|
+
cli.cmd "echo 'bananas'" # timeout is set to 11
|
85
|
+
# => "bananas"
|
86
|
+
cli.cmd "echo 'bananas'", timeout: 22 # timeout is set to 22
|
87
|
+
# => "bananas"
|
88
|
+
cli.cmd "sleep 33", timeout: 22 # timeout is set to 22
|
89
|
+
# Net::SSH::CLI::Error::CMD
|
90
|
+
```
|
91
|
+
|
72
92
|
### #cmds
|
73
93
|
|
74
|
-
It's the same as
|
94
|
+
It's the same as `#cmd` but for multiple commands.
|
75
95
|
|
76
96
|
```ruby
|
77
97
|
cli.cmds ["echo 'bananas'", "echo 'apples'"], rm_command: true, rm_prompt: true
|
@@ -162,6 +182,8 @@ The following callbacks are available
|
|
162
182
|
- after_open_channel
|
163
183
|
- before_on_stdout
|
164
184
|
- after_on_stdout
|
185
|
+
- before_on_stdin
|
186
|
+
- after_on_stdin
|
165
187
|
|
166
188
|
```ruby
|
167
189
|
cli.before_open_channel do
|
@@ -184,12 +206,33 @@ cli.after_on_stdout do
|
|
184
206
|
end
|
185
207
|
```
|
186
208
|
|
209
|
+
```ruby
|
210
|
+
cli.after_on_stdout do
|
211
|
+
puts "the following new data arrived on stdout #{new_data.inspect} from #{hostname}"
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
215
|
+
or convert new lines between different OS
|
187
216
|
```ruby
|
188
217
|
cli.after_on_stdout do
|
189
218
|
stdout.gsub!("\r\n", "\n")
|
190
219
|
end
|
191
220
|
```
|
192
221
|
|
222
|
+
or hide passwords
|
223
|
+
```ruby
|
224
|
+
cli.after_on_stdout do
|
225
|
+
stdout.gsub!(/password:\S+/, "<HIDDEN>")
|
226
|
+
end
|
227
|
+
```
|
228
|
+
|
229
|
+
or change the stdin before sending it
|
230
|
+
```ruby
|
231
|
+
cli.before_on_stdin do
|
232
|
+
content.gsub("\n\n", "\n")
|
233
|
+
end
|
234
|
+
```
|
235
|
+
|
193
236
|
### #hostname #host #to_s
|
194
237
|
|
195
238
|
```ruby
|
@@ -213,6 +256,38 @@ This works usually, but is not guaranteed to work well.
|
|
213
256
|
# => "[my prompt]"
|
214
257
|
```
|
215
258
|
|
259
|
+
### An outdated view of all available Options
|
260
|
+
|
261
|
+
Please check the file `lib/net/ssh/cli.rb` `OPTIONS` in order to get an up-to-date view of all available options, flags and arguments.
|
262
|
+
|
263
|
+
```ruby
|
264
|
+
OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
|
265
|
+
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for
|
266
|
+
cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
|
267
|
+
cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
|
268
|
+
cmd_rm_command_tail: "\n", # which format does the end of line return after a command has been submitted. Could be something like "ls\n" "ls\r\n" or "ls \n" (extra spaces)
|
269
|
+
run_impact: false, # whether to run #impact commands. This might align with testing|development|production. example #impact("reboot")
|
270
|
+
read_till_timeout: nil, # timeout for #read_till to find the match
|
271
|
+
read_till_hard_timeout: nil, # hard timeout for #read_till to find the match using Timeout.timeout(hard_timeout) {}. Might creates unpredicted sideffects
|
272
|
+
read_till_hard_timeout_factor: 1.2, # hard timeout factor in case read_till_hard_timeout is true
|
273
|
+
named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
|
274
|
+
before_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before #cmd
|
275
|
+
after_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after #cmd
|
276
|
+
before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
|
277
|
+
after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
|
278
|
+
before_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data is sent to the underlying channel
|
279
|
+
after_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data is sent to the underlying channel
|
280
|
+
before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
|
281
|
+
after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
|
282
|
+
open_channel_timeout: nil, # timeout to open the channel
|
283
|
+
net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
|
284
|
+
process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
|
285
|
+
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
|
286
|
+
on_stdout_processing: 100, # whether to optimize the on_stdout performance by calling #process #optimize_on_stdout-times in case more data arrives
|
287
|
+
sleep_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call instead of Kernel.sleep(), perfect for async hooks
|
288
|
+
)
|
289
|
+
```
|
290
|
+
|
216
291
|
## Development
|
217
292
|
|
218
293
|
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/lib/net/ssh/cli/version.rb
CHANGED
data/lib/net/ssh/cli.rb
CHANGED
@@ -40,20 +40,36 @@ module Net
|
|
40
40
|
attr_accessor :channel, :stdout, :net_ssh, :logger, :new_data, :process_count
|
41
41
|
|
42
42
|
OPTIONS = ActiveSupport::HashWithIndifferentAccess.new(
|
43
|
-
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for
|
43
|
+
default_prompt: /\n?^(\S+@.*)\z/, # the default prompt to search for. It is recommended to use \z to ensure you don't match the prompt too early.
|
44
44
|
cmd_rm_prompt: false, # whether the prompt should be removed in the output of #cmd
|
45
45
|
cmd_rm_command: false, # whether the given command should be removed in the output of #cmd
|
46
|
+
cmd_rm_command_tail: "\n", # which format does the end of line return after a command has been submitted. Could be something like "ls\n" "ls\r\n" or "ls \n" (extra spaces)
|
47
|
+
cmd_minimum_duration: 0, # how long do you want to wait/sleep after sending the command. After this waiting time, the output will be processed and the prompt will be searched.
|
46
48
|
run_impact: false, # whether to run #impact commands. This might align with testing|development|production. example #impact("reboot")
|
47
49
|
read_till_timeout: nil, # timeout for #read_till to find the match
|
50
|
+
read_till_hard_timeout: nil, # hard timeout for #read_till to find the match using Timeout.timeout(hard_timeout) {}. Might creates unpredicted sideffects
|
51
|
+
read_till_hard_timeout_factor: 1.2, # hard timeout factor in case read_till_hard_timeout is true
|
48
52
|
named_prompts: ActiveSupport::HashWithIndifferentAccess.new, # you can used named prompts for #with_prompt {}
|
53
|
+
before_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before #cmd
|
54
|
+
after_cmd_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after #cmd
|
49
55
|
before_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data arrives from the underlying connection
|
50
56
|
after_on_stdout_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data arrives from the underlying connection
|
57
|
+
before_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before data is sent to the underlying channel
|
58
|
+
after_on_stdin_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after data is sent to the underlying channel
|
51
59
|
before_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call before opening a channel
|
52
60
|
after_open_channel_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call after opening a channel, for example you could call #detect_prompt or #read_till
|
53
61
|
open_channel_timeout: nil, # timeout to open the channel
|
54
62
|
net_ssh_options: ActiveSupport::HashWithIndifferentAccess.new, # a wrapper for options to pass to Net::SSH.start in case net_ssh is undefined
|
55
63
|
process_time: 0.00001, # how long #process is processing net_ssh#process or sleeping (waiting for something)
|
56
64
|
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
|
65
|
+
on_stdout_processing: 100, # whether to optimize the on_stdout performance by calling #process #optimize_on_stdout-times in case more data arrives
|
66
|
+
sleep_procs: ActiveSupport::HashWithIndifferentAccess.new, # procs to call instead of Kernel.sleep(), perfect for async hooks
|
67
|
+
terminal_chars_width: 320, # Sets and sends the terminal dimensions during the opening of the channel. It does not send a channel_request on change.
|
68
|
+
terminal_chars_height: 120, # See also https://github.com/net-ssh/net-ssh/blob/master/lib/net/ssh/connection/channel.rb#L220 section 'def request_pty'
|
69
|
+
terminal_pixels_width: 1920, # See also https://www.ietf.org/rfc/rfc4254.txt section pty-req and section window-change
|
70
|
+
terminal_pixels_height: 1080, #
|
71
|
+
terminal_term: nil, # Sets the terminal term, usually xterm
|
72
|
+
terminal_modes: nil, #
|
57
73
|
)
|
58
74
|
|
59
75
|
def options
|
@@ -89,7 +105,9 @@ module Net
|
|
89
105
|
|
90
106
|
OPTIONS.keys.select {|key| key.to_s.include? "procs"}.each do |name|
|
91
107
|
define_method name.sub("_procs","") do |&blk|
|
92
|
-
|
108
|
+
id = SecureRandom.uuid
|
109
|
+
self.send(name)[id] = Proc.new {blk.call}
|
110
|
+
id
|
93
111
|
end
|
94
112
|
end
|
95
113
|
|
@@ -108,19 +126,19 @@ module Net
|
|
108
126
|
before_on_stdout_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
109
127
|
stdout << new_data
|
110
128
|
after_on_stdout_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
111
|
-
|
112
|
-
process unless process_count > 100 # if we receive data, we probably receive more - improves performance - but on a lot of data, this leads to a stack level too deep
|
113
|
-
self.process_count -= 1
|
129
|
+
optimise_stdout_processing
|
114
130
|
stdout
|
115
131
|
end
|
116
132
|
|
117
|
-
def
|
133
|
+
def stdin(content = String.new)
|
118
134
|
logger.debug { "#write #{content.inspect}" }
|
135
|
+
before_on_stdin_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
119
136
|
channel.send_data content
|
120
137
|
process
|
138
|
+
after_on_stdin_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
121
139
|
content
|
122
140
|
end
|
123
|
-
alias stdin
|
141
|
+
alias write stdin
|
124
142
|
|
125
143
|
def write_n(content = String.new)
|
126
144
|
write content + "\n"
|
@@ -129,7 +147,7 @@ module Net
|
|
129
147
|
def read
|
130
148
|
process
|
131
149
|
var = stdout!
|
132
|
-
logger.debug
|
150
|
+
logger.debug { "#read: \n#{var}" }
|
133
151
|
var
|
134
152
|
end
|
135
153
|
|
@@ -140,6 +158,16 @@ module Net
|
|
140
158
|
with_prompts[-1] || default_prompt
|
141
159
|
end
|
142
160
|
|
161
|
+
# run something with a different named prompt
|
162
|
+
#
|
163
|
+
# named_prompts["root"] = /(?<prompt>\nroot)\z/
|
164
|
+
#
|
165
|
+
# with_named_prompt("root") do
|
166
|
+
# cmd("sudo -i")
|
167
|
+
# cmd("cat /etc/passwd")
|
168
|
+
# end
|
169
|
+
# cmd("exit")
|
170
|
+
#
|
143
171
|
def with_named_prompt(name)
|
144
172
|
raise Error::UndefinedMatch, "unknown named_prompt #{name}" unless named_prompts[name]
|
145
173
|
|
@@ -148,20 +176,25 @@ module Net
|
|
148
176
|
end
|
149
177
|
end
|
150
178
|
|
179
|
+
# tries to detect the prompt
|
180
|
+
# sends a "\n", waits for a X seconds, and uses the last line as prompt
|
181
|
+
# this won't work reliable if the prompt changes during the session
|
151
182
|
def detect_prompt(seconds: 3)
|
152
183
|
write_n
|
153
|
-
|
154
|
-
while future > Time.now
|
155
|
-
process
|
156
|
-
sleep 0.1
|
157
|
-
end
|
184
|
+
process(seconds)
|
158
185
|
self.default_prompt = read[/\n?^.*\z/]
|
159
186
|
raise Error::PromptDetection, "couldn't detect a prompt" unless default_prompt.present?
|
160
187
|
|
161
188
|
default_prompt
|
162
189
|
end
|
163
190
|
|
164
|
-
#
|
191
|
+
# run something with a different prompt
|
192
|
+
#
|
193
|
+
# with_prompt(/(?<prompt>\nroot)\z/) do
|
194
|
+
# cmd("sudo -i")
|
195
|
+
# cmd("cat /etc/passwd")
|
196
|
+
# end
|
197
|
+
# cmd("exit")
|
165
198
|
def with_prompt(prompt)
|
166
199
|
logger.debug { "#with_prompt: #{current_prompt.inspect} => #{prompt.inspect}" }
|
167
200
|
with_prompts << prompt
|
@@ -172,26 +205,48 @@ module Net
|
|
172
205
|
logger.debug { "#with_prompt: => #{current_prompt.inspect}" }
|
173
206
|
end
|
174
207
|
|
175
|
-
|
208
|
+
# continues to process the ssh connection till #stdout matches the given prompt.
|
209
|
+
# might raise a timeout error if a soft/hard timeout is given
|
210
|
+
# be carefull when using the hard_timeout, this is using the dangerous Timeout.timeout
|
211
|
+
# this gets really slow on large outputs, since the prompt will be searched in the whole output. Use \z in the regex if possible
|
212
|
+
#
|
213
|
+
# Optional named arguments:
|
214
|
+
# - prompt: expected to be a regex
|
215
|
+
# - timeout: nil or a number
|
216
|
+
# - hard_timeout: nil, true, or a number
|
217
|
+
# - hard_timeout_factor: nil, true, or a number
|
218
|
+
# - when hard_timeout == true, this will set the hard_timeout as (read_till_hard_timeout_factor * read_till_timeout), defaults to 1.2 = +20%
|
219
|
+
def read_till(prompt: current_prompt, timeout: read_till_timeout, hard_timeout: read_till_hard_timeout, hard_timeout_factor: read_till_hard_timeout_factor, **_opts)
|
176
220
|
raise Error::UndefinedMatch, 'no prompt given or default_prompt defined' unless prompt
|
221
|
+
hard_timeout = (read_till_hard_timeout_factor * timeout) if timeout and hard_timeout == true
|
222
|
+
hard_timeout = nil if hard_timeout == true
|
177
223
|
|
178
|
-
|
179
|
-
|
180
|
-
::Timeout.timeout(hard_timeout, Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{timeout}s") do
|
181
|
-
with_prompt(prompt) do
|
224
|
+
with_prompt(prompt) do
|
225
|
+
::Timeout.timeout(hard_timeout, Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{hard_timeout}s") do
|
182
226
|
soft_timeout = Time.now + timeout if timeout
|
183
|
-
until
|
227
|
+
until prompt_in_stdout? do
|
184
228
|
if timeout and soft_timeout < Time.now
|
185
229
|
raise Error::ReadTillTimeout, "#{current_prompt.inspect} didn't match on #{stdout.inspect} within #{timeout}s"
|
186
230
|
end
|
187
231
|
process
|
188
|
-
sleep 0.
|
232
|
+
sleep 0.01 # don't race for CPU
|
189
233
|
end
|
190
234
|
end
|
191
235
|
end
|
192
236
|
read
|
193
237
|
end
|
194
238
|
|
239
|
+
def prompt_in_stdout?
|
240
|
+
case current_prompt
|
241
|
+
when Regexp
|
242
|
+
!!stdout[current_prompt]
|
243
|
+
when String
|
244
|
+
stdout.include?(current_prompt)
|
245
|
+
else
|
246
|
+
raise Net::SSH::CLI::Error, "prompt/current_prompt is not a String/Regex #{current_prompt.inspect}"
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
195
250
|
def read_for(seconds:)
|
196
251
|
process(seconds)
|
197
252
|
read
|
@@ -202,18 +257,26 @@ module Net
|
|
202
257
|
cmd(command, **opts)
|
203
258
|
end
|
204
259
|
|
205
|
-
#
|
206
|
-
#
|
207
|
-
|
260
|
+
# send a command and get the output as return value
|
261
|
+
# 1. sends the given command to the ssh connection channel
|
262
|
+
# 2. continues to process the ssh connection until the prompt is found in the stdout
|
263
|
+
# 3. prepares the output using your callbacks
|
264
|
+
# 4. returns the output of your command
|
265
|
+
# Hint: 'read' first on purpose as a feature. once you cmd you ignore what happend before. otherwise use read|write directly.
|
266
|
+
# this should avoid many horrible state issues where the prompt is not the last prompt
|
267
|
+
def cmd(command, pre_read: true, rm_prompt: cmd_rm_prompt, rm_command: cmd_rm_command, prompt: current_prompt, minimum_duration: cmd_minimum_duration, **opts)
|
208
268
|
opts = opts.clone.merge(pre_read: pre_read, rm_prompt: rm_prompt, rm_command: rm_command, prompt: prompt)
|
209
269
|
if pre_read
|
210
270
|
pre_read_data = read
|
211
271
|
logger.debug { "#cmd ignoring pre-command output: #{pre_read_data.inspect}" } if pre_read_data.present?
|
212
272
|
end
|
273
|
+
before_cmd_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
213
274
|
write_n command
|
275
|
+
sleep(minimum_duration)
|
214
276
|
output = read_till(**opts)
|
215
277
|
rm_prompt!(output, **opts)
|
216
278
|
rm_command!(output, command, **opts)
|
279
|
+
after_cmd_procs.each { |_name, a_proc| instance_eval(&a_proc) }
|
217
280
|
output
|
218
281
|
rescue Error::ReadTillTimeout => error
|
219
282
|
raise Error::CMD, "#{error.message} after cmd #{command.inspect} was sent"
|
@@ -221,20 +284,36 @@ module Net
|
|
221
284
|
alias command cmd
|
222
285
|
alias exec cmd
|
223
286
|
|
287
|
+
# Execute multiple cmds, see #cmd
|
224
288
|
def cmds(*commands, **opts)
|
225
289
|
commands.flatten.map { |command| [command, cmd(command, **opts)] }
|
226
290
|
end
|
227
291
|
alias commands cmds
|
228
292
|
|
229
293
|
def rm_command!(output, command, **opts)
|
230
|
-
output[command +
|
294
|
+
output[command + cmd_rm_command_tail] = '' if rm_command?(**opts) && output[command + cmd_rm_command_tail]
|
231
295
|
end
|
232
296
|
|
233
|
-
|
297
|
+
# removes the prompt from the given output
|
298
|
+
# prompt should contain a named match 'prompt' /(?<prompt>.*something.*)\z/
|
299
|
+
# for backwards compatibility it also tries to replace the first match of the prompt /(something)\z/
|
300
|
+
# it removes the whole match if no matches are given /something\z/
|
301
|
+
def rm_prompt!(output, prompt: current_prompt, **opts)
|
234
302
|
if rm_prompt?(**opts)
|
235
|
-
prompt = opts[:prompt] || current_prompt
|
236
303
|
if output[prompt]
|
237
|
-
|
304
|
+
case prompt
|
305
|
+
when String then output[prompt] = ''
|
306
|
+
when Regexp
|
307
|
+
if prompt.names.include?("prompt")
|
308
|
+
output[prompt, "prompt"] = ''
|
309
|
+
else
|
310
|
+
begin
|
311
|
+
output[prompt, 1] = ''
|
312
|
+
rescue IndexError
|
313
|
+
output[prompt] = ''
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
238
317
|
end
|
239
318
|
end
|
240
319
|
end
|
@@ -261,6 +340,19 @@ module Net
|
|
261
340
|
alias hostname host
|
262
341
|
alias to_s host
|
263
342
|
|
343
|
+
# if #sleep_procs are set, they will be called instead of Kernel.sleep
|
344
|
+
# great for async
|
345
|
+
# .sleep_procs["async"] = proc do |duration| async_reactor.sleep(duration) end
|
346
|
+
#
|
347
|
+
# cli.sleep(1)
|
348
|
+
def sleep(duration)
|
349
|
+
if sleep_procs.any?
|
350
|
+
sleep_procs.each { |_name, a_proc| instance_exec(duration, &a_proc) }
|
351
|
+
else
|
352
|
+
Kernel.sleep(duration)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
264
356
|
## NET::SSH
|
265
357
|
#
|
266
358
|
|
@@ -291,7 +383,7 @@ module Net
|
|
291
383
|
net_ssh.open_channel do |new_channel|
|
292
384
|
logger.debug 'channel is open'
|
293
385
|
self.channel = new_channel
|
294
|
-
new_channel.request_pty do |_ch, success|
|
386
|
+
new_channel.request_pty(terminal_options) do |_ch, success|
|
295
387
|
raise Error::Pty, "#{host || ip} Failed to open ssh pty" unless success
|
296
388
|
end
|
297
389
|
new_channel.send_channel_request('shell') do |_ch, success|
|
@@ -333,6 +425,28 @@ module Net
|
|
333
425
|
def rm_command?(**opts)
|
334
426
|
opts[:rm_cmd].nil? ? cmd_rm_command : opts[:rm_cmd]
|
335
427
|
end
|
428
|
+
|
429
|
+
# when new data is beeing received, likely more data will arrive - this improves the performance by a large factor
|
430
|
+
# but on a lot of data, this leads to a stack level too deep
|
431
|
+
# therefore it is limited to max #on_stdout_processing
|
432
|
+
# the bigger on_stdout_processing, the closer we get to a stack level too deep
|
433
|
+
def optimise_stdout_processing
|
434
|
+
self.process_count += 1
|
435
|
+
process unless process_count > on_stdout_processing
|
436
|
+
ensure
|
437
|
+
self.process_count -= 1
|
438
|
+
end
|
439
|
+
|
440
|
+
def terminal_options
|
441
|
+
{
|
442
|
+
term: terminal_term,
|
443
|
+
chars_wide: terminal_chars_width,
|
444
|
+
chars_high: terminal_chars_height,
|
445
|
+
pixels_wide: terminal_pixels_width,
|
446
|
+
pixels_high: terminal_pixels_height,
|
447
|
+
modes: terminal_modes
|
448
|
+
}.reject {|k,v| v.nil?}
|
449
|
+
end
|
336
450
|
end
|
337
451
|
end
|
338
452
|
end
|
data/net-ssh-cli.gemspec
CHANGED
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
|
|
39
39
|
|
40
40
|
spec.add_development_dependency 'bundler'
|
41
41
|
spec.add_development_dependency 'bump'
|
42
|
-
spec.add_development_dependency 'rake', '~>
|
42
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
43
43
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
44
44
|
spec.add_dependency 'activesupport', '>= 4.0'
|
45
45
|
spec.add_dependency 'net-ssh', '>= 4.0'
|
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: 1.
|
4
|
+
version: 1.9.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:
|
11
|
+
date: 2021-09-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.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: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -140,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
140
|
- !ruby/object:Gem::Version
|
141
141
|
version: '0'
|
142
142
|
requirements: []
|
143
|
-
rubygems_version: 3.
|
143
|
+
rubygems_version: 3.2.24
|
144
144
|
signing_key:
|
145
145
|
specification_version: 4
|
146
146
|
summary: 'Net::SSH::CLI: A library to handle CLI Sessions'
|