net-ssh 3.0.1 → 3.0.2.rc1
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
- checksums.yaml.gz.sig +1 -1
- data.tar.gz.sig +0 -0
- data/CHANGES.txt +4 -0
- data/README.rdoc +1 -1
- data/Rakefile +5 -1
- data/lib/net/ssh.rb +5 -2
- data/lib/net/ssh/authentication/key_manager.rb +1 -4
- data/lib/net/ssh/connection/channel.rb +26 -7
- data/lib/net/ssh/connection/session.rb +11 -2
- data/lib/net/ssh/proxy/command.rb +17 -2
- data/lib/net/ssh/version.rb +2 -2
- data/net-ssh-public_cert.pem +15 -15
- data/net-ssh.gemspec +7 -5
- data/test/README.txt +0 -22
- data/test/connection/test_channel.rb +3 -0
- data/test/connection/test_session.rb +14 -5
- data/test/integration/README.txt +2 -4
- data/test/integration/common.rb +6 -0
- data/test/integration/playbook.yml +8 -0
- data/test/integration/test_forward.rb +435 -0
- data/test/integration/test_id_rsa_keys.rb +18 -6
- data/test/integration/test_proxy.rb +93 -0
- data/test/start/test_user_nil.rb +27 -0
- data/test/test_all.rb +1 -1
- metadata +22 -20
- metadata.gz.sig +0 -0
- data/test/manual/test_forward.rb +0 -285
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93524ab10733902b604b76acc36193d8bcf4ca1b
|
4
|
+
data.tar.gz: 88e5c681181e9011458adb039bc2853b4f31310b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afbd93ffc5d6ce03bdd1e4a311baf4b7341ab46d0048fe165c32ab7d6de403c6070ae9d5be78cd7ebbebf71d0dd03cc42ab2b2602f453c013cba92ece19c87db
|
7
|
+
data.tar.gz: 0a1f1f8cdd3abc441295acba9ff48b7149bd7625aa9d0fa53a505c62b697d87b8661ca02324047b2fbc348b02f5039bd0b9e8239413de708edfa6ae9c5e8228b
|
checksums.yaml.gz.sig
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
�?�.���$�?\��dË��(��h�Qڛ���J�s&(/�W��C1AaWͩkEF��} �S�����������g)"��i7o �I�ĩ�<JCY0?}��&qt"��_��+i1*`�I�ޞ�f9����ژ�X*�9��<�晚���.
|
data.tar.gz.sig
CHANGED
Binary file
|
data/CHANGES.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
* fixed rare WaitWritable error with proxy commands [Miklos Fazkas, Andre Meij]]
|
2
|
+
* if Net::SSH.start user is nil and config has no entry we default to Etc.getlogin
|
3
|
+
* Bugfix: CHANNEL_CLOSE was sent before draining ouput buffer #280 [Christopher F. Auston]
|
4
|
+
|
1
5
|
=== 3.0.1
|
2
6
|
=== 3.0.1.rc1
|
3
7
|
|
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -74,7 +74,11 @@ end
|
|
74
74
|
|
75
75
|
require 'rake/testtask'
|
76
76
|
Rake::TestTask.new do |t|
|
77
|
-
|
77
|
+
if ENV['NET_SSH_RUN_INTEGRATION_TESTS']
|
78
|
+
t.libs = ["lib","test","test/integration"]
|
79
|
+
else
|
80
|
+
t.libs = ["lib", "test"]
|
81
|
+
end
|
78
82
|
end
|
79
83
|
|
80
84
|
Rake::TestTask.new(:'integration-test') do |t|
|
data/lib/net/ssh.rb
CHANGED
@@ -188,7 +188,10 @@ module Net
|
|
188
188
|
# password auth method
|
189
189
|
# * :non_interactive => non interactive applications should set it to true
|
190
190
|
# to prefer failing a password/etc auth methods vs asking for password
|
191
|
-
|
191
|
+
#
|
192
|
+
# If +user+ parameter is nil it defaults to USER from ssh_config, or
|
193
|
+
# local username
|
194
|
+
def self.start(host, user=nil, options={}, &block)
|
192
195
|
invalid_options = options.keys - VALID_OPTIONS
|
193
196
|
if invalid_options.any?
|
194
197
|
raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
|
@@ -222,7 +225,7 @@ module Net
|
|
222
225
|
transport = Transport::Session.new(host, options)
|
223
226
|
auth = Authentication::Session.new(transport, options)
|
224
227
|
|
225
|
-
user = options.fetch(:user, user)
|
228
|
+
user = options.fetch(:user, user) || Etc.getlogin
|
226
229
|
if auth.authenticate("ssh-connection", user, options[:password])
|
227
230
|
connection = Connection::Session.new(transport, options)
|
228
231
|
if block_given?
|
@@ -232,16 +232,13 @@ module Net
|
|
232
232
|
identity
|
233
233
|
end
|
234
234
|
|
235
|
-
rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError => e
|
235
|
+
rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError, OpenSSL::PKey::ECError, ArgumentError => e
|
236
236
|
if ignore_decryption_errors
|
237
237
|
identity
|
238
238
|
else
|
239
239
|
process_identity_loading_error(identity, e)
|
240
240
|
nil
|
241
241
|
end
|
242
|
-
rescue ArgumentError => e
|
243
|
-
process_identity_loading_error(identity, e)
|
244
|
-
nil
|
245
242
|
rescue Exception => e
|
246
243
|
process_identity_loading_error(identity, e)
|
247
244
|
nil
|
@@ -126,7 +126,7 @@ module Net; module SSH; module Connection
|
|
126
126
|
@pending_requests = []
|
127
127
|
@on_open_failed = @on_data = @on_extended_data = @on_process = @on_close = @on_eof = nil
|
128
128
|
@on_request = {}
|
129
|
-
@closing = @eof = @sent_eof = false
|
129
|
+
@closing = @eof = @sent_eof = @local_closed = @remote_closed = false
|
130
130
|
end
|
131
131
|
|
132
132
|
# A shortcut for accessing properties of the channel (see #properties).
|
@@ -269,14 +269,28 @@ module Net; module SSH; module Connection
|
|
269
269
|
connection.loop { active? }
|
270
270
|
end
|
271
271
|
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
275
|
-
#
|
272
|
+
# True if close() has been called; NOTE: if the channel has data waiting to
|
273
|
+
# be sent then the channel will close after all the data is sent. See
|
274
|
+
# closed?() to determine if we have actually sent CHANNEL_CLOSE to server.
|
275
|
+
# This may be true for awhile before closed? returns true if we are still
|
276
|
+
# sending buffered output to server.
|
276
277
|
def closing?
|
277
278
|
@closing
|
278
279
|
end
|
279
280
|
|
281
|
+
# True if we have sent CHANNEL_CLOSE to the remote server.
|
282
|
+
def local_closed?
|
283
|
+
@local_closed
|
284
|
+
end
|
285
|
+
|
286
|
+
def remote_closed?
|
287
|
+
@remote_closed
|
288
|
+
end
|
289
|
+
|
290
|
+
def remote_closed!
|
291
|
+
@remote_closed = true
|
292
|
+
end
|
293
|
+
|
280
294
|
# Requests that the channel be closed. If the channel is already closing,
|
281
295
|
# this does nothing, nor does it do anything if the channel has not yet
|
282
296
|
# been confirmed open (see #do_open_confirmation). Otherwise, it sends a
|
@@ -285,7 +299,6 @@ module Net; module SSH; module Connection
|
|
285
299
|
return if @closing
|
286
300
|
if remote_id
|
287
301
|
@closing = true
|
288
|
-
connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
|
289
302
|
end
|
290
303
|
end
|
291
304
|
|
@@ -311,10 +324,16 @@ module Net; module SSH; module Connection
|
|
311
324
|
@on_process.call(self) if @on_process
|
312
325
|
enqueue_pending_output
|
313
326
|
|
314
|
-
if @eof and not @sent_eof and output.empty? and remote_id
|
327
|
+
if @eof and not @sent_eof and output.empty? and remote_id and not @local_closed
|
315
328
|
connection.send_message(Buffer.from(:byte, CHANNEL_EOF, :long, remote_id))
|
316
329
|
@sent_eof = true
|
317
330
|
end
|
331
|
+
|
332
|
+
if @closing and not @local_closed and output.empty? and remote_id
|
333
|
+
connection.send_message(Buffer.from(:byte, CHANNEL_CLOSE, :long, remote_id))
|
334
|
+
@local_closed = true
|
335
|
+
connection.cleanup_channel(self)
|
336
|
+
end
|
318
337
|
end
|
319
338
|
|
320
339
|
# Registers a callback to be invoked when data packets are received by the
|
@@ -220,7 +220,7 @@ module Net; module SSH; module Connection
|
|
220
220
|
def preprocess
|
221
221
|
return false if block_given? && !yield(self)
|
222
222
|
dispatch_incoming_packets
|
223
|
-
channels.each { |id, channel| channel.process unless channel.
|
223
|
+
channels.each { |id, channel| channel.process unless channel.local_closed? }
|
224
224
|
return false if block_given? && !yield(self)
|
225
225
|
return true
|
226
226
|
end
|
@@ -455,6 +455,14 @@ module Net; module SSH; module Connection
|
|
455
455
|
old
|
456
456
|
end
|
457
457
|
|
458
|
+
def cleanup_channel(channel)
|
459
|
+
if channel.local_closed? and channel.remote_closed?
|
460
|
+
info { "#{host} delete channel #{channel.local_id} which closed locally and remotely" }
|
461
|
+
channels.delete(channel.local_id)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
|
458
466
|
private
|
459
467
|
|
460
468
|
# Read all pending packets from the connection and dispatch them as
|
@@ -583,9 +591,10 @@ module Net; module SSH; module Connection
|
|
583
591
|
info { "channel_close: #{packet[:local_id]}" }
|
584
592
|
|
585
593
|
channel = channels[packet[:local_id]]
|
594
|
+
channel.remote_closed!
|
586
595
|
channel.close
|
587
596
|
|
588
|
-
|
597
|
+
cleanup_channel(channel)
|
589
598
|
channel.do_close
|
590
599
|
end
|
591
600
|
|
@@ -79,11 +79,26 @@ module Net; module SSH; module Proxy
|
|
79
79
|
end
|
80
80
|
else
|
81
81
|
def io.send(data, flag)
|
82
|
-
|
82
|
+
begin
|
83
|
+
result = write_nonblock(data)
|
84
|
+
rescue IO::WaitWritable, Errno::EINTR
|
85
|
+
IO.select(nil, [self])
|
86
|
+
retry
|
87
|
+
end
|
88
|
+
result
|
83
89
|
end
|
84
90
|
|
85
91
|
def io.recv(size)
|
86
|
-
|
92
|
+
begin
|
93
|
+
result = read_nonblock(size)
|
94
|
+
rescue IO::WaitReadable, Errno::EINTR
|
95
|
+
timeout_in_seconds = 20
|
96
|
+
if IO.select([self], nil, [self], timeout_in_seconds) == nil
|
97
|
+
raise "Unexpected spurious read wakeup"
|
98
|
+
end
|
99
|
+
retry
|
100
|
+
end
|
101
|
+
result
|
87
102
|
end
|
88
103
|
end
|
89
104
|
io
|
data/lib/net/ssh/version.rb
CHANGED
@@ -51,11 +51,11 @@ module Net; module SSH
|
|
51
51
|
MINOR = 0
|
52
52
|
|
53
53
|
# The tiny component of this version of the Net::SSH library
|
54
|
-
TINY =
|
54
|
+
TINY = 2
|
55
55
|
|
56
56
|
# The prerelease component of this version of the Net::SSH library
|
57
57
|
# nil allowed
|
58
|
-
PRE =
|
58
|
+
PRE = "rc1"
|
59
59
|
|
60
60
|
# The current version of the Net::SSH library as a Version instance
|
61
61
|
CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
|
data/net-ssh-public_cert.pem
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
2
2
|
MIIDODCCAiCgAwIBAgIBADANBgkqhkiG9w0BAQUFADBCMRAwDgYDVQQDDAduZXQt
|
3
3
|
c3NoMRkwFwYKCZImiZPyLGQBGRYJc29sdXRpb3VzMRMwEQYKCZImiZPyLGQBGRYD
|
4
|
-
|
4
|
+
Y29tMB4XDTE1MTIwNjIxMDYyNFoXDTE2MTIwNTIxMDYyNFowQjEQMA4GA1UEAwwH
|
5
5
|
bmV0LXNzaDEZMBcGCgmSJomT8ixkARkWCXNvbHV0aW91czETMBEGCgmSJomT8ixk
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
6
|
+
ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMYnhNtn0f6p
|
7
|
+
nTylB8mE8lMdoMLJC8KwpMWsvk73Pe2WVDsH/OSwwwz6oUGk1i70cJyDjIEBNpwT
|
8
|
+
88GpVXJSumvqVsf9fCg3mWNeb5t0J+aeNm9MIvYVMTqj5tydoXQiwnILRDYHV9tZ
|
9
|
+
1c3o59/VlahSTpZ7YEgzVufpAkvEGkbJiG849exiipK7MN/ZIkMOxYVnyRXk43Xc
|
10
|
+
6GYlsHOfSgPwcXwW5g57DCwLQLWrjDsTka28dxDmO7B5Lv5EqzINxVxWsu43OgZG
|
11
|
+
21Io/jIyf5PNpeKPKNGDuAQJ8mvdMYBJoDhtCwgsUYbl0BZzA7g4ytl51HtIeP+j
|
12
|
+
Qp/eAvs/RrECAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQUBfKiwO2eM4NE
|
13
|
+
iRrVG793qEPLYyMwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQCfZFdb
|
14
|
+
p4jzkfIzGDbiOxd0R8sdqJoC4nMLEgnQ7dLulawwA3IXe3sHAKgA5kmH3prsKc5H
|
15
|
+
zVmM5NlH2P1nRbegIkQTYiIod1hZQCNxdmVG/fprMqPq0ybpUOjjrP5pj0OtszE1
|
16
|
+
F2dQia1hOEstMR+n0nAtWII9HJAEyeZjVV0s2Cl7Pt85XJ3hxFcCKwzqsK5xRI7a
|
17
|
+
B3vwh3/JJYrFonIohQ//Lg9qTZASEkoKLlq1/hFeICoCGGIGLq45ZB7CzXLooCKi
|
18
|
+
s/ZUKye79ELwFYKJOhjW5g725OL3hy+llhEleytwKRwgXFQBPTC4f5UkdxZVVWGH
|
19
|
+
e2C9M1m/2odPZo8h
|
20
20
|
-----END CERTIFICATE-----
|
data/net-ssh.gemspec
CHANGED
@@ -2,17 +2,17 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: net-ssh 3.0.
|
5
|
+
# stub: net-ssh 3.0.2.rc1 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "net-ssh"
|
9
|
-
s.version = "3.0.
|
9
|
+
s.version = "3.0.2.rc1"
|
10
10
|
|
11
|
-
s.required_rubygems_version = Gem::Requirement.new("
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Jamis Buck", "Delano Mandelbaum", "Mikl\u{f3}s Fazekas"]
|
14
14
|
s.cert_chain = ["net-ssh-public_cert.pem"]
|
15
|
-
s.date = "2015-
|
15
|
+
s.date = "2015-12-10"
|
16
16
|
s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
|
17
17
|
s.email = "net-ssh@solutious.com"
|
18
18
|
s.extra_rdoc_files = [
|
@@ -144,14 +144,16 @@ Gem::Specification.new do |s|
|
|
144
144
|
"test/integration/Vagrantfile",
|
145
145
|
"test/integration/common.rb",
|
146
146
|
"test/integration/playbook.yml",
|
147
|
+
"test/integration/test_forward.rb",
|
147
148
|
"test/integration/test_id_rsa_keys.rb",
|
149
|
+
"test/integration/test_proxy.rb",
|
148
150
|
"test/known_hosts/github",
|
149
151
|
"test/known_hosts/github_hash",
|
150
|
-
"test/manual/test_forward.rb",
|
151
152
|
"test/manual/test_pageant.rb",
|
152
153
|
"test/start/test_connection.rb",
|
153
154
|
"test/start/test_options.rb",
|
154
155
|
"test/start/test_transport.rb",
|
156
|
+
"test/start/test_user_nil.rb",
|
155
157
|
"test/test_all.rb",
|
156
158
|
"test/test_buffer.rb",
|
157
159
|
"test/test_buffered_io.rb",
|
data/test/README.txt
CHANGED
@@ -16,25 +16,3 @@ INTEGRATION TESTS
|
|
16
16
|
|
17
17
|
brew install ansible ; ansible-galaxy install rvm_io.rvm1-ruby ; vagrant up ; vagrant ssh
|
18
18
|
cd /net-ssh ; rake integration-test
|
19
|
-
|
20
|
-
PORT FORWARDING TESTS
|
21
|
-
|
22
|
-
ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb
|
23
|
-
|
24
|
-
test_forward.rb must be run separately from the test suite because
|
25
|
-
it requires authorizing your public SSH keys on you localhost.
|
26
|
-
|
27
|
-
If you already have keys you can do this:
|
28
|
-
|
29
|
-
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
30
|
-
|
31
|
-
If you don't have keys see:
|
32
|
-
|
33
|
-
http://kimmo.suominen.com/docs/ssh/#ssh-keygen
|
34
|
-
|
35
|
-
You should now be able to login to your localhost with out
|
36
|
-
bring prompted for a password:
|
37
|
-
|
38
|
-
ssh localhost
|
39
|
-
|
40
|
-
-Delano
|
@@ -74,8 +74,11 @@ module Connection
|
|
74
74
|
assert !channel.closing?
|
75
75
|
|
76
76
|
connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type }
|
77
|
+
connection.expects(:cleanup_channel).with(channel)
|
77
78
|
channel.close
|
78
79
|
|
80
|
+
channel.process
|
81
|
+
|
79
82
|
assert channel.closing?
|
80
83
|
end
|
81
84
|
|
@@ -117,14 +117,14 @@ module Connection
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def test_process_should_exit_after_processing_if_block_is_true_then_false
|
120
|
-
session.channels[0] = stub("channel", :
|
120
|
+
session.channels[0] = stub("channel", :local_closed? => false)
|
121
121
|
session.channels[0].expects(:process)
|
122
122
|
IO.expects(:select).never
|
123
123
|
process_times(2)
|
124
124
|
end
|
125
125
|
|
126
126
|
def test_process_should_not_process_channels_that_are_closing
|
127
|
-
session.channels[0] = stub("channel", :
|
127
|
+
session.channels[0] = stub("channel", :local_closed? => true)
|
128
128
|
session.channels[0].expects(:process).never
|
129
129
|
IO.expects(:select).never
|
130
130
|
process_times(2)
|
@@ -299,8 +299,17 @@ module Connection
|
|
299
299
|
end
|
300
300
|
|
301
301
|
def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed
|
302
|
-
|
303
|
-
|
302
|
+
session.channels[14] = stub("channel") do
|
303
|
+
# this simulates the case where we closed the channel first, sent
|
304
|
+
# CHANNEL_CLOSE to server and are waiting for server's response.
|
305
|
+
expects(:local_closed?).returns(true)
|
306
|
+
expects(:do_close)
|
307
|
+
expects(:close).with()
|
308
|
+
expects(:remote_closed!).with()
|
309
|
+
expects(:remote_closed?).with().returns(true)
|
310
|
+
expects(:local_id).returns(14)
|
311
|
+
end
|
312
|
+
|
304
313
|
transport.return(CHANNEL_CLOSE, :long, 14)
|
305
314
|
process_times(2)
|
306
315
|
assert session.channels.empty?
|
@@ -526,7 +535,7 @@ module Connection
|
|
526
535
|
end
|
527
536
|
|
528
537
|
def channel_at(local_id)
|
529
|
-
session.channels[local_id] = stub("channel", :process => true, :
|
538
|
+
session.channels[local_id] = stub("channel", :process => true, :local_closed? => false)
|
530
539
|
end
|
531
540
|
|
532
541
|
def transport(options={})
|
data/test/integration/README.txt
CHANGED
data/test/integration/common.rb
CHANGED
@@ -14,6 +14,12 @@ module IntegrationTestHelpers
|
|
14
14
|
raise "Command: #{command} failed:#{status.exitstatus}" unless res
|
15
15
|
end
|
16
16
|
|
17
|
+
def tmpdir(&block)
|
18
|
+
Dir.mktmpdir do |dir|
|
19
|
+
yield(dir)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
17
23
|
def set_authorized_key(user,pubkey)
|
18
24
|
authorized_key = "/home/#{user}/.ssh/authorized_keys"
|
19
25
|
sh "sudo cp #{pubkey} #{authorized_key}"
|
@@ -34,6 +34,14 @@
|
|
34
34
|
- name: sshd debug
|
35
35
|
lineinfile: dest='/etc/ssh/sshd_config' line='LogLevel DEBUG' regexp=LogLevel
|
36
36
|
notify: restart sshd
|
37
|
+
- name: put NET_SSH_RUN_INTEGRATION_TESTS=YES environment
|
38
|
+
lineinfile: dest='/etc/environment' line='NET_SSH_RUN_INTEGRATION_TESTS=YES'
|
39
|
+
- name: change dir in bashrc
|
40
|
+
lineinfile: dest=/home/vagrant/.bashrc owner=vagrant mode=0644
|
41
|
+
regexp='^cd ' line='cd /net-ssh'
|
42
|
+
- apt: name="{{item}}" state=present
|
43
|
+
with_items:
|
44
|
+
- pv
|
37
45
|
- gem: name="{{item}}" state=present executable=/usr/local/rvm/rubies/ruby-{{ruby_version}}/bin/gem
|
38
46
|
with_items:
|
39
47
|
- byebug
|