net-ssh-cli 1.5.0 → 1.9.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/.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'
|