delano-rye 0.6.6 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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.6
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 <akr@fsij.org>
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
@@ -46,7 +46,7 @@ module Rye
46
46
  extend self
47
47
 
48
48
  unless defined?(SYSINFO)
49
- VERSION = 0.6.freeze
49
+ VERSION = 0.7.freeze
50
50
  SYSINFO = SysInfo.new.freeze
51
51
  end
52
52
 
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
- # An instance of Net::SSH::Connection::Session
34
- attr_reader :ssh
35
-
36
- attr_reader :info
37
- attr_reader :debug
38
- attr_reader :error
39
-
40
- attr_accessor :host
41
- attr_accessor :safe
42
- attr_accessor :opts
43
-
44
- # The most recent value from Box.cd or Box.[]
45
- attr_reader :current_working_directory
46
- # The most recent valud for umask (or 0022)
47
- attr_reader :current_umask
48
-
49
- attr_writer :post_command_hook
50
- attr_writer :pre_command_hook
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
- @host = host
72
+ @rye_host = host
71
73
 
72
74
  # These opts are use by Rye::Box and also passed to Net::SSH
73
- @opts = {
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
- # @opts gets sent to Net::SSH so we need to remove the keys
90
+ # @rye_opts gets sent to Net::SSH so we need to remove the keys
89
91
  # that are not meant for it.
90
- @safe, @debug = @opts.delete(:safe), @opts.delete(:debug)
91
- @info, @error = @opts.delete(:info), @opts.delete(:error)
92
- @getenv = {} if @opts.delete(:getenv) # Enable getenv with a hash
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
- @debug = STDERR if @debug == true
96
- @error = STDERR if @error == true
97
- @info = STDOUT if @info == true
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
- @opts[:logger] = Logger.new(@debug) if @debug # Enable Net::SSH debugging
100
- @opts[:paranoid] = true unless @opts[:safe] == false # See Net::SSH.start
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 @host
103
- add_keys(@opts[: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
- @opts.delete(:keys)
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 @opts.inspect
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
- @current_working_directory = key
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
- @current_working_directory = key
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
- @current_umask = val
154
+ @rye_current_umask = val
153
155
  self
154
156
  end
155
157
 
156
158
 
157
- # Open an SSH session with +@host+. This called automatically
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 +@host+ is not specified.
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 @host
167
- return if @ssh && !reconnect
168
- disconnect if @ssh
169
- debug "Opening connection to #{@host} as #{@opts[:user]}"
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
- @ssh = Net::SSH.start(@host, @opts[:user], @opts || {})
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 @info # Ring the bell
180
+ print "\a" if @rye_info # Ring the bell
179
181
  if highline.ask("Continue? ").strip.match(/\Ay|yes|sure|ya\z/i)
180
- @opts[:paranoid] = false
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 && @info # Ring the bell once
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 #{@opts[:user]}"
190
- @opts[:password] = highline.ask("Password: ") { |q| q.echo = '' }
191
- @opts[:auth_methods] ||= []
192
- @opts[:auth_methods] << 'password'
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 @opts otherwise it lingers until
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
- @opts.delete :auth_methods if @opts.has_key?(:auth_methods)
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
- @opts ||= {}
217
- @opts[:user] = newuser
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 +@host+. This is called
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 @ssh && !@ssh.closed?
227
- @ssh.loop(0.1) { @ssh.busy? }
228
- debug "Closing connection to #{@ssh.host}"
229
- @ssh.close
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", "#{@opts[:user]}@#{@host}")
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: #{@getenv[n]}"
268
- @getenv[n] = v
269
- (@current_environment_variables ||= {})[n] = v
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; (@opts || {})[:user]; end
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@host+
281
- def to_s; '%s@%s' % [user, @host]; end
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
- @current_working_directory, @current_umask,
287
- (@current_environment_variables || '').inspect,
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
- @host == other.host
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 @host
300
- Rye.remote_host_keys(@host)
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
- @guessed_homes ||= {}
345
+ @rye_guessed_homes ||= {}
309
346
 
310
347
  # A simple cache.
311
- return @guessed_homes[this_user] if @guessed_homes.has_key?(this_user)
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
- @guessed_homes[this_user] = "#{user_defaults['HOME']}/#{this_user}"
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
- @pre_command_hook = block if block
443
- @pre_command_hook
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
- instance_eval &block
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
- @post_command_hook = block if block
471
- @post_command_hook
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"); @debug.puts msg if @debug; end
478
- def error(msg="unknown error msg"); @error.puts msg if @error; end
479
- def pinfo(msg="unknown info msg"); @info.print msg if @info; end
480
- def info(msg="unknown info msg"); @info.puts msg if @info; end
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 @current_environment_variables.is_a?(Hash)
538
+ return cmd unless @rye_current_environment_variables.is_a?(Hash)
485
539
  env = ''
486
- @current_environment_variables.each_pair do |n,v|
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 !@ssh || @ssh.closed?
517
- raise Rye::NotConnected, @host unless @ssh && !@ssh.closed?
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(@safe, cmd, args)
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 @current_working_directory
525
- cwd = Rye.escape(@safe, 'cd', @current_working_directory)
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 @current_umask
531
- cwd = Rye.escape(@safe, 'umask', @current_umask)
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 @pre_command_hook.is_a?(Proc)
540
- @pre_command_hook.call(cmd, args, opts[:user])
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 @post_command_hook.is_a?(Proc)
562
- @post_command_hook.call(rap)
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 = @ssh.exec(command, &block)
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 @current_working_directory
659
- info "CWD (#{@current_working_directory})"
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(@host, @opts[:user], @opts || {}) do |scp|
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
- @info.flush if @info # make sure every line is printed
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.6.6"
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: delano-rye
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.6
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum