command_runner_ng 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/command_runner.rb +34 -5
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3421bf48db312e8cd8cec1544e7c2ca673be5179
4
- data.tar.gz: 67fcb9afc773b88e9b7c4ec283b4553863dc3966
3
+ metadata.gz: a81766b72e2152688fa7ec9329557394dbecb320
4
+ data.tar.gz: 4443fc0fa5f5b0c3524607505283f8c20e4bc4d3
5
5
  SHA512:
6
- metadata.gz: 979bfd1c39af20bf5dc04e86ed14f80125229ec037d6f47b943bb5733219e25226a46b28afde0acf6689e1a130c0cffb7f911e9d0c488b9b89b4a546d7a362e2
7
- data.tar.gz: 3931eb2093d52b9ffdf551cc6b01f8a574534e596acea48fb47f8a2520f70979a510b8921bb9fcd0bd730a14d3b6aa3562cba9ca2ff7e9a577cddbdc1abad24e
6
+ metadata.gz: 3e479989e2f54239a84f27561e8c754838b0924a323ebdc1341fc5cc539a468b05e69af1940164370a6ddca0060bcc820ad46c7fb57b04d995b392bb0f749e1d
7
+ data.tar.gz: 9bffe871f6c0155167ee0f97410345b59d92e10f0acff36f88bef8808f18917a167e628d4826e43ca5ce6fd32f215b2dbba9c27d3c1cf6e32a4d80c349e83f21
@@ -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
- read_nonblock_safe!(io, data, tick)
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.2
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-04-05 00:00:00.000000000 Z
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.1
40
+ rubygems_version: 2.5.2
41
41
  signing_key:
42
42
  specification_version: 4
43
43
  summary: Command Runner NG