robot-army 0.1.8
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/LICENSE +13 -0
- data/README.markdown +34 -0
- data/Rakefile +9 -0
- data/examples/whoami.rb +13 -0
- data/lib/robot-army.rb +109 -0
- data/lib/robot-army/at_exit.rb +19 -0
- data/lib/robot-army/connection.rb +174 -0
- data/lib/robot-army/dependency_loader.rb +38 -0
- data/lib/robot-army/eval_builder.rb +84 -0
- data/lib/robot-army/eval_command.rb +17 -0
- data/lib/robot-army/gate_keeper.rb +28 -0
- data/lib/robot-army/io.rb +106 -0
- data/lib/robot-army/keychain.rb +10 -0
- data/lib/robot-army/loader.rb +85 -0
- data/lib/robot-army/marshal_ext.rb +52 -0
- data/lib/robot-army/messenger.rb +31 -0
- data/lib/robot-army/officer.rb +35 -0
- data/lib/robot-army/officer_connection.rb +5 -0
- data/lib/robot-army/officer_loader.rb +13 -0
- data/lib/robot-army/proxy.rb +35 -0
- data/lib/robot-army/remote_evaler.rb +59 -0
- data/lib/robot-army/ruby2ruby_ext.rb +19 -0
- data/lib/robot-army/soldier.rb +37 -0
- data/lib/robot-army/task_master.rb +317 -0
- data/spec/at_exit_spec.rb +25 -0
- data/spec/connection_spec.rb +126 -0
- data/spec/dependency_loader_spec.rb +46 -0
- data/spec/gate_keeper_spec.rb +46 -0
- data/spec/integration_spec.rb +40 -0
- data/spec/io_spec.rb +36 -0
- data/spec/keychain_spec.rb +15 -0
- data/spec/loader_spec.rb +13 -0
- data/spec/marshal_ext_spec.rb +89 -0
- data/spec/messenger_spec.rb +28 -0
- data/spec/officer_spec.rb +36 -0
- data/spec/proxy_spec.rb +52 -0
- data/spec/ruby2ruby_ext_spec.rb +67 -0
- data/spec/soldier_spec.rb +71 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/task_master_spec.rb +306 -0
- metadata +142 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe RobotArmy::Soldier do
|
4
|
+
before do
|
5
|
+
# given
|
6
|
+
@messenger = mock(:messenger)
|
7
|
+
@soldier = RobotArmy::Soldier.new(@messenger)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can accept eval commands" do
|
11
|
+
# then
|
12
|
+
@soldier.run(:eval, :code => '3+4', :file => __FILE__, :line => __LINE__).
|
13
|
+
should == 7
|
14
|
+
|
15
|
+
# and
|
16
|
+
@soldier.run(:eval, :code => 'Time.now', :file => __FILE__, :line => __LINE__).
|
17
|
+
should be_an_instance_of(Time)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "evaluates each command in the same process" do
|
21
|
+
# when
|
22
|
+
pid = proc{ @soldier.run(:eval, :code => 'Process.pid', :file => __FILE__, :line => __LINE__) }
|
23
|
+
|
24
|
+
# then
|
25
|
+
pid.call.should == pid.call
|
26
|
+
end
|
27
|
+
|
28
|
+
it "raises on unrecognized commands" do
|
29
|
+
proc{ @soldier.run(:foo, nil) }.should raise_error(ArgumentError)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "listens for commands from the messenger to run" do
|
33
|
+
# then
|
34
|
+
@soldier.should_receive(:run).with(:eval, :code => 'Hash.new')
|
35
|
+
|
36
|
+
# when
|
37
|
+
@messenger.stub!(:post)
|
38
|
+
@messenger.stub!(:get).and_return(:command => :eval, :data => {:code => 'Hash.new'})
|
39
|
+
@soldier.listen
|
40
|
+
end
|
41
|
+
|
42
|
+
it "posts through the messenger the result of commands run by listening" do
|
43
|
+
# then
|
44
|
+
@messenger.should_receive(:post).with(:status => 'ok', :data => 1)
|
45
|
+
|
46
|
+
# when
|
47
|
+
@messenger.stub!(:get).and_return(:command => :eval, :data => {:code => '1'})
|
48
|
+
@soldier.stub!(:run).and_return(1)
|
49
|
+
@soldier.listen
|
50
|
+
end
|
51
|
+
|
52
|
+
it "posts back and raises RobotArmy::Exit when running the exit command" do
|
53
|
+
@messenger.should_receive(:post).with(:status => 'ok')
|
54
|
+
proc{ @soldier.run(:exit, nil) }.should raise_error(RobotArmy::Exit)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns the pid and type when asked for info" do
|
58
|
+
@soldier.run(:info, nil).should == {:pid => Process.pid, :type => 'RobotArmy::Soldier'}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "posts back a warning if the :eval return value is not marshalable" do
|
62
|
+
# then
|
63
|
+
@messenger.should_receive(:post).
|
64
|
+
with(:status => 'warning', :data => "ignoring invalid remote return value #{$stdin.inspect}")
|
65
|
+
|
66
|
+
# when
|
67
|
+
@messenger.stub!(:get).and_return(
|
68
|
+
:command => :eval, :data => {:code => '$stdin', :file => __FILE__, :line => __LINE__})
|
69
|
+
@soldier.listen
|
70
|
+
end
|
71
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$TESTING=true
|
2
|
+
load File.join(File.dirname(__FILE__), '..', 'lib', 'robot-army.rb')
|
3
|
+
|
4
|
+
Spec::Runner.configure do |config|
|
5
|
+
def capture(stream)
|
6
|
+
begin
|
7
|
+
stream = stream.to_s
|
8
|
+
eval "$#{stream} = StringIO.new"
|
9
|
+
yield
|
10
|
+
result = eval("$#{stream}").string
|
11
|
+
ensure
|
12
|
+
eval("$#{stream} = #{stream.upcase}")
|
13
|
+
end
|
14
|
+
|
15
|
+
result
|
16
|
+
end
|
17
|
+
|
18
|
+
alias silence capture
|
19
|
+
end
|
@@ -0,0 +1,306 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
class Example < RobotArmy::TaskMaster
|
4
|
+
hosts %[www1.example.com www2.example.com]
|
5
|
+
end
|
6
|
+
|
7
|
+
class Localhost < RobotArmy::TaskMaster
|
8
|
+
host :localhost
|
9
|
+
end
|
10
|
+
|
11
|
+
describe RobotArmy::TaskMaster, 'host management' do
|
12
|
+
before do
|
13
|
+
@example = Example.new
|
14
|
+
end
|
15
|
+
|
16
|
+
it "allows setting a single host" do
|
17
|
+
Example.host 'example.com'
|
18
|
+
Example.host.should == 'example.com'
|
19
|
+
end
|
20
|
+
|
21
|
+
it "allows accessing multi-hosts when using the single-host interface" do
|
22
|
+
Example.host 'example.com'
|
23
|
+
Example.hosts.should == %w[example.com]
|
24
|
+
end
|
25
|
+
|
26
|
+
it "allows setting multiple hosts on the class" do
|
27
|
+
Example.hosts %w[example.com test.com]
|
28
|
+
Example.hosts.should == %w[example.com test.com]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "denies accessing a single host when using the multi-host interface" do
|
32
|
+
Example.hosts %w[example.com test.com]
|
33
|
+
proc { Example.host }.should raise_error(
|
34
|
+
RobotArmy::HostArityError, "There are 2 hosts, so calling host doesn't make sense")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "instances default to the hosts set on the class" do
|
38
|
+
Example.host 'example.com'
|
39
|
+
@example.host.should == 'example.com'
|
40
|
+
|
41
|
+
Example.hosts %w[example.com test.com]
|
42
|
+
@example.hosts.should == %w[example.com test.com]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "allows setting a single host on an instance" do
|
46
|
+
@example.host = 'example.com'
|
47
|
+
@example.host.should == 'example.com'
|
48
|
+
end
|
49
|
+
|
50
|
+
it "allows accessing multi-hosts when using the single-host interface on instances" do
|
51
|
+
@example.host = 'example.com'
|
52
|
+
@example.hosts.should == %w[example.com]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "allows setting multiple hosts on an instance" do
|
56
|
+
@example.hosts = %w[example.com test.com]
|
57
|
+
@example.hosts.should == %w[example.com test.com]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "denies accessing a single host when using the multi-host interface" do
|
61
|
+
@example.hosts = %w[example.com test.com test2.com]
|
62
|
+
proc { @example.host }.should raise_error(
|
63
|
+
RobotArmy::HostArityError, "There are 3 hosts, so calling host doesn't make sense")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe RobotArmy::TaskMaster, 'remote' do
|
68
|
+
before do
|
69
|
+
@localhost = Localhost.new
|
70
|
+
@example = Example.new
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns a single item when using the single-host interface" do
|
74
|
+
@localhost.stub!(:remote_eval).and_return(7)
|
75
|
+
@localhost.remote { 3+4 }.should == 7
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns an array of items when using the multi-host interface" do
|
79
|
+
@example.stub!(:remote_eval).and_return(7)
|
80
|
+
@example.remote { 3+4 }.should == [7, 7]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe RobotArmy::TaskMaster do
|
85
|
+
before do
|
86
|
+
@localhost = Localhost.new
|
87
|
+
@example = Example.new
|
88
|
+
end
|
89
|
+
|
90
|
+
it "runs a remote block on each host" do
|
91
|
+
@example.should_receive(:remote_eval).exactly(2).times
|
92
|
+
@example.remote { 3+4 }
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
it "can execute a Ruby block and return the result" do
|
97
|
+
@localhost.remote { 3+4 }.should == 7
|
98
|
+
end
|
99
|
+
|
100
|
+
it "executes its block in a different process" do
|
101
|
+
@localhost.remote { Process.pid }.should_not == Process.pid
|
102
|
+
end
|
103
|
+
|
104
|
+
it "preserves local variables" do
|
105
|
+
a = 42
|
106
|
+
@localhost.remote { a }.should == 42
|
107
|
+
end
|
108
|
+
|
109
|
+
it "warns about invalid remote return values" do
|
110
|
+
capture(:stderr) { @localhost.remote { $stdin } }.
|
111
|
+
should =~ /WARNING: ignoring invalid remote return value/
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns nil if the remote return value is invalid" do
|
115
|
+
silence(:stderr) { @localhost.remote { $stdin }.should be_nil }
|
116
|
+
end
|
117
|
+
|
118
|
+
it "re-raises exceptions thrown remotely" do
|
119
|
+
proc { @localhost.remote { raise ArgumentError, "You fool!" } }.
|
120
|
+
should raise_error(ArgumentError)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "prints the child Ruby's stderr to stderr" do
|
124
|
+
pending('we may not want to do this, even')
|
125
|
+
capture(:stderr) { @localhost.remote { $stderr.print "foo" } }.should == "foo"
|
126
|
+
end
|
127
|
+
|
128
|
+
it "runs multiple remote blocks for the same host in different processes" do
|
129
|
+
@localhost.remote { $a = 1 }
|
130
|
+
@localhost.remote { $a }.should be_nil
|
131
|
+
end
|
132
|
+
|
133
|
+
it "only loads one Officer process on the remote machine" do
|
134
|
+
info = @localhost.connection(@localhost.host).info
|
135
|
+
info[:pid].should_not == Process.pid
|
136
|
+
info[:type].should == 'RobotArmy::Officer'
|
137
|
+
@localhost.connection(@localhost.host).info.should == info
|
138
|
+
end
|
139
|
+
|
140
|
+
it "runs as a normal (non-super) user by default" do
|
141
|
+
@localhost.remote{ Process.uid }.should_not == 0
|
142
|
+
end
|
143
|
+
|
144
|
+
it "loads dependencies" do
|
145
|
+
@localhost.dependency "thor"
|
146
|
+
@localhost.remote { Thor ; 45 }.should == 45 # loading should not bail here
|
147
|
+
end
|
148
|
+
|
149
|
+
it "delegates scp to the scp binary" do
|
150
|
+
@localhost.should_receive(:`).with('scp -q file.tgz example.com:/tmp 2>&1')
|
151
|
+
@localhost.host = 'example.com'
|
152
|
+
@localhost.scp 'file.tgz', '/tmp'
|
153
|
+
end
|
154
|
+
|
155
|
+
it "delegates to scp without a host when host is localhost" do
|
156
|
+
@localhost.should_receive(:`).with('scp -q file.tgz /tmp 2>&1')
|
157
|
+
@localhost.scp 'file.tgz', '/tmp'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe RobotArmy::TaskMaster, 'scp' do
|
162
|
+
before do
|
163
|
+
@localhost = Localhost.new
|
164
|
+
end
|
165
|
+
|
166
|
+
it "raises if scp fails due to a permissions error" do
|
167
|
+
@localhost.stub!(:`).and_return("scp: /tmp/foo: Permission denied\n")
|
168
|
+
$?.stub!(:exitstatus).and_return(1)
|
169
|
+
lambda { @localhost.scp('foo', '/tmp') }.should raise_error(Errno::EACCES)
|
170
|
+
end
|
171
|
+
|
172
|
+
it "raises if scp cannot locate the source file" do
|
173
|
+
lambda { @localhost.scp('i-dont-exist', '/tmp') }.should raise_error(Errno::ENOENT)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe RobotArmy::TaskMaster, 'sh' do
|
178
|
+
before do
|
179
|
+
@localhost = Localhost.new
|
180
|
+
end
|
181
|
+
|
182
|
+
it "raises exceptions on failed commands" do
|
183
|
+
lambda {
|
184
|
+
@localhost.remote { sh 'which this-command-does-not-exist' }
|
185
|
+
}.should raise_error(RobotArmy::ShellCommandError)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "does not raise exceptions on successful commands" do
|
189
|
+
lambda {
|
190
|
+
@localhost.remote { sh 'echo foo' }
|
191
|
+
}.should_not raise_error(RobotArmy::ShellCommandError)
|
192
|
+
end
|
193
|
+
|
194
|
+
it "includes stderr from failed commands" do
|
195
|
+
begin
|
196
|
+
@localhost.remote { sh 'ruby -e "STDERR.puts %{stderr message}; exit(1)"' }
|
197
|
+
rescue RobotArmy::ShellCommandError => e
|
198
|
+
e.output.should == "stderr message\n"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it "includes stdout from failed commands" do
|
203
|
+
begin
|
204
|
+
@localhost.remote { sh 'ruby -e "STDOUT.puts %{stdout message}; exit(1)"' }
|
205
|
+
rescue RobotArmy::ShellCommandError => e
|
206
|
+
e.output.should == "stdout message\n"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
describe RobotArmy::TaskMaster, 'remote (with args)' do
|
212
|
+
before do
|
213
|
+
@localhost = Localhost.new
|
214
|
+
end
|
215
|
+
|
216
|
+
it "can pass arguments explicitly" do
|
217
|
+
@localhost.remote(:args => [42]) { |a| a }.should == 42
|
218
|
+
end
|
219
|
+
|
220
|
+
it "shadows local variables of the same name" do
|
221
|
+
a = 23
|
222
|
+
@localhost.remote(:args => [42]) { |a| a }.should == 42
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe RobotArmy::TaskMaster, 'cptemp' do
|
227
|
+
before do
|
228
|
+
@localhost = Localhost.new
|
229
|
+
@path = 'cptemp-spec-file'
|
230
|
+
File.open(@path, 'w') {|f| f << 'testing'}
|
231
|
+
end
|
232
|
+
|
233
|
+
it "safely copies to a new temporary directory" do
|
234
|
+
destination = @localhost.cptemp @path
|
235
|
+
File.read(destination).should == 'testing'
|
236
|
+
end
|
237
|
+
|
238
|
+
it "yields the path to each host if a block is passed" do
|
239
|
+
path, pid = @localhost.cptemp(@path) { |path| [path, Process.pid] }
|
240
|
+
File.basename(path).should == @path
|
241
|
+
pid.should_not be_nil
|
242
|
+
pid.should_not == Process.pid
|
243
|
+
end
|
244
|
+
|
245
|
+
it "deletes the file on exit" do
|
246
|
+
destination = @localhost.cptemp @path
|
247
|
+
RobotArmy::AtExit.shared_instance.do_exit
|
248
|
+
fail "Expected cptemp'ed file to be deleted when exit callbacks were run" if File.exist?(destination)
|
249
|
+
end
|
250
|
+
|
251
|
+
after do
|
252
|
+
FileUtils.rm_f(@path)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
describe RobotArmy::TaskMaster, 'with proxies' do
|
257
|
+
before do
|
258
|
+
@localhost = Localhost.new
|
259
|
+
end
|
260
|
+
|
261
|
+
it "can allow remote method calls on the local object" do
|
262
|
+
def @localhost.foo; 'bar'; end
|
263
|
+
@localhost.remote { foo }.should == 'bar'
|
264
|
+
end
|
265
|
+
|
266
|
+
it "allows calling methods with arguments" do
|
267
|
+
def @localhost.echo(o) o; end
|
268
|
+
@localhost.remote { echo 42 }.should == 42
|
269
|
+
end
|
270
|
+
|
271
|
+
it "allows passing a block to method calls on proxy objects" do
|
272
|
+
pending('this is insane. should I do this?')
|
273
|
+
end
|
274
|
+
|
275
|
+
it "allows interaction with IOs" do
|
276
|
+
capture(:stdout) {
|
277
|
+
stdout = $stdout
|
278
|
+
@localhost.remote { stdout.puts "hey there" }
|
279
|
+
}.should == "hey there\n"
|
280
|
+
end
|
281
|
+
|
282
|
+
it "returns a proxy if the return value of an upstream call can't be marshaled" do
|
283
|
+
def @localhost.stdout; $stdout; end
|
284
|
+
capture(:stdout) { @localhost.remote { stdout.puts "foo" } }.should == "foo\n"
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
describe RobotArmy::TaskMaster, 'sudo' do
|
289
|
+
before do
|
290
|
+
@localhost = Localhost.new
|
291
|
+
end
|
292
|
+
|
293
|
+
it "runs remote with the root user by default" do
|
294
|
+
@localhost.should_receive(:remote).
|
295
|
+
with(@localhost.hosts, :user => 'root')
|
296
|
+
|
297
|
+
@localhost.sudo { File.read('/etc/passwd') }
|
298
|
+
end
|
299
|
+
|
300
|
+
it "allows specifying a particular user" do
|
301
|
+
@localhost.should_receive(:remote).
|
302
|
+
with(@localhost.hosts, :user => 'www-data')
|
303
|
+
|
304
|
+
@localhost.sudo(:user => 'www-data') { %x{/etc/init.d/apache2 restart} }
|
305
|
+
end
|
306
|
+
end
|
metadata
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: robot-army
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 8
|
9
|
+
version: 0.1.8
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Brian Donovan
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-04-26 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ParseTree
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 3
|
29
|
+
- 0
|
30
|
+
- 0
|
31
|
+
version: 3.0.0
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: ruby2ruby
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 2
|
44
|
+
- 0
|
45
|
+
version: 1.2.0
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: thor
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
- 11
|
58
|
+
- 7
|
59
|
+
version: 0.11.7
|
60
|
+
type: :runtime
|
61
|
+
version_requirements: *id003
|
62
|
+
description: Deploy using Thor by executing Ruby remotely
|
63
|
+
email: brian@wesabe.com
|
64
|
+
executables: []
|
65
|
+
|
66
|
+
extensions: []
|
67
|
+
|
68
|
+
extra_rdoc_files:
|
69
|
+
- LICENSE
|
70
|
+
- README.markdown
|
71
|
+
files:
|
72
|
+
- LICENSE
|
73
|
+
- README.markdown
|
74
|
+
- Rakefile
|
75
|
+
- lib/robot-army.rb
|
76
|
+
- lib/robot-army/at_exit.rb
|
77
|
+
- lib/robot-army/connection.rb
|
78
|
+
- lib/robot-army/dependency_loader.rb
|
79
|
+
- lib/robot-army/eval_builder.rb
|
80
|
+
- lib/robot-army/eval_command.rb
|
81
|
+
- lib/robot-army/gate_keeper.rb
|
82
|
+
- lib/robot-army/io.rb
|
83
|
+
- lib/robot-army/keychain.rb
|
84
|
+
- lib/robot-army/loader.rb
|
85
|
+
- lib/robot-army/marshal_ext.rb
|
86
|
+
- lib/robot-army/messenger.rb
|
87
|
+
- lib/robot-army/officer.rb
|
88
|
+
- lib/robot-army/officer_connection.rb
|
89
|
+
- lib/robot-army/officer_loader.rb
|
90
|
+
- lib/robot-army/proxy.rb
|
91
|
+
- lib/robot-army/remote_evaler.rb
|
92
|
+
- lib/robot-army/ruby2ruby_ext.rb
|
93
|
+
- lib/robot-army/soldier.rb
|
94
|
+
- lib/robot-army/task_master.rb
|
95
|
+
has_rdoc: true
|
96
|
+
homepage: http://github.com/wesabe/robot-army
|
97
|
+
licenses: []
|
98
|
+
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options:
|
101
|
+
- --charset=UTF-8
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
segments:
|
109
|
+
- 0
|
110
|
+
version: "0"
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
segments:
|
116
|
+
- 0
|
117
|
+
version: "0"
|
118
|
+
requirements: []
|
119
|
+
|
120
|
+
rubyforge_project: robot-army
|
121
|
+
rubygems_version: 1.3.6
|
122
|
+
signing_key:
|
123
|
+
specification_version: 3
|
124
|
+
summary: Deploy using Thor by executing Ruby remotely
|
125
|
+
test_files:
|
126
|
+
- spec/at_exit_spec.rb
|
127
|
+
- spec/connection_spec.rb
|
128
|
+
- spec/dependency_loader_spec.rb
|
129
|
+
- spec/gate_keeper_spec.rb
|
130
|
+
- spec/integration_spec.rb
|
131
|
+
- spec/io_spec.rb
|
132
|
+
- spec/keychain_spec.rb
|
133
|
+
- spec/loader_spec.rb
|
134
|
+
- spec/marshal_ext_spec.rb
|
135
|
+
- spec/messenger_spec.rb
|
136
|
+
- spec/officer_spec.rb
|
137
|
+
- spec/proxy_spec.rb
|
138
|
+
- spec/ruby2ruby_ext_spec.rb
|
139
|
+
- spec/soldier_spec.rb
|
140
|
+
- spec/spec_helper.rb
|
141
|
+
- spec/task_master_spec.rb
|
142
|
+
- examples/whoami.rb
|