raykit 0.0.471 → 0.0.472

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.
@@ -1,343 +1,341 @@
1
- require "open3"
2
- require "timeout"
3
- require "json"
4
- require "yaml"
5
- require "logger"
6
- require "securerandom"
7
-
8
- BUFFER_SIZE = 1024 unless defined?(BUFFER_SIZE)
9
-
10
- module Raykit
11
- # Functionality to support executing and logging system commands
12
- class Command
13
- @@commands = []
14
- # The timeout in seconds, defaults to 0 if not specified
15
- attr_accessor :timeout
16
- # The working directory, defaults the current directory if not specified
17
- attr_accessor :directory
18
- # The start time
19
- attr_accessor :start_time
20
- # The execution time in seconds
21
- attr_accessor :elapsed
22
- attr_accessor :command, :output, :error, :exitstatus, :user, :machine, :logging_enabled, :success_log_level
23
-
24
- def init_defaults
25
- @timeout = 0
26
- @directory = Dir.pwd
27
- @output = +""
28
- @error = +""
29
- @exitstatus = 0
30
- @user = Environment.user
31
- @machine = Environment.machine
32
- @logging_enabled = true
33
- end
34
-
35
- def initialize(command)
36
- #def initialize(command, timeout = 0, success_log_level = Logger::DEBUG, logging_enabled = true)
37
- timeout = 0
38
- success_log_level = nil
39
- logging_enabled = false
40
- @@commands = []
41
- init_defaults
42
- @success_log_level = success_log_level
43
- @logging_enabled = logging_enabled
44
- @command = command
45
- @timeout = timeout
46
- #t = Time.now
47
- @elapsed = 0
48
- #run if @command.length.positive?
49
- self
50
- end
51
-
52
- def set_timeout(timeout)
53
- @timeout = timeout
54
- self
55
- end
56
-
57
- def run
58
- # puts '---running---'
59
- @start_time = Time.now
60
- @elapsed = 0
61
- timer = Timer.new
62
- if @timeout.zero?
63
- @output, @error, process_status = Open3.capture3(@command)
64
- @exitstatus = process_status.exitstatus
65
- else
66
- # =================================================
67
- #puts "@timeout is #{@timeout}"
68
- Open3.popen3(@command, chdir: @directory) do |_stdin, stdout, stderr, thread|
69
- tick = 1
70
- pid = thread.pid
71
- start = Time.now
72
- while ((Time.now - start) < @timeout) && thread.alive?
73
- Kernel.select([stdout, stderr], nil, nil, tick)
74
- begin
75
- @output << stdout.read_nonblock(BUFFER_SIZE)
76
- #@error << stderr.read_nonblock(BUFFER_SIZE)
77
- rescue IO::WaitReadable
78
- rescue EOFError
79
- #puts "rescue block entered"
80
- @exitstatus = thread.value.exitstatus
81
- until stdout.eof?
82
- @output << stdout.read_nonblock(BUFFER_SIZE)
83
- end
84
- until stderr.eof?
85
- @error << stderr.read_nonblock(BUFFER_SIZE)
86
- end
87
- break
88
- end
89
-
90
- #begin
91
- # @output << stdout.read_nonblock(BUFFER_SIZE)
92
- # @error << stderr.read_nonblock(BUFFER_SIZE)
93
- #rescue IO::WaitReadable
94
- #rescue EOFError
95
- # @exitstatus = thread.value.exitstatus
96
- # until stdout.eof?
97
- # @output << stdout.read_nonblock(BUFFER_SIZE)
98
- # end
99
- # until stderr.eof?
100
- # @error << stderr.read_nonblock(BUFFER_SIZE)
101
- # end
102
- # break
103
- #end
104
- end
105
- sleep 1
106
- if thread.alive?
107
- if Gem.win_platform?
108
- `taskkill /f /pid #{pid}`
109
- else
110
- Process.kill("TERM", pid)
111
- end
112
- @exitstatus = 5
113
- @error = +"timed out"
114
- else
115
- @exitstatus = thread.value.exitstatus
116
- end
117
- end
118
-
119
- # =================================================
120
- end
121
- @elapsed = timer.elapsed
122
- if @logging_enabled
123
- log
124
- if @exitstatus != 0
125
- to_log_event.to_seq
126
- else
127
- # puts '---logging---'
128
- unless @success_log_level.nil?
129
- e = to_log_event
130
- e["Level"] = "Verbose" if @success_log_level == "Verbose"
131
- e["Level"] = "Debug" if @success_log_level == Logger::DEBUG
132
- e["Level"] = "Information" if @success_log_level == Logger::INFO
133
- e["Level"] = "Warning" if @elapsed > 60 * 2
134
- e.to_seq
135
- end
136
- end
137
- end
138
- self
139
- end
140
-
141
- def to_log_event
142
- secrets = Secrets.new
143
- msg = secrets.hide(@command)
144
- level = "Verbose"
145
- level = "Warning" if @exitstatus != 0
146
- output = @output
147
- error = @error
148
- output = @output[-1000..-1] if @output.length > 1200
149
- error = @error[-1000..-1] if @error.length > 1200
150
- Raykit::LogEvent.new(level, msg, {
151
- "SourceContext" => "Raykit::Command",
152
- "Category" => "Command",
153
- "Timeout" => @timeout,
154
- "Directory" => @directory,
155
- "Output" => output,
156
- "Error" => error,
157
- "ExitStatus" => @exitstatus,
158
- "Elapsed" => elapsed_str,
159
- "ElapsedSeconds" => @elapsed,
160
- })
161
- end
162
-
163
- def log
164
- # --- Rolling File JSON -----
165
- log = Logger.new("#{Raykit::Environment.log_dir}/Raykit.Commands.txt", "daily")
166
- log.formatter = proc do |_severity, _datetime, _progname, msg|
167
- "#{msg}\n"
168
- end
169
- secrets = Secrets.new
170
- msg = secrets.hide(@command) # "?"# self.summary(false)
171
- level = "Verbose"
172
- level = "Warning" if @exitstatus != 0
173
- event = Raykit::LogEvent.new(level, msg, {
174
- "Timeout" => @timeout,
175
- "Directory" => @directory,
176
- "Output" => @output,
177
- "Error" => @error,
178
- "ExitStatus" => @exitstatus,
179
- "Elapsed" => elapsed_str,
180
- })
181
- log.info event.to_json
182
- # ---------------------------
183
- begin
184
- json = JSON.generate(to_hash)
185
- log_filename = "#{Environment.get_dev_dir("log")}/Raykit.Command/#{SecureRandom.uuid}.json"
186
- log_dir = File.dirname(log_filename)
187
- FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
188
- File.open(log_filename, "w") { |f| f.write(json) }
189
- if !@exitstatus.nil? && @exitstatus.zero?
190
- LOG.log("Raykit.Command", Logger::Severity::INFO, json)
191
- else
192
- LOG.log("Raykit.Command", Logger::Severity::ERROR, json)
193
- end
194
- rescue JSON::GeneratorError => e
195
- puts to_hash.to_s
196
- puts e.to_s
197
- json = JSON.generate(to_hash)
198
- end
199
- end
200
-
201
- def elapsed_str
202
- if elapsed < 1.0
203
- end
204
- "#{format("%.0f", elapsed)}s"
205
- end
206
-
207
- def summary(show_directory = false)
208
- checkmark = "\u2713"
209
- # warning="\u26A0"
210
- error = "\u0058"
211
- symbol = Rainbow(checkmark.encode("utf-8")).green
212
- symbol = Rainbow(error.encode("utf-8")).red if @exitstatus != 0
213
- cmd = "#{Rainbow(SECRETS.hide(@command)).yellow}"
214
- if show_directory
215
- puts "#{symbol} #{cmd} " + Rainbow("#{elapsed_str}").cyan
216
- puts Rainbow(" #{@directory}").white + " "
217
- else
218
- puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} " + Rainbow("#{elapsed_str}").cyan
219
- #puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} (#{elapsed_str})"
220
- end
221
- self
222
- end
223
-
224
- def details_on_failure
225
- if @exitstatus != 0
226
- details
227
- end
228
- self
229
- end
230
-
231
- def details
232
- #puts " exit_code: " + @exitstatus.to_s
233
- summary
234
- if @output.length > 0
235
- @output.lines.each do |line|
236
- puts " " + line
237
- end
238
- end
239
- if @error.length > 0
240
- @error.lines.each do |line|
241
- puts " " + line
242
- end
243
- end
244
- self
245
- end
246
-
247
- def to_markdown
248
- checkmark = "\u2713"
249
- error = "\u0058"
250
- symbol = checkmark.encode("utf-8")
251
- symbol = error.encode("utf-8") if @exitstatus != 0
252
- cmd = "#{SECRETS.hide(@command)}"
253
- md = "#{symbol} #{SECRETS.hide(@command)} (#{elapsed_str})"
254
- md
255
- end
256
-
257
- def save
258
- filename = "#{Environment.get_dev_dir("log")}/Commands/#{SecureRandom.uuid}"
259
- log_dir = File.dirname(filename)
260
- FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
261
-
262
- File.open(filename, "w") do |f|
263
- f.write(JSON.pretty_generate(to_hash))
264
- end
265
- self
266
- end
267
-
268
- def save_as(filename)
269
- File.open(filename, "w") do |f|
270
- f.write(JSON.pretty_generate(to_hash))
271
- end
272
- self
273
- end
274
-
275
- def log_to_file(filename)
276
- File.delete(filename) if File.exist?(filename)
277
- File.open(filename, "w") { |f|
278
- f.puts output
279
- f.puts error
280
- }
281
- self
282
- end
283
-
284
- def to_hash
285
- hash = {}
286
- hash[:command] = @command
287
- hash[:directory] = @directory
288
- hash[:timeout] = @timeout
289
- hash[:start_time] = @start_time
290
- hash[:elapsed] = @elapsed
291
- hash[:output] = @output.force_encoding("ISO-8859-1").encode("UTF-8")
292
- hash[:error] = @error.force_encoding("ISO-8859-1").encode("UTF-8")
293
- hash[:exitstatus] = @exitstatus
294
- hash[:user] = @user
295
- hash[:machine] = @machine
296
- hash[:context] = "Raykit.Command"
297
- hash
298
- end
299
-
300
- def from_hash(hash)
301
- @command = hash["command"]
302
- @directory = hash["directory"]
303
- @timeout = hash["timeout"]
304
- @start_time = hash["start_time"]
305
- @elapsed = hash["elapsed"]
306
- @output = hash["output"]
307
- @error = hash["error"]
308
- @exitstatus = hash["exitstatus"]
309
- @user = hash["user"] if hash.include?("user")
310
- @machine = hash["machine"] if hash.include?("machine")
311
- end
312
-
313
- def self.parse(json)
314
- cmd = Command.new("")
315
- cmd.from_hash(JSON.parse(json))
316
- cmd
317
- end
318
-
319
- def self.parse_yaml_commands(yaml)
320
- # commands=Array.new()
321
- data = YAML.safe_load(yaml)
322
- commands = get_script_commands(data)
323
- end
324
-
325
- def self.get_script_commands(hash)
326
- commands = []
327
- if hash.key?("script")
328
- hash["script"].each do |cmd|
329
- commands << cmd
330
- end
331
- end
332
- hash.each do |_k, v|
333
- next unless v.is_a?(Hash)
334
-
335
- subcommands = get_script_commands(v)
336
- subcommands.each do |c|
337
- commands << c
338
- end
339
- end
340
- commands
341
- end
342
- end
343
- end
1
+ require "open3"
2
+ require "timeout"
3
+ require "json"
4
+ require "yaml"
5
+ require "logger"
6
+ require "securerandom"
7
+
8
+ module Raykit
9
+ # Functionality to support executing and logging system commands
10
+ class Command
11
+ @@commands = []
12
+ # The timeout in seconds, defaults to 0 if not specified
13
+ attr_accessor :timeout
14
+ # The working directory, defaults the current directory if not specified
15
+ attr_accessor :directory
16
+ # The start time
17
+ attr_accessor :start_time
18
+ # The execution time in seconds
19
+ attr_accessor :elapsed
20
+ attr_accessor :command, :output, :error, :exitstatus, :user, :machine, :logging_enabled, :success_log_level
21
+
22
+ def init_defaults
23
+ @timeout = 0
24
+ @directory = Dir.pwd
25
+ @output = +""
26
+ @error = +""
27
+ @exitstatus = 0
28
+ @user = Environment.user
29
+ @machine = Environment.machine
30
+ @logging_enabled = true
31
+ end
32
+
33
+ def initialize(command)
34
+ #def initialize(command, timeout = 0, success_log_level = Logger::DEBUG, logging_enabled = true)
35
+ timeout = 0
36
+ success_log_level = nil
37
+ logging_enabled = false
38
+ @@commands = []
39
+ init_defaults
40
+ @success_log_level = success_log_level
41
+ @logging_enabled = logging_enabled
42
+ @command = command
43
+ @timeout = timeout
44
+ #t = Time.now
45
+ @elapsed = 0
46
+ #run if @command.length.positive?
47
+ self
48
+ end
49
+
50
+ def set_timeout(timeout)
51
+ @timeout = timeout
52
+ self
53
+ end
54
+
55
+ def run
56
+ # puts '---running---'
57
+ @start_time = Time.now
58
+ @elapsed = 0
59
+ timer = Timer.new
60
+ if @timeout.zero?
61
+ @output, @error, process_status = Open3.capture3(@command)
62
+ @exitstatus = process_status.exitstatus
63
+ else
64
+ # =================================================
65
+ #puts "@timeout is #{@timeout}"
66
+ Open3.popen3(@command, chdir: @directory) do |_stdin, stdout, stderr, thread|
67
+ tick = 1
68
+ pid = thread.pid
69
+ start = Time.now
70
+ while ((Time.now - start) < @timeout) && thread.alive?
71
+ Kernel.select([stdout, stderr], nil, nil, tick)
72
+ begin
73
+ @output << stdout.read_nonblock(BUFFER_SIZE)
74
+ #@error << stderr.read_nonblock(BUFFER_SIZE)
75
+ rescue IO::WaitReadable
76
+ rescue EOFError
77
+ #puts "rescue block entered"
78
+ @exitstatus = thread.value.exitstatus
79
+ until stdout.eof?
80
+ @output << stdout.read_nonblock(BUFFER_SIZE)
81
+ end
82
+ until stderr.eof?
83
+ @error << stderr.read_nonblock(BUFFER_SIZE)
84
+ end
85
+ break
86
+ end
87
+
88
+ #begin
89
+ # @output << stdout.read_nonblock(BUFFER_SIZE)
90
+ # @error << stderr.read_nonblock(BUFFER_SIZE)
91
+ #rescue IO::WaitReadable
92
+ #rescue EOFError
93
+ # @exitstatus = thread.value.exitstatus
94
+ # until stdout.eof?
95
+ # @output << stdout.read_nonblock(BUFFER_SIZE)
96
+ # end
97
+ # until stderr.eof?
98
+ # @error << stderr.read_nonblock(BUFFER_SIZE)
99
+ # end
100
+ # break
101
+ #end
102
+ end
103
+ sleep 1
104
+ if thread.alive?
105
+ if Gem.win_platform?
106
+ `taskkill /f /pid #{pid}`
107
+ else
108
+ Process.kill("TERM", pid)
109
+ end
110
+ @exitstatus = 5
111
+ @error = +"timed out"
112
+ else
113
+ @exitstatus = thread.value.exitstatus
114
+ end
115
+ end
116
+
117
+ # =================================================
118
+ end
119
+ @elapsed = timer.elapsed
120
+ if @logging_enabled
121
+ log
122
+ if @exitstatus != 0
123
+ to_log_event.to_seq
124
+ else
125
+ # puts '---logging---'
126
+ unless @success_log_level.nil?
127
+ e = to_log_event
128
+ e["Level"] = "Verbose" if @success_log_level == "Verbose"
129
+ e["Level"] = "Debug" if @success_log_level == Logger::DEBUG
130
+ e["Level"] = "Information" if @success_log_level == Logger::INFO
131
+ e["Level"] = "Warning" if @elapsed > 60 * 2
132
+ e.to_seq
133
+ end
134
+ end
135
+ end
136
+ self
137
+ end
138
+
139
+ def to_log_event
140
+ secrets = Secrets.new
141
+ msg = secrets.hide(@command)
142
+ level = "Verbose"
143
+ level = "Warning" if @exitstatus != 0
144
+ output = @output
145
+ error = @error
146
+ output = @output[-1000..-1] if @output.length > 1200
147
+ error = @error[-1000..-1] if @error.length > 1200
148
+ Raykit::LogEvent.new(level, msg, {
149
+ "SourceContext" => "Raykit::Command",
150
+ "Category" => "Command",
151
+ "Timeout" => @timeout,
152
+ "Directory" => @directory,
153
+ "Output" => output,
154
+ "Error" => error,
155
+ "ExitStatus" => @exitstatus,
156
+ "Elapsed" => elapsed_str,
157
+ "ElapsedSeconds" => @elapsed,
158
+ })
159
+ end
160
+
161
+ def log
162
+ # --- Rolling File JSON -----
163
+ log = Logger.new("#{Raykit::Environment.log_dir}/Raykit.Commands.txt", "daily")
164
+ log.formatter = proc do |_severity, _datetime, _progname, msg|
165
+ "#{msg}\n"
166
+ end
167
+ secrets = Secrets.new
168
+ msg = secrets.hide(@command) # "?"# self.summary(false)
169
+ level = "Verbose"
170
+ level = "Warning" if @exitstatus != 0
171
+ event = Raykit::LogEvent.new(level, msg, {
172
+ "Timeout" => @timeout,
173
+ "Directory" => @directory,
174
+ "Output" => @output,
175
+ "Error" => @error,
176
+ "ExitStatus" => @exitstatus,
177
+ "Elapsed" => elapsed_str,
178
+ })
179
+ log.info event.to_json
180
+ # ---------------------------
181
+ begin
182
+ json = JSON.generate(to_hash)
183
+ log_filename = "#{Environment.get_dev_dir("log")}/Raykit.Command/#{SecureRandom.uuid}.json"
184
+ log_dir = File.dirname(log_filename)
185
+ FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
186
+ File.open(log_filename, "w") { |f| f.write(json) }
187
+ if !@exitstatus.nil? && @exitstatus.zero?
188
+ LOG.log("Raykit.Command", Logger::Severity::INFO, json)
189
+ else
190
+ LOG.log("Raykit.Command", Logger::Severity::ERROR, json)
191
+ end
192
+ rescue JSON::GeneratorError => e
193
+ puts to_hash.to_s
194
+ puts e.to_s
195
+ json = JSON.generate(to_hash)
196
+ end
197
+ end
198
+
199
+ def elapsed_str
200
+ if elapsed < 1.0
201
+ end
202
+ "#{format("%.0f", elapsed)}s"
203
+ end
204
+
205
+ def summary(show_directory = false)
206
+ checkmark = "\u2713"
207
+ # warning="\u26A0"
208
+ error = "\u0058"
209
+ symbol = Rainbow(checkmark.encode("utf-8")).green
210
+ symbol = Rainbow(error.encode("utf-8")).red if @exitstatus != 0
211
+ cmd = "#{Rainbow(SECRETS.hide(@command)).yellow}"
212
+ if show_directory
213
+ puts "#{symbol} #{cmd} " + Rainbow("#{elapsed_str}").cyan
214
+ puts Rainbow(" #{@directory}").white + " "
215
+ else
216
+ puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} " + Rainbow("#{elapsed_str}").cyan
217
+ #puts "#{symbol} #{Rainbow(SECRETS.hide(@command)).yellow} (#{elapsed_str})"
218
+ end
219
+ self
220
+ end
221
+
222
+ def details_on_failure
223
+ if @exitstatus != 0
224
+ details
225
+ end
226
+ self
227
+ end
228
+
229
+ def details
230
+ #puts " exit_code: " + @exitstatus.to_s
231
+ summary
232
+ if @output.length > 0
233
+ @output.lines.each do |line|
234
+ puts " " + line
235
+ end
236
+ end
237
+ if @error.length > 0
238
+ @error.lines.each do |line|
239
+ puts " " + line
240
+ end
241
+ end
242
+ self
243
+ end
244
+
245
+ def to_markdown
246
+ checkmark = "\u2713"
247
+ error = "\u0058"
248
+ symbol = checkmark.encode("utf-8")
249
+ symbol = error.encode("utf-8") if @exitstatus != 0
250
+ cmd = "#{SECRETS.hide(@command)}"
251
+ md = "#{symbol} #{SECRETS.hide(@command)} (#{elapsed_str})"
252
+ md
253
+ end
254
+
255
+ def save
256
+ filename = "#{Environment.get_dev_dir("log")}/Commands/#{SecureRandom.uuid}"
257
+ log_dir = File.dirname(filename)
258
+ FileUtils.mkdir_p(log_dir) unless Dir.exist?(log_dir)
259
+
260
+ File.open(filename, "w") do |f|
261
+ f.write(JSON.pretty_generate(to_hash))
262
+ end
263
+ self
264
+ end
265
+
266
+ def save_as(filename)
267
+ File.open(filename, "w") do |f|
268
+ f.write(JSON.pretty_generate(to_hash))
269
+ end
270
+ self
271
+ end
272
+
273
+ def log_to_file(filename)
274
+ File.delete(filename) if File.exist?(filename)
275
+ File.open(filename, "w") { |f|
276
+ f.puts output
277
+ f.puts error
278
+ }
279
+ self
280
+ end
281
+
282
+ def to_hash
283
+ hash = {}
284
+ hash[:command] = @command
285
+ hash[:directory] = @directory
286
+ hash[:timeout] = @timeout
287
+ hash[:start_time] = @start_time
288
+ hash[:elapsed] = @elapsed
289
+ hash[:output] = @output.force_encoding("ISO-8859-1").encode("UTF-8")
290
+ hash[:error] = @error.force_encoding("ISO-8859-1").encode("UTF-8")
291
+ hash[:exitstatus] = @exitstatus
292
+ hash[:user] = @user
293
+ hash[:machine] = @machine
294
+ hash[:context] = "Raykit.Command"
295
+ hash
296
+ end
297
+
298
+ def from_hash(hash)
299
+ @command = hash["command"]
300
+ @directory = hash["directory"]
301
+ @timeout = hash["timeout"]
302
+ @start_time = hash["start_time"]
303
+ @elapsed = hash["elapsed"]
304
+ @output = hash["output"]
305
+ @error = hash["error"]
306
+ @exitstatus = hash["exitstatus"]
307
+ @user = hash["user"] if hash.include?("user")
308
+ @machine = hash["machine"] if hash.include?("machine")
309
+ end
310
+
311
+ def self.parse(json)
312
+ cmd = Command.new("")
313
+ cmd.from_hash(JSON.parse(json))
314
+ cmd
315
+ end
316
+
317
+ def self.parse_yaml_commands(yaml)
318
+ # commands=Array.new()
319
+ data = YAML.safe_load(yaml)
320
+ commands = get_script_commands(data)
321
+ end
322
+
323
+ def self.get_script_commands(hash)
324
+ commands = []
325
+ if hash.key?("script")
326
+ hash["script"].each do |cmd|
327
+ commands << cmd
328
+ end
329
+ end
330
+ hash.each do |_k, v|
331
+ next unless v.is_a?(Hash)
332
+
333
+ subcommands = get_script_commands(v)
334
+ subcommands.each do |c|
335
+ commands << c
336
+ end
337
+ end
338
+ commands
339
+ end
340
+ end
341
+ end