ey-deploy 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/lib/ey-deploy.rb +4 -0
  2. data/lib/ey-deploy/cli.rb +27 -16
  3. data/lib/ey-deploy/deploy.rb +13 -13
  4. data/lib/ey-deploy/deploy_hook.rb +13 -6
  5. data/lib/ey-deploy/logged_output.rb +58 -0
  6. data/lib/ey-deploy/server.rb +5 -5
  7. data/lib/ey-deploy/strategies/git.rb +5 -5
  8. data/lib/ey-deploy/task.rb +6 -12
  9. data/lib/ey-deploy/version.rb +1 -1
  10. data/lib/vendor/dataflow/HISTORY +52 -0
  11. data/lib/vendor/dataflow/LICENSE +19 -0
  12. data/lib/vendor/dataflow/README.textile +290 -0
  13. data/lib/vendor/dataflow/Rakefile +36 -0
  14. data/lib/vendor/dataflow/dataflow.rb +120 -0
  15. data/lib/vendor/dataflow/dataflow/actor.rb +22 -0
  16. data/lib/vendor/dataflow/dataflow/equality.rb +28 -0
  17. data/lib/vendor/dataflow/dataflow/future_queue.rb +24 -0
  18. data/lib/vendor/dataflow/dataflow/port.rb +54 -0
  19. data/lib/vendor/dataflow/examples/barrier.rb +9 -0
  20. data/lib/vendor/dataflow/examples/data_driven.rb +17 -0
  21. data/lib/vendor/dataflow/examples/dataflow_http_gets.rb +13 -0
  22. data/lib/vendor/dataflow/examples/flow.rb +20 -0
  23. data/lib/vendor/dataflow/examples/future_http_gets.rb +12 -0
  24. data/lib/vendor/dataflow/examples/future_queue.rb +11 -0
  25. data/lib/vendor/dataflow/examples/instance_variables.rb +15 -0
  26. data/lib/vendor/dataflow/examples/laziness.rb +9 -0
  27. data/lib/vendor/dataflow/examples/local_variables.rb +11 -0
  28. data/lib/vendor/dataflow/examples/messages.rb +26 -0
  29. data/lib/vendor/dataflow/examples/port_http_gets.rb +13 -0
  30. data/lib/vendor/dataflow/examples/port_send.rb +10 -0
  31. data/lib/vendor/dataflow/examples/ring.rb +21 -0
  32. data/lib/vendor/dataflow/spec/actor_spec.rb +28 -0
  33. data/lib/vendor/dataflow/spec/anonymous_variables_spec.rb +21 -0
  34. data/lib/vendor/dataflow/spec/barrier_spec.rb +25 -0
  35. data/lib/vendor/dataflow/spec/by_need_spec.rb +55 -0
  36. data/lib/vendor/dataflow/spec/dataflow_spec.rb +151 -0
  37. data/lib/vendor/dataflow/spec/equality_spec.rb +40 -0
  38. data/lib/vendor/dataflow/spec/flow_spec.rb +25 -0
  39. data/lib/vendor/dataflow/spec/forker_spec.rb +28 -0
  40. data/lib/vendor/dataflow/spec/future_queue_spec.rb +31 -0
  41. data/lib/vendor/dataflow/spec/inspect_spec.rb +19 -0
  42. data/lib/vendor/dataflow/spec/need_later_spec.rb +12 -0
  43. data/lib/vendor/dataflow/spec/port_spec.rb +26 -0
  44. data/lib/vendor/dataflow/spec/spec.opts +1 -0
  45. data/lib/vendor/dataflow/spec/spec_helper.rb +10 -0
  46. data/lib/vendor/open4/lib/open4.rb +403 -0
  47. data/spec/deploy_hook_spec.rb +16 -2
  48. data/spec/fixtures/invalid_hook.rb +1 -0
  49. data/spec/fixtures/valid_hook.rb +1 -0
  50. metadata +43 -4
  51. data/lib/ey-deploy/verbose_system.rb +0 -12
