ey-deploy 0.7.0 → 0.7.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.
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