sproc 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8938c9977d79ffd99b2526dc698d66f33c0afc225121307a7cbf79fe9284acdb
4
- data.tar.gz: 0fcd34ab808b76a121a581443b0bf9c075e8bf92633a112c406b11b11691b9ab
3
+ metadata.gz: bec73dcb32037bd849d44167aea25574b4575a704fc5fb88e822001667c2080a
4
+ data.tar.gz: 3ebc7e7a8be82a6e69c59fcbc29881b365e6af37c9652dc3065a8fa320bfe7b8
5
5
  SHA512:
6
- metadata.gz: e4da5cce84127eb2b3fda62ba9cb893b588ad1e68b173ad8ef558d8c7f4945c089c74dea20e7be62e7e685bd7efbcf91a6d0616bcc413a7fb4663731151c25da
7
- data.tar.gz: 7abf027ed86e29fbc531698feec84c318fda64d3138fa3e22bce81c1f2ef7a79b73aa255274de5f0265453c8df59964a616b912cb41cb55b8e692cd36b0d1fae
6
+ metadata.gz: 5fb7cfb22432f1a81913dfb07faf969306672f3e436b4e19451928934e169c256dd0b474f5d6ae653f49022b8a21f683bf2e31ebc808c22c7ce8463552c2298b
7
+ data.tar.gz: 03bb0859af3885655a8c26cdfce0360701b9f8aee111bf6228424f0cb63ed869814677292a845eda6f9059a2383732744ba70b33f1604c582aec67cb02605d94
data/lib/sproc/core.rb CHANGED
@@ -15,7 +15,8 @@ module SProc
15
15
  ].freeze
16
16
  end
17
17
 
18
- # The possible states of this subprocess
18
+ # The available execution states of a the subprocess
19
+ # running within an SProc instance.
19
20
  module ExecutionState
20
21
  NOT_STARTED = 0
21
22
  RUNNING = 1
@@ -73,42 +74,6 @@ module SProc
73
74
  @env = env
74
75
  end
75
76
 
76
- # Return the execution state of this SubProcess. Note that it is not
77
- # identical with the life-cycle of the underlying ProcessStatus object
78
- #
79
- # @return current ExecutionState
80
- def execution_state
81
- return ExecutionState::NOT_STARTED if @execution_thread.nil?
82
-
83
- # Count this SubProcess as running as long as the thread
84
- # that executes it is alive (this includes book keeping
85
- # chores within this class as well)
86
- return ExecutionState::RUNNING if @execution_thread.alive?
87
-
88
- status = task_info[:process_status]
89
-
90
- # an execution thread that has run but not generated a task_info
91
- # means that we tried to start a process but failed
92
- return ExecutionState::FAILED_TO_START if status.nil?
93
-
94
- # a process can terminate for different reasons:
95
- # - its done
96
- # - an uncaught exception-
97
- # - an uncaught signal
98
-
99
- # this should take care of uncaught signals
100
- return ExecutionState::ABORTED if status.signaled?
101
-
102
- # If the process completed (either successfully or not)
103
- return ExecutionState::COMPLETED if status.exited?
104
-
105
- # We don't currently handle a process that has been stopped...
106
- raise NotImplementedError("Unhandled process 'stopped' status!") if status.stopped?
107
-
108
- # We should never come here
109
- raise RuntimeError("Unhandled process status: #{status.inspect}")
110
- end
111
-
112
77
  # Start the sub-process and block until it has completed.
113
78
  #
114
79
  #
@@ -147,6 +112,48 @@ module SProc
147
112
  task_info
148
113
  end
149
114
 
115
+ # Return the execution state of this SubProcess. Note that it is not
116
+ # identical with the life-cycle of the underlying ProcessStatus object
117
+ #
118
+ # @return current ExecutionState
119
+ def execution_state
120
+ return ExecutionState::NOT_STARTED if @execution_thread.nil?
121
+
122
+ # Count this SubProcess as running as long as the thread
123
+ # that executes it is alive (this includes book keeping
124
+ # chores within this class as well)
125
+ return ExecutionState::RUNNING if @execution_thread.alive?
126
+
127
+ status = task_info[:process_status]
128
+
129
+ # an execution thread that has run but not generated a task_info
130
+ # means that we tried to start a process but failed
131
+ return ExecutionState::FAILED_TO_START if status.nil?
132
+
133
+ # a process can terminate for different reasons:
134
+ # - its done
135
+ # - an uncaught exception-
136
+ # - an uncaught signal
137
+
138
+ # this should take care of uncaught signals
139
+ return ExecutionState::ABORTED if status.signaled?
140
+
141
+ # If the process completed (either successfully or not)
142
+ return ExecutionState::COMPLETED if status.exited?
143
+
144
+ # We don't currently handle a process that has been stopped...
145
+ raise NotImplementedError("Unhandled process 'stopped' status!") if status.stopped?
146
+
147
+ # We should never come here
148
+ raise RuntimeError("Unhandled process status: #{status.inspect}")
149
+ end
150
+
151
+ # @return the TaskInfo representing this SubProcess, nil if
152
+ # process has not started
153
+ def task_info
154
+ @runner.task_info
155
+ end
156
+
150
157
  # blocks until all processes in the given array are completed/aborted.
