solidstats 3.0.0.beta.1 → 3.0.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +33 -0
  3. data/app/assets/javascripts/solidstats/application.js +257 -0
  4. data/app/assets/javascripts/solidstats/dashboard.js +179 -0
  5. data/app/assets/stylesheets/solidstats/application.css +6 -1
  6. data/app/controllers/solidstats/dashboard_controller.rb +28 -35
  7. data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
  8. data/app/controllers/solidstats/logs_controller.rb +12 -12
  9. data/app/controllers/solidstats/performance_controller.rb +2 -2
  10. data/app/controllers/solidstats/productivity_controller.rb +10 -10
  11. data/app/controllers/solidstats/quality_controller.rb +32 -32
  12. data/app/controllers/solidstats/securities_controller.rb +7 -7
  13. data/app/helpers/solidstats/application_helper.rb +10 -10
  14. data/app/helpers/solidstats/performance_helper.rb +32 -32
  15. data/app/helpers/solidstats/productivity_helper.rb +20 -20
  16. data/app/services/solidstats/bundler_audit_service.rb +13 -13
  17. data/app/services/solidstats/coverage_compass_service.rb +59 -59
  18. data/app/services/solidstats/load_lens_service.rb +90 -70
  19. data/app/services/solidstats/log_size_monitor_service.rb +59 -59
  20. data/app/services/solidstats/my_todo_service.rb +68 -68
  21. data/app/services/solidstats/style_patrol_service.rb +44 -44
  22. data/app/views/layouts/solidstats/application.html.erb +1 -1
  23. data/app/views/solidstats/shared/_quick_actions.html.erb +1 -1
  24. data/config/routes.rb +4 -4
  25. data/lib/generators/solidstats/clean/clean_generator.rb +24 -0
  26. data/lib/generators/solidstats/clean/templates/README +8 -0
  27. data/lib/generators/solidstats/install/install_generator.rb +32 -17
  28. data/lib/generators/solidstats/templates/initializer.rb +112 -0
  29. data/lib/solidstats/asset_compatibility.rb +238 -0
  30. data/lib/solidstats/asset_manifest.rb +205 -0
  31. data/lib/solidstats/engine.rb +49 -9
  32. data/lib/solidstats/version.rb +1 -1
  33. data/lib/solidstats.rb +24 -11
  34. data/lib/tasks/solidstats.rake +67 -0
  35. data/lib/tasks/solidstats_performance.rake +6 -29
  36. data/lib/tasks/solidstats_tasks.rake +16 -0
  37. metadata +14 -5
  38. data/lib/tasks/solidstats_install.rake +0 -13
@@ -1,14 +1,14 @@
1
1
  module Solidstats
2
2
  class LogsController < ApplicationController
3
- layout 'solidstats/dashboard'
4
-
3
+ layout "solidstats/dashboard"
4
+
5
5
  def logs_size
6
6
  @logs_data = Solidstats::LogSizeMonitorService.get_logs_data
7
7
  end
8
8
 
9
9
  def truncate
10
10
  filename = params[:filename]
11
-
11
+
12
12
  # Validate filename presence
13
13
  if filename.blank?
14
14
  return render json: {
@@ -16,20 +16,20 @@ module Solidstats
16
16
  message: "Filename is required"
17
17
  }, status: :bad_request
18
18
  end
19
-
19
+
20
20
  # Remove any path traversal attempts for security
21
21
  filename = File.basename(filename)
22
-
22
+
23
23
  # Ensure it's a .log file
24
- unless filename.end_with?('.log')
24
+ unless filename.end_with?(".log")
25
25
  filename = "#{filename}.log" if filename.present?
26
26
  end
27
27
 
28
28
  # Additional security check
