rye 0.6.6 → 0.7.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.
- data/CHANGES.txt +9 -2
- data/README.rdoc +3 -2
- data/lib/rye.rb +1 -1
- data/lib/rye/box.rb +159 -105
- data/lib/rye/cmd.rb +18 -35
- data/rye.gemspec +1 -1
- metadata +3 -3
data/CHANGES.txt
CHANGED
@@ -4,13 +4,20 @@ TODO
|
|
4
4
|
|
5
5
|
* Re-implement Rye::Rap as an Observable StringIO object for dynamic printing of output.
|
6
6
|
* Fingerprints: ssh-keygen -l -f id_rsa_repos.pub
|
7
|
+
* Add S3 support for Rye::Box.upload / download
|
8
|
+
|
9
|
+
|
10
|
+
#### 0.7.0 (2009-05-30) #############################
|
11
|
+
|
12
|
+
* CHANGE: Rye::Box now uses unique instance variable names to encourage using
|
13
|
+
instance variables in batch command blocks.
|
14
|
+
* ADDED: Rye::Box#file_append
|
7
15
|
|
8
16
|
|
9
17
|
#### 0.6.6 (2009-05-21) #############################
|
10
18
|
|
11
19
|
* CHANGE: Key management is handled by ssh-agent again (instead of Net::SSH)
|
12
20
|
|
13
|
-
|
14
21
|
#### 0.6.5 (2009-05-10) #############################
|
15
22
|
|
16
23
|
* CHANGE: Default exit code is now 0 instead of -1
|
@@ -37,7 +44,6 @@ TODO
|
|
37
44
|
|
38
45
|
* FIXED: I forgot to add highline to the gemspec file manifest. Cripes!
|
39
46
|
|
40
|
-
|
41
47
|
#### 0.6.1 (2009-04-29) #############################
|
42
48
|
|
43
49
|
* ADDED: Prints message to STDERR when passwordless login fails.
|
@@ -45,6 +51,7 @@ TODO
|
|
45
51
|
1.5.1 is not released as a gem yet)
|
46
52
|
* CHANGE: Cleaned examples and links in README
|
47
53
|
|
54
|
+
|
48
55
|
#### 0.6.0 (2009-04-28) #############################
|
49
56
|
|
50
57
|
* FIXED: handling of Process::Status ($?) in Rye.shell
|
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Rye - v0.
|
1
|
+
= Rye - v0.7
|
2
2
|
|
3
3
|
Safely run SSH commands on a bunch of machines at the same time (from Ruby).
|
4
4
|
|
@@ -132,7 +132,8 @@ This list will grow. If you find one let me know!
|
|
132
132
|
== Credits
|
133
133
|
|
134
134
|
* Delano Mandelbaum (delano@solutious.com)
|
135
|
-
* Escape, Copyright (C) 2006,2007 Tanaka Akira
|
135
|
+
* Escape, Copyright (C) 2006,2007 Tanaka Akira <akr@fsij.org>
|
136
|
+
* Rye::Box#instance_exec (for Ruby 1.8) Mauricio Fernandez
|
136
137
|
|
137
138
|
== License
|
138
139
|
|
data/lib/rye.rb
CHANGED
data/lib/rye/box.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module Rye
|
4
4
|
|
5
|
-
|
6
5
|
# = Rye::Box
|
7
6
|
#
|
8
7
|
# The Rye::Box class represents a machine. All system
|
@@ -30,25 +29,28 @@ module Rye
|
|
30
29
|
class Box
|
31
30
|
include Rye::Cmd
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
32
|
+
def host; @rye_host; end
|
33
|
+
def opts; @rye_opts; end
|
34
|
+
def safe; @rye_safe; end
|
35
|
+
|
36
|
+
def host=(val); @rye_host = val; end
|
37
|
+
def opts=(val); @rye_opts = val; end
|
38
|
+
def safe=(val); @rye_safe = val; end
|
39
|
+
|
40
|
+
# The most recent value from Box.cd or Box.[]
|
41
|
+
def current_working_directory; @rye_current_working_directory; end
|
42
|
+
|
43
|
+
# The most recent valud for umask (or 0022)
|
44
|
+
def current_umask; @rye_current_umask; end
|
45
|
+
|
46
|
+
def ssh; @rye_ssh; end
|
47
|
+
def info; @rye_info; end
|
48
|
+
def debug; @rye_debug; end
|
49
|
+
def error; @rye_error; end
|
50
|
+
|
51
|
+
def pre_command_hook=(val); @rye_pre_command_hook = val; end
|
52
|
+
def post_command_hook=(val); @rye_post_command_hook = val; end
|
53
|
+
|
52
54
|
# * +host+ The hostname to connect to. The default is localhost.
|
53
55
|
# * +opts+ a hash of optional arguments.
|
54
56
|
#
|
@@ -67,10 +69,10 @@ module Rye
|
|
67
69
|
# Net::SSH.start that is not already mentioned above.
|
68
70
|
#
|
69
71
|
def initialize(host='localhost', opts={})
|
70
|
-
@
|
72
|
+
@rye_host = host
|
71
73
|
|
72
74
|
# These opts are use by Rye::Box and also passed to Net::SSH
|
73
|
-
@
|
75
|
+
@rye_opts = {
|
74
76
|
:user => Rye.sysinfo.user,
|
75
77
|
:safe => true,
|
76
78
|
:port => 22,
|
@@ -85,33 +87,33 @@ module Rye
|
|
85
87
|
# if disconnect has already been called explicitly.
|
86
88
|
at_exit { self.disconnect }
|
87
89
|
|
88
|
-
# @
|
90
|
+
# @rye_opts gets sent to Net::SSH so we need to remove the keys
|
89
91
|
# that are not meant for it.
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
92
|
+
@rye_safe, @rye_debug = @rye_opts.delete(:safe), @rye_opts.delete(:debug)
|
93
|
+
@rye_info, @rye_error = @rye_opts.delete(:info), @rye_opts.delete(:error)
|
94
|
+
@rye_getenv = {} if @rye_opts.delete(:getenv) # Enable getenv with a hash
|
93
95
|
|
94
96
|
# Just in case someone sends a true value rather than IO object
|
95
|
-
@
|
96
|
-
@
|
97
|
-
@
|
97
|
+
@rye_debug = STDERR if @rye_debug == true
|
98
|
+
@rye_error = STDERR if @rye_error == true
|
99
|
+
@rye_info = STDOUT if @rye_info == true
|
98
100
|
|
99
|
-
@
|
100
|
-
@
|
101
|
+
@rye_opts[:logger] = Logger.new(@rye_debug) if @rye_debug # Enable Net::SSH debugging
|
102
|
+
@rye_opts[:paranoid] = true unless @rye_opts[:safe] == false # See Net::SSH.start
|
101
103
|
|
102
|
-
# Add the given private keys to the keychain that will be used for @
|
103
|
-
add_keys(@
|
104
|
+
# Add the given private keys to the keychain that will be used for @rye_host
|
105
|
+
add_keys(@rye_opts[:keys])
|
104
106
|
|
105
107
|
# We don't want Net::SSH to handle the keypairs. This may change
|
106
108
|
# but for we're letting ssh-agent do it.
|
107
109
|
# TODO: Check if this should ot should not be enabled.
|
108
|
-
@
|
110
|
+
@rye_opts.delete(:keys)
|
109
111
|
|
110
112
|
# From: capistrano/lib/capistrano/cli.rb
|
111
113
|
STDOUT.sync = true # so that Net::SSH prompts show up
|
112
114
|
|
113
115
|
debug "ssh-agent info: #{Rye.sshagent_info.inspect}"
|
114
|
-
debug @
|
116
|
+
debug @rye_opts.inspect
|
115
117
|
|
116
118
|
end
|
117
119
|
|
@@ -135,61 +137,61 @@ module Rye
|
|
135
137
|
# rbox.pwd # => /usr/bin ($ cd /usr/bin && pwd)
|
136
138
|
#
|
137
139
|
def [](key=nil)
|
138
|
-
@
|
140
|
+
@rye_current_working_directory = key
|
139
141
|
self
|
140
142
|
end
|
141
143
|
# Like [] except it returns an empty Rye::Rap object to mimick
|
142
144
|
# a regular command method. Call with nil key (or no arg) to
|
143
145
|
# reset.
|
144
146
|
def cd(key=nil)
|
145
|
-
@
|
147
|
+
@rye_current_working_directory = key
|
146
148
|
ret = Rye::Rap.new(self)
|
147
149
|
end
|
148
150
|
|
149
151
|
# Change the current umask (sort of -- works the same way as cd)
|
150
152
|
# The default umask is 0022
|
151
153
|
def umask=(val='0022')
|
152
|
-
@
|
154
|
+
@rye_current_umask = val
|
153
155
|
self
|
154
156
|
end
|
155
157
|
|
156
158
|
|
157
|
-
# Open an SSH session with +@
|
159
|
+
# Open an SSH session with +@rye_host+. This called automatically
|
158
160
|
# when you the first comamnd is run if it's not already connected.
|
159
|
-
# Raises a Rye::NoHost exception if +@
|
161
|
+
# Raises a Rye::NoHost exception if +@rye_host+ is not specified.
|
160
162
|
# Will attempt a password login up to 3 times if the initial
|
161
163
|
# authentication fails.
|
162
164
|
# * +reconnect+ Disconnect first if already connected. The default
|
163
165
|
# is true. When set to false, connect will do nothing if already
|
164
166
|
# connected.
|
165
167
|
def connect(reconnect=true)
|
166
|
-
raise Rye::NoHost unless @
|
167
|
-
return if @
|
168
|
-
disconnect if @
|
169
|
-
debug "Opening connection to #{@
|
168
|
+
raise Rye::NoHost unless @rye_host
|
169
|
+
return if @rye_ssh && !reconnect
|
170
|
+
disconnect if @rye_ssh
|
171
|
+
debug "Opening connection to #{@rye_host} as #{@rye_opts[:user]}"
|
170
172
|
highline = HighLine.new # Used for password prompt
|
171
173
|
retried = 0
|
172
174
|
|
173
175
|
begin
|
174
|
-
@
|
176
|
+
@rye_ssh = Net::SSH.start(@rye_host, @rye_opts[:user], @rye_opts || {})
|
175
177
|
rescue Net::SSH::HostKeyMismatch => ex
|
176
178
|
STDERR.puts ex.message
|
177
179
|
STDERR.puts "NOTE: EC2 instances generate new SSH keys on first boot."
|
178
|
-
print "\a" if @
|
180
|
+
print "\a" if @rye_info # Ring the bell
|
179
181
|
if highline.ask("Continue? ").strip.match(/\Ay|yes|sure|ya\z/i)
|
180
|
-
@
|
182
|
+
@rye_opts[:paranoid] = false
|
181
183
|
retry
|
182
184
|
else
|
183
185
|
raise Net::SSH::HostKeyMismatch
|
184
186
|
end
|
185
187
|
rescue Net::SSH::AuthenticationFailed => ex
|
186
|
-
print "\a" if retried == 0 && @
|
188
|
+
print "\a" if retried == 0 && @rye_info # Ring the bell once
|
187
189
|
retried += 1
|
188
190
|
if STDIN.tty? && retried <= 3
|
189
|
-
STDERR.puts "Passwordless login failed for #{@
|
190
|
-
@
|
191
|
-
@
|
192
|
-
@
|
191
|
+
STDERR.puts "Passwordless login failed for #{@rye_opts[:user]}"
|
192
|
+
@rye_opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
|
193
|
+
@rye_opts[:auth_methods] ||= []
|
194
|
+
@rye_opts[:auth_methods] << 'password'
|
193
195
|
retry
|
194
196
|
else
|
195
197
|
raise Net::SSH::AuthenticationFailed
|
@@ -198,9 +200,9 @@ module Rye
|
|
198
200
|
|
199
201
|
# We add :auth_methods (a Net::SSH joint) to force asking for a
|
200
202
|
# password if the initial (key-based) authentication fails. We
|
201
|
-
# need to delete the key from @
|
203
|
+
# need to delete the key from @rye_opts otherwise it lingers until
|
202
204
|
# the next connection (if we switch_user is called for example).
|
203
|
-
@
|
205
|
+
@rye_opts.delete :auth_methods if @rye_opts.has_key?(:auth_methods)
|
204
206
|
|
205
207
|
self
|
206
208
|
end
|
@@ -213,20 +215,20 @@ module Rye
|
|
213
215
|
# and a new one is opened for the given user.
|
214
216
|
def switch_user(newuser)
|
215
217
|
return if newuser.to_s == self.user.to_s
|
216
|
-
@
|
217
|
-
@
|
218
|
+
@rye_opts ||= {}
|
219
|
+
@rye_opts[:user] = newuser
|
218
220
|
disconnect
|
219
221
|
connect
|
220
222
|
end
|
221
223
|
|
222
224
|
|
223
|
-
# Close the SSH session with +@
|
225
|
+
# Close the SSH session with +@rye_host+. This is called
|
224
226
|
# automatically at exit if the connection is open.
|
225
227
|
def disconnect
|
226
|
-
return unless @
|
227
|
-
@
|
228
|
-
debug "Closing connection to #{@
|
229
|
-
@
|
228
|
+
return unless @rye_ssh && !@rye_ssh.closed?
|
229
|
+
@rye_ssh.loop(0.1) { @rye_ssh.busy? }
|
230
|
+
debug "Closing connection to #{@rye_ssh.host}"
|
231
|
+
@rye_ssh.close
|
230
232
|
end
|
231
233
|
|
232
234
|
|
@@ -239,7 +241,7 @@ module Rye
|
|
239
241
|
def interactive_ssh(run=true)
|
240
242
|
debug "interactive_ssh with keys: #{Rye.keys.inspect}"
|
241
243
|
run = false unless STDIN.tty?
|
242
|
-
cmd = Rye.prepare_command("ssh", "#{@
|
244
|
+
cmd = Rye.prepare_command("ssh", "#{@rye_opts[:user]}@rye_#{@rye_host}")
|
243
245
|
return cmd unless run
|
244
246
|
system(cmd)
|
245
247
|
end
|
@@ -260,44 +262,79 @@ module Rye
|
|
260
262
|
end
|
261
263
|
alias :add_key :add_keys
|
262
264
|
|
265
|
+
# Return the value of uname in lowercase
|
266
|
+
# This is a temporary fix. We can use SysInfo for this, upload
|
267
|
+
# it, execute it directly, parse the output.
|
268
|
+
def ostype
|
269
|
+
return @rye_ostype if @rye_ostype # simple cache
|
270
|
+
os = self.uname.first rescue nil
|
271
|
+
os ||= 'unknown'
|
272
|
+
os &&= os.downcase
|
273
|
+
@rye_ostype = os
|
274
|
+
end
|
275
|
+
|
276
|
+
# Returns the hash containing the parsed output of "env" on the
|
277
|
+
# remote machine. If the initialize option +:getenv+ was set to
|
278
|
+
# false, this will return an empty hash.
|
279
|
+
# This is a lazy loaded method so it fetches the remote envvars
|
280
|
+
# the first time this method is called.
|
281
|
+
#
|
282
|
+
# puts rbox.getenv['HOME'] # => "/home/gloria" (remote)
|
283
|
+
#
|
284
|
+
# NOTE: This method should not raise an exception under normal
|
285
|
+
# circumstances.
|
286
|
+
#
|
287
|
+
def getenv
|
288
|
+
if @rye_getenv && @rye_getenv.empty? && self.can?(:env)
|
289
|
+
env = self.env rescue []
|
290
|
+
env.each do |nv|
|
291
|
+
# Parse "GLORIA_HOME=/gloria/lives/here" into a name/value
|
292
|
+
# pair. The regexp ensures we split only at the 1st = sign
|
293
|
+
n, v = nv.scan(/\A([\w_-]+?)=(.+)\z/).flatten
|
294
|
+
@rye_getenv[n] = v
|
295
|
+
end
|
296
|
+
end
|
297
|
+
@rye_getenv
|
298
|
+
end
|
299
|
+
|
263
300
|
# Add an environment variable. +n+ and +v+ are the name and value.
|
264
301
|
# Returns the instance of Rye::Box
|
265
302
|
def setenv(n, v)
|
266
303
|
debug "Adding env: #{n}=#{v}"
|
267
|
-
debug "prev value: #{@
|
268
|
-
@
|
269
|
-
(@
|
304
|
+
debug "prev value: #{@rye_getenv[n]}"
|
305
|
+
@rye_getenv[n] = v
|
306
|
+
(@rye_current_environment_variables ||= {})[n] = v
|
270
307
|
self
|
271
308
|
end
|
272
309
|
alias :add_env :setenv # deprecated?
|
273
310
|
|
274
311
|
# The name of the user that opened the SSH connection
|
275
|
-
def user; (@
|
312
|
+
def user; (@rye_opts || {})[:user]; end
|
276
313
|
|
277
314
|
# See Rye.keys
|
278
315
|
def keys; Rye.keys; end
|
279
316
|
|
280
|
-
# Returns +user@
|
281
|
-
def to_s; '%s
|
317
|
+
# Returns +user@rye_host+
|
318
|
+
def to_s; '%s@rye_%s' % [user, @rye_host]; end
|
282
319
|
|
283
320
|
def inspect
|
284
321
|
%q{#<%s:%s cwd=%s umask=%s env=%s safe=%s opts=%s>} %
|
285
322
|
[self.class.to_s, self.host,
|
286
|
-
@
|
287
|
-
(@
|
323
|
+
@rye_current_working_directory, @rye_current_umask,
|
324
|
+
(@rye_current_environment_variables || '').inspect,
|
288
325
|
self.safe, self.opts.inspect]
|
289
326
|
end
|
290
327
|
|
291
328
|
# Compares itself with the +other+ box. If the hostnames
|
292
329
|
# are the same, this will return true. Otherwise false.
|
293
330
|
def ==(other)
|
294
|
-
@
|
331
|
+
@rye_host == other.host
|
295
332
|
end
|
296
333
|
|
297
334
|
# Returns the host SSH keys for this box
|
298
335
|
def host_key
|
299
|
-
raise "No host" unless @
|
300
|
-
Rye.remote_host_keys(@
|
336
|
+
raise "No host" unless @rye_host
|
337
|
+
Rye.remote_host_keys(@rye_host)
|
301
338
|
end
|
302
339
|
|
303
340
|
# Uses the output of "useradd -D" to determine the default home
|
@@ -305,10 +342,10 @@ module Rye
|
|
305
342
|
# home directory. Currently used only by authorize_keys_remote.
|
306
343
|
def guess_user_home(other_user=nil)
|
307
344
|
this_user = other_user || opts[:user]
|
308
|
-
@
|
345
|
+
@rye_guessed_homes ||= {}
|
309
346
|
|
310
347
|
# A simple cache.
|
311
|
-
return @
|
348
|
+
return @rye_guessed_homes[this_user] if @rye_guessed_homes.has_key?(this_user)
|
312
349
|
|
313
350
|
# Some junk to determine where user home directories are by default.
|
314
351
|
# We're relying on the command "useradd -D" so this may not work on
|
@@ -337,7 +374,7 @@ module Rye
|
|
337
374
|
end
|
338
375
|
end
|
339
376
|
|
340
|
-
@
|
377
|
+
@rye_guessed_homes[this_user] = "#{user_defaults['HOME']}/#{this_user}"
|
341
378
|
end
|
342
379
|
|
343
380
|
# Copy the local public keys (as specified by Rye.keys) to
|
@@ -439,8 +476,8 @@ module Rye
|
|
439
476
|
# with three arguments: command name, an Array of arguments, user name
|
440
477
|
#
|
441
478
|
def pre_command_hook(&block)
|
442
|
-
@
|
443
|
-
@
|
479
|
+
@rye_pre_command_hook = block if block
|
480
|
+
@rye_pre_command_hook
|
444
481
|
end
|
445
482
|
|
446
483
|
# Execute a block in the context of an instance of Rye::Box.
|
@@ -455,8 +492,25 @@ module Rye
|
|
455
492
|
# rbox.batch(&block)
|
456
493
|
#
|
457
494
|
#
|
458
|
-
def batch(&block)
|
459
|
-
|
495
|
+
def batch(*args, &block)
|
496
|
+
self.instance_exec *args, &block
|
497
|
+
end
|
498
|
+
|
499
|
+
# instance_exec for Ruby 1.8 written by Mauricio Fernandez
|
500
|
+
# http://eigenclass.org/hiki/instance_exec
|
501
|
+
if RUBY_VERSION =~ /1.8/
|
502
|
+
module InstanceExecHelper; end
|
503
|
+
include InstanceExecHelper
|
504
|
+
def instance_exec(*args, &block) # !> method redefined; discarding old instance_exec
|
505
|
+
mname = "__instance_exec_#{Thread.current.object_id.abs}_#{object_id.abs}"
|
506
|
+
InstanceExecHelper.module_eval{ define_method(mname, &block) }
|
507
|
+
begin
|
508
|
+
ret = send(mname, *args)
|
509
|
+
ensure
|
510
|
+
InstanceExecHelper.module_eval{ undef_method(mname) } rescue nil
|
511
|
+
end
|
512
|
+
ret
|
513
|
+
end
|
460
514
|
end
|
461
515
|
|
462
516
|
# Supply a block to be called after every command. It's called
|
@@ -467,23 +521,23 @@ module Rye
|
|
467
521
|
# behavior) so the block needs to check the Rye::Rap object to
|
468
522
|
# determine whether an exception should be raised.
|
469
523
|
def post_command_hook(&block)
|
470
|
-
@
|
471
|
-
@
|
524
|
+
@rye_post_command_hook = block if block
|
525
|
+
@rye_post_command_hook
|
472
526
|
end
|
473
527
|
|
474
528
|
|
475
529
|
private
|
476
530
|
|
477
|
-
def debug(msg="unknown debug msg"); @
|
478
|
-
def error(msg="unknown error msg"); @
|
479
|
-
def pinfo(msg="unknown info msg"); @
|
480
|
-
def info(msg="unknown info msg"); @
|
531
|
+
def debug(msg="unknown debug msg"); @rye_debug.puts msg if @rye_debug; end
|
532
|
+
def error(msg="unknown error msg"); @rye_error.puts msg if @rye_error; end
|
533
|
+
def pinfo(msg="unknown info msg"); @rye_info.print msg if @rye_info; end
|
534
|
+
def info(msg="unknown info msg"); @rye_info.puts msg if @rye_info; end
|
481
535
|
|
482
536
|
# Add the current environment variables to the beginning of +cmd+
|
483
537
|
def prepend_env(cmd)
|
484
|
-
return cmd unless @
|
538
|
+
return cmd unless @rye_current_environment_variables.is_a?(Hash)
|
485
539
|
env = ''
|
486
|
-
@
|
540
|
+
@rye_current_environment_variables.each_pair do |n,v|
|
487
541
|
env << "export #{n}=#{Escape.shell_single_word(v)}; "
|
488
542
|
end
|
489
543
|
[env, cmd].join(' ')
|
@@ -513,22 +567,22 @@ module Rye
|
|
513
567
|
|
514
568
|
cmd, args = prep_args(*args)
|
515
569
|
|
516
|
-
connect if !@
|
517
|
-
raise Rye::NotConnected, @
|
570
|
+
connect if !@rye_ssh || @rye_ssh.closed?
|
571
|
+
raise Rye::NotConnected, @rye_host unless @rye_ssh && !@rye_ssh.closed?
|
518
572
|
|
519
|
-
cmd_clean = Rye.escape(@
|
573
|
+
cmd_clean = Rye.escape(@rye_safe, cmd, args)
|
520
574
|
cmd_clean = prepend_env(cmd_clean)
|
521
575
|
|
522
576
|
# Add the current working directory before the command if supplied.
|
523
577
|
# The command will otherwise run in the user's home directory.
|
524
|
-
if @
|
525
|
-
cwd = Rye.escape(@
|
578
|
+
if @rye_current_working_directory
|
579
|
+
cwd = Rye.escape(@rye_safe, 'cd', @rye_current_working_directory)
|
526
580
|
cmd_clean = [cwd, cmd_clean].join(' && ')
|
527
581
|
end
|
528
582
|
|
529
583
|
# ditto (same explanation as cwd)
|
530
|
-
if @
|
531
|
-
cwd = Rye.escape(@
|
584
|
+
if @rye_current_umask
|
585
|
+
cwd = Rye.escape(@rye_safe, 'umask', @rye_current_umask)
|
532
586
|
cmd_clean = [cwd, cmd_clean].join(' && ')
|
533
587
|
end
|
534
588
|
|
@@ -536,8 +590,8 @@ module Rye
|
|
536
590
|
info "COMMAND: #{cmd_clean}"
|
537
591
|
debug "Executing: %s" % cmd_clean
|
538
592
|
|
539
|
-
if @
|
540
|
-
@
|
593
|
+
if @rye_pre_command_hook.is_a?(Proc)
|
594
|
+
@rye_pre_command_hook.call(cmd, args, opts[:user])
|
541
595
|
end
|
542
596
|
|
543
597
|
## NOTE: Do not raise a CommandNotFound exception in this method.
|
@@ -558,8 +612,8 @@ module Rye
|
|
558
612
|
rap.exit_signal = esignal
|
559
613
|
rap.cmd = cmd
|
560
614
|
|
561
|
-
if @
|
562
|
-
@
|
615
|
+
if @rye_post_command_hook.is_a?(Proc)
|
616
|
+
@rye_post_command_hook.call(rap)
|
563
617
|
else
|
564
618
|
# It seems a convention for various commands to return -1
|
565
619
|
# when something only mildly concerning happens. ls even
|
@@ -623,7 +677,7 @@ module Rye
|
|
623
677
|
#end
|
624
678
|
end
|
625
679
|
|
626
|
-
channel = @
|
680
|
+
channel = @rye_ssh.exec(command, &block)
|
627
681
|
channel.wait # block until we get a response
|
628
682
|
|
629
683
|
channel[:exit_code] = 0 if channel[:exit_code] == nil
|
@@ -655,8 +709,8 @@ module Rye
|
|
655
709
|
raise "Must be one of: upload, download"
|
656
710
|
end
|
657
711
|
|
658
|
-
if @
|
659
|
-
info "CWD (#{@
|
712
|
+
if @rye_current_working_directory
|
713
|
+
info "CWD (#{@rye_current_working_directory})"
|
660
714
|
end
|
661
715
|
|
662
716
|
files = [files].flatten.compact || []
|
@@ -674,7 +728,7 @@ module Rye
|
|
674
728
|
raise "Cannot upload to a StringIO object"
|
675
729
|
end
|
676
730
|
|
677
|
-
# Fail early. We check the
|
731
|
+
# Fail early. We check whether the StringIO object is available to read
|
678
732
|
files.each do |file|
|
679
733
|
if file.is_a?(StringIO)
|
680
734
|
raise "Cannot download a StringIO object" if direction == :download
|
@@ -694,13 +748,13 @@ module Rye
|
|
694
748
|
self.mkdir(:p, other) unless self.file_exists?(other)
|
695
749
|
end
|
696
750
|
|
697
|
-
Net::SCP.start(@
|
751
|
+
Net::SCP.start(@rye_host, @rye_opts[:user], @rye_opts || {}) do |scp|
|
698
752
|
transfers = []
|
699
753
|
files.each do |file|
|
700
754
|
debug file.to_s
|
701
755
|
transfers << scp.send(direction, file, other) do |ch, n, s, t|
|
702
756
|
pinfo "#{n}: #{s}/#{t}b\r" # update line: "file: sent/total"
|
703
|
-
@
|
757
|
+
@rye_info.flush if @rye_info # make sure every line is printed
|
704
758
|
end
|
705
759
|
end
|
706
760
|
transfers.each { |t| t.wait } # Run file transfers in parallel
|
data/lib/rye/cmd.rb
CHANGED
@@ -98,6 +98,24 @@ module Rye;
|
|
98
98
|
# NOTE: Changes to current working directory with +cd+ or +[]+ are ignored.
|
99
99
|
def download(*files); net_scp_transfer!(:download, *files); end
|
100
100
|
|
101
|
+
|
102
|
+
def file_append(filepath, newcontent, backup=false)
|
103
|
+
if self.file_exists?(filepath) && backup
|
104
|
+
self.cp filepath, "#{filepath}-previous"
|
105
|
+
end
|
106
|
+
|
107
|
+
file_content = self.download filepath
|
108
|
+
file_content ||= StringIO.new
|
109
|
+
if newcontent.is_a?(StringIO)
|
110
|
+
newcontent.rewind
|
111
|
+
file_content.puts newcontent.read
|
112
|
+
else
|
113
|
+
file_content.puts newcontent
|
114
|
+
end
|
115
|
+
|
116
|
+
self.upload file_content, filepath
|
117
|
+
end
|
118
|
+
|
101
119
|
# Does a remote path exist?
|
102
120
|
def file_exists?(path)
|
103
121
|
begin
|
@@ -109,41 +127,6 @@ module Rye;
|
|
109
127
|
# But on OSX exit code is 1. This is why we look at STDERR.
|
110
128
|
ret.stderr.empty?
|
111
129
|
end
|
112
|
-
|
113
|
-
# Return the value of uname in lowercase
|
114
|
-
# This is a temporary fix. We can use SysInfo for this, upload
|
115
|
-
# it, execute it directly, parse the output.
|
116
|
-
def ostype
|
117
|
-
return @ostype if @ostype # simple cache
|
118
|
-
os = self.uname.first rescue nil
|
119
|
-
os ||= 'unknown'
|
120
|
-
os &&= os.downcase
|
121
|
-
@ostype = os
|
122
|
-
end
|
123
|
-
|
124
|
-
# Returns the hash containing the parsed output of "env" on the
|
125
|
-
# remote machine. If the initialize option +:getenv+ was set to
|
126
|
-
# false, this will return an empty hash.
|
127
|
-
# This is a lazy loaded method so it fetches the remote envvars
|
128
|
-
# the first time this method is called.
|
129
|
-
#
|
130
|
-
# puts rbox.getenv['HOME'] # => "/home/gloria" (remote)
|
131
|
-
#
|
132
|
-
# NOTE: This method should not raise an exception under normal
|
133
|
-
# circumstances.
|
134
|
-
#
|
135
|
-
def getenv
|
136
|
-
if @getenv && @getenv.empty? && self.can?(:env)
|
137
|
-
env = self.env rescue []
|
138
|
-
env.each do |nv|
|
139
|
-
# Parse "GLORIA_HOME=/gloria/lives/here" into a name/value
|
140
|
-
# pair. The regexp ensures we split only at the 1st = sign
|
141
|
-
n, v = nv.scan(/\A([\w_-]+?)=(.+)\z/).flatten
|
142
|
-
@getenv[n] = v
|
143
|
-
end
|
144
|
-
end
|
145
|
-
@getenv
|
146
|
-
end
|
147
130
|
|
148
131
|
# Returns an Array of system commands available over SSH
|
149
132
|
def can
|
data/rye.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
@spec = Gem::Specification.new do |s|
|
2
2
|
s.name = "rye"
|
3
3
|
s.rubyforge_project = "rye"
|
4
|
-
s.version = "0.
|
4
|
+
s.version = "0.7.0"
|
5
5
|
s.summary = "Rye: Safely run SSH commands on a bunch of machines at the same time (from Ruby)."
|
6
6
|
s.description = s.summary
|
7
7
|
s.author = "Delano Mandelbaum"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rye
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-30 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
123
|
requirements: []
|
124
124
|
|
125
125
|
rubyforge_project: rye
|
126
|
-
rubygems_version: 1.3.
|
126
|
+
rubygems_version: 1.3.3
|
127
127
|
signing_key:
|
128
128
|
specification_version: 2
|
129
129
|
summary: "Rye: Safely run SSH commands on a bunch of machines at the same time (from Ruby)."
|