command_runner_ng 0.1.1 → 0.1.2

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 +45 -9
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5ec595e87ef6bc2b18247db034e7f6c16833c7ea
4
- data.tar.gz: f35ed38a143ae56e0964d5fb146f47f43217f4ae
3
+ metadata.gz: 3421bf48db312e8cd8cec1544e7c2ca673be5179
4
+ data.tar.gz: 67fcb9afc773b88e9b7c4ec283b4553863dc3966
5
5
  SHA512:
6
- metadata.gz: 2a5e1afcb667b1d18cecbeb7f5bc998df37a881b6ca3b33b986480faab4264af3189df20c12f2ae82ece3d67e251295d466f0be2ce22f3c06a184e2db039bdcd
7
- data.tar.gz: 0c9b71180c50ba117e0c75975e226886608b8eb4f4e9b7ed17d419eba7c70e4bae6148e9d0e2d9601dd993cf6b99469db935cb6f1dc822845f700648fbd51cfd
6
+ metadata.gz: 979bfd1c39af20bf5dc04e86ed14f80125229ec037d6f47b943bb5733219e25226a46b28afde0acf6689e1a130c0cffb7f911e9d0c488b9b89b4a546d7a362e2
7
+ data.tar.gz: 3931eb2093d52b9ffdf551cc6b01f8a574534e596acea48fb47f8a2520f70979a510b8921bb9fcd0bd730a14d3b6aa3562cba9ca2ff7e9a577cddbdc1abad24e
@@ -2,6 +2,8 @@ module CommandRunner
2
2
 
3
3
  MAX_TIME = Time.new(2**63 - 1)
4
4
 
5
+ DEFAULT_OPTIONS = {:err=>[:child, :out]}
6
+
5
7
  # Like IO.popen(), but block until the child completes.
6
8
  #
7
9
  # For convenience allows you to pass > 1 string args without a boxing array,
@@ -28,8 +30,9 @@ module CommandRunner
28
30
  # Takes an optional environment parameter (a Hash). The environment is
29
31
  # populated with the keys/values of this parameter.
30
32
  #
31
- # Returns a Hash with :out and :status. :out is a string with stdout
32
- # and stderr merged, and :status is a Process::Status.
33
+ # Returns a Hash with :out, :pid, and :status. :out is a string with stdout
34
+ # and stderr merged, and :status is a Process::Status. The :pid field is the
35
+ # the PID the child process had when it ran.
33
36
  #
34
37
  # As a special case - if an action Proc raises an exception, the child
35
38
  # will be killed with SIGKILL, cleaned up, and the exception rethrown
@@ -44,7 +47,16 @@ module CommandRunner
44
47
  #
45
48
  # All Kernel.spawn features, like setting umasks, process group, and are supported through the options hash.
46
49
  #
47
- def self.run(*args, timeout: nil, environment: {}, options: {:err=>[:child, :out]})
50
+ # Debugging: To help debugging your app you can set the debug_log parameter. It can be any old object responding
51
+ # to :puts. Fx. $stderr, $stdout, or the write end of an IO.pipe. CommandRunnerNG will put some info about
52
+ # all process start, stop, and timeouts here. To enable debug logging for all commands call
53
+ # CommandRunner.set_debug_log!($stderr) (or with some other object responding to :puts).
54
+ #
55
+ def self.run(*args, timeout: nil, environment: {}, debug_log: nil, options: DEFAULT_OPTIONS)
56
+ if debug_log.nil?
57
+ debug_log = @@global_debug_log
58
+ end
59
+
48
60
  # If args is an array of strings, allow that as a shorthand for [arg1, arg2, arg3]
49
61
  if args.length > 1 && args.all? {|arg| arg.is_a? String}
50
62
  args = [args]
@@ -78,6 +90,7 @@ module CommandRunner
78
90
 
79
91
  # Spawn child, merging stderr into stdout
80
92
  io = IO.popen(environment, *args, options)
93
+ debug_log.puts("CommandRunnerNG spawn: args=#{args}, timeout=#{timeout}, options: #{options}, PID: #{io.pid}") if debug_log.respond_to?(:puts)
81
94
  data = ""
82
95
 
83
96
  # Run through all deadlines until command completes.
@@ -88,7 +101,8 @@ module CommandRunner
88
101
  while Time.now < point[:deadline]
89
102
  if Process.wait(io.pid, Process::WNOHANG)
