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.
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