rye 0.8.0 → 0.8.1

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.
@@ -6,6 +6,14 @@ TODO
6
6
  * Fingerprints: ssh-keygen -l -f id_rsa_repos.pub
7
7
  * Add S3 support for Rye::Box.upload / download
8
8
 
9
+ #### 0.8.1 (2009-06-22) #############################
10
+
11
+ * FIXED: file_upload now handles globs like a champ
12
+ * FIXED: Handling of relative paths in Rye::Box#cd and Rye::Box#[]
13
+ * ADDED: file_upload now assumes uploading to home directory when only 1 arg
14
+ * ADDED: Rudyfile for remote 'clean-machine' tests
15
+ * CHANGE: guess_user_home, ostype, and getenv now run quietly
16
+
9
17
 
10
18
  #### 0.8.0 (2009-06-21) #############################
11
19
 
@@ -0,0 +1,135 @@
1
+ # = Rudy configuration
2
+ #
3
+
4
+ # ----------------------------------------------------------- DEFAULTS --------
5
+ # These values are used as defaults for their respective global settings. All
6
+ # non-boolean values are expected to be Symbols.
7
+ #
8
+ defaults do
9
+ zone :'eu-west-1b'
10
+ environment :rye
11
+ color true # Terminal colors? true/false
12
+ yes false # Auto-confirm? true/false
13
+ end
14
+
15
+ # --------------------------------------------------------- MACHINES --------
16
+ # The machines block describes the 'physical' characteristics of your machines.
17
+ machines do
18
+
19
+ zone :'us-east-1b' do
20
+ ami 'ami-e348af8a' # Alestic Debian 5.0, 32-bit (US)
21
+ bucket 'rudy-ami-us'
22
+ end
23
+ zone :'eu-west-1b' do
24
+ ami 'ami-1dbd9569' # rudy-ami-eu/debian-5.0-32-ruby-r1
25
+ bucket 'rudy-ami-eu'
26
+ end
27
+
28
+ hostname :rudy # One of: :default, :rudy, 'your-name'
29
+
30
+ env :rye do
31
+ user :root # User to connect as
32
+ size 'm1.small' # EC2 machine type for all machines
33
+ end
34
+
35
+ end
36
+
37
+
38
+ # ----------------------------------------------------------- COMMANDS --------
39
+ # The commands block defines shell commands that can be used in routines. The
40
+ # ones defined here are added to the default list defined by Rye::Cmd (Rudy
41
+ # executes all SSH commands via Rye).
42
+ #
43
+ # Usage:
44
+ #
45
+ # allow COMMAND-NAME
46
+ # allow COMMAND-NAME, '/path/2/COMMAND'
47
+ # allow COMMAND-NAME, '/path/2/COMMAND', 'default argument', 'another arg'
48
+ #
49
+ commands do
50
+ allow :apt_get, "apt-get", :y, :q
51
+ allow :gem18_install, "/usr/bin/gem1.8", "install", :n, '/usr/bin', :y, :V, "--no-rdoc", "--no-ri"
52
+ allow :gem18_sources, "/usr/bin/gem1.8", "sources"
53
+ allow :gem19_install, "/usr/local/bin/gem", "install", :n, '/usr/bin', :y, :V, "--no-rdoc", "--no-ri"
54
+ allow :gem19_sources, "/usr/local/bin/gem", "sources"
55
+ allow :update_rubygems
56
+ allow :ruby18, "/usr/bin/ruby1.8"
57
+ allow :ruby19, "/usr/local/bin/ruby"
58
+ allow :ssh_keygen, 'ssh-keygen'
59
+ allow :rm
60
+ end
61
+
62
+ # ----------------------------------------------------------- ROUTINES --------
63
+ # The routines block describes the repeatable processes for each machine group.
64
+ # To run a routine, specify its name on the command-line: rudy startup
65
+ routines do
66
+
67
+ env :rye do
68
+ startup do
69
+ after :installdeps, :authorize
70
+ after :runtest
71
+ end
72
+ end
73
+
74
+ runtest do
75
+ remote :root do
76
+ ruby18 :r, 'rubygems', 'rye/bin/try'
77
+ ruby19 'rye/bin/try'
78
+ end
79
+ end
80
+
81
+ authorize do
82
+ remote :root do
83
+ rm :f, '/root/.ssh/id_rsa'
84
+ ssh_keygen :q, :f, '/root/.ssh/id_rsa', :N, ''
85
+ rye 'authorize_local'
86
+ end
87
+ end
88
+
89
+ installdeps do
90
+ local do
91
+ rake 'package'
92
+ end
93
+ remote :root do
94
+ gem18_install "rye", "delano-rye"
95
+ gem19_install "rye", 'delano-rye'
96
+ disable_safe_mode
97
+ rm :r, :f, 'rye*'
98
+ file_upload 'pkg/rye-*gz'
99
+ tar :z, :x, :f, 'rye-*gz'
100
+ rm 'rye-*gz'
101
+ mv 'rye-*', 'rye'
102
+ cd 'rye'
103
+ end
104
+ end
105
+
106
+ # NOTE: sysupdate only needs to be run for the bare instances.
107
+ sysupdate do
108
+ remote :root do
109
+ apt_get "update"
110
+ apt_get "install", "build-essential", "git-core"
111
+ apt_get "install", "libssl-dev", "libreadline5-dev", "zlib1g-dev"
112
+ apt_get "install", "ruby1.8-dev", "rubygems"
113
+ gem18_install 'rubygems-update' # for 1.8
114
+ update_rubygems # for 1.8
115
+ gem18_sources :a, "http://gems.github.com"
116
+ end
117
+ after :install_ruby19
118
+ end
119
+
120
+ install_ruby19 do
121
+ remote :root do
122
+ wget :q, 'ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.1-p129.tar.gz'
123
+ tar :z, :x, :f, 'ruby-1.9.1-p129.tar.gz'
124
+ cd 'ruby-1.9.1-p129'
125
+ configure
126
+ make
127
+ make 'install'
128
+ apt_get "install", "rubygems1.9"
129
+ gem19_sources :a, "http://gems.github.com"
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+
data/bin/try ADDED
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # Rye -- A working example
4
+ #
5
+ # If your reading this via the rdocs you won't be able to see the code
6
+ # See: http://github.com/delano/rye/blob/master/bin/try
7
+ #
8
+ # Usage: bin/try
9
+ #
10
+
11
+ RYE_HOME = File.join(File.dirname(__FILE__), '..')
12
+ $:.unshift File.join(RYE_HOME, 'lib')
13
+ %w{net-ssh sysinfo storable drydock}.each { |dir| $:.unshift File.join(File.dirname(__FILE__), '..', '..', dir, 'lib') }
14
+
15
+ require 'stringio'
16
+ require 'yaml'
17
+ require 'rye'
18
+
19
+
20
+ puts %Q(
21
+ # ------------------------------------------------------------------
22
+ # EXAMPLE 1 -- Basic Usage
23
+ #)
24
+
25
+ rbox = Rye::Box.new('localhost')
26
+
27
+ # Commands are run as methods on the Rye::Box object
28
+ puts rbox.uptime # => 11:02 up 16:01, 3 users
29
+
30
+ # The response value for all commands is a Rye::Rap object. The rap is a
31
+ # subclass of Array so you can treat it as an Array, but it can also act
32
+ # like a String if there's only one element.
33
+ p rbox.ls(:a, '/') # => ['.', '..', 'bin', 'etc', ...]
34
+
35
+ # You can change directories
36
+ puts rbox.pwd # => /home/rye
37
+ puts rbox['/usr/bin'].pwd # => /usr/bin
38
+ puts rbox.pwd # => /usr/bin
39
+
40
+ # You can specify environment variables
41
+ rbox.setenv(:RYE, "Forty Creek")
42
+ rbox.env # => ['HOME=/home/rye', 'RYE=Forty Creek', ...]
43
+
44
+ # The commands method returns an Array of available commands:
45
+ puts rbox.commands.join(', ') # => pwd, touch, echo, wc, ...
46
+
47
+ # When you're done you can disconnect explicitly.
48
+ # (Although Rye does this automatically at exit.)
49
+ rbox.disconnect
50
+
51
+
52
+ puts %Q(
53
+ # ------------------------------------------------------------------
54
+ # EXAMPLE 2 -- Disabling Safe-Mode
55
+ #)
56
+
57
+ rbox_safe = Rye::Box.new('localhost')
58
+ rbox_wild = Rye::Box.new('localhost', :safe => false)
59
+
60
+ # Safe-mode is enabled by default. In safe-mode, all command
61
+ # arguments are thoroughly escaped. This prevents access to
62
+ # environment variables and file globs (among other things).
63
+ p rbox_safe.echo('$HOME') # => "$HOME"
64
+ p rbox_safe['/etc'].ls('host*') rescue Rye::CommandError # Doesn't exist
65
+ p rbox_safe.ls('-l | wc -l') rescue Rye::CommandError # => '|' is not a valid ls arg
66
+
67
+ # Here's the same commands with safe-mode disabled:
68
+ p rbox_wild.echo('$HOME') # => "/home/rye"
69
+ p rbox_wild['/etc'].ls('host*') # => ["hostconfig", "hosts"]
70
+ p rbox_wild.ls('-l | wc -l') # => 110
71
+ p rbox_wild.echo('$HOME > /tmp/rye-home') # =>
72
+ p rbox_wild.cat('/tmp/rye-home') # => "/home/rye"
73
+
74
+
75
+
76
+ puts %Q(
77
+ # ------------------------------------------------------------------
78
+ # EXAMPLE 3 -- Custom Commands
79
+ #)
80
+
81
+ rbox = Rye::Box.new('localhost')
82
+ rbox.add_keys('/private/key/path') # Specify additional private keys
83
+
84
+ # There's currently no rye900 command
85
+ p rbox.commands.member?('rye9000') # => false
86
+
87
+ # But we can add our own commands to the Rye::Cmd class. They
88
+ # automatically become available to all Rye::Box objects.
89
+ module Rye::Cmd
90
+ def rye9000(*args)
91
+ run_command("ls", args)
92
+ end
93
+ def somescript(*args)
94
+ run_command("/path/to/my/script", args)
95
+ end
96
+ end
97
+
98
+ # We can now run rye9000 (with arguments)
99
+ p rbox.rye9000('-a') # => [".", "..", ".bashrc", ...]
100
+ p rbox.commands.member?('rye9000') # => true
101
+
102
+
103
+ puts %Q(
104
+ # ------------------------------------------------------------------
105
+ # EXAMPLE 4 -- Accessing Multiple Machines
106
+ #)
107
+
108
+ rset = Rye::Set.new('default', :parallel => true)
109
+ rbox = Rye::Box.new
110
+ p rbox
111
+
112
+ rset.add_keys('/private/key/path') # For passwordless logins
113
+ rset.add_boxes(rbox, '127.0.0.1') # Add boxes as hostnames or objects
114
+
115
+ # Calling methods on Rye::Set objects is very similar to calling them on
116
+ # Rye::Box objects. In fact, it's identical:
117
+ p rset.uptime # => [[14:19:02 up 32 days, 19:35 ...], [14:19:02 up 30 days, 01:35]]
118
+ p rset['/usr'].ls # => [['bin', 'etc', ...], ['bin', 'etc', ...]]
119
+
120
+ # Like Rye::Box, the response value is a Rye::Rap object containing the
121
+ # responses from each box. Each response is itself a Rye::Rap object.
122
+ unames = rset.uname
123
+ p unames # => [["Darwin"], ["Darwin"]]
124
+ puts unames.class # => Rye::Rap
125
+
126
+ # The Rye::Rap object also keeps a reference to the object that called the
127
+ # command. In this case, it will keep a reference to Rye::Set:
128
+ puts unames.set.class # => Rye::Set
129
+ puts unames.set == rset # => true
130
+ puts unames.size # => 2
131
+ puts unames.first # => Darwin
132
+ puts unames.first.class # => Rye::Rap
133
+ puts unames.first.box.class # => Rye::Box
134
+ puts unames.first.box == rbox # => true
135
+
136
+ # Envrionment variables can be set the same way as with Rye::Box
137
+ rset.setenv(:RYE, "Forty Creek")
138
+ p rset.env.first.select { |env| env =~ /RYE/ } # => ["RYE=Forty Creek"]
139
+
140
+
141
+ puts %Q(
142
+ # ------------------------------------------------------------------
143
+ # EXAMPLE 5 -- ERROR HANDLING
144
+ #)
145
+
146
+ rbox = Rye::Box.new('localhost', :safe => false) # Note: safe mode is off
147
+
148
+ # Rye follows the standard convention of taking exception to a non-zero
149
+ # exit code by raising a Rye::CommandError. In this case, rye9000.test
150
+ # is not found by the ls command.
151
+ begin
152
+ rbox.ls('rye.test')
153
+ rescue Rye::CommandError => ex
154
+ puts ex.exit_code # => 1
155
+ puts ex.stderr # => ls: rye.test: No such file or directory
156
+ end
157
+
158
+ # The Rye:Rap response objects also give you the STDOUT and STDERR
159
+ # content separately. Here we redirect STDOUT to STDERR, so this
160
+ # will return nothing:
161
+ puts rbox.uname('-a 1>&2').stdout # =>
162
+
163
+ # It all went to STDERR:
164
+ puts rbox.uname('-a 1>&2').stderr # => Darwin ryehost 9.6.0 ...
165
+
166
+ # There were no actual errors so the exit code should be 0.
167
+ puts rbox.uname('-a 1>&2').exit_code # => 0
168
+
169
+
170
+ puts %Q(
171
+ # ------------------------------------------------------------------
172
+ # EXAMPLE 6 -- FILE TRANSFERS
173
+ #)
174
+
175
+ dir_upload = "#{Rye.sysinfo.tmpdir}/rye-upload/"
176
+ dir_download = "#{Rye.sysinfo.tmpdir}/rye-download/"
177
+
178
+ rbox = Rye::Box.new("localhost", :info => false)
179
+
180
+ # Rye ships without an rm method (for safety!). Here
181
+ # we add the rm method only to this instance of rbox.
182
+ def rbox.rm(*args); cmd('rm', args); end
183
+
184
+ rbox.rm(:r, :f, dir_upload) # Silently delete test dirs
185
+ rbox.rm(:r, :f, dir_download)
186
+
187
+ rbox.file_upload("#{RYE_HOME}/README.rdoc",
188
+ "#{RYE_HOME}/LICENSE.txt", dir_upload)
189
+
190
+ applejack = StringIO.new("Some in-memory content")
191
+ rbox.file_upload(applejack, "#{dir_upload}/applejack.txt")
192
+
193
+ p rbox.ls(dir_upload) # => [README.rdoc, LICENSE.txt, applejack.txt]
194
+ p rbox.cat("#{dir_upload}/applejack.txt") # => "Some in-memory content"
195
+
196
+ filecontent = StringIO.new
197
+ rbox.file_download("#{dir_upload}/applejack.txt", filecontent)
198
+ filecontent.rewind
199
+ p filecontent.read
200
+
201
+
202
+
data/lib/rye.rb CHANGED
@@ -43,7 +43,7 @@ module Rye
43
43
  extend self
44
44
 
45
45
  unless defined?(SYSINFO)
46
- VERSION = "0.8.0".freeze
46
+ VERSION = "0.8.1".freeze
47
47
  SYSINFO = SysInfo.new.freeze
48
48
  end
49
49
 
@@ -155,28 +155,26 @@ module Rye
155
155
  # rbox['/usr/bin'].pwd # => /usr/bin ($ cd /usr/bin && pwd)
156
156
  # rbox.pwd # => /usr/bin ($ cd /usr/bin && pwd)
157
157
  #
158
- def [](key=nil)
159
- if key.nil? || key.index('/') == 0
160
- @rye_current_working_directory = key
158
+ def [](fpath=nil)
159
+ if fpath.nil? || fpath.index('/') == 0
160
+ @rye_current_working_directory = fpath
161
161
  else
162
162
  # Append to non-absolute paths
163
- newpath = File.join(@rye_current_working_directory, key)
164
- @rye_current_working_directory = File.expand_path(newpath)
163
+ if @rye_current_working_directory
164
+ newpath = File.join(@rye_current_working_directory, fpath)
165
+ @rye_current_working_directory = newpath
166
+ else
167
+ @rye_current_working_directory = fpath
168
+ end
165
169
  end
170
+ info "CWD: #{@rye_current_working_directory}"
166
171
  self
167
172
  end
168
173
  # Like [] except it returns an empty Rye::Rap object to mimick
169
174
  # a regular command method. Call with nil key (or no arg) to
170
175
  # reset.
171
- def cd(key=nil)
172
- if key.nil? || key.index('/') == 0
173
- @rye_current_working_directory = key
174
- else
175
- # Append to non-absolute paths
176
- newpath = File.join(@rye_current_working_directory, key)
177
- @rye_current_working_directory = File.expand_path(newpath)
178
- end
179
- ret = Rye::Rap.new(self)
176
+ def cd(fpath=nil)
177
+ Rye::Rap.new(self[fpath])
180
178
  end
181
179
 
182
180
  # Change the current umask (sort of -- works the same way as cd)
@@ -237,7 +235,7 @@ module Rye
237
235
  # it, execute it directly, parse the output.
238
236
  def ostype
239
237
  return @rye_ostype if @rye_ostype # simple cache
240
- os = self.uname.first rescue nil
238
+ os = self.quietly { uname.first } rescue nil
241
239
  os ||= 'unknown'
242
240
  os &&= os.downcase
243
241
  @rye_ostype = os
@@ -256,11 +254,11 @@ module Rye
256
254
  #
257
255
  def getenv
258
256
  if @rye_getenv && @rye_getenv.empty? && self.can?(:env)
259
- env = self.env rescue []
260
- env.each do |nv|
257
+ vars = self.quietly { env } rescue []
258
+ vars.each do |nvpair|
261
259
  # Parse "GLORIA_HOME=/gloria/lives/here" into a name/value
262
260
  # pair. The regexp ensures we split only at the 1st = sign
263
- n, v = nv.scan(/\A([\w_-]+?)=(.+)\z/).flatten
261
+ n, v = nvpair.scan(/\A([\w_-]+?)=(.+)\z/).flatten
264
262
  @rye_getenv[n] = v
265
263
  end
266
264
  end
@@ -307,6 +305,7 @@ module Rye
307
305
  # Uses the output of "useradd -D" to determine the default home
308
306
  # directory. This returns a GUESS rather than the a user's real
309
307
  # home directory. Currently used only by authorize_keys_remote.
308
+ # Only useful before you've logged in. Otherwise check $HOME
310
309
  def guess_user_home(other_user=nil)
311
310
  this_user = other_user || opts[:user]
312
311
  @rye_guessed_homes ||= {}
@@ -321,7 +320,7 @@ module Rye
321
320
  # /etc/default/useradd, HOME=/home OR useradd -D
322
321
  # /etc/adduser.config, DHOME=/home OR ??
323
322
  user_defaults = {}
324
- raw = self.useradd(:D) rescue ["HOME=/home"]
323
+ raw = self.quietly { useradd(:D) } rescue ["HOME=/home"]
325
324
  ostmp = self.ostype
326
325
  raw.each do |nv|
327
326
 
@@ -697,8 +696,7 @@ module Rye
697
696
  ## raise Rye::CommandNotFound unless self.can?(cmd)
698
697
 
699
698
  begin
700
- info "COMMAND: #{cmd_clean}"
701
- debug "Executing: #{cmd_internal}"
699
+ info "COMMAND: #{cmd_internal}"
702
700
 
703
701
  if !@rye_quiet && @rye_pre_command_hook.is_a?(Proc)
704
702
  @rye_pre_command_hook.call(cmd_clean, user, host, nickname)
@@ -870,15 +868,26 @@ module Rye
870
868
 
871
869
  # We allow a single file to be downloaded into a StringIO object
872
870
  # but only when no target has been specified.
873
- if direction == :download && files.size == 1
874
- debug "Created StringIO for download"
875
- other = StringIO.new
876
- else
877
- other = files.pop
878
- end
879
-
880
- if direction == :upload && other.is_a?(StringIO)
881
- raise "Cannot upload to a StringIO object"
871
+ if direction == :download
872
+ if files.size == 1
873
+ debug "Created StringIO for download"
874
+ target = StringIO.new
875
+ else
876
+ target = files.pop # The last path is the download target.
877
+ end
878
+
879
+ elsif direction == :upload
880
+ raise "Cannot upload to a StringIO object" if target.is_a?(StringIO)
881
+ if files.size == 1
882
+ target = self.getenv['HOME'] || guess_user_home
883
+ debug "Assuming upload to #{target}"
884
+ else
885
+ target = files.pop
886
+ end
887
+
888
+ # Expand fileglobs (e.g. path/*.rb becomes [path/1.rb, path/2.rb]).
889
+ # This should happen after checking files.size to determine the target
890
+ files = files.collect { |file| Dir.glob file }.flatten unless @rye_safe
882
891
  end
883
892
 
884
893
  # Fail early. We check whether the StringIO object is available to read
@@ -891,21 +900,21 @@ module Rye
891
900
  end
892
901
  end
893
902
 
894
- debug "#{direction.to_s.upcase} TO: #{other}"
903
+ info "#{direction.to_s.upcase} TO: #{target}"
895
904
  debug "FILES: " << files.join(', ')
896
905
 
897
906
  # Make sure the remote directory exists. We can do this only when
898
- # there's more than one file because "other" could be a file name
899
- if files.size > 1 && !other.is_a?(StringIO)
900
- debug "CREATING TARGET DIRECTORY: #{other}"
901
- self.mkdir(:p, other) unless self.file_exists?(other)
907
+ # there's more than one file because "target" could be a file name
908
+ if files.size > 1 && !target.is_a?(StringIO)
909
+ debug "CREATING TARGET DIRECTORY: #{target}"
910
+ self.mkdir(:p, target) unless self.file_exists?(target)
902
911
  end
903
912
 
904
913
  Net::SCP.start(@rye_host, @rye_opts[:user], @rye_opts || {}) do |scp|
905
914
  transfers = []
906
915
  files.each do |file|
907
916
  debug file.to_s
908
- transfers << scp.send(direction, file, other) do |ch, n, s, t|
917
+ transfers << scp.send(direction, file, target) do |ch, n, s, t|
909
918
  pinfo "#{n}: #{s}/#{t}b\r" # update line: "file: sent/total"
910
919
  @rye_info.flush if @rye_info # make sure every line is printed
911
920
  end
@@ -914,7 +923,7 @@ module Rye
914
923
  info $/
915
924
  end
916
925
 
917
- other.is_a?(StringIO) ? other : nil
926
+ target.is_a?(StringIO) ? target : nil
918
927
  end
919
928
 
920
929
 
@@ -36,6 +36,7 @@ module Rye;
36
36
  def du(*args); cmd('du', args); end
37
37
 
38
38
  def env; cmd "env"; end
39
+ def rye(*args); cmd "rye", args; end
39
40
  def pwd(*args); cmd "pwd", args; end
40
41
  def svn(*args); cmd('svn', args); end
41
42
  def cvs(*args); cmd('cvs', args); end
@@ -50,6 +51,7 @@ module Rye;
50
51
  def grep(*args); cmd('grep', args); end
51
52
  def date(*args); cmd('date', args); end
52
53
  def ruby(*args); cmd('ruby', args); end
54
+ def rudy(*args); cmd('rudy', args); end
53
55
  def perl(*args); cmd('perl', args); end
54
56
  def bash(*args); cmd('bash', args); end
55
57
  def echo(*args); cmd('echo', args); end
@@ -77,8 +79,11 @@ module Rye;
77
79
  def bunzip2(*args); cmd('bunzip2', args); end
78
80
  def getconf(*args); cmd('getconf', args); end
79
81
  def history(*args); cmd('history', args); end
82
+ def rudy_s3(*args); cmd('rudy-s3', args); end
80
83
  def printenv(*args); cmd('printenv', args); end
81
84
  def hostname(*args); cmd('hostname', args); end
85
+ def rudy_ec2(*args); cmd('rudy-ec2', args); end
86
+ def rudy_edb(*args); cmd('rudy-sdb', args); end
82
87
  def configure(*args); cmd('./configure', args); end
83
88
 
84
89
  # Transfer files to a machine via Net::SCP.
@@ -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.8.0"
4
+ s.version = "0.8.1"
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"
@@ -35,7 +35,9 @@
35
35
  LICENSE.txt
36
36
  README.rdoc
37
37
  Rakefile
38
+ Rudyfile
38
39
  bin/rye
40
+ bin/try
39
41
  lib/esc.rb
40
42
  lib/rye.rb
41
43
  lib/rye/box.rb
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.8.0
4
+ version: 0.8.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -86,7 +86,9 @@ files:
86
86
  - LICENSE.txt
87
87
  - README.rdoc
88
88
  - Rakefile
89
+ - Rudyfile
89
90
  - bin/rye
91
+ - bin/try
90
92
  - lib/esc.rb
91
93
  - lib/rye.rb
92
94
  - lib/rye/box.rb