90
103
  read_nonblock_safe!(io, data, tick)
91
- result = {:out => data, :status => $?}
104
+ result = {:out => data, :status => $?, pid: io.pid}
105
+ debug_log.puts("CommandRunnerNG exit: PID: #{io.pid}, code: #{result[:status].exitstatus}") if debug_log.respond_to?(:puts)
92
106
  io.close
93
107
  return result
94
108
  elsif !eof
@@ -98,6 +112,7 @@ module CommandRunner
98
112
 
99
113
  # Deadline for this point reached. Fire the action.
100
114
  action = point[:action]
115
+ debug_log.puts("CommandRunnerNG timeout: PID: #{io.pid}, action: #{action}") if debug_log.respond_to?(:puts)
101
116
  if action.is_a? String or action.is_a? Integer
102
117
  Process.kill(action, io.pid)
103
118
  elsif action.is_a? Proc
@@ -123,7 +138,9 @@ module CommandRunner
123
138
  # Either we didn't have a deadline, or none of the deadlines killed off the child.
124
139
  Process.wait(io.pid)
125
140
  read_nonblock_safe!(io, data, tick)
126
- result = {:out => data, :status => $?}
141
+ result = {:out => data, :status => $?, pid: io.pid}
142
+ debug_log.puts("CommandRunnerNG exit: PID: #{io.pid}, code: #{result[:status].exitstatus}") if debug_log.respond_to?(:puts)
143
+
127
144
  io.close
128
145
 
129
146
  result
@@ -140,15 +157,28 @@ module CommandRunner
140
157
  # git.run(:pull, 'origin', 'master')
141
158
  # git.run(:pull, 'origin', 'master', timeout: 2) # override default timeout of 10
142
159
  # git.run(:status) # will raise an error because :status is not in list of allowed commands
143
- def self.create(*args, timeout: nil, environment: {}, allowed_sub_commands: [])
144
- CommandInstance.new(args, timeout, environment, allowed_sub_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)
162
+ end
163
+
164
+ # Log all command line invocations to a logger object responding to :puts. Set to nil to disable.
165
+ # Setting the :debug_log keyword argument on individual invocations of CommandRunner.run() overrides this value.
166
+ def self.set_debug_log!(logger)
167
+ if logger.respond_to?(:puts) || logger.nil?
168
+ @@global_debug_log = logger
169
+ else
170
+ raise ArgumentError.new("Logger '#{logger}' not responding to :puts")
171
+ end
145
172
  end
146
173
 
147
174
  private
148
175
 
176
+ # Set to an object that respond to :puts to enable global debug logging for all
177
+ @@global_debug_log = nil
178
+
149
179
  class CommandInstance
150
180
 
151
- def initialize(default_args, default_timeout, default_environment, allowed_sub_commands)
181
+ def initialize(default_args, default_timeout, default_environment, allowed_sub_commands, debug_log, options)
152
182
  unless default_args.first.is_a? Array
153
183
  raise "First argument must be an array of command line args. Found #{default_args}"
154
184
  end
@@ -157,6 +187,8 @@ module CommandRunner
157
187
  @default_timeout = default_timeout
158
188
  @default_environment = default_environment
159
189
  @allowed_sub_commands = allowed_sub_commands
190
+ @debug_log = debug_log
191
+ @options = options
160
192
  end
161
193
 
162
194
  def run(*args, timeout: nil, environment: {})
@@ -178,7 +210,11 @@ module CommandRunner
178
210
 
179
211
  full_args = @default_args.dup
180
212
  full_args[0] += args_list.map {|arg| arg.to_s }
181
- CommandRunner.run(*full_args, timeout: (timeout || @default_timeout), environment: @default_environment.merge(environment))
213
+ CommandRunner.run(*full_args,
214
+ timeout: (timeout || @default_timeout),
215
+ environment: @default_environment.merge(environment),
216
+ debug_log: @debug_log,
217
+ options: @options)
182
218
  end
183
219
 
184
220
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_runner_ng
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
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: 2015-09-16 00:00:00.000000000 Z
11
+ date: 2017-04-05 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Helper APIs for advanced interactions with subprocesses and shell commands
14
- email: mikkel.kamstrup@xamarin.com
14
+ email: kamikkel@microsoft.com
15
15
  executables: []
16
16
  extensions: []
17
17
  extra_rdoc_files: []