command_runner_ng 0.1.2 → 0.1.3
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.
- checksums.yaml +4 -4
- data/lib/command_runner.rb +34 -5
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a81766b72e2152688fa7ec9329557394dbecb320
|
4
|
+
data.tar.gz: 4443fc0fa5f5b0c3524607505283f8c20e4bc4d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e479989e2f54239a84f27561e8c754838b0924a323ebdc1341fc5cc539a468b05e69af1940164370a6ddca0060bcc820ad46c7fb57b04d995b392bb0f749e1d
|
7
|
+
data.tar.gz: 9bffe871f6c0155167ee0f97410345b59d92e10f0acff36f88bef8808f18917a167e628d4826e43ca5ce6fd32f215b2dbba9c27d3c1cf6e32a4d80c349e83f21
|
data/lib/command_runner.rb
CHANGED
@@ -45,6 +45,9 @@ module CommandRunner
|
|
45
45
|
# Fx. redirecting stderr to /dev/null would look like:
|
46
46
|
# run('ls', 'nosuchfile', options: {:err => "/dev/null"})
|
47
47
|
#
|
48
|
+
# For simple case of splitting stderr into a buffer separate from stdout you can pass
|
49
|
+
# the argument split_stderr: true. This will make an :err entry available in the result.
|
50
|
+
#
|
48
51
|
# All Kernel.spawn features, like setting umasks, process group, and are supported through the options hash.
|
49
52
|
#
|
50
53
|
# Debugging: To help debugging your app you can set the debug_log parameter. It can be any old object responding
|
@@ -52,7 +55,7 @@ module CommandRunner
|
|
52
55
|
# all process start, stop, and timeouts here. To enable debug logging for all commands call
|
53
56
|
# CommandRunner.set_debug_log!($stderr) (or with some other object responding to :puts).
|
54
57
|
#
|
55
|
-
def self.run(*args, timeout: nil, environment: {}, debug_log: nil, options: DEFAULT_OPTIONS)
|
58
|
+
def self.run(*args, timeout: nil, environment: {}, debug_log: nil, split_stderr: false, options: DEFAULT_OPTIONS)
|
56
59
|
if debug_log.nil?
|
57
60
|
debug_log = @@global_debug_log
|
58
61
|
end
|
@@ -88,11 +91,19 @@ module CommandRunner
|
|
88
91
|
deadline_sequence = [{:deadline => MAX_TIME, :action => 0}]
|
89
92
|
end
|
90
93
|
|
94
|
+
if split_stderr
|
95
|
+
err_r, err_w = IO.pipe
|
96
|
+
errbuf = ""
|
97
|
+
options = options.merge({:err => err_w})
|
98
|
+
end
|
99
|
+
|
91
100
|
# Spawn child, merging stderr into stdout
|
92
101
|
io = IO.popen(environment, *args, options)
|
93
102
|
debug_log.puts("CommandRunnerNG spawn: args=#{args}, timeout=#{timeout}, options: #{options}, PID: #{io.pid}") if debug_log.respond_to?(:puts)
|
94
103
|
data = ""
|
95
104
|
|
105
|
+
err_w.close if split_stderr
|
106
|
+
|
96
107
|
# Run through all deadlines until command completes.
|
97
108
|
# We could merge this block into the selecting block above,
|
98
109
|
# but splitting like this saves us a Process.wait syscall per iteration.
|
@@ -101,12 +112,18 @@ module CommandRunner
|
|
101
112
|
while Time.now < point[:deadline]
|
102
113
|
if Process.wait(io.pid, Process::WNOHANG)
|
103
114
|
read_nonblock_safe!(io, data, tick)
|
115
|
+
read_nonblock_safe!(err_r, errbuf, 0) if split_stderr
|
104
116
|
result = {:out => data, :status => $?, pid: io.pid}
|
117
|
+
if split_stderr
|
118
|
+
result[:err] = errbuf
|
119
|
+
err_r.close
|
120
|
+
end
|
105
121
|
debug_log.puts("CommandRunnerNG exit: PID: #{io.pid}, code: #{result[:status].exitstatus}") if debug_log.respond_to?(:puts)
|
106
122
|
io.close
|
107
123
|
return result
|
108
124
|
elsif !eof
|
109
125
|
eof = read_nonblock_safe!(io, data, tick)
|
126
|
+
read_nonblock_safe!(err_r, errbuf, 0) if split_stderr
|
110
127
|
end
|
111
128
|
end
|
112
129
|
|
@@ -136,9 +153,19 @@ module CommandRunner
|
|
136
153
|
end
|
137
154
|
|
138
155
|
# Either we didn't have a deadline, or none of the deadlines killed off the child.
|
156
|
+
loop do
|
157
|
+
dead = read_nonblock_safe!(io, data, tick)
|
158
|
+
read_nonblock_safe!(err_r, errbuf, 0) if split_stderr
|
159
|
+
break if dead
|
160
|
+
end
|
139
161
|
Process.wait(io.pid)
|
140
|
-
|
162
|
+
|
141
163
|
result = {:out => data, :status => $?, pid: io.pid}
|
164
|
+
if split_stderr
|
165
|
+
result[:err] = errbuf
|
166
|
+
err_r.close
|
167
|
+
end
|
168
|
+
|
142
169
|
debug_log.puts("CommandRunnerNG exit: PID: #{io.pid}, code: #{result[:status].exitstatus}") if debug_log.respond_to?(:puts)
|
143
170
|
|
144
171
|
io.close
|
@@ -157,8 +184,8 @@ module CommandRunner
|
|
157
184
|
# git.run(:pull, 'origin', 'master')
|
158
185
|
# git.run(:pull, 'origin', 'master', timeout: 2) # override default timeout of 10
|
159
186
|
# git.run(:status) # will raise an error because :status is not in list of allowed commands
|
160
|
-
def self.create(*args, timeout: nil, environment: {}, allowed_sub_commands: [], debug_log: nil, options: DEFAULT_OPTIONS)
|
161
|
-
CommandInstance.new(args, timeout, environment, allowed_sub_commands, debug_log, options)
|
187
|
+
def self.create(*args, timeout: nil, environment: {}, allowed_sub_commands: [], debug_log: nil, split_stderr: false, options: DEFAULT_OPTIONS)
|
188
|
+
CommandInstance.new(args, timeout, environment, allowed_sub_commands, debug_log, split_stderr, options)
|
162
189
|
end
|
163
190
|
|
164
191
|
# Log all command line invocations to a logger object responding to :puts. Set to nil to disable.
|
@@ -178,7 +205,7 @@ module CommandRunner
|
|
178
205
|
|
179
206
|
class CommandInstance
|
180
207
|
|
181
|
-
def initialize(default_args, default_timeout, default_environment, allowed_sub_commands, debug_log, options)
|
208
|
+
def initialize(default_args, default_timeout, default_environment, allowed_sub_commands, debug_log, split_stderr, options)
|
182
209
|
unless default_args.first.is_a? Array
|
183
210
|
raise "First argument must be an array of command line args. Found #{default_args}"
|
184
211
|
end
|
@@ -188,6 +215,7 @@ module CommandRunner
|
|
188
215
|
@default_environment = default_environment
|
189
216
|
@allowed_sub_commands = allowed_sub_commands
|
190
217
|
@debug_log = debug_log
|
218
|
+
@split_stderr = split_stderr
|
191
219
|
@options = options
|
192
220
|
end
|
193
221
|
|
@@ -214,6 +242,7 @@ module CommandRunner
|
|
214
242
|
timeout: (timeout || @default_timeout),
|
215
243
|
environment: @default_environment.merge(environment),
|
216
244
|
debug_log: @debug_log,
|
245
|
+
split_stderr: @split_stderr,
|
217
246
|
options: @options)
|
218
247
|
end
|
219
248
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: command_runner_ng
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Kamstrup Erlandsen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Helper APIs for advanced interactions with subprocesses and shell commands
|
14
14
|
email: kamikkel@microsoft.com
|
@@ -37,7 +37,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
37
|
version: '0'
|
38
38
|
requirements: []
|
39
39
|
rubyforge_project:
|
40
|
-
rubygems_version: 2.5.
|
40
|
+
rubygems_version: 2.5.2
|
41
41
|
signing_key:
|
42
42
|
specification_version: 4
|
43
43
|
summary: Command Runner NG
|