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.
- checksums.yaml +4 -4
- data/lib/command_runner.rb +45 -9
- 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: 3421bf48db312e8cd8cec1544e7c2ca673be5179
|
4
|
+
data.tar.gz: 67fcb9afc773b88e9b7c4ec283b4553863dc3966
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 979bfd1c39af20bf5dc04e86ed14f80125229ec037d6f47b943bb5733219e25226a46b28afde0acf6689e1a130c0cffb7f911e9d0c488b9b89b4a546d7a362e2
|
7
|
+
data.tar.gz: 3931eb2093d52b9ffdf551cc6b01f8a574534e596acea48fb47f8a2520f70979a510b8921bb9fcd0bd730a14d3b6aa3562cba9ca2ff7e9a577cddbdc1abad24e
|
data/lib/command_runner.rb
CHANGED
@@ -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
|
-
|
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,
|
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.
|
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:
|
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:
|
14
|
+
email: kamikkel@microsoft.com
|
15
15
|
executables: []
|
16
16
|
extensions: []
|
17
17
|
extra_rdoc_files: []
|