greenhat 0.4.0 → 0.6.1
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/bin/greenhat +1 -2
- data/lib/greenhat/accessors/disk.rb +1 -1
- data/lib/greenhat/accessors/gitlab.rb +4 -2
- data/lib/greenhat/archive.rb +7 -1
- data/lib/greenhat/cli.rb +24 -11
- data/lib/greenhat/entrypoint.rb +37 -34
- data/lib/greenhat/host.rb +1 -1
- data/lib/greenhat/logbot.rb +1 -1
- data/lib/greenhat/paper/flag_helper.rb +18 -0
- data/lib/greenhat/paper/paper_helper.rb +118 -0
- data/lib/greenhat/paper.rb +34 -0
- data/lib/greenhat/reports/builder.rb +98 -0
- data/lib/greenhat/reports/helpers.rb +101 -0
- data/lib/greenhat/reports/internal_methods.rb +156 -0
- data/lib/greenhat/reports/reports/errors.rb +49 -0
- data/lib/greenhat/reports/reports/faststats.rb +42 -0
- data/lib/greenhat/reports/reports/full.rb +143 -0
- data/lib/greenhat/reports/runner.rb +58 -0
- data/lib/greenhat/reports/shared.rb +37 -0
- data/lib/greenhat/reports/shell_helper.rb +34 -0
- data/lib/greenhat/reports.rb +79 -0
- data/lib/greenhat/settings.rb +35 -8
- data/lib/greenhat/shell/args.rb +9 -9
- data/lib/greenhat/shell/color_string.rb +1 -1
- data/lib/greenhat/shell/faststats.rb +24 -5
- data/lib/greenhat/shell/field_helper.rb +1 -1
- data/lib/greenhat/shell/filter_help.rb +65 -185
- data/lib/greenhat/shell/gitlab.rb +1 -0
- data/lib/greenhat/shell/log.rb +24 -30
- data/lib/greenhat/shell/markdown.rb +355 -352
- data/lib/greenhat/shell/process.rb +11 -5
- data/lib/greenhat/shell/query.rb +534 -0
- data/lib/greenhat/shell/report.rb +415 -410
- data/lib/greenhat/shell/reports.rb +41 -0
- data/lib/greenhat/shell/shell_helper.rb +95 -387
- data/lib/greenhat/shell.rb +22 -3
- data/lib/greenhat/thing/file_types.rb +30 -1
- data/lib/greenhat/thing/formatters/api_json.rb +4 -2
- data/lib/greenhat/thing/formatters/bracket_log.rb +1 -1
- data/lib/greenhat/thing/formatters/clean_raw.rb +1 -1
- data/lib/greenhat/thing/formatters/colon_split_strip.rb +2 -2
- data/lib/greenhat/thing/formatters/dotenv.rb +1 -1
- data/lib/greenhat/thing/formatters/format.rb +0 -11
- data/lib/greenhat/thing/formatters/free_m.rb +2 -2
- data/lib/greenhat/thing/formatters/json.rb +41 -17
- data/lib/greenhat/thing/formatters/json_shellwords.rb +3 -2
- data/lib/greenhat/thing/formatters/kube_json.rb +3 -2
- data/lib/greenhat/thing/formatters/multiline_json.rb +1 -1
- data/lib/greenhat/thing/formatters/nginx.rb +5 -1
- data/lib/greenhat/thing/formatters/runner_log.rb +70 -0
- data/lib/greenhat/thing/formatters/table.rb +17 -3
- data/lib/greenhat/thing/formatters/time_json.rb +12 -1
- data/lib/greenhat/thing/helpers.rb +0 -11
- data/lib/greenhat/thing/info_format.rb +4 -4
- data/lib/greenhat/thing/kind.rb +1 -1
- data/lib/greenhat/thing.rb +21 -25
- data/lib/greenhat/version.rb +1 -1
- data/lib/greenhat.rb +6 -8
- metadata +32 -4
- data/lib/greenhat/pry_helpers.rb +0 -51
- data/lib/greenhat/thing/super_log.rb +0 -102
@@ -0,0 +1,41 @@
|
|
1
|
+
# Reporting
|
2
|
+
require 'greenhat/reports/shared' # Methods for Shell and Builder
|
3
|
+
require 'greenhat/reports/internal_methods' # Class / Execution Only
|
4
|
+
require 'greenhat/reports/runner' # Class execution
|
5
|
+
require 'greenhat/reports/builder' # CLI Reports
|
6
|
+
require 'greenhat/reports/shell_helper'
|
7
|
+
|
8
|
+
module GreenHat
|
9
|
+
# CLI Helper
|
10
|
+
module Shell
|
11
|
+
# Logs
|
12
|
+
module Reports
|
13
|
+
# Easy Show All
|
14
|
+
def self.ls(raw = [])
|
15
|
+
filter, _flags, _args = Args.parse(raw)
|
16
|
+
|
17
|
+
list = GreenHat::ShellHelper::Reports.list
|
18
|
+
list.select! { |x| x.include? filter.first } unless filter.blank?
|
19
|
+
|
20
|
+
list.each do |x|
|
21
|
+
puts "- #{File.basename(x, '.rb').pastel(:yellow)}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.default(raw = [])
|
26
|
+
list, flags, args = Args.parse(raw)
|
27
|
+
|
28
|
+
# Collect Reporst to Run
|
29
|
+
run_list = GreenHat::ShellHelper::Reports.list.select do |entry|
|
30
|
+
list.include? File.basename(entry, '.rb')
|
31
|
+
end
|
32
|
+
|
33
|
+
run_list.uniq!
|
34
|
+
|
35
|
+
run_list.each do |file|
|
36
|
+
GreenHat::ShellHelper::Reports.run(file: file, args: args, flags: flags)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -48,23 +48,50 @@ module GreenHat
|
|
48
48
|
|
49
49
|
# Check if content needs to paged, or if auto_height is off
|
50
50
|
if Page.skip?(flags, data)
|
51
|
-
|
51
|
+
create_ledger(data, flags).each { |entry| puts entry.render }
|
52
52
|
return true
|
53
53
|
end
|
54
54
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
output = "\n#{entry_show(flags, entry)}"
|
55
|
+
# Create Jobs
|
56
|
+
# Generate List of Formatted Output
|
57
|
+
ledger = create_ledger(data, flags)
|
59
58
|
|
60
|
-
|
59
|
+
# Start Thread
|
60
|
+
# TODO: Confirm / Test or Remove
|
61
|
+
unless flags.no_paper_thread
|
62
|
+
thread = Thread.new do
|
63
|
+
pending = ledger.clone
|
64
|
+
loop do
|
65
|
+
pending = pending.reject(&:done)
|
66
|
+
break if pending.empty?
|
61
67
|
|
62
|
-
|
63
|
-
|
68
|
+
pending.first.async
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
64
72
|
|
65
|
-
|
73
|
+
TTY::Pager.page do |pager|
|
74
|
+
ledger.each do |paper|
|
75
|
+
# New Line is only needed on pager.write
|
76
|
+
pager.write "#{paper.render}\n"
|
66
77
|
end
|
67
78
|
end
|
79
|
+
|
80
|
+
# Terminate
|
81
|
+
thread.kill unless flags.no_paper_thread
|
82
|
+
|
83
|
+
# -----------------
|
84
|
+
end
|
85
|
+
|
86
|
+
# Collect Class Objects for output
|
87
|
+
def self.create_ledger(data, flags)
|
88
|
+
data.map do |x|
|
89
|
+
Paper.new(data: x, flags: flags)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.puts(text = '')
|
94
|
+
$stdout.puts text
|
68
95
|
end
|
69
96
|
|
70
97
|
# Entry Shower / Top Level
|
@@ -72,11 +99,13 @@ module GreenHat
|
|
72
99
|
LogBot.debug('Entry Show', entry.class) if ENV['DEBUG']
|
73
100
|
case entry
|
74
101
|
when Hash then render_table(entry, flags)
|
75
|
-
when Float, Integer
|
102
|
+
when Float, Integer
|
76
103
|
format_table_entry(flags, entry, key)
|
77
|
-
|
104
|
+
# Ignore Special Formatting for Strings / Usually already formatted
|
78
105
|
when String
|
79
106
|
entry
|
107
|
+
when Array
|
108
|
+
render_array_table(entry, flags)
|
80
109
|
else
|
81
110
|
LogBot.warn('Shell Show', "Unknown #{entry.class}")
|
82
111
|
nil
|
@@ -90,13 +119,12 @@ module GreenHat
|
|
90
119
|
when Float, Integer || entry.numeric?
|
91
120
|
flags.key?(:round) ? entry.to_f.round(flags.round).ai : entry.ai
|
92
121
|
|
93
|
-
|
122
|
+
# General Inspecting
|
94
123
|
when Hash then entry.ai(ruby19_syntax: true)
|
95
124
|
|
96
125
|
# Arrays often contain Hashes. Dangerous Recursive?
|
97
126
|
when Array
|
98
127
|
entry.map { |x| format_table_entry(flags, x) }.join("\n")
|
99
|
-
|
100
128
|
when Time
|
101
129
|
entry.to_s.pastel(:bright_white)
|
102
130
|
|
@@ -118,9 +146,25 @@ module GreenHat
|
|
118
146
|
end
|
119
147
|
end
|
120
148
|
|
149
|
+
def self.render_array_table(entry, flags)
|
150
|
+
table_style = flags[:table_style]&.to_sym || :unicode
|
151
|
+
header, rows = entry
|
152
|
+
table = TTY::Table.new(header: header, rows: rows)
|
153
|
+
|
154
|
+
LogBot.debug('Rendering Entries') if ENV['DEBUG']
|
155
|
+
output = table.render(table_style, padding: [0, 1, 0, 1], multiline: true) do |renderer|
|
156
|
+
renderer.border.style = :cyan
|
157
|
+
end
|
158
|
+
|
159
|
+
# Line breaks for basic tables
|
160
|
+
output += "\n" if flags[:table_style] == 'basic'
|
161
|
+
|
162
|
+
output
|
163
|
+
end
|
164
|
+
|
121
165
|
# Print the Table in a Nice way
|
122
166
|
def self.render_table(entry, flags)
|
123
|
-
entry = entry.
|
167
|
+
entry = entry.to_h { |k, v| [k, format_table_entry(flags, v, k)] }
|
124
168
|
# Pre-format Entry
|
125
169
|
|
126
170
|
table_style = flags[:table_style]&.to_sym || :unicode
|
@@ -136,9 +180,6 @@ module GreenHat
|
|
136
180
|
output += "\n" if flags[:table_style] == 'basic'
|
137
181
|
|
138
182
|
output
|
139
|
-
|
140
|
-
# LogBot.debug('Finish Render Table') if ENV['DEBUG']
|
141
|
-
# Fall Back to Amazing Inspect
|
142
183
|
rescue StandardError => e
|
143
184
|
if ENV['DEBUG']
|
144
185
|
LogBot.warn('Table', message: e.message)
|
@@ -168,201 +209,7 @@ module GreenHat
|
|
168
209
|
# Default to everything
|
169
210
|
files = Thing.all.map(&:name) if files.empty?
|
170
211
|
|
171
|
-
|
172
|
-
end
|
173
|
-
|
174
|
-
# Main Entry Point for Filtering
|
175
|
-
def self.filter_start(files, flags = {}, args = {})
|
176
|
-
# Convert to Things
|
177
|
-
logs = ShellHelper.find_things(files, flags).select(&:processed?)
|
178
|
-
|
179
|
-
# Ignore Archive/Host Dividers
|
180
|
-
if flags[:combine]
|
181
|
-
results = logs.reject(&:blank?).map(&:data).flatten.compact
|
182
|
-
ShellHelper.filter(results, flags, args)
|
183
|
-
else
|
184
|
-
# Iterate and Preserve Archive/Host Index
|
185
|
-
logs.each_with_object({}) do |log, obj|
|
186
|
-
# Ignore Empty Results / No Thing
|
187
|
-
next if log&.blank?
|
188
|
-
|
189
|
-
# Include Total Count in Name
|
190
|
-
results = ShellHelper.filter(log.data, flags, args)
|
191
|
-
duration = calculate_duration(results)
|
192
|
-
|
193
|
-
title = [
|
194
|
-
log.friendly_name,
|
195
|
-
" #{results.count}".pastel(:bright_black)
|
196
|
-
|
197
|
-
]
|
198
|
-
|
199
|
-
# Append Duration
|
200
|
-
title.push(" #{duration.pastel(:cyan, :dim)}") unless duration.blank?
|
201
|
-
|
202
|
-
# Save unless empty
|
203
|
-
obj[title.join] = results unless results.count.zero?
|
204
|
-
|
205
|
-
obj
|
206
|
-
end
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def self.calculate_duration(results)
|
211
|
-
# Skip for Pluck
|
212
|
-
only_with_time = results.select { |x| x.instance_of?(Hash) && x.key?(:time) }
|
213
|
-
|
214
|
-
# If slice is used ignore
|
215
|
-
return nil if only_with_time.empty?
|
216
|
-
|
217
|
-
sorted = only_with_time.map(&:time).sort
|
218
|
-
humanize_time(sorted.first, sorted.last)
|
219
|
-
end
|
220
|
-
|
221
|
-
# Replace TimeDifference with https://stackoverflow.com/a/4136485/1678507
|
222
|
-
def self.humanize_time(time_start, time_end, increments = 2)
|
223
|
-
miliseconds = (time_end - time_start) * 1000
|
224
|
-
|
225
|
-
list = [[1000, :ms], [60, :s], [60, :m], [24, :h]].map do |count, name|
|
226
|
-
next unless miliseconds.positive?
|
227
|
-
|
228
|
-
miliseconds, n = miliseconds.divmod(count)
|
229
|
-
|
230
|
-
"#{n.to_i}#{name}" unless n.to_i.zero?
|
231
|
-
end
|
232
|
-
|
233
|
-
list.compact.reverse[0..increments - 1].join(' ')
|
234
|
-
end
|
235
|
-
|
236
|
-
# Filter Logic
|
237
|
-
# TODO: Simplify
|
238
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
239
|
-
def self.filter(data, flags = {}, args = {})
|
240
|
-
# results = data.clone.flatten.compact
|
241
|
-
|
242
|
-
# Experimenting with deep clone
|
243
|
-
results = Marshal.load(Marshal.dump(data))
|
244
|
-
results.select! do |row|
|
245
|
-
args.send(flags.logic || :all?) do |arg|
|
246
|
-
filter_row_key(row, arg, flags)
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
# Ensure presecense of a specific field
|
251
|
-
results = filter_exists(results, flags[:exists]) if flags.key?(:exists)
|
252
|
-
|
253
|
-
# Time Zone
|
254
|
-
results = filter_modify_timezone(results, flags[:time_zone]) if flags.key?(:time_zone)
|
255
|
-
|
256
|
-
# Time Filtering
|
257
|
-
results = filter_time(results, flags) if flags.key?(:start) || flags.key?(:end)
|
258
|
-
|
259
|
-
# Strip Results if Slice is defined
|
260
|
-
results = filter_slice(results, flags[:slice]) if flags.key?(:slice)
|
261
|
-
|
262
|
-
# Strip Results if Except is defined
|
263
|
-
results = filter_except(results, flags[:except]) if flags.key?(:except)
|
264
|
-
|
265
|
-
# Remove Blank from either slice or except
|
266
|
-
results.reject!(&:empty?)
|
267
|
-
|
268
|
-
# Sort
|
269
|
-
results.sort_by! { |x| x.slice(*flags[:sort]).values } if flags.key?(:sort)
|
270
|
-
|
271
|
-
# JSON Formatting
|
272
|
-
results = results.map { |x| Oj.dump(x) } if flags.key?(:json)
|
273
|
-
|
274
|
-
# Show Unique Only
|
275
|
-
results = filter_uniq(results, flags[:uniq]) if flags.key?(:uniq)
|
276
|
-
|
277
|
-
# Reverse
|
278
|
-
results.reverse! if flags[:reverse]
|
279
|
-
|
280
|
-
# Count occurrences / Skip Results
|
281
|
-
return filter_stats(results, flags) if flags.key?(:stats)
|
282
|
-
|
283
|
-
# Limit before Pluck / Flattening
|
284
|
-
results = filter_limit(results, flags[:limit]) if flags.key?(:limit)
|
285
|
-
|
286
|
-
# Pluck
|
287
|
-
results = filter_pluck(results, flags[:pluck]) if flags.key?(:pluck)
|
288
|
-
|
289
|
-
results
|
290
|
-
end
|
291
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
292
|
-
|
293
|
-
# Limit / Ensure Exists and Valid Number
|
294
|
-
def self.filter_limit(results, limit)
|
295
|
-
return results unless limit.integer? && limit.positive?
|
296
|
-
|
297
|
-
results.shift limit
|
298
|
-
end
|
299
|
-
|
300
|
-
def self.filter_modify_timezone(results, time_zone)
|
301
|
-
results.each do |x|
|
302
|
-
next unless x.key? :time
|
303
|
-
|
304
|
-
x[:time] = x[:time].in_time_zone time_zone
|
305
|
-
end
|
306
|
-
|
307
|
-
results
|
308
|
-
end
|
309
|
-
|
310
|
-
# Filter Start and End Times
|
311
|
-
# TODO: This is a bit icky, simplify/dry
|
312
|
-
def self.filter_time(results, flags)
|
313
|
-
if flags.key?(:start)
|
314
|
-
begin
|
315
|
-
time_start = Time.parse(flags[:start])
|
316
|
-
|
317
|
-
results.select! do |x|
|
318
|
-
if x.time
|
319
|
-
time_start < x.time
|
320
|
-
else
|
321
|
-
true
|
322
|
-
end
|
323
|
-
end
|
324
|
-
rescue StandardError
|
325
|
-
puts 'Unable to Process Start Time Filter'.pastel(:red)
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
if flags.key?(:end)
|
330
|
-
begin
|
331
|
-
time_start = Time.parse(flags[:end])
|
332
|
-
|
333
|
-
results.select! do |x|
|
334
|
-
if x.time
|
335
|
-
time_start > x.time
|
336
|
-
else
|
337
|
-
true
|
338
|
-
end
|
339
|
-
end
|
340
|
-
rescue StandardError
|
341
|
-
puts 'Unable to Process End Time Filter'.pastel(:red)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
results
|
346
|
-
end
|
347
|
-
|
348
|
-
def self.filter_except(results, except)
|
349
|
-
# Avoid Empty Results
|
350
|
-
if except.empty?
|
351
|
-
filter_empty_arg('except')
|
352
|
-
return results
|
353
|
-
end
|
354
|
-
|
355
|
-
results.map { |row| row.except(*except) }
|
356
|
-
end
|
357
|
-
|
358
|
-
def self.filter_exists(results, exists)
|
359
|
-
# Avoid Empty Results
|
360
|
-
if exists.empty?
|
361
|
-
filter_empty_arg('exists')
|
362
|
-
return results
|
363
|
-
end
|
364
|
-
|
365
|
-
results.select { |row| (exists - row.keys).empty? }
|
212
|
+
Query.start(files, flags, args)
|
366
213
|
end
|
367
214
|
|
368
215
|
def self.entry_truncate(entry, truncate)
|
@@ -376,184 +223,42 @@ module GreenHat
|
|
376
223
|
"#{entry.to_s[0..truncate]} #{'...'.pastel(:bright_blue)}"
|
377
224
|
end
|
378
225
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
end
|
385
|
-
|
386
|
-
results.map { |row| row.slice(*slice) }
|
387
|
-
end
|
388
|
-
|
389
|
-
def self.filter_pluck(results, pluck)
|
390
|
-
# Avoid Empty Results
|
391
|
-
if pluck.empty?
|
392
|
-
filter_empty_arg('pluck')
|
393
|
-
return results
|
394
|
-
end
|
395
|
-
|
396
|
-
results.map { |x| x.slice(*pluck).values }.flatten
|
397
|
-
end
|
398
|
-
|
399
|
-
def self.filter_uniq(results, unique)
|
400
|
-
# Avoid Empty Results
|
401
|
-
if unique.empty?
|
402
|
-
filter_empty_arg('uniq')
|
403
|
-
return results
|
404
|
-
end
|
405
|
-
|
406
|
-
unique.map do |field|
|
407
|
-
results.uniq { |x| x[field] }
|
408
|
-
end.inject(:&)
|
409
|
-
end
|
410
|
-
|
411
|
-
def self.filter_stats(results, flags)
|
412
|
-
stats = flags[:stats]
|
413
|
-
|
414
|
-
# Avoid Empty Results
|
415
|
-
if stats.empty?
|
416
|
-
filter_empty_arg('stats')
|
417
|
-
return results
|
418
|
-
end
|
419
|
-
|
420
|
-
# Loop through Stats, Separate Hash/Tables
|
421
|
-
stats.map do |field|
|
422
|
-
occurrences = filter_count_occurrences(results, field, flags)
|
423
|
-
|
424
|
-
# Use Truncate For Long Keys
|
425
|
-
occurrences.transform_keys! { |key| key.to_s[0..flags[:truncate]] } if flags[:truncate]
|
426
|
-
|
427
|
-
# Total Occurences
|
428
|
-
total = occurrences.values.sum
|
429
|
-
|
430
|
-
# Percs
|
431
|
-
occurrences.transform_values! do |count|
|
432
|
-
[
|
433
|
-
count,
|
434
|
-
" #{percent(count, total)}%".pastel(:bright_black)
|
435
|
-
]
|
436
|
-
end
|
437
|
-
|
438
|
-
# Sort by total occurances / New Variable for Total
|
439
|
-
output = occurrences.sort_by(&:last).to_h.transform_values!(&:join).to_a
|
440
|
-
|
441
|
-
# Append Header / Total with field name
|
442
|
-
output.unshift([field.to_s.pastel(:bright_black), total])
|
443
|
-
|
444
|
-
# Format
|
445
|
-
output.to_h
|
446
|
-
end
|
447
|
-
end
|
448
|
-
|
449
|
-
# Percent Helper
|
450
|
-
def self.percent(value, total)
|
451
|
-
((value / total.to_f) * 100).round
|
452
|
-
end
|
453
|
-
|
454
|
-
# Helper to Count occurrences
|
455
|
-
def self.filter_count_occurrences(results, field, flags = {})
|
456
|
-
results.each_with_object(Hash.new(0)) do |entry, counts|
|
457
|
-
if entry.key? field
|
458
|
-
# Rounding in pagination breaks stats
|
459
|
-
key = if flags.key?(:round) && entry[field].numeric?
|
460
|
-
entry[field].to_f.round(flags.round)
|
461
|
-
else
|
462
|
-
entry[field]
|
463
|
-
end
|
464
|
-
|
465
|
-
counts[key] += 1
|
466
|
-
else
|
467
|
-
counts['None'.pastel(:bright_black)] += 1
|
468
|
-
end
|
469
|
-
|
470
|
-
counts
|
471
|
-
end
|
472
|
-
end
|
226
|
+
# Total Count Helper
|
227
|
+
def self.total_count(results, flags)
|
228
|
+
# Support Combine Helper
|
229
|
+
# TODO I Believe this should be removed now
|
230
|
+
# total_count_hash(results, flags) if results.instance_of? Hash
|
473
231
|
|
474
|
-
|
475
|
-
puts [
|
476
|
-
'Ignoring'.pastel(:bright_yellow),
|
477
|
-
"--#{arg}".pastel(:cyan),
|
478
|
-
'it requires an argument'.pastel(:red)
|
479
|
-
].join(' ')
|
232
|
+
total_count_array(results, flags) if results.instance_of? Array
|
480
233
|
end
|
481
234
|
|
482
|
-
#
|
483
|
-
def self.
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
# Sensitivity Check / Check for Match / Full Text Searching
|
488
|
-
search_data = arg[:field] == :text ? row : row[arg.field]
|
489
|
-
match = filter_row_entry(search_data.to_s, arg, flags)
|
490
|
-
|
491
|
-
# Pivot of off include vs exclude
|
492
|
-
if arg.bang
|
493
|
-
!match
|
494
|
-
else
|
495
|
-
match
|
235
|
+
# Hash Iteration
|
236
|
+
def self.total_count_hash(results, flags)
|
237
|
+
results.flat_map do |k, values|
|
238
|
+
[k, total_count_array(values, flags)]
|
496
239
|
end
|
497
240
|
end
|
498
241
|
|
499
|
-
#
|
500
|
-
def self.
|
501
|
-
#
|
502
|
-
return
|
242
|
+
# List Calculation
|
243
|
+
def self.total_count_array(results, flags)
|
244
|
+
# Option to only return the count
|
245
|
+
return results.count if flags[:total] == 'simple'
|
503
246
|
|
504
|
-
|
505
|
-
|
247
|
+
output = {}
|
248
|
+
output[:total] = results.count
|
249
|
+
output[:duration] = Query.calculate_duration(results)
|
506
250
|
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
# Cast to String
|
513
|
-
value = arg.value.to_s
|
514
|
-
|
515
|
-
case arg.logic
|
516
|
-
when :include?
|
517
|
-
|
518
|
-
# Exact Case argument
|
519
|
-
unless flags.key?(:case)
|
520
|
-
entry.downcase!
|
521
|
-
value.downcase!
|
522
|
-
end
|
523
|
-
when :>=, :<=
|
524
|
-
entry = entry.to_i if entry.numeric?
|
525
|
-
value = value.to_i if value&.numeric?
|
251
|
+
# Sort / Get first and Last
|
252
|
+
list = results.select(&:time).map(&:time)
|
253
|
+
unless list.blank?
|
254
|
+
output[:start] = list.first
|
255
|
+
output[:end] = list.last
|
526
256
|
end
|
527
257
|
|
528
|
-
|
529
|
-
|
258
|
+
# Hide empty results
|
259
|
+
output.reject! { |_k, v| v.blank? }
|
530
260
|
|
531
|
-
|
532
|
-
entry.send(arg.logic, arg.value)
|
533
|
-
end
|
534
|
-
|
535
|
-
# Total Count Helper
|
536
|
-
def self.total_count(results, flags)
|
537
|
-
results.map do |k, v|
|
538
|
-
puts k
|
539
|
-
# puts "Total: #{v.count.to_s.pastel(:blue)}"
|
540
|
-
# puts "Time Covered: #{calculate_duration(v).pastel(:cyan)}"
|
541
|
-
|
542
|
-
output = {}
|
543
|
-
output[:total] = v.count
|
544
|
-
output[:duration] = calculate_duration(v)
|
545
|
-
|
546
|
-
# Sort / Get first and Last
|
547
|
-
list = v.select(&:time).map(&:time)
|
548
|
-
unless list.blank?
|
549
|
-
output[:start] = list.first
|
550
|
-
output[:end] = list.last
|
551
|
-
end
|
552
|
-
|
553
|
-
puts render_table(output, flags)
|
554
|
-
|
555
|
-
puts
|
556
|
-
end
|
261
|
+
render_table(output, flags)
|
557
262
|
end
|
558
263
|
|
559
264
|
# Table Printer Helper
|
@@ -590,12 +295,12 @@ module GreenHat
|
|
590
295
|
file_list = prepare_list(file_list, base_list)
|
591
296
|
|
592
297
|
# Convert to Things
|
593
|
-
find_things(file_list, flags)
|
298
|
+
find_things(file_list, flags, base_list)
|
594
299
|
end
|
595
300
|
|
596
301
|
# Total Log List Manipulator
|
597
|
-
def self.prepare_list(log_list, base_list = nil
|
598
|
-
base_list ||=
|
302
|
+
def self.prepare_list(log_list, base_list = nil)
|
303
|
+
base_list ||= Thing.list
|
599
304
|
|
600
305
|
# Assume all
|
601
306
|
log_list.push '*' if log_list.empty?
|
@@ -606,21 +311,23 @@ module GreenHat
|
|
606
311
|
log_list
|
607
312
|
end
|
608
313
|
|
609
|
-
# Fuzzy match for things
|
314
|
+
# Fuzzy match for things / List used for processable (Hash Entries)
|
610
315
|
def self.thing_list
|
611
|
-
@thing_list ||= Thing.
|
316
|
+
@thing_list ||= Thing.list.map(&:name)
|
612
317
|
|
613
318
|
@thing_list
|
614
319
|
end
|
615
320
|
|
616
321
|
# Shortcut find things
|
617
|
-
def self.find_things(
|
618
|
-
|
322
|
+
def self.find_things(file_list, flags = {}, base_list = nil)
|
323
|
+
base_list ||= Thing.all
|
324
|
+
|
325
|
+
things = file_list.uniq.flat_map do |file|
|
619
326
|
# If Thing, Return Thing
|
620
327
|
return file if file.instance_of?(Thing)
|
621
328
|
|
622
329
|
if flags.fuzzy_file_match
|
623
|
-
|
330
|
+
base_list.select { |x| x.name.include?(file) || x.type.include?(file) }
|
624
331
|
else
|
625
332
|
Thing.where name: file
|
626
333
|
end
|
@@ -666,5 +373,6 @@ module GreenHat
|
|
666
373
|
puts
|
667
374
|
end
|
668
375
|
end
|
376
|
+
|
669
377
|
# rubocop:enable Metrics/ModuleLength
|
670
378
|
end
|
data/lib/greenhat/shell.rb
CHANGED
@@ -7,6 +7,10 @@ module GreenHat
|
|
7
7
|
# rubocop:enable Lint/Debugger
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.settings
|
11
|
+
Settings.configure_settings
|
12
|
+
end
|
13
|
+
|
10
14
|
def self.web
|
11
15
|
# Load Required Files
|
12
16
|
require 'greenhat/web'
|
@@ -14,10 +18,20 @@ module GreenHat
|
|
14
18
|
GreenHat::Web.toggle
|
15
19
|
end
|
16
20
|
|
21
|
+
# Append Log Location so follow up commands are executed there
|
22
|
+
def self.default(raw_list = [])
|
23
|
+
Cli.move_shell Shell::Log
|
24
|
+
Log.default(raw_list)
|
25
|
+
end
|
26
|
+
|
17
27
|
def self.df
|
18
28
|
Disk.df
|
19
29
|
end
|
20
30
|
|
31
|
+
def self.quit(_raw = {})
|
32
|
+
exit 0
|
33
|
+
end
|
34
|
+
|
21
35
|
def self.ps(raw = {})
|
22
36
|
Process.ps raw
|
23
37
|
end
|
@@ -60,7 +74,7 @@ module GreenHat
|
|
60
74
|
end
|
61
75
|
|
62
76
|
def self.quiet
|
63
|
-
Cli.
|
77
|
+
Cli.quiet_toggle
|
64
78
|
|
65
79
|
if Cli.quiet
|
66
80
|
puts "GreenHat Quiet Logging #{'Off'.pastel(:red)}"
|
@@ -93,8 +107,13 @@ module GreenHat
|
|
93
107
|
end
|
94
108
|
end
|
95
109
|
|
96
|
-
def self.ls
|
97
|
-
|
110
|
+
def self.ls(raw = [])
|
111
|
+
if raw.empty?
|
112
|
+
GreenHat::Cli.help(false)
|
113
|
+
else
|
114
|
+
Cli.move_shell Shell::Log
|
115
|
+
Log.ls(raw)
|
116
|
+
end
|
98
117
|
end
|
99
118
|
|
100
119
|
def self.ll
|