@@ -0,0 +1,151 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ context 'Using "local" for local variables' do
4
+ describe 'An unbound Variable' do
5
+ it 'suspends if an unbound variable has a method called on it until it is bound' do
6
+ local do |big_cat, small_cat|
7
+ Thread.new { unify big_cat, small_cat.upcase }
8
+ unify small_cat, 'cat'
9
+ big_cat.should == 'CAT'
10
+ end
11
+ end
12
+
13
+ it 'suspends if an unbound variable has a method called on it until it is bound with nil' do
14
+ local do |is_nil, var_nil|
15
+ Thread.new { unify is_nil, var_nil.nil? }
16
+ unify var_nil, nil
17
+ is_nil.should be_true
18
+ end
19
+ end
20
+
21
+ it 'suspends if an unbound variable has a method called on it until it is bound (with nested local variables)' do
22
+ local do |small_cat|
23
+ local do |big_cat|
24
+ Thread.new { unify big_cat, small_cat.upcase }
25
+ unify small_cat, 'cat'
26
+ big_cat.should == 'CAT'
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'performs order-determining concurrency' do
32
+ local do |x, y, z|
33
+ Thread.new { unify y, x + 2 }
34
+ Thread.new { unify z, y + 3 }
35
+ Thread.new { unify x, 1 }
36
+ z.should == 6
37
+ end
38
+ end
39
+
40
+ it 'binds on unification' do
41
+ local do |animal|
42
+ unify animal, 'cat'
43
+ animal.should == 'cat'
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ describe 'A bound Variable' do
50
+ it 'does not complain when unifying with an equal object' do
51
+ lambda do
52
+ local do |animal|
53
+ unify animal, 'cat'
54
+ unify animal, 'cat'
55
+ end
56
+ end.should_not raise_error
57
+ end
58
+
59
+ it 'does not complain when unifying with an unequal object when shadowing' do
60
+ lambda do
61
+ local do |animal|
62
+ unify animal, 'cat'
63
+ local do |animal|
64
+ unify animal, 'dog'
65
+ end
66
+ end
67
+ end.should_not raise_error
68
+ end
69
+
70
+ it 'complains when unifying with an unequal object' do
71
+ lambda do
72
+ local do |animal|
73
+ unify animal, 'cat'
74
+ unify animal, 'dog'
75
+ end
76
+ end.should raise_error(Dataflow::UnificationError)
77
+ end
78
+ end
79
+
80
+ context 'Using "declare" for object-specific read-only attributes' do
81
+ class Store
82
+ include Dataflow
83
+ declare :animal, :big_cat, :small_cat, :x, :y, :z, :is_nil, :var_nil
84
+ end
85
+ before { @store = Store.new }
86
+
87
+ describe 'An unbound Variable' do
88
+ it 'suspends if an unbound variable has a method called on it until it is bound' do
89
+ Thread.new { unify @store.big_cat, @store.small_cat.upcase }
90
+ unify @store.small_cat, 'cat'
91
+ @store.big_cat.should == 'CAT'
92
+ end
93
+
94
+ it 'suspends if an unbound variable has a method called on it until it is bound with nil' do
95
+ Thread.new { unify @store.is_nil, @store.var_nil.nil? }
96
+ unify @store.var_nil, nil
97
+ @store.is_nil.should be_true
98
+ end
99
+
100
+ it 'performs order-determining concurrency' do
101
+ Thread.new { unify @store.y, @store.x + 2 }
102
+ Thread.new { unify @store.z, @store.y + 3 }
103
+ Thread.new { unify @store.x, 1 }
104
+ @store.z.should == 6
105
+ end
106
+
107
+ it 'binds on unification' do
108
+ unify @store.animal, 'cat'
109
+ @store.animal.should == 'cat'
110
+ end
111
+ end
112
+
113
+ describe 'A bound Variable' do
114
+ it 'does not complain when unifying with an equal object' do
115
+ lambda do
116
+ unify @store.animal, 'cat'
117
+ unify @store.animal, 'cat'
118
+ end.should_not raise_error
119
+ end
120
+
121
+ it 'complains when unifying with an unequal object' do
122
+ lambda do
123
+ unify @store.animal, 'cat'
124
+ unify @store.animal, 'dog'
125
+ end.should raise_error(Dataflow::UnificationError)
126
+ end
127
+ end
128
+ end
129
+
130
+ describe 'Binding a variable that proxies through another' do
131
+ it 'binds through successfully' do
132
+ local do |x, y|
133
+ lambda do
134
+ unify x, y
135
+ unify x, 1337
136
+ x.should == 1337
137
+ y.should == 1337
138
+ end.should_not raise_error
139
+ end
140
+ end
141
+ end
142
+
143
+ describe 'Using static/module method' do
144
+ it 'works like the mixin versions' do
145
+ Dataflow.local do |big_cat, small_cat|
146
+ Thread.new { Dataflow.unify big_cat, small_cat.upcase }
147
+ Dataflow.unify small_cat, 'cat'
148
+ big_cat.should == 'CAT'
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,40 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Unifying a bound value' do
4
+ [nil, true, false,
5
+ :sym, "str", /regex/,
6
+ 3, 2.0,
7
+ Object.new, Class.new.new,
8
+ [], {}].each do |type|
9
+ describe "for #{type.class} instances" do
10
+ it 'passes unification for an object of equal value' do
11
+ local do |var, var2|
12
+ unify var, type
13
+ var.should == type
14
+ type.should == var
15
+ lambda {unify var, type}.should_not raise_error
16
+
17
+ unify var2, type
18
+ var.should == var2
19
+ var2.should == var
20
+ lambda {unify var, var2}.should_not raise_error
21
+ end
22
+ end
23
+
24
+ it 'fails unification for an object of inequal value' do
25
+ different = Object.new
26
+ local do |var, var2|
27
+ unify var, type
28
+ var.should_not == different
29
+ different.should_not == var
30
+ lambda {unify var, different}.should raise_error(Dataflow::UnificationError)
31
+
32
+ unify var2, different
33
+ var.should_not == var2
34
+ var2.should_not == var
35
+ lambda {unify var, different}.should raise_error(Dataflow::UnificationError)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,25 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Using flow without a parameter' do
4
+ it 'works like normal threads' do
5
+ local do |big_cat, small_cat|
6
+ flow { unify big_cat, small_cat.upcase }
7
+ unify small_cat, 'cat'
8
+ big_cat.should == 'CAT'
9
+ end
10
+ end
11
+ end
12
+
13
+ describe 'Using flow with a parameter' do
14
+ it 'binds the parameter to the last line of the block' do
15
+ local do |big_cat, small_cat, output|
16
+ flow(output) do
17
+ unify big_cat, small_cat.upcase
18
+ 'returned'
19
+ end
20
+ unify small_cat, 'cat'
21
+ big_cat.should == 'CAT'
22
+ output.should == 'returned'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Setting a customer forker' do
4
+ before(:all) do
5
+ @original_forker = Dataflow.forker
6
+ Dataflow.forker = Class.new do
7
+ def self.synchronous_forker(&block)
8
+ block.call
9
+ end
10
+ end.method(:synchronous_forker)
11
+ end
12
+
13
+ after(:all) do
14
+ Dataflow.forker = @original_forker
15
+ end
16
+
17
+ it 'uses the custom forker in #flow' do
18
+ local do |my_var|
19
+ flow(my_var) { 1337 }
20
+ my_var.should == 1337
21
+ end
22
+ end
23
+
24
+ it 'uses the custom forker in #need_later' do
25
+ my_var = need_later { 1337 }
26
+ my_var.should == 1337
27
+ end
28
+ end
@@ -0,0 +1,31 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'A future queue' do
4
+ it 'accepts synchronous pushes and pops' do
5
+ local do |queue, first, second, third|
6
+ unify queue, Dataflow::FutureQueue.new
7
+ queue.pop first
8
+ queue.pop second
9
+ queue.push 1
10
+ queue.push 2
11
+ queue.push 3
12
+ queue.pop third
13
+
14
+ [first, second, third].should == [1, 2, 3]
15
+ end
16
+ end
17
+
18
+ it 'accepts asynchronous pushes and pops' do
19
+ local do |queue, first, second, third|
20
+ unify queue, Dataflow::FutureQueue.new
21
+ Thread.new { queue.pop first }
22
+ Thread.new { queue.pop second }
23
+ Thread.new { queue.push 1 }
24
+ Thread.new { queue.push 2 }
25
+ Thread.new { queue.push 3 }
26
+ Thread.new { queue.pop third }
27
+
28
+ [first, second, third].sort.should == [1, 2, 3]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,19 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe "An unbound variable" do
4
+ it "should be inspectable for debugging purposes" do
5
+ local do |unbound|
6
+ unbound.inspect.should =~ /#<Dataflow::Variable:[0-9]+ unbound>/
7
+ end
8
+ end
9
+ end
10
+
11
+ describe "An bound variable" do
12
+ it "should proxy inspect" do
13
+ local do |bound|
14
+ bound.inspect
15
+ unify bound, "str"
16
+ bound.should == "str"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'A need_later expression' do
4
+ it 'returns a future to be calculated later' do
5
+ local do |x, y, z|
6
+ unify y, need_later { 4 }
7
+ unify z, need_later { x + y }
8
+ unify x, need_later { 3 }
9
+ z.should == 7
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,26 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Syncronously sending to a Port' do
4
+ it 'extends the length of the stream and preserves order' do
5
+ local do |port, stream|
6
+ unify port, Dataflow::Port.new(stream)
7
+ port.send 1
8
+ port.send 2
9
+ stream.take(2).should == [1, 2]
10
+ port.send 3
11
+ stream.take(3).should == [1, 2, 3]
12
+ end
13
+ end
14
+ end
15
+
16
+ describe 'Asyncronously sending to a Port' do
17
+ it 'extends the length of the stream and does not preserve order' do
18
+ local do |port, stream|
19
+ unify port, Dataflow::Port.new(stream)
20
+ Thread.new {port.send 2}
21
+ Thread.new {port.send 8}
22
+ Thread.new {port.send 1024}
23
+ stream.take(3).sort.should == [2, 8, 1024]
24
+ end
25
+ end
26
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require "#{File.dirname(__FILE__)}/../dataflow"
4
+ require "#{File.dirname(__FILE__)}/../dataflow/equality"
5
+
6
+ Thread.abort_on_exception = true
7
+
8
+ Spec::Runner.configure do |config|
9
+ config.include Dataflow
10
+ end
@@ -0,0 +1,403 @@
1
+ # vim: ts=2:sw=2:sts=2:et:fdm=marker
2
+ require 'fcntl'
3
+ require 'timeout'
4
+ require 'thread'
5
+
6
+ module Open4
7
+ #--{{{
8
+ VERSION = '1.1.0'
9
+ def self.version() VERSION end
10
+
11
+ class Error < ::StandardError; end
12
+
13
+ def popen4(*cmd, &b)
14
+ #--{{{
15
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
16
+
17
+ verbose = $VERBOSE
18
+ begin
19
+ $VERBOSE = nil
20
+ ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
21
+ ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
22
+
23
+ cid = fork {
24
+ pw.last.close
25
+ STDIN.reopen pw.first
26
+ pw.first.close
27
+
28
+ pr.first.close
29
+ STDOUT.reopen pr.last
30
+ pr.last.close
31
+
32
+ pe.first.close
33
+ STDERR.reopen pe.last
34
+ pe.last.close
35
+
36
+ STDOUT.sync = STDERR.sync = true
37
+
38
+ begin
39
+ exec(*cmd)
40
+ raise 'forty-two'
41
+ rescue Exception => e
42
+ Marshal.dump(e, ps.last)
43
+ ps.last.flush
44
+ end
45
+ ps.last.close unless (ps.last.closed?)
46
+ exit!
47
+ }
48
+ ensure
49
+ $VERBOSE = verbose
50
+ end
51
+
52
+ [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
53
+
54
+ begin
55
+ e = Marshal.load ps.first
56
+ raise(Exception === e ? e : "unknown failure!")
57
+ rescue EOFError # If we get an EOF error, then the exec was successful
58
+ 42
59
+ ensure
60
+ ps.first.close
61
+ end
62
+
63
+ pw.last.sync = true
64
+
65
+ pi = [pw.last, pr.first, pe.first]
66
+
67
+ if b
68
+ begin
69
+ b[cid, *pi]
70
+ Process.waitpid2(cid).last
71
+ ensure
72
+ pi.each{|fd| fd.close unless fd.closed?}
73
+ end
74
+ else
75
+ [cid, pw.last, pr.first, pe.first]
76
+ end
77
+ #--}}}
78
+ end
79
+ alias open4 popen4
80
+ module_function :popen4
81
+ module_function :open4
82
+
83
+ class SpawnError < Error
84
+ #--{{{
85
+ attr 'cmd'
86
+ attr 'status'
87
+ attr 'signals'
88
+ def exitstatus
89
+ @status.exitstatus
90
+ end
91
+ def initialize cmd, status
92
+ @cmd, @status = cmd, status
93
+ @signals = {}
94
+ if status.signaled?
95
+ @signals['termsig'] = status.termsig
96
+ @signals['stopsig'] = status.stopsig
97
+ end
98
+ sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
99
+ super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
100
+ end
101
+ #--}}}
102
+ end
103
+
104
+ class ThreadEnsemble
105
+ #--{{{
106
+ attr 'threads'
107
+
108
+ def initialize cid
109
+ @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false
110
+ @killed = false
111
+ end
112
+
113
+ def add_thread *a, &b
114
+ @running ? raise : (@argv << [a, b])
115
+ end
116
+
117
+ #
118
+ # take down process more nicely
119
+ #
120
+ def killall
121
+ c = Thread.critical
122
+ return nil if @killed
123
+ Thread.critical = true
124
+ (@threads - [Thread.current]).each{|t| t.kill rescue nil}
125
+ @killed = true
126
+ ensure
127
+ Thread.critical = c
128
+ end
129
+
130
+ def run
131
+ @running = true
132
+
133
+ begin
134
+ @argv.each do |a, b|
135
+ @threads << Thread.new(*a) do |*a|
136
+ begin
137
+ b[*a]
138
+ ensure
139
+ killall rescue nil if $!
140
+ @done.push Thread.current
141
+ end
142
+ end
143
+ end
144
+ rescue
145
+ killall
146
+ raise
147
+ ensure
148
+ all_done
149
+ end
150
+
151
+ @threads.map{|t| t.value}
152
+ end
153
+
154
+ def all_done
155
+ @threads.size.times{ @done.pop }
156
+ end
157
+ #--}}}
158
+ end
159
+
160
+ def to timeout = nil
161
+ #--{{{
162
+ Timeout.timeout(timeout){ yield }
163
+ #--}}}
164
+ end
165
+ module_function :to
166
+
167
+ def new_thread *a, &b
168
+ #--{{{
169
+ cur = Thread.current
170
+ Thread.new(*a) do |*a|
171
+ begin
172
+ b[*a]
173
+ rescue Exception => e
174
+ cur.raise e
175
+ end
176
+ end
177
+ #--}}}
178
+ end
179
+ module_function :new_thread
180
+
181
+ def getopts opts = {}
182
+ #--{{{
183
+ lambda do |*args|
184
+ keys, default, ignored = args
185
+ catch('opt') do
186
+ [keys].flatten.each do |key|
187
+ [key, key.to_s, key.to_s.intern].each do |key|
188
+ throw 'opt', opts[key] if opts.has_key?(key)
189
+ end
190
+ end
191
+ default
192
+ end
193
+ end
194
+ #--}}}
195
+ end
196
+ module_function :getopts
197
+
198
+ def relay src, dst = nil, t = nil
199
+ #--{{{
200
+ send_dst =
201
+ if dst.respond_to?(:call)
202
+ lambda{|buf| dst.call(buf)}
203
+ elsif dst.respond_to?(:<<)
204
+ lambda{|buf| dst << buf }
205
+ else
206
+ lambda{|buf| buf }
207
+ end
208
+
209
+ unless src.nil?
210
+ if src.respond_to? :gets
211
+ while buf = to(t){ src.gets }
212
+ send_dst[buf]
213
+ end
214
+
215
+ elsif src.respond_to? :each
216
+ q = Queue.new
217
+ th = nil
218
+
219
+ timer_set = lambda do |t|
220
+ th = new_thread{ to(t){ q.pop } }
221
+ end
222
+
223
+ timer_cancel = lambda do |t|
224
+ th.kill if th rescue nil
225
+ end
226
+
227
+ timer_set[t]
228
+ begin
229
+ src.each do |buf|
230
+ timer_cancel[t]
231
+ send_dst[buf]
232
+ timer_set[t]
233
+ end
234
+ ensure
235
+ timer_cancel[t]
236
+ end
237
+
238
+ elsif src.respond_to? :read
239
+ buf = to(t){ src.read }
240
+ send_dst[buf]
241
+
242
+ else
243
+ buf = to(t){ src.to_s }
244
+ send_dst[buf]
245
+ end
246
+ end
247
+ #--}}}
248
+ end
249
+ module_function :relay
250
+
251
+ def spawn arg, *argv
252
+ #--{{{
253
+ argv.unshift(arg)
254
+ opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
255
+ argv.flatten!
256
+ cmd = argv.join(' ')
257
+
258
+
259
+ getopt = getopts opts
260
+
261
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
262
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
263
+ exitstatus = getopt[ %w( exitstatus exit_status status ) ]
264
+ stdin = getopt[ %w( stdin in i 0 ) << 0 ]
265
+ stdout = getopt[ %w( stdout out o 1 ) << 1 ]
266
+ stderr = getopt[ %w( stderr err e 2 ) << 2 ]
267
+ pid = getopt[ 'pid' ]
268
+ timeout = getopt[ %w( timeout spawn_timeout ) ]
269
+ stdin_timeout = getopt[ %w( stdin_timeout ) ]
270
+ stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
271
+ stderr_timeout = getopt[ %w( stderr_timeout ) ]
272
+ status = getopt[ %w( status ) ]
273
+ cwd = getopt[ %w( cwd dir ) ]
274
+
275
+ exitstatus =
276
+ case exitstatus
277
+ when TrueClass, FalseClass
278
+ ignore_exit_failure = true if exitstatus
279
+ [0]
280
+ else
281
+ [*(exitstatus || 0)].map{|i| Integer i}
282
+ end
283
+
284
+ stdin ||= '' if stdin_timeout
285
+ stdout ||= '' if stdout_timeout
286
+ stderr ||= '' if stderr_timeout
287
+
288
+ started = false
289
+
290
+ status =
291
+ begin
292
+ chdir(cwd) do
293
+ Timeout::timeout(timeout) do
294
+ popen4(*argv) do |c, i, o, e|
295
+ started = true
296
+
297
+ %w( replace pid= << push update ).each do |msg|
298
+ break(pid.send(msg, c)) if pid.respond_to? msg
299
+ end
300
+
301
+ te = ThreadEnsemble.new c
302
+
303
+ te.add_thread(i, stdin) do |i, stdin|
304
+ relay stdin, i, stdin_timeout
305
+ i.close rescue nil
306
+ end
307
+
308
+ te.add_thread(o, stdout) do |o, stdout|
309
+ relay o, stdout, stdout_timeout
310
+ end
311
+
312
+ te.add_thread(e, stderr) do |o, stderr|
313
+ relay e, stderr, stderr_timeout
314
+ end
315
+
316
+ te.run
317
+ end
318
+ end
319
+ end
320
+ rescue
321
+ raise unless(not started and ignore_exec_failure)
322
+ end
323
+
324
+ raise SpawnError.new(cmd, status) unless
325
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
326
+
327
+ status
328
+ #--}}}
329
+ end
330
+ module_function :spawn
331
+
332
+ def chdir cwd, &block
333
+ return(block.call Dir.pwd) unless cwd
334
+ Dir.chdir cwd, &block
335
+ end
336
+ module_function :chdir
337
+
338
+ def background arg, *argv
339
+ #--{{{
340
+ require 'thread'
341
+ q = Queue.new
342
+ opts = { 'pid' => q, :pid => q }
343
+ case argv.last
344
+ when Hash
345
+ argv.last.update opts
346
+ else
347
+ argv.push opts
348
+ end
349
+ thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv}
350
+ sc = class << thread; self; end
351
+ sc.module_eval {
352
+ define_method(:pid){ @pid ||= q.pop }
353
+ define_method(:spawn_status){ @spawn_status ||= value }
354
+ define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
355
+ }
356
+ thread
357
+ #--}}}
358
+ end
359
+ alias bg background
360
+ module_function :background
361
+ module_function :bg
362
+
363
+ def maim pid, opts = {}
364
+ #--{{{
365
+ getopt = getopts opts
366
+ sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
367
+ suspend = getopt[ 'suspend', 4 ]
368
+ pid = Integer pid
369
+ existed = false
370
+ sigs.each do |sig|
371
+ begin
372
+ Process.kill sig, pid
373
+ existed = true
374
+ rescue Errno::ESRCH
375
+ return(existed ? nil : true)
376
+ end
377
+ return true unless alive? pid
378
+ sleep suspend
379
+ return true unless alive? pid
380
+ end
381
+ return(not alive?(pid))
382
+ #--}}}
383
+ end
384
+ module_function :maim
385
+
386
+ def alive pid
387
+ #--{{{
388
+ pid = Integer pid
389
+ begin
390
+ Process.kill 0, pid
391
+ true
392
+ rescue Errno::ESRCH
393
+ false
394
+ end
395
+ #--}}}
396
+ end
397
+ alias alive? alive
398
+ module_function :alive
399
+ module_function :'alive?'
400
+ #--}}}
401
+ end
402
+
403
+ def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end