151
158
  #
152
159
  # the implementation polls each process after each given poll interval
@@ -211,12 +218,6 @@ module SProc
211
218
  all_proc
212
219
  end
213
220
 
214
- # @return the TaskInfo representing this SubProcess, nil if
215
- # process has not started
216
- def task_info
217
- @runner.task_info
218
- end
219
-
220
221
  # return processes that are no longer running
221
222
  def self.get_finished(running_proc)
222
223
  running_proc.select do |p|
@@ -228,6 +229,8 @@ module SProc
228
229
 
229
230
  private
230
231
 
232
+ # a helper method that supports both synch/async execution
233
+ # depending on the supplied args
231
234
  def exec(synch, env, cmd, *args, **opts)
232
235
  raise 'Subprocess already running!' unless @execution_thread.nil? || !@execution_thread.alive?
233
236
 
@@ -0,0 +1,196 @@
1
+ require_relative 'core'
2
+
3
+ # This module is written to provide sub-process
4
+ # execution with some human readable logging of process start/stop/errors
5
+ #
6
+ # It wraps SProc instances and execution with calls to logging methods
7
+ # that tries to make the resulting log user friendly
8
+ module SProc
9
+ module Reporting
10
+ class << self
11
+ attr_accessor :logger
12
+ end
13
+
14
+ def logger
15
+ Reporting.logger
16
+ end
17
+
18
+ # Run a process synchronuously via the native shell and log
19
+ # output suitable for a build log
20
+ #
21
+ # @param cmd_name a String containing a descriptive
22
+ # name for what the process will do.
23
+ # @param cmd, args, opts see SProc.exec_sync
24
+ #
25
+ # @return the SProc instance containing the completed process
26
+ def report_sync(cmd_name, cmd, *args, **opts)
27
+ p = create_proc_and_log(cmd_name,
28
+ ShellType::NONE, :exec_sync,
29
+ cmd, args, opts)
30
+ report_completed(p)
31
+ p
32
+ end
33
+
34
+ # Run a process asynchronuously via the native shell and log
35
+ # output suitable for a build log
36
+ #
37
+ # @param cmd_name a String containing a descriptive
38
+ # name for what the process will do.
39
+ # @param cmd, args, opts see SProc.exec_sync
40
+ #
41
+ # @return the created SProc instance
42
+ def report_async(cmd_name, cmd, *args, **opts)
43
+ create_proc_and_log(cmd_name,
44
+ ShellType::NONE, :exec_async,
45
+ cmd, args, opts)
46
+ end
47
+
48
+ # Run a process asynchronuously via the Bash shell and log
49
+ # output suitable for a build log
50
+ #
51
+ # @param cmd_name a String containing a descriptive
52
+ # name for what the process will do.
53
+ # @param cmd, args, opts see SProc.exec_sync
54
+ #
55
+ # @return the created SProc instance
56
+ def report_async_within_bash(cmd_name, cmd, *args, **opts)
57
+ create_proc_and_log(cmd_name,
58
+ ShellType::BASH, :exec_async,
59
+ cmd, args, opts)
60
+ end
61
+
62
+ # Log output from a completed/aborted process
63
+ #
64
+ # @param process the SProc instance that has run
65
+ # @return true/false corresponding to process success
66
+ def report_completed(process)
67
+ friendly_name = if @log_friendly_name_map&.key?(process)
68
+ "#{@log_friendly_name_map[process]}"
69
+ else
70
+ 'Process'
71
+ end
72
+ started_ok = true
73
+ case process.execution_state
74
+ when ExecutionState::COMPLETED
75
+ process.exit_zero? && log_completed_ok(friendly_name, process.task_info)
76
+ !process.exit_zero? && log_completed_err(friendly_name, process.task_info)
77
+ when ExecutionState::ABORTED
78
+ log_aborted(friendly_name, process.task_info)
79
+ started_ok = false
80
+ when ExecutionState::FAILED_TO_START
81
+ log_failed_start(friendly_name, process.task_info)
82
+ started_ok = false
83
+ else
84
+ log_unexpected(friendly_name, process.task_info)
85
+ end
86
+ started_ok && process.exit_zero?
87
+ end
88
+
89
+ private
90
+
91
+ def create_proc_and_log(cmd_name, type, method, cmd, args, opts)
92
+ log_start(cmd_name, type, method, cmd, args, **opts)
93
+ p = SProc.new(type: type).send(method, cmd, args, **opts)
94
+ @log_friendly_name_map ||= {}
95
+ @log_friendly_name_map[p] = cmd_name
96
+ p
97
+ end
98
+
99
+ def log_method_result_ok(friendly_name, delta)
100
+ logger.info do
101
+ "#{friendly_name} completed successfully after #{delta.round(3)}s"
102
+ end
103
+ end
104
+
105
+ def log_method_result_error(friendly_name_method, delta, exc)
106
+ logger.error do
107
+ "#{friendly_name_method} aborted by #{exc.class} after #{delta.round(3)}s\n"\
108
+ "Exception info: #{exc.message}"
109
+ end
110
+
111
+ logger.debug do
112
+ exc.backtrace.to_s
113
+ end
114
+ end
115
+
116
+ def log_start(cmd_name, type, method, cmd, *args, **opts)
117
+ logger.info do
118
+ async_str = method == :exec_async ? 'asynchronuously' : 'synchronuously'
119
+ type_str = type == ShellType::NONE ? 'without shell' : 'within the bash shell'
120
+ "'#{cmd_name}' executing #{async_str} #{type_str}..."
121
+ end
122
+ logger.debug do
123
+ msg = String.new("Starting #{cmd}")
124
+ msg << " with args: #{args.flatten.inspect}" unless args.nil? || args.empty?
125
+ msg << " and opts: #{opts.inspect}" unless opts.nil? || opts.empty?
126
+ msg
127
+ end
128
+ end
129
+
130
+ def log_one_dll(regex, cmd_str, time)
131
+ m = regex.match(cmd_str)
132
+ s = m.nil? ? cmd_str : m[1]
133
+ max = 45
134
+ s = s.length > max ? s.slice(0..max - 1) : s.ljust(max)
135
+ logger.info { "#{s} took #{time.round(3)}s." }
136
+ end
137
+
138
+ def log_aborted(friendly_name, p_info)
139
+ logger.error do
140
+ "'#{friendly_name}' aborted!\n"\
141
+ "When running: #{p_info[:cmd_str]}\n"\
142
+ "#{merge_proc_output(p_info)}"\
143
+ "#{p_info[:process_status] unless p_info[:process_status].nil?}"
144
+ end
145
+ end
146
+
147
+ def log_failed_start(friendly_name, p_info)
148
+ logger.error do
149
+ "'#{friendly_name}' not run!\n"\
150
+ "Could not start process using: #{p_info[:cmd_str]}\n"\
151
+ "#{merge_proc_output(p_info)}"
152
+ end
153
+ end
154
+
155
+ def log_completed_ok(friendly_name, p_info)
156
+ logger.info do
157
+ "'#{friendly_name}' completed successfully after #{p_info[:wall_time].round(3)}s"
158
+ end
159
+ logger.debug do
160
+ "Cmd: #{p_info[:cmd_str]}"
161
+ end
162
+ end
163
+
164
+ def log_completed_err(friendly_name, p_info)
165
+ logger.error do
166
+ "'#{friendly_name}' completed with exit code "\
167
+ "#{p_info[:process_status].exitstatus}\n"\
168
+ "When running: #{p_info[:cmd_str]}\n"\
169
+ "after #{p_info[:wall_time].round(3)}s\n"\
170
+ "#{merge_proc_output(p_info)}"
171
+ end
172
+ end
173
+
174
+ def log_unexpected(friendly_name, p_info)
175
+ logger.error do
176
+ "'#{friendly_name}' caused unexpected error!"\
177
+ ' Trying to display info on a running process'\
178
+ "(#{p_info[:cmd_str]})"
179
+ end
180
+ end
181
+
182
+ # @return String with sections for each non-empty output stream
183
+ # and exception messages
184
+ def merge_proc_output(p_info)
185
+ inf_str = %i[stdout stderr].collect do |sym|
186
+ next('') if p_info[sym].empty?
187
+
188
+ "--- #{sym} ---\n#{p_info[sym]}"
189
+ end.join("\n")
190
+
191
+ exc = p_info[:exception]
192
+ inf_str << "--- exception ---\n#{exc}\n" unless exc.nil?
193
+ inf_str
194
+ end
195
+ end
196
+ end
data/lib/sproc/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SProc
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sproc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Anders Rillbert
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-27 00:00:00.000000000 Z
11
+ date: 2021-03-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -43,6 +43,7 @@ files:
43
43
  - bin/setup
44
44
  - lib/sproc.rb
45
45
  - lib/sproc/core.rb
46
+ - lib/sproc/reporter.rb
46
47
  - lib/sproc/utils.rb
47
48
  - lib/sproc/version.rb
48
49
  - sproc.gemspec