childprocess 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/README.md +11 -0
- data/childprocess.gemspec +1 -1
- data/lib/childprocess/jruby/process.rb +7 -2
- data/lib/childprocess/version.rb +1 -1
- data/spec/abstract_io_spec.rb +2 -2
- data/spec/childprocess_spec.rb +25 -24
- data/spec/io_spec.rb +13 -13
- data/spec/jruby_spec.rb +2 -2
- data/spec/pid_behavior.rb +1 -1
- data/spec/spec_helper.rb +2 -2
- data/spec/unix_spec.rb +14 -14
- data/spec/windows_spec.rb +2 -2
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77e7009c5202bfecb17e331b56d15c79b92d33b0
|
4
|
+
data.tar.gz: 343d119d1130d05fe395d5ee2631aeb1a50866a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f84deda774210ec0d133c08095389c212770ed6afa58b37bddef82a7ca0513e76fcdcd3aaeb0ea4ca0e878329c160a7d9ba4770f19faceb472d9dbe9333cc45a
|
7
|
+
data.tar.gz: 7ded6c7fa7364c4c5fe423c7a06e671dce947e6290e62fb3d4be7d62554be1339e0e706159ee9fc4350c0a3a1fc4f5f608122c2c281780485793523d42dcfb5f
|
data/.travis.yml
CHANGED
@@ -6,8 +6,8 @@ rvm:
|
|
6
6
|
- 2.1.0
|
7
7
|
- ruby-head
|
8
8
|
env:
|
9
|
-
- CHILDPROCESS_POSIX_SPAWN=true
|
10
|
-
- CHILDPROCESS_POSIX_SPAWN=false
|
9
|
+
- CHILDPROCESS_POSIX_SPAWN=true CHILDPROCESS_UNSET=should-be-unset
|
10
|
+
- CHILDPROCESS_POSIX_SPAWN=false CHILDPROCESS_UNSET=should-be-unset
|
11
11
|
matrix:
|
12
12
|
allow_failures:
|
13
13
|
- rvm: rbx
|
data/README.md
CHANGED
@@ -142,6 +142,17 @@ process.detach = true
|
|
142
142
|
process.start
|
143
143
|
```
|
144
144
|
|
145
|
+
#### Invoking a shell
|
146
|
+
|
147
|
+
As opposed to `Kernel#system`, `Kernel#exec` et al., ChildProcess will not automatically execute your command in a shell (like `/bin/sh` or `cmd.exe`) depending on the arguments.
|
148
|
+
This means that if you try to execute e.g. gem executables (like `bundle` or `gem`) or Windows executables (with `.com` or `.bat` extensions) you may see a `ChildProcess::LaunchError`.
|
149
|
+
You can work around this by being explicit about what interpreter to invoke:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
ChildProcess.build("cmd.exe", "/c", "bundle")
|
153
|
+
ChildProcess.build("ruby", "-S", "bundle")
|
154
|
+
```
|
155
|
+
|
145
156
|
# Implementation
|
146
157
|
|
147
158
|
How the process is launched and killed depends on the platform:
|
data/childprocess.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |s|
|
|
19
19
|
s.test_files = `git ls-files -- spec/*`.split("\n")
|
20
20
|
s.require_paths = ["lib"]
|
21
21
|
|
22
|
-
s.add_development_dependency "rspec", "
|
22
|
+
s.add_development_dependency "rspec", "~> 3.0.0"
|
23
23
|
s.add_development_dependency "yard", ">= 0"
|
24
24
|
s.add_development_dependency "rake", "~> 0.9.2"
|
25
25
|
s.add_development_dependency 'coveralls'
|
@@ -21,7 +21,8 @@ module ChildProcess
|
|
21
21
|
stop_pumps
|
22
22
|
|
23
23
|
true
|
24
|
-
rescue java.lang.IllegalThreadStateException
|
24
|
+
rescue java.lang.IllegalThreadStateException => ex
|
25
|
+
log(ex.class => ex.message)
|
25
26
|
false
|
26
27
|
ensure
|
27
28
|
log(:exit_code => @exit_code)
|
@@ -113,7 +114,11 @@ module ChildProcess
|
|
113
114
|
|
114
115
|
def set_env(env)
|
115
116
|
ENV.to_hash.merge(@environment).each do |k,v|
|
116
|
-
|
117
|
+
if v
|
118
|
+
env.put(k.to_s, v.to_s)
|
119
|
+
else
|
120
|
+
env.remove(k.to_s) if env.key?(k.to_s)
|
121
|
+
end
|
117
122
|
end
|
118
123
|
end
|
119
124
|
|
data/lib/childprocess/version.rb
CHANGED
data/spec/abstract_io_spec.rb
CHANGED
data/spec/childprocess_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe ChildProcess do
|
|
6
6
|
it "returns self when started" do
|
7
7
|
process = sleeping_ruby
|
8
8
|
|
9
|
-
process.start.
|
10
|
-
process.
|
9
|
+
expect(process.start).to eq process
|
10
|
+
expect(process).to be_alive
|
11
11
|
end
|
12
12
|
|
13
13
|
# We can't detect failure to execve() when using posix_spawn() on Linux
|
@@ -30,35 +30,35 @@ describe ChildProcess do
|
|
30
30
|
process = exit_with(1).start
|
31
31
|
process.wait
|
32
32
|
|
33
|
-
process.
|
33
|
+
expect(process).to be_crashed
|
34
34
|
end
|
35
35
|
|
36
36
|
it "knows if the process didn't crash" do
|
37
37
|
process = exit_with(0).start
|
38
38
|
process.wait
|
39
39
|
|
40
|
-
process.
|
40
|
+
expect(process).to_not be_crashed
|
41
41
|
end
|
42
42
|
|
43
43
|
it "can wait for a process to finish" do
|
44
44
|
process = exit_with(0).start
|
45
45
|
return_value = process.wait
|
46
46
|
|
47
|
-
process.
|
48
|
-
return_value.
|
47
|
+
expect(process).to_not be_alive
|
48
|
+
expect(return_value).to eq 0
|
49
49
|
end
|
50
50
|
|
51
51
|
it 'ignores #wait if process already finished' do
|
52
52
|
process = exit_with(0).start
|
53
53
|
sleep 0.01 until process.exited?
|
54
54
|
|
55
|
-
process.wait.
|
55
|
+
expect(process.wait).to eql 0
|
56
56
|
end
|
57
57
|
|
58
58
|
it "escalates if TERM is ignored" do
|
59
59
|
process = ignored('TERM').start
|
60
60
|
process.stop
|
61
|
-
process.
|
61
|
+
expect(process).to be_exited
|
62
62
|
end
|
63
63
|
|
64
64
|
it "accepts a timeout argument to #stop" do
|
@@ -74,7 +74,7 @@ describe ChildProcess do
|
|
74
74
|
end
|
75
75
|
|
76
76
|
child_env = eval rewind_and_read(file)
|
77
|
-
child_env['INHERITED'].
|
77
|
+
expect(child_env['INHERITED']).to eql 'yes'
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
@@ -84,12 +84,12 @@ describe ChildProcess do
|
|
84
84
|
process.environment['CHILD_ONLY'] = '1'
|
85
85
|
process.start
|
86
86
|
|
87
|
-
ENV['CHILD_ONLY'].
|
87
|
+
expect(ENV['CHILD_ONLY']).to be_nil
|
88
88
|
|
89
89
|
process.wait
|
90
90
|
|
91
91
|
child_env = eval rewind_and_read(file)
|
92
|
-
child_env['CHILD_ONLY'].
|
92
|
+
expect(child_env['CHILD_ONLY']).to eql '1'
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -104,8 +104,8 @@ describe ChildProcess do
|
|
104
104
|
|
105
105
|
child_env = eval rewind_and_read(file)
|
106
106
|
|
107
|
-
child_env['INHERITED'].
|
108
|
-
child_env['CHILD_ONLY'].
|
107
|
+
expect(child_env['INHERITED']).to eq 'yes'
|
108
|
+
expect(child_env['CHILD_ONLY']).to eq 'yes'
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -120,7 +120,7 @@ describe ChildProcess do
|
|
120
120
|
process.wait
|
121
121
|
|
122
122
|
child_env = eval rewind_and_read(file)
|
123
|
-
child_env.
|
123
|
+
expect(child_env).to_not have_key('CHILDPROCESS_UNSET')
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
@@ -132,12 +132,13 @@ describe ChildProcess do
|
|
132
132
|
process = write_argv(file.path, *args).start
|
133
133
|
process.wait
|
134
134
|
|
135
|
-
rewind_and_read(file).
|
135
|
+
expect(rewind_and_read(file)).to eql args.inspect
|
136
136
|
end
|
137
137
|
end
|
138
138
|
|
139
139
|
it "lets a detached child live on" do
|
140
140
|
pending "how do we spec this?"
|
141
|
+
fail
|
141
142
|
end
|
142
143
|
|
143
144
|
it "preserves Dir.pwd in the child" do
|
@@ -153,7 +154,7 @@ describe ChildProcess do
|
|
153
154
|
|
154
155
|
process.wait
|
155
156
|
|
156
|
-
rewind_and_read(file).
|
157
|
+
expect(rewind_and_read(file)).to eq expected_dir
|
157
158
|
end
|
158
159
|
end
|
159
160
|
|
@@ -164,7 +165,7 @@ describe ChildProcess do
|
|
164
165
|
process = write_argv(file.path, *args).start
|
165
166
|
process.wait
|
166
167
|
|
167
|
-
rewind_and_read(file).
|
168
|
+
expect(rewind_and_read(file)).to eq args.inspect
|
168
169
|
end
|
169
170
|
end
|
170
171
|
|
@@ -172,14 +173,14 @@ describe ChildProcess do
|
|
172
173
|
path = File.expand_path('foo bar')
|
173
174
|
|
174
175
|
with_executable_at(path) do |proc|
|
175
|
-
proc.start.
|
176
|
-
proc.
|
176
|
+
expect(proc.start).to eq proc
|
177
|
+
expect(proc).to be_alive
|
177
178
|
end
|
178
179
|
end
|
179
180
|
|
180
181
|
it "times out when polling for exit" do
|
181
182
|
process = sleeping_ruby.start
|
182
|
-
|
183
|
+
expect { process.poll_for_exit(0.1) }.to raise_error(ChildProcess::TimeoutError)
|
183
184
|
end
|
184
185
|
|
185
186
|
it "can change working directory" do
|
@@ -196,10 +197,10 @@ describe ChildProcess do
|
|
196
197
|
process.start
|
197
198
|
process.wait
|
198
199
|
|
199
|
-
rewind_and_read(file).
|
200
|
+
expect(rewind_and_read(file)).to eq dir
|
200
201
|
end
|
201
202
|
|
202
|
-
Dir.pwd.
|
203
|
+
expect(Dir.pwd).to eq orig_pwd
|
203
204
|
}
|
204
205
|
end
|
205
206
|
|
@@ -214,7 +215,7 @@ describe ChildProcess do
|
|
214
215
|
end
|
215
216
|
|
216
217
|
process.stop
|
217
|
-
wait_until(3) { alive?(pid).
|
218
|
+
wait_until(3) { expect(alive?(pid)).to eql(false) }
|
218
219
|
end
|
219
220
|
end
|
220
221
|
|
@@ -223,7 +224,7 @@ describe ChildProcess do
|
|
223
224
|
threads = []
|
224
225
|
|
225
226
|
threads << Thread.new { sleeping_ruby(1).start.wait }
|
226
|
-
threads << Thread.new(time) { (Time.now - time).
|
227
|
+
threads << Thread.new(time) { expect(Time.now - time).to be < 0.5 }
|
227
228
|
|
228
229
|
threads.each { |t| t.join }
|
229
230
|
end
|
data/spec/io_spec.rb
CHANGED
@@ -17,11 +17,11 @@ describe ChildProcess do
|
|
17
17
|
process.io.stderr = err
|
18
18
|
|
19
19
|
process.start
|
20
|
-
process.io.stdin.
|
20
|
+
expect(process.io.stdin).to be_nil
|
21
21
|
process.wait
|
22
22
|
|
23
|
-
rewind_and_read(out).
|
24
|
-
rewind_and_read(err).
|
23
|
+
expect(rewind_and_read(out)).to eq "0\n"
|
24
|
+
expect(rewind_and_read(err)).to eq "1\n"
|
25
25
|
ensure
|
26
26
|
out.close
|
27
27
|
err.close
|
@@ -44,7 +44,7 @@ describe ChildProcess do
|
|
44
44
|
process.start
|
45
45
|
process.wait
|
46
46
|
|
47
|
-
rewind_and_read(out).
|
47
|
+
expect(rewind_and_read(out)).to eq "0\n"
|
48
48
|
ensure
|
49
49
|
out.close
|
50
50
|
end
|
@@ -61,7 +61,7 @@ describe ChildProcess do
|
|
61
61
|
process.start
|
62
62
|
process.wait
|
63
63
|
|
64
|
-
rewind_and_read(out).
|
64
|
+
expect(rewind_and_read(out)).to eq "hello\n"
|
65
65
|
ensure
|
66
66
|
out.close
|
67
67
|
end
|
@@ -84,7 +84,7 @@ describe ChildProcess do
|
|
84
84
|
|
85
85
|
process.poll_for_exit(exit_timeout)
|
86
86
|
|
87
|
-
rewind_and_read(out).
|
87
|
+
expect(rewind_and_read(out)).to eq "hello world\n"
|
88
88
|
ensure
|
89
89
|
out.close
|
90
90
|
end
|
@@ -108,23 +108,23 @@ describe ChildProcess do
|
|
108
108
|
|
109
109
|
stdin.puts "hello"
|
110
110
|
stdin.flush
|
111
|
-
wait_until { rewind_and_read(out_receiver).
|
111
|
+
wait_until { expect(rewind_and_read(out_receiver)).to match(/\Ahello\r?\n\z/m) }
|
112
112
|
|
113
113
|
stdin.putc "n"
|
114
114
|
stdin.flush
|
115
|
-
wait_until { rewind_and_read(out_receiver).
|
115
|
+
wait_until { expect(rewind_and_read(out_receiver)).to match(/\Ahello\r?\nn\z/m) }
|
116
116
|
|
117
117
|
stdin.print "e"
|
118
118
|
stdin.flush
|
119
|
-
wait_until { rewind_and_read(out_receiver).
|
119
|
+
wait_until { expect(rewind_and_read(out_receiver)).to match(/\Ahello\r?\nne\z/m) }
|
120
120
|
|
121
121
|
stdin.printf "w"
|
122
122
|
stdin.flush
|
123
|
-
wait_until { rewind_and_read(out_receiver).
|
123
|
+
wait_until { expect(rewind_and_read(out_receiver)).to match(/\Ahello\r?\nnew\z/m) }
|
124
124
|
|
125
125
|
stdin.write "\nworld\n"
|
126
126
|
stdin.flush
|
127
|
-
wait_until { rewind_and_read(out_receiver).
|
127
|
+
wait_until { expect(rewind_and_read(out_receiver)).to match(/\Ahello\r?\nnew\r?\nworld\r?\n\z/m) }
|
128
128
|
|
129
129
|
stdin.close
|
130
130
|
process.poll_for_exit(exit_timeout)
|
@@ -168,7 +168,7 @@ describe ChildProcess do
|
|
168
168
|
out = stdout.read
|
169
169
|
err = stderr.read
|
170
170
|
|
171
|
-
[out, err].
|
171
|
+
expect([out, err]).to eq %w[stdout stderr]
|
172
172
|
end
|
173
173
|
|
174
174
|
it "can set close-on-exec when IO is inherited" do
|
@@ -199,7 +199,7 @@ describe ChildProcess do
|
|
199
199
|
process.start
|
200
200
|
process.wait
|
201
201
|
|
202
|
-
rewind_and_read(out).size.
|
202
|
+
expect(rewind_and_read(out).size).to eq 3000
|
203
203
|
ensure
|
204
204
|
out.close
|
205
205
|
end
|
data/spec/jruby_spec.rb
CHANGED
@@ -6,7 +6,7 @@ if ChildProcess.jruby? && !ChildProcess.windows?
|
|
6
6
|
let(:io) { ChildProcess::JRuby::IO.new }
|
7
7
|
|
8
8
|
it "raises an ArgumentError if given IO does not respond to :to_outputstream" do
|
9
|
-
|
9
|
+
expect { io.stdout = nil }.to raise_error(ArgumentError)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -17,7 +17,7 @@ if ChildProcess.jruby? && !ChildProcess.windows?
|
|
17
17
|
it "raises an error when trying to access the child's pid" do
|
18
18
|
process = exit_with(0)
|
19
19
|
process.start
|
20
|
-
|
20
|
+
expect { process.pid }.to raise_error(NotImplementedError)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
data/spec/pid_behavior.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -128,9 +128,9 @@ module ChildProcessSpecHelper
|
|
128
128
|
def with_executable_at(path, &blk)
|
129
129
|
if ChildProcess.os == :windows
|
130
130
|
path << ".cmd"
|
131
|
-
content = "@echo foo"
|
131
|
+
content = "#{RUBY} -e 'sleep 10' \n @echo foo"
|
132
132
|
else
|
133
|
-
content = "#!/bin/sh\necho foo"
|
133
|
+
content = "#!/bin/sh\nsleep 10\necho foo"
|
134
134
|
end
|
135
135
|
|
136
136
|
File.open(path, 'w', 0744) { |io| io << content }
|
data/spec/unix_spec.rb
CHANGED
@@ -9,29 +9,29 @@ if ChildProcess.unix? && !ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
|
9
9
|
it "handles ECHILD race condition where process dies between timeout and KILL" do
|
10
10
|
process = sleeping_ruby
|
11
11
|
|
12
|
-
process.
|
13
|
-
process.
|
14
|
-
process.
|
15
|
-
process.
|
12
|
+
allow(process).to receive(:fork).and_return('fakepid')
|
13
|
+
allow(process).to receive(:send_term)
|
14
|
+
allow(process).to receive(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
|
15
|
+
allow(process).to receive(:send_kill).and_raise(Errno::ECHILD.new)
|
16
16
|
|
17
17
|
process.start
|
18
|
-
|
18
|
+
expect { process.stop }.not_to raise_error
|
19
19
|
|
20
|
-
process.
|
20
|
+
allow(process).to receive(:alive?).and_return(false)
|
21
21
|
end
|
22
22
|
|
23
23
|
it "handles ESRCH race condition where process dies between timeout and KILL" do
|
24
24
|
process = sleeping_ruby
|
25
25
|
|
26
|
-
process.
|
27
|
-
process.
|
28
|
-
process.
|
29
|
-
process.
|
26
|
+
allow(process).to receive(:fork).and_return('fakepid')
|
27
|
+
allow(process).to receive(:send_term)
|
28
|
+
allow(process).to receive(:poll_for_exit).and_raise(ChildProcess::TimeoutError)
|
29
|
+
allow(process).to receive(:send_kill).and_raise(Errno::ESRCH.new)
|
30
30
|
|
31
31
|
process.start
|
32
|
-
|
32
|
+
expect { process.stop }.not_to raise_error
|
33
33
|
|
34
|
-
process.
|
34
|
+
allow(process).to receive(:alive?).and_return(false)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -39,14 +39,14 @@ if ChildProcess.unix? && !ChildProcess.jruby? && !ChildProcess.posix_spawn?
|
|
39
39
|
let(:io) { ChildProcess::Unix::IO.new }
|
40
40
|
|
41
41
|
it "raises an ArgumentError if given IO does not respond to :to_io" do
|
42
|
-
|
42
|
+
expect { io.stdout = nil }.to raise_error(ArgumentError, /to respond to :to_io/)
|
43
43
|
end
|
44
44
|
|
45
45
|
it "raises a TypeError if #to_io does not return an IO" do
|
46
46
|
fake_io = Object.new
|
47
47
|
def fake_io.to_io() StringIO.new end
|
48
48
|
|
49
|
-
|
49
|
+
expect { io.stdout = fake_io }.to raise_error(TypeError, /expected IO, got/)
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
data/spec/windows_spec.rb
CHANGED
@@ -10,14 +10,14 @@ if ChildProcess.windows?
|
|
10
10
|
let(:io) { ChildProcess::Windows::IO.new }
|
11
11
|
|
12
12
|
it "raises an ArgumentError if given IO does not respond to :fileno" do
|
13
|
-
|
13
|
+
expect { io.stdout = nil }.to raise_error(ArgumentError, /must have :fileno or :to_io/)
|
14
14
|
end
|
15
15
|
|
16
16
|
it "raises an ArgumentError if the #to_io does not return an IO " do
|
17
17
|
fake_io = Object.new
|
18
18
|
def fake_io.to_io() StringIO.new end
|
19
19
|
|
20
|
-
|
20
|
+
expect { io.stdout = fake_io }.to raise_error(ArgumentError, /must have :fileno or :to_io/)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: childprocess
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jari Bakken
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 3.0.0
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 3.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: yard
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|