solidstats 1.1.0 → 3.0.0.beta.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/CHANGELOG.md +17 -0
- data/README.md +27 -0
- data/Rakefile +3 -3
- data/app/assets/stylesheets/solidstats/dashboard.css +48 -0
- data/app/controllers/solidstats/dashboard_controller.rb +82 -60
- data/app/controllers/solidstats/logs_controller.rb +72 -0
- data/app/controllers/solidstats/performance_controller.rb +25 -0
- data/app/controllers/solidstats/productivity_controller.rb +39 -0
- data/app/controllers/solidstats/quality_controller.rb +152 -0
- data/app/controllers/solidstats/securities_controller.rb +30 -0
- data/app/helpers/solidstats/application_helper.rb +155 -0
- data/app/helpers/solidstats/performance_helper.rb +87 -0
- data/app/helpers/solidstats/productivity_helper.rb +38 -0
- data/app/services/solidstats/bundler_audit_service.rb +206 -0
- data/app/services/solidstats/coverage_compass_service.rb +335 -0
- data/app/services/solidstats/load_lens_service.rb +454 -0
- data/app/services/solidstats/log_size_monitor_service.rb +205 -74
- data/app/services/solidstats/my_todo_service.rb +242 -0
- data/app/services/solidstats/style_patrol_service.rb +319 -0
- data/app/views/layouts/solidstats/application.html.erb +9 -2
- data/app/views/layouts/solidstats/dashboard.html.erb +84 -0
- data/app/views/solidstats/dashboard/dashboard.html.erb +39 -0
- data/app/views/solidstats/logs/logs_size.html.erb +409 -0
- data/app/views/solidstats/performance/load_lens.html.erb +158 -0
- data/app/views/solidstats/productivity/_todo_list.html.erb +49 -0
- data/app/views/solidstats/productivity/my_todos.html.erb +84 -0
- data/app/views/solidstats/quality/coverage_compass.html.erb +420 -0
- data/app/views/solidstats/quality/style_patrol.html.erb +463 -0
- data/app/views/solidstats/securities/bundler_audit.html.erb +345 -0
- data/app/views/solidstats/shared/_dashboard_card.html.erb +160 -0
- data/app/views/solidstats/shared/_quick_actions.html.erb +26 -0
- data/config/routes.rb +32 -4
- data/lib/generators/solidstats/install/install_generator.rb +28 -2
- data/lib/generators/solidstats/install/templates/README +7 -0
- data/lib/solidstats/version.rb +1 -1
- data/lib/tasks/solidstats_performance.rake +84 -0
- metadata +43 -19
- data/app/services/solidstats/audit_service.rb +0 -56
- data/app/services/solidstats/data_collector_service.rb +0 -83
- data/app/services/solidstats/todo_service.rb +0 -114
- data/app/views/solidstats/dashboard/_log_monitor.html.erb +0 -759
- data/app/views/solidstats/dashboard/_todos.html.erb +0 -151
- data/app/views/solidstats/dashboard/audit/_additional_styles.css +0 -22
- data/app/views/solidstats/dashboard/audit/_audit_badge.html.erb +0 -5
- data/app/views/solidstats/dashboard/audit/_audit_details.html.erb +0 -495
- data/app/views/solidstats/dashboard/audit/_audit_summary.html.erb +0 -26
- data/app/views/solidstats/dashboard/audit/_no_vulnerabilities.html.erb +0 -3
- data/app/views/solidstats/dashboard/audit/_security_audit.html.erb +0 -14
- data/app/views/solidstats/dashboard/audit/_vulnerabilities_table.html.erb +0 -1120
- data/app/views/solidstats/dashboard/audit/_vulnerability_details.html.erb +0 -63
- data/app/views/solidstats/dashboard/index.html.erb +0 -1351
- data/lib/tasks/solidstats_tasks.rake +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd313b23c968d18619f31b5157b96b2a9c75435398121e800deaf6c2e7d4bfd4
|
4
|
+
data.tar.gz: 1f7407373917b47d1ef56ebe7e03138cd36ef111517a930e0c8b6354bc2e389f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adcd27c59ddc3e68c8843583dd274d96312959155c3a0491f06540cd1eb474681bcb7de407b6d286bc1a9e97dd03b4e94099b3e40f5232a37929b3c2565e1cd2
|
7
|
+
data.tar.gz: 515a1510e1911e65f8b7784bf746341112f469de7800170923f3f3f716c537825fe742afa266ecb5fc091ad9c1e3c7aebfbef328eef964da69960a7b0d63506b
|
data/CHANGELOG.md
CHANGED
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
+
## [3.0.0] - 2025-06-11
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- **LoadLens Performance Monitor**: A new development performance monitoring feature that parses development logs to track Rails application performance. It includes metrics for response times, database performance, view rendering, and error rates.
|
12
|
+
- **Style Patrol**: A new code quality analysis feature to enforce style guidelines and improve code health.
|
13
|
+
- **My TODOs Feature**: A new service to manage and track TODO items within the codebase, with a dedicated UI and refresh functionality.
|
14
|
+
- **Bundler Audit Security**: Added security auditing for Bundler, with a dedicated UI to display vulnerability details and remediation suggestions.
|
15
|
+
|
16
|
+
### Changed
|
17
|
+
- **Dashboard Redesign**: The Solidstats dashboard has been completely redesigned with DaisyUI, providing a modern, consistent, and themeable user interface.
|
18
|
+
- **Refactored Services**: The `LogSizeMonitorService` has been refactored for enhanced log management and caching.
|
19
|
+
- **Improved Installation**: The installation process has been enhanced with automatic directory creation and `.gitignore` updates.
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- Updated route aliases and paths for better consistency across the application.
|
23
|
+
- Replaced static dashboard titles with dynamic links for improved navigation.
|
24
|
+
|
8
25
|
## [1.1.0] - 2025-05-23
|
9
26
|
|
10
27
|
### Added
|
data/README.md
CHANGED
@@ -9,6 +9,7 @@ Solidstats is a local-only Rails engine that shows your project's health at `/so
|
|
9
9
|
- Gem impact analysis showing affected gems by severity
|
10
10
|
- Log Size Monitor for tracking and managing application log files
|
11
11
|
- Log file truncation tool for individual or all log files
|
12
|
+
- LoadLens - Development performance monitoring with request tracking
|
12
13
|
- Rubocop offense count and quality metrics
|
13
14
|
- TODO/FIXME tracker with file hotspots
|
14
15
|
- Test coverage summary
|
@@ -77,6 +78,32 @@ You can refresh the dashboard data at any time by clicking the "Refresh" button
|
|
77
78
|
### Code Quality
|
78
79
|
Displays code quality metrics, test coverage, and code health indicators.
|
79
80
|
|
81
|
+
### LoadLens Performance
|
82
|
+
Monitors your Rails app's performance in development by parsing `log/development.log`:
|
83
|
+
- **Response Times**: Average response time across all requests
|
84
|
+
- **Database Performance**: ActiveRecord query timing analysis
|
85
|
+
- **View Rendering**: Template rendering performance metrics
|
86
|
+
- **Error Tracking**: HTTP status code analysis and error rates
|
87
|
+
- **Slow Request Detection**: Identifies requests taking >1000ms
|
88
|
+
- **Request History**: Recent request details with timing breakdown
|
89
|
+
|
90
|
+
LoadLens automatically tracks performance data and provides manual refresh capability. Data is stored in daily rotating JSON files and cleaned up after 7 days.
|
91
|
+
|
92
|
+
**CLI Commands:**
|
93
|
+
```bash
|
94
|
+
# Parse development logs manually
|
95
|
+
rake solidstats:load_lens:parse_logs
|
96
|
+
|
97
|
+
# Refresh performance data
|
98
|
+
rake solidstats:load_lens:refresh
|
99
|
+
|
100
|
+
# View performance summary
|
101
|
+
rake solidstats:load_lens:summary
|
102
|
+
|
103
|
+
# Clean old data files
|
104
|
+
rake solidstats:load_lens:clean_old_data
|
105
|
+
```
|
106
|
+
|
80
107
|
### Tasks
|
81
108
|
Shows a breakdown of TODO, FIXME, and HACK annotations found in your codebase, with file hotspots and expandable details.
|
82
109
|
|
data/Rakefile
CHANGED
@@ -0,0 +1,48 @@
|
|
1
|
+
/* Solidstats Dashboard with DaisyUI */
|
2
|
+
|
3
|
+
/* Custom animations for dashboard */
|
4
|
+
.dashboard-enter {
|
5
|
+
animation: fadeInUp 0.6s ease-out;
|
6
|
+
}
|
7
|
+
|
8
|
+
@keyframes fadeInUp {
|
9
|
+
from {
|
10
|
+
opacity: 0;
|
11
|
+
transform: translateY(30px);
|
12
|
+
}
|
13
|
+
to {
|
14
|
+
opacity: 1;
|
15
|
+
transform: translateY(0);
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
/* Gradient text for brand */
|
20
|
+
.gradient-text {
|
21
|
+
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
|
22
|
+
-webkit-background-clip: text;
|
23
|
+
-webkit-text-fill-color: transparent;
|
24
|
+
background-clip: text;
|
25
|
+
}
|
26
|
+
|
27
|
+
/* Light mode improvements */
|
28
|
+
[data-theme="light"] .card {
|
29
|
+
background-color: #ffffff;
|
30
|
+
border: 1px solid #e5e7eb;
|
31
|
+
}
|
32
|
+
|
33
|
+
[data-theme="light"] .card:hover {
|
34
|
+
box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
35
|
+
}
|
36
|
+
|
37
|
+
[data-theme="light"] .stat-value {
|
38
|
+
color: #1f2937;
|
39
|
+
}
|
40
|
+
|
41
|
+
[data-theme="light"] .card-title {
|
42
|
+
color: #1f2937;
|
43
|
+
}
|
44
|
+
|
45
|
+
[data-theme="light"] .navbar {
|
46
|
+
background-color: #ffffff;
|
47
|
+
border-bottom: 1px solid #e5e7eb;
|
48
|
+
}
|
@@ -1,77 +1,99 @@
|
|
1
1
|
module Solidstats
|
2
2
|
class DashboardController < ApplicationController
|
3
|
+
layout 'solidstats/dashboard'
|
4
|
+
|
3
5
|
TODO_CACHE_FILE = Rails.root.join("tmp", "solidstats_todos.json")
|
4
6
|
AUDIT_CACHE_HOURS = 12 # Configure how many hours before refreshing
|
5
7
|
|
6
|
-
def index
|
7
|
-
# Use new services for data collection
|
8
|
-
audit_service = AuditService.new
|
9
|
-
todo_service = TodoService.new
|
10
|
-
log_monitor_service = LogSizeMonitorService.new
|
11
|
-
|
12
|
-
# Get full data for detailed views
|
13
|
-
@audit_output = audit_service.fetch
|
14
|
-
@todo_items = todo_service.fetch
|
15
|
-
@log_data = log_monitor_service.collect_data
|
16
8
|
|
17
|
-
|
18
|
-
|
19
|
-
@
|
20
|
-
|
21
|
-
|
22
|
-
@rubocop_output = "JSON.parse(`rubocop --format json`)"
|
23
|
-
@coverage = "100"
|
9
|
+
def dashboard
|
10
|
+
# Load dashboard cards from JSON file
|
11
|
+
@dashboard_cards = load_dashboard_cards
|
12
|
+
@quick_actions = quick_actions_data
|
13
|
+
render 'dashboard'
|
24
14
|
end
|
25
15
|
|
26
|
-
# Force refresh all dashboard data
|
27
16
|
def refresh
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# Get updated summaries
|
39
|
-
audit_summary = audit_service.summary
|
40
|
-
todo_summary = todo_service.summary
|
17
|
+
# Refresh all services
|
18
|
+
Solidstats::LogSizeMonitorService.scan_and_cache
|
19
|
+
Solidstats::BundlerAuditService.scan_and_cache
|
20
|
+
Solidstats::MyTodoService.collect_todos
|
21
|
+
Solidstats::StylePatrolService.refresh_cache
|
22
|
+
Solidstats::CoverageCompassService.refresh_cache
|
23
|
+
Solidstats::LoadLensService.scan_and_cache
|
24
|
+
|
25
|
+
redirect_to solidstats_dashboard_path, notice: 'Dashboard data refreshed successfully!'
|
26
|
+
end
|
41
27
|
|
42
|
-
|
43
|
-
last_updated = Time.now.strftime("%B %d, %Y at %H:%M")
|
28
|
+
private
|
44
29
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
30
|
+
def quick_actions_data
|
31
|
+
[
|
32
|
+
{
|
33
|
+
icon: 'refresh-cw',
|
34
|
+
label: 'Refresh Data',
|
35
|
+
color: 'blue',
|
36
|
+
action: 'refresh_dashboard'
|
37
|
+
},
|
38
|
+
{
|
39
|
+
icon: 'settings',
|
40
|
+
label: 'Configure',
|
41
|
+
color: 'purple',
|
42
|
+
action: 'open_settings'
|
43
|
+
},
|
44
|
+
{
|
45
|
+
icon: 'download',
|
46
|
+
label: 'Export',
|
47
|
+
color: 'green',
|
48
|
+
action: 'export_data'
|
49
|
+
},
|
50
|
+
{
|
51
|
+
icon: 'bell',
|
52
|
+
label: 'Alerts',
|
53
|
+
color: 'orange',
|
54
|
+
action: 'view_alerts'
|
55
|
+
}
|
56
|
+
]
|
61
57
|
end
|
62
|
-
|
63
|
-
def
|
64
|
-
|
65
|
-
filename = params[:filename]
|
58
|
+
|
59
|
+
def load_dashboard_cards
|
60
|
+
json_file_path = Rails.root.join("solidstats", "summary.json")
|
66
61
|
|
67
|
-
|
68
|
-
|
69
|
-
|
62
|
+
begin
|
63
|
+
# Read and parse the JSON file
|
64
|
+
json_data = JSON.parse(File.read(json_file_path))
|
65
|
+
|
66
|
+
# Transform the JSON data into the format expected by the view
|
67
|
+
json_data.map do |name, data|
|
68
|
+
{
|
69
|
+
name: name,
|
70
|
+
icon: data["icon"],
|
71
|
+
status: data["status"],
|
72
|
+
value: data["value"],
|
73
|
+
last_updated: Time.parse(data["last_updated"]),
|
74
|
+
url: data["url"],
|
75
|
+
badges: data["badges"] || []
|
76
|
+
}
|
77
|
+
end
|
78
|
+
rescue Errno::ENOENT
|
79
|
+
Rails.logger.warn("Summary JSON file not found, generating initial data...")
|
80
|
+
generate_initial_data
|
81
|
+
retry
|
82
|
+
rescue JSON::ParserError => e
|
83
|
+
Rails.logger.error("Error parsing summary JSON: #{e.message}")
|
84
|
+
# Fallback to empty array if JSON is invalid
|
85
|
+
[]
|
70
86
|
end
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
87
|
+
end
|
88
|
+
|
89
|
+
def generate_initial_data
|
90
|
+
# Force a scan to create initial data if missing
|
91
|
+
Solidstats::LogSizeMonitorService.scan_and_cache
|
92
|
+
Solidstats::BundlerAuditService.scan_and_cache
|
93
|
+
Solidstats::MyTodoService.collect_todos
|
94
|
+
Solidstats::StylePatrolService.refresh_cache
|
95
|
+
Solidstats::CoverageCompassService.refresh_cache
|
96
|
+
Solidstats::DevLogParserService.scan_and_cache
|
75
97
|
end
|
76
98
|
end
|
77
99
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Solidstats
|
2
|
+
class LogsController < ApplicationController
|
3
|
+
layout 'solidstats/dashboard'
|
4
|
+
|
5
|
+
def logs_size
|
6
|
+
@logs_data = Solidstats::LogSizeMonitorService.get_logs_data
|
7
|
+
end
|
8
|
+
|
9
|
+
def truncate
|
10
|
+
filename = params[:filename]
|
11
|
+
|
12
|
+
# Validate filename presence
|
13
|
+
if filename.blank?
|
14
|
+
return render json: {
|
15
|
+
status: "error",
|
16
|
+
message: "Filename is required"
|
17
|
+
}, status: :bad_request
|
18
|
+
end
|
19
|
+
|
20
|
+
# Remove any path traversal attempts for security
|
21
|
+
filename = File.basename(filename)
|
22
|
+
|
23
|
+
# Ensure it's a .log file
|
24
|
+
unless filename.end_with?('.log')
|
25
|
+
filename = "#{filename}.log" if filename.present?
|
26
|
+
end
|
27
|
+
|
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."
|
33
|
+
}, status: :bad_request
|
34
|
+
end
|
35
|
+
|
36
|
+
result = Solidstats::LogSizeMonitorService.truncate_log(filename)
|
37
|
+
|
38
|
+
if result[:status] == "success"
|
39
|
+
render json: {
|
40
|
+
status: "success",
|
41
|
+
message: result[:message] || "Log file truncated successfully",
|
42
|
+
filename: filename,
|
43
|
+
original_size: result[:original_size]
|
44
|
+
}
|
45
|
+
else
|
46
|
+
render json: result, status: :unprocessable_entity
|
47
|
+
end
|
48
|
+
rescue StandardError => e
|
49
|
+
Rails.logger.error("Failed to truncate log #{filename}: #{e.message}")
|
50
|
+
render json: {
|
51
|
+
status: "error",
|
52
|
+
message: "Failed to truncate log: #{e.message}"
|
53
|
+
}, status: :internal_server_error
|
54
|
+
end
|
55
|
+
|
56
|
+
def refresh
|
57
|
+
# Force refresh of log monitoring data
|
58
|
+
result = Solidstats::LogSizeMonitorService.scan_and_cache
|
59
|
+
|
60
|
+
render json: {
|
61
|
+
status: "success",
|
62
|
+
message: "Log data refreshed",
|
63
|
+
data: result
|
64
|
+
}
|
65
|
+
rescue StandardError => e
|
66
|
+
render json: {
|
67
|
+
status: "error",
|
68
|
+
message: "Failed to refresh log data: #{e.message}"
|
69
|
+
}, status: :internal_server_error
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
class PerformanceController < ApplicationController
|
5
|
+
layout 'solidstats/dashboard'
|
6
|
+
|
7
|
+
def load_lens
|
8
|
+
@performance_data = LoadLensService.get_performance_data
|
9
|
+
@metrics = @performance_data[:summary] || {}
|
10
|
+
@recent_requests = @performance_data[:recent_requests] || []
|
11
|
+
end
|
12
|
+
|
13
|
+
def refresh
|
14
|
+
# Parse new log entries and refresh cache
|
15
|
+
parse_result = LoadLensService.parse_log_and_save
|
16
|
+
LoadLensService.refresh_data
|
17
|
+
|
18
|
+
if parse_result[:success]
|
19
|
+
redirect_to load_lens_performance_index_path, notice: "LoadLens data refreshed! Parsed #{parse_result[:processed]} new requests."
|
20
|
+
else
|
21
|
+
redirect_to load_lens_performance_index_path, alert: "Failed to parse logs: #{parse_result[:error]}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
class ProductivityController < ApplicationController
|
5
|
+
layout 'solidstats/dashboard'
|
6
|
+
|
7
|
+
def my_todos
|
8
|
+
@todos = MyTodoService.collect_todos
|
9
|
+
@summary = MyTodoService.get_summary
|
10
|
+
|
11
|
+
# Group todos by type for display
|
12
|
+
@todos_by_type = @todos.group_by { |todo| todo[:type] }
|
13
|
+
|
14
|
+
# Recent todos (first 10 for quick view)
|
15
|
+
@recent_todos = @todos.first(10)
|
16
|
+
|
17
|
+
# Stats for dashboard
|
18
|
+
@stats = {
|
19
|
+
total: @todos.length,
|
20
|
+
by_type: @todos_by_type.transform_values(&:count),
|
21
|
+
files_with_todos: @todos.map { |t| t[:file] }.uniq.length
|
22
|
+
}
|
23
|
+
|
24
|
+
respond_to do |format|
|
25
|
+
format.html
|
26
|
+
format.json { render json: { todos: @todos, summary: @summary, stats: @stats } }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def refresh_todos
|
31
|
+
@todos = MyTodoService.collect_todos(force_refresh: true)
|
32
|
+
|
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 } }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
class QualityController < ApplicationController
|
5
|
+
layout 'solidstats/dashboard'
|
6
|
+
|
7
|
+
# Display Standard gem code quality analysis
|
8
|
+
def style_patrol
|
9
|
+
@analysis_data = Solidstats::StylePatrolService.collect_data
|
10
|
+
@summary = Solidstats::StylePatrolService.get_summary
|
11
|
+
|
12
|
+
# Group issues by file for better display
|
13
|
+
if @analysis_data[:issues].present?
|
14
|
+
@issues_by_file = @analysis_data[:issues].group_by { |issue| issue[:file] }
|
15
|
+
@issues_by_severity = @analysis_data[:issues].group_by { |issue| issue[:severity] }
|
16
|
+
else
|
17
|
+
@issues_by_file = {}
|
18
|
+
@issues_by_severity = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
render 'style_patrol'
|
22
|
+
end
|
23
|
+
|
24
|
+
# Force refresh of style patrol data
|
25
|
+
def refresh_style_patrol
|
26
|
+
@analysis_data = Solidstats::StylePatrolService.refresh_cache
|
27
|
+
redirect_to quality_style_patrol_path, notice: 'Code quality analysis refreshed successfully.'
|
28
|
+
rescue => e
|
29
|
+
redirect_to quality_style_patrol_path, alert: "Error refreshing analysis: #{e.message}"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Display SimpleCov code coverage analysis
|
33
|
+
def coverage_compass
|
34
|
+
# Get coverage data using the correct method
|
35
|
+
@analysis_data = Solidstats::CoverageCompassService.collect_data
|
36
|
+
|
37
|
+
# Handle different response types from service
|
38
|
+
if @analysis_data
|
39
|
+
if @analysis_data[:setup_required]
|
40
|
+
@setup_instructions = @analysis_data[:instructions]
|
41
|
+
@show_setup = true
|
42
|
+
@analysis_data[:status] = 'setup_required'
|
43
|
+
elsif @analysis_data[:stale_data]
|
44
|
+
@stale_warning = true
|
45
|
+
@data_age_hours = @analysis_data[:data_age_hours]
|
46
|
+
@suggestions = @analysis_data[:suggestions]
|
47
|
+
@analysis_data = @analysis_data[:last_coverage] || @analysis_data
|
48
|
+
@analysis_data[:status] = 'stale'
|
49
|
+
elsif @analysis_data[:error]
|
50
|
+
@error_message = @analysis_data[:error]
|
51
|
+
@analysis_data[:status] = 'error'
|
52
|
+
else
|
53
|
+
@analysis_data[:status] = 'success'
|
54
|
+
end
|
55
|
+
|
56
|
+
# Organize file coverage data for better display
|
57
|
+
organize_coverage_data
|
58
|
+
else
|
59
|
+
set_error_state
|
60
|
+
@analysis_data = { status: 'error' }
|
61
|
+
end
|
62
|
+
|
63
|
+
render 'coverage_compass'
|
64
|
+
end
|
65
|
+
|
66
|
+
# Force refresh of coverage compass data
|
67
|
+
def refresh_coverage_compass
|
68
|
+
Solidstats::CoverageCompassService.refresh_cache
|
69
|
+
redirect_to quality_coverage_compass_path, notice: 'Code coverage analysis refreshed successfully.'
|
70
|
+
rescue => e
|
71
|
+
redirect_to quality_coverage_compass_path, alert: "Error refreshing coverage analysis: #{e.message}"
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def organize_coverage_data
|
77
|
+
file_coverage_data = @analysis_data[:file_coverage]
|
78
|
+
if file_coverage_data.present?
|
79
|
+
@file_coverage = file_coverage_data.map do |path, data|
|
80
|
+
{
|
81
|
+
file_path: path,
|
82
|
+
coverage_percent: data[:coverage_percentage] || 0,
|
83
|
+
total_lines: data[:total_lines] || 0,
|
84
|
+
covered_lines: data[:covered_lines] || 0,
|
85
|
+
missed_lines: data[:missed_lines] || []
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
# Group files by coverage ranges for better visualization
|
90
|
+
@coverage_ranges = {
|
91
|
+
excellent: @file_coverage.select { |f| f[:coverage_percent] >= 90 },
|
92
|
+
good: @file_coverage.select { |f| f[:coverage_percent] >= 70 && f[:coverage_percent] < 90 },
|
93
|
+
needs_improvement: @file_coverage.select { |f| f[:coverage_percent] >= 50 && f[:coverage_percent] < 70 },
|
94
|
+
poor: @file_coverage.select { |f| f[:coverage_percent] < 50 }
|
95
|
+
}
|
96
|
+
else
|
97
|
+
@file_coverage = []
|
98
|
+
@coverage_ranges = { excellent: [], good: [], needs_improvement: [], poor: [] }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Set summary data
|
102
|
+
@summary = {
|
103
|
+
overall_coverage: @analysis_data[:overall_coverage] || 0,
|
104
|
+
coverage_percent: @analysis_data[:overall_coverage] || 0,
|
105
|
+
total_lines: @analysis_data[:total_lines] || 0,
|
106
|
+
covered_lines: @analysis_data[:covered_lines] || 0,
|
107
|
+
missed_lines: (@analysis_data[:total_lines] || 0) - (@analysis_data[:covered_lines] || 0),
|
108
|
+
total_files: @file_coverage.size,
|
109
|
+
health_score: calculate_health_score(@analysis_data[:overall_coverage] || 0),
|
110
|
+
coverage_grade: determine_coverage_grade(@analysis_data[:overall_coverage] || 0)
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_error_state
|
115
|
+
@error_message = "Failed to retrieve coverage data"
|
116
|
+
@file_coverage = []
|
117
|
+
@coverage_ranges = { excellent: [], good: [], needs_improvement: [], poor: [] }
|
118
|
+
@summary = {
|
119
|
+
overall_coverage: 0,
|
120
|
+
coverage_percent: 0,
|
121
|
+
total_lines: 0,
|
122
|
+
covered_lines: 0,
|
123
|
+
missed_lines: 0,
|
124
|
+
total_files: 0,
|
125
|
+
health_score: 0,
|
126
|
+
coverage_grade: 'F'
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
def calculate_health_score(coverage_percent)
|
131
|
+
case coverage_percent
|
132
|
+
when 90..100 then 95
|
133
|
+
when 80..89 then 85
|
134
|
+
when 70..79 then 75
|
135
|
+
when 60..69 then 65
|
136
|
+
when 50..59 then 55
|
137
|
+
else 25
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def determine_coverage_grade(coverage_percent)
|
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'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
class SecuritiesController < ApplicationController
|
5
|
+
layout 'solidstats/dashboard'
|
6
|
+
|
7
|
+
# Display bundler audit security vulnerabilities
|
8
|
+
def bundler_audit
|
9
|
+
@vulnerabilities_data = Solidstats::BundlerAuditService.fetch_vulnerabilities
|
10
|
+
@vulnerabilities = @vulnerabilities_data.dig("output", "results") || []
|
11
|
+
@summary = Solidstats::BundlerAuditService.summary
|
12
|
+
@last_updated = @vulnerabilities_data.dig("output", "created_at")
|
13
|
+
|
14
|
+
# Group vulnerabilities by severity for better display
|
15
|
+
@vulnerabilities_by_severity = @vulnerabilities.group_by do |vuln|
|
16
|
+
vuln.dig("advisory", "criticality") || "unknown"
|
17
|
+
end
|
18
|
+
|
19
|
+
render 'bundler_audit'
|
20
|
+
end
|
21
|
+
|
22
|
+
# Force refresh of bundler audit data
|
23
|
+
def refresh_bundler_audit
|
24
|
+
@vulnerabilities_data = Solidstats::BundlerAuditService.scan_and_cache
|
25
|
+
redirect_to securities_bundler_audit_path, notice: 'Security vulnerabilities refreshed successfully.'
|
26
|
+
rescue => e
|
27
|
+
redirect_to securities_bundler_audit_path, alert: "Error refreshing vulnerabilities: #{e.message}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|