29
- unless filename.end_with?('.log')
30
- return render json: {
31
- status: "error",
32
- message: "Invalid file type. Only .log files can be truncated."
29
+ unless filename.end_with?(".log")
30
+ return render json: {
31
+ status: "error",
32
+ message: "Invalid file type. Only .log files can be truncated."
33
33
  }, status: :bad_request
34
34
  end
35
35
 
@@ -56,7 +56,7 @@ module Solidstats
56
56
  def refresh
57
57
  # Force refresh of log monitoring data
58
58
  result = Solidstats::LogSizeMonitorService.scan_and_cache
59
-
59
+
60
60
  render json: {
61
61
  status: "success",
62
62
  message: "Log data refreshed",
@@ -64,7 +64,7 @@ module Solidstats
64
64
  }
65
65
  rescue StandardError => e
66
66
  render json: {
67
- status: "error",
67
+ status: "error",
68
68
  message: "Failed to refresh log data: #{e.message}"
69
69
  }, status: :internal_server_error
70
70
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Solidstats
4
4
  class PerformanceController < ApplicationController
5
- layout 'solidstats/dashboard'
5
+ layout "solidstats/dashboard"
6
6
 
7
7
  def load_lens
8
8
  @performance_data = LoadLensService.get_performance_data
@@ -14,7 +14,7 @@ module Solidstats
14
14
  # Parse new log entries and refresh cache
15
15
  parse_result = LoadLensService.parse_log_and_save
16
16
  LoadLensService.refresh_data
17
-
17
+
18
18
  if parse_result[:success]
19
19
  redirect_to load_lens_performance_index_path, notice: "LoadLens data refreshed! Parsed #{parse_result[:processed]} new requests."
20
20
  else
@@ -2,37 +2,37 @@
2
2
 
3
3
  module Solidstats
4
4
  class ProductivityController < ApplicationController
5
- layout 'solidstats/dashboard'
6
-
5
+ layout "solidstats/dashboard"
6
+
7
7
  def my_todos
8
8
  @todos = MyTodoService.collect_todos
9
9
  @summary = MyTodoService.get_summary
10
-
10
+
11
11
  # Group todos by type for display
12
12
  @todos_by_type = @todos.group_by { |todo| todo[:type] }
13
-
13
+
14
14
  # Recent todos (first 10 for quick view)
15
15
  @recent_todos = @todos.first(10)
16
-
16
+
17
17
  # Stats for dashboard
18
18
  @stats = {
19
19
  total: @todos.length,
20
20
  by_type: @todos_by_type.transform_values(&:count),
21
21
  files_with_todos: @todos.map { |t| t[:file] }.uniq.length
22
22
  }
23
-
23
+
24
24
  respond_to do |format|
25
25
  format.html
26
26
  format.json { render json: { todos: @todos, summary: @summary, stats: @stats } }
27
27
  end
28
28
  end
29
-
29
+
30
30
  def refresh_todos
31
31
  @todos = MyTodoService.collect_todos(force_refresh: true)
32
-
32
+
33
33
  respond_to do |format|
34
- format.html { redirect_to my_todos_productivity_index_path, notice: 'TODOs refreshed successfully!' }
35
- format.json { render json: { status: 'success', message: 'TODOs refreshed', count: @todos.length } }
34
+ format.html { redirect_to my_todos_productivity_index_path, notice: "TODOs refreshed successfully!" }
35
+ format.json { render json: { status: "success", message: "TODOs refreshed", count: @todos.length } }
36
36
  end
37
37
  end
38
38
  end
@@ -2,13 +2,13 @@
2
2
 
3
3
  module Solidstats
4
4
  class QualityController < ApplicationController
5
- layout 'solidstats/dashboard'
6
-
5
+ layout "solidstats/dashboard"
6
+
7
7
  # Display Standard gem code quality analysis
8
8
  def style_patrol
9
9
  @analysis_data = Solidstats::StylePatrolService.collect_data
10
10
  @summary = Solidstats::StylePatrolService.get_summary
11
-
11
+
12
12
  # Group issues by file for better display
13
13
  if @analysis_data[:issues].present?
14
14
  @issues_by_file = @analysis_data[:issues].group_by { |issue| issue[:file] }
@@ -17,56 +17,56 @@ module Solidstats
17
17
  @issues_by_file = {}
18
18
  @issues_by_severity = {}
19
19
  end
20
-
21
- render 'style_patrol'
20
+
21
+ render "style_patrol"
22
22
  end
23
-
23
+
24
24
  # Force refresh of style patrol data
25
25
  def refresh_style_patrol
26
26
  @analysis_data = Solidstats::StylePatrolService.refresh_cache
27
- redirect_to quality_style_patrol_path, notice: 'Code quality analysis refreshed successfully.'
27
+ redirect_to quality_style_patrol_path, notice: "Code quality analysis refreshed successfully."
28
28
  rescue => e
29
29
  redirect_to quality_style_patrol_path, alert: "Error refreshing analysis: #{e.message}"
30
30
  end
31
-
31
+
32
32
  # Display SimpleCov code coverage analysis
33
33
  def coverage_compass
34
34
  # Get coverage data using the correct method
35
35
  @analysis_data = Solidstats::CoverageCompassService.collect_data
36
-
36
+
37
37
  # Handle different response types from service
38
38
  if @analysis_data
39
39
  if @analysis_data[:setup_required]
40
40
  @setup_instructions = @analysis_data[:instructions]
41
41
  @show_setup = true
42
- @analysis_data[:status] = 'setup_required'
42
+ @analysis_data[:status] = "setup_required"
43
43
  elsif @analysis_data[:stale_data]
44
44
  @stale_warning = true
45
45
  @data_age_hours = @analysis_data[:data_age_hours]
46
46
  @suggestions = @analysis_data[:suggestions]
47
47
  @analysis_data = @analysis_data[:last_coverage] || @analysis_data
48
- @analysis_data[:status] = 'stale'
48
+ @analysis_data[:status] = "stale"
49
49
  elsif @analysis_data[:error]
50
50
  @error_message = @analysis_data[:error]
51
- @analysis_data[:status] = 'error'
51
+ @analysis_data[:status] = "error"
52
52
  else
53
- @analysis_data[:status] = 'success'
53
+ @analysis_data[:status] = "success"
54
54
  end
55
-
55
+
56
56
  # Organize file coverage data for better display
57
57
  organize_coverage_data
58
58
  else
59
59
  set_error_state
60
- @analysis_data = { status: 'error' }
60
+ @analysis_data = { status: "error" }
61
61
  end
62
-
63
- render 'coverage_compass'
62
+
63
+ render "coverage_compass"
64
64
  end
65
-
65
+
66
66
  # Force refresh of coverage compass data
67
67
  def refresh_coverage_compass
68
68
  Solidstats::CoverageCompassService.refresh_cache
69
- redirect_to quality_coverage_compass_path, notice: 'Code coverage analysis refreshed successfully.'
69
+ redirect_to quality_coverage_compass_path, notice: "Code coverage analysis refreshed successfully."
70
70
  rescue => e
71
71
  redirect_to quality_coverage_compass_path, alert: "Error refreshing coverage analysis: #{e.message}"
72
72
  end
@@ -85,7 +85,7 @@ module Solidstats
85
85
  missed_lines: data[:missed_lines] || []
86
86
  }
87
87
  end
88
-
88
+
89
89
  # Group files by coverage ranges for better visualization
90
90
  @coverage_ranges = {
91
91
  excellent: @file_coverage.select { |f| f[:coverage_percent] >= 90 },
@@ -97,7 +97,7 @@ module Solidstats
97
97
  @file_coverage = []
98
98
  @coverage_ranges = { excellent: [], good: [], needs_improvement: [], poor: [] }
99
99
  end
100
-
100
+
101
101
  # Set summary data
102
102
  @summary = {
103
103
  overall_coverage: @analysis_data[:overall_coverage] || 0,
@@ -115,15 +115,15 @@ module Solidstats
115
115
  @error_message = "Failed to retrieve coverage data"
116
116
  @file_coverage = []
117
117
  @coverage_ranges = { excellent: [], good: [], needs_improvement: [], poor: [] }
118
- @summary = {
119
- overall_coverage: 0,
118
+ @summary = {
119
+ overall_coverage: 0,
120
120
  coverage_percent: 0,
121
- total_lines: 0,
122
- covered_lines: 0,
121
+ total_lines: 0,
122
+ covered_lines: 0,
123
123
  missed_lines: 0,
124
124
  total_files: 0,
125
125
  health_score: 0,
126
- coverage_grade: 'F'
126
+ coverage_grade: "F"
127
127
  }
128
128
  end
129
129
 
@@ -140,12 +140,12 @@ module Solidstats
140
140
 
141
141
  def determine_coverage_grade(coverage_percent)
142
142
  case coverage_percent
143
- when 90..100 then 'A+'
144
- when 80..89 then 'A'
145
- when 70..79 then 'B'
146
- when 60..69 then 'C'
147
- when 50..59 then 'D'
148
- else 'F'
143
+ when 90..100 then "A+"
144
+ when 80..89 then "A"
145
+ when 70..79 then "B"
146
+ when 60..69 then "C"
147
+ when 50..59 then "D"
148
+ else "F"
149
149
  end
150
150
  end
151
151
  end
@@ -2,27 +2,27 @@
2
2
 
3
3
  module Solidstats
4
4
  class SecuritiesController < ApplicationController
5
- layout 'solidstats/dashboard'
6
-
5
+ layout "solidstats/dashboard"
6
+
7
7
  # Display bundler audit security vulnerabilities
8
8
  def bundler_audit
9
9
  @vulnerabilities_data = Solidstats::BundlerAuditService.fetch_vulnerabilities
10
10
  @vulnerabilities = @vulnerabilities_data.dig("output", "results") || []
11
11
  @summary = Solidstats::BundlerAuditService.summary
12
12
  @last_updated = @vulnerabilities_data.dig("output", "created_at")
13
-
13
+
14
14
  # Group vulnerabilities by severity for better display
15
15
  @vulnerabilities_by_severity = @vulnerabilities.group_by do |vuln|
16
16
  vuln.dig("advisory", "criticality") || "unknown"
17
17
  end
18
-
19
- render 'bundler_audit'
18
+
19
+ render "bundler_audit"
20
20
  end
21
-
21
+
22
22
  # Force refresh of bundler audit data
23
23
  def refresh_bundler_audit
24
24
  @vulnerabilities_data = Solidstats::BundlerAuditService.scan_and_cache
25
- redirect_to securities_bundler_audit_path, notice: 'Security vulnerabilities refreshed successfully.'
25
+ redirect_to securities_bundler_audit_path, notice: "Security vulnerabilities refreshed successfully."
26
26
  rescue => e
27
27
  redirect_to securities_bundler_audit_path, alert: "Error refreshing vulnerabilities: #{e.message}"
28
28
  end
@@ -2,9 +2,9 @@ module Solidstats
2
2
  module ApplicationHelper
3
3
  def time_ago_in_words(from_time)
4
4
  return "just now" if from_time.nil?
5
-
5
+
6
6
  distance_in_seconds = (Time.current - from_time).to_i
7
-
7
+
8
8
  case distance_in_seconds
9
9
  when 0..29
10
10
  "just now"
@@ -24,10 +24,10 @@ module Solidstats
24
24
 
25
25
  def quick_actions
26
26
  [
27
- { icon: 'refresh-ccw', label: 'Refresh All', color: 'blue' },
28
- { icon: 'download', label: 'Export Data', color: 'green' },
29
- { icon: 'bar-chart-2', label: 'View Reports', color: 'purple' },
30
- { icon: 'tool', label: 'Settings', color: 'orange' }
27
+ { icon: "refresh-ccw", label: "Refresh All", color: "blue" },
28
+ { icon: "download", label: "Export Data", color: "green" },
29
+ { icon: "bar-chart-2", label: "View Reports", color: "purple" },
30
+ { icon: "tool", label: "Settings", color: "orange" }
31
31
  ]
32
32
  end
33
33
 
@@ -41,10 +41,10 @@ module Solidstats
41
41
 
42
42
  combined_css = css_files.map do |file_path|
43
43
  relative_path = file_path.gsub("#{engine_root}/app/assets/stylesheets/solidstats/", "")
44
-
44
+
45
45
  # Skip manifest files (application.css with require statements)
46
46
  next if relative_path == "application.css"
47
-
47
+
48
48
  "/* === #{relative_path} === */\n#{File.read(file_path)}"
49
49
  end.compact.join("\n\n")
50
50
 
@@ -80,7 +80,7 @@ module Solidstats
80
80
  if (typeof feather !== 'undefined') {
81
81
  feather.replace();
82
82
  }
83
-
83
+ #{' '}
84
84
  // Auto-refresh functionality with animations
85
85
  setInterval(function() {
86
86
  document.querySelectorAll('.card').forEach(function(card) {
@@ -114,7 +114,7 @@ module Solidstats
114
114
  if (typeof feather !== 'undefined') {
115
115
  feather.replace();
116
116
  }
117
-
117
+ #{' '}
118
118
  // Auto-refresh functionality with animations
119
119
  setInterval(function() {
120
120
  document.querySelectorAll('.card').forEach(function(card) {
@@ -5,15 +5,15 @@ module Solidstats
5
5
  # Provides view helpers for displaying performance metrics
6
6
  module PerformanceHelper
7
7
  def load_lens_status_class(avg_response_time, error_rate)
8
- return 'text-error' if error_rate > 10
9
- return 'text-warning' if avg_response_time > 1000
10
- 'text-success'
8
+ return "text-error" if error_rate > 10
9
+ return "text-warning" if avg_response_time > 1000
10
+ "text-success"
11
11
  end
12
12
 
13
13
  def load_lens_status_icon(avg_response_time, error_rate)
14
- return 'alert-circle' if error_rate > 10
15
- return 'alert-triangle' if avg_response_time > 1000
16
- 'check-circle'
14
+ return "alert-circle" if error_rate > 10
15
+ return "alert-triangle" if avg_response_time > 1000
16
+ "check-circle"
17
17
  end
18
18
 
19
19
  def format_response_time(time_ms)
@@ -29,58 +29,58 @@ module Solidstats
29
29
  def request_status_badge_class(status)
30
30
  case status.to_i
31
31
  when 200..299
32
- 'badge-success'
32
+ "badge-success"
33
33
  when 300..399
34
- 'badge-info'
34
+ "badge-info"
35
35
  when 400..499
36
- 'badge-warning'
36
+ "badge-warning"
37
37
  when 500..599
38
- 'badge-error'
38
+ "badge-error"
39
39
  else
40
- 'badge-neutral'
40
+ "badge-neutral"
41
41
  end
42
42
  end
43
43
 
44
44
  def response_time_color_class(time_ms)
45
- return 'text-base-content' if time_ms.nil? || time_ms == 0
46
-
45
+ return "text-base-content" if time_ms.nil? || time_ms == 0
46
+
47
47
  case time_ms.to_f
48
48
  when 0..100
49
- 'text-success'
49
+ "text-success"
50
50
  when 100..500
51
- 'text-info'
51
+ "text-info"
52
52
  when 500..1000
53
- 'text-warning'
53
+ "text-warning"
54
54
  else
55
- 'text-error'
55
+ "text-error"
56
56
  end
57
57
  end
58
58
 
59
59
  def performance_trend_indicator(current, previous)
60
- return '' if current.nil? || previous.nil? || previous == 0
61
-
60
+ return "" if current.nil? || previous.nil? || previous == 0
61
+
62
62
  percentage_change = ((current - previous) / previous.to_f) * 100
63
-
63
+
64
64
  if percentage_change > 5
65
- content_tag(:span, '', class: 'text-error', title: "#{percentage_change.round(1)}% slower")
65
+ content_tag(:span, "", class: "text-error", title: "#{percentage_change.round(1)}% slower")
66
66
  elsif percentage_change < -5
67
- content_tag(:span, '', class: 'text-success', title: "#{percentage_change.abs.round(1)}% faster")
67
+ content_tag(:span, "", class: "text-success", title: "#{percentage_change.abs.round(1)}% faster")
68
68
  else
69
- content_tag(:span, '', class: 'text-info', title: 'Similar performance')
69
+ content_tag(:span, "", class: "text-info", title: "Similar performance")
70
70
  end
71
71
  end
72
72
 
73
- def load_lens_metric_badge(value, threshold_warning, threshold_error, unit = 'ms')
73
+ def load_lens_metric_badge(value, threshold_warning, threshold_error, unit = "ms")
74
74
  formatted_value = "#{value}#{unit}"
75
-
75
+
76
76
  badge_class = if value > threshold_error
77
- 'badge-error'
78
- elsif value > threshold_warning
79
- 'badge-warning'
80
- else
81
- 'badge-success'
82
- end
83
-
77
+ "badge-error"
78
+ elsif value > threshold_warning
79
+ "badge-warning"
80
+ else
81
+ "badge-success"
82
+ end
83
+
84
84
  content_tag(:span, formatted_value, class: "badge #{badge_class}")
85
85
  end
86
86
  end
@@ -4,34 +4,34 @@ module Solidstats
4
4
  module ProductivityHelper
5
5
  def todo_type_color(type)
6
6
  case type.to_s.downcase
7
- when 'todo' then 'primary'
8
- when 'fixme' then 'error'
9
- when 'hack' then 'warning'
10
- when 'note' then 'info'
11
- when 'bug' then 'error'
12
- else 'neutral'
7
+ when "todo" then "primary"
8
+ when "fixme" then "error"
9
+ when "hack" then "warning"
10
+ when "note" then "info"
11
+ when "bug" then "error"
12
+ else "neutral"
13
13
  end
14
14
  end
15
-
15
+
16
16
  def todo_type_icon(type)
17
17
  case type.to_s.downcase
18
- when 'todo' then '📝'
19
- when 'fixme' then '🔧'
20
- when 'hack' then '⚠️'
21
- when 'note' then '📋'
22
- when 'bug' then '🐛'
23
- else '📌'
18
+ when "todo" then "📝"
19
+ when "fixme" then "🔧"
20
+ when "hack" then "⚠️"
21
+ when "note" then "📋"
22
+ when "bug" then "🐛"
23
+ else "📌"
24
24
  end
25
25
  end
26
-
26
+
27
27
  def todo_priority_class(type)
28
28
  case type.to_s.downcase
29
- when 'bug' then 'priority-high'
30
- when 'fixme' then 'priority-high'
31
- when 'hack' then 'priority-medium'
32
- when 'todo' then 'priority-low'
33
- when 'note' then 'priority-low'
34
- else 'priority-low'
29
+ when "bug" then "priority-high"
30
+ when "fixme" then "priority-high"
31
+ when "hack" then "priority-medium"
32
+ when "todo" then "priority-low"
33
+ when "note" then "priority-low"
34
+ else "priority-low"
35
35
  end
36
36
  end
37
37
  end
@@ -21,7 +21,7 @@ module Solidstats
21
21
  # @return [Hash] Fresh vulnerability data
22
22
  def scan_and_cache
23
23
  Rails.logger.info("Running bundler audit scan...")
24
-
24
+
25
25
  begin
26
26
  vulnerabilities_data = collect_bundler_audit_data
27
27
  save_to_cache(vulnerabilities_data)
@@ -54,7 +54,7 @@ module Solidstats
54
54
  # @return [Boolean] true if cache is stale or missing
55
55
  def cache_stale?
56
56
  return true unless File.exist?(CACHE_FILE)
57
-
57
+
58
58
  file_age = Time.current - File.mtime(CACHE_FILE)
59
59
  file_age > CACHE_HOURS.hours
60
60
  end
@@ -63,7 +63,7 @@ module Solidstats
63
63
  # @return [Hash] Cached vulnerability data
64
64
  def load_cached_data
65
65
  return { "output" => { "results" => [] } } unless File.exist?(CACHE_FILE)
66
-
66
+
67
67
  JSON.parse(File.read(CACHE_FILE))
68
68
  rescue JSON::ParserError => e
69
69
  Rails.logger.error("Error parsing bundler audit cache: #{e.message}")
@@ -75,17 +75,17 @@ module Solidstats
75
75
  def collect_bundler_audit_data
76
76
  # Run bundler audit with JSON format
77
77
  result = `bundle audit check --update --format json 2>&1`
78
-
78
+
79
79
  # Check if bundler-audit is installed
80
80
  if $?.exitstatus == 127 || result.include?("command not found")
81
81
  raise "bundler-audit gem is not installed. Please run: gem install bundler-audit"
82
82
  end
83
-
83
+
84
84
  # Extract JSON part from output (bundler-audit may include extra text)
85
85
  json_match = result.match(/(\{.*\})/m)
86
86
  if json_match
87
87
  parsed_data = JSON.parse(json_match[1])
88
-
88
+
89
89
  # Add metadata
90
90
  {
91
91
  "output" => {
@@ -120,26 +120,26 @@ module Solidstats
120
120
  # @param data [Hash] Vulnerability data
121
121
  def update_summary_json(data)
122
122
  summary_file = Rails.root.join("solidstats", "summary.json")
123
-
123
+
124
124
  # Ensure directory exists
125
125
  FileUtils.mkdir_p(File.dirname(summary_file))
126
-
126
+
127
127
  # Load existing summary or create new
128
128
  existing_summary = if File.exist?(summary_file)
129
129
  JSON.parse(File.read(summary_file))
130
130
  else
131
131
  {}
132
132
  end
133
-
133
+
134
134
  # Get vulnerability count and status
135
135
  results = data.dig("output", "results") || []
136
136
  vuln_count = results.count
137
137
  status = determine_status(vuln_count)
138
-
138
+
139
139
  # Calculate severity distribution
140
140
  severity_counts = results.group_by { |r| r.dig("advisory", "criticality") || "unknown" }
141
141
  .transform_values(&:count)
142
-
142
+
143
143
  # Create badges for severity levels
144
144
  badges = []
145
145
  %w[critical high medium low].each do |severity|
@@ -151,7 +151,7 @@ module Solidstats
151
151
  }
152
152
  end
153
153
  end
154
-
154
+
155
155
  # Update summary
156
156
  existing_summary["Security Vulnerabilities"] = {
157
157
  "icon" => "shield-alert",
@@ -161,7 +161,7 @@ module Solidstats
161
161
  "url" => "/solidstats/securities/bundler_audit",
162
162
  "badges" => badges
163
163
  }
164
-
164
+
165
165
  # Save updated summary
166
166
  File.write(summary_file, JSON.pretty_generate(existing_summary))
167
167
  Rails.logger.info("Updated summary.json with security vulnerabilities")