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.
- checksums.yaml +4 -4
- data/README.md +33 -0
- data/app/assets/javascripts/solidstats/application.js +257 -0
- data/app/assets/javascripts/solidstats/dashboard.js +179 -0
- data/app/assets/stylesheets/solidstats/application.css +6 -1
- data/app/controllers/solidstats/dashboard_controller.rb +28 -35
- data/app/controllers/solidstats/gem_metadata_controller.rb +12 -0
- data/app/controllers/solidstats/logs_controller.rb +12 -12
- data/app/controllers/solidstats/performance_controller.rb +2 -2
- data/app/controllers/solidstats/productivity_controller.rb +10 -10
- data/app/controllers/solidstats/quality_controller.rb +32 -32
- data/app/controllers/solidstats/securities_controller.rb +7 -7
- data/app/helpers/solidstats/application_helper.rb +10 -10
- data/app/helpers/solidstats/performance_helper.rb +32 -32
- data/app/helpers/solidstats/productivity_helper.rb +20 -20
- data/app/services/solidstats/bundler_audit_service.rb +13 -13
- data/app/services/solidstats/coverage_compass_service.rb +59 -59
- data/app/services/solidstats/load_lens_service.rb +90 -70
- data/app/services/solidstats/log_size_monitor_service.rb +59 -59
- data/app/services/solidstats/my_todo_service.rb +68 -68
- data/app/services/solidstats/style_patrol_service.rb +44 -44
- data/app/views/layouts/solidstats/application.html.erb +1 -1
- data/app/views/solidstats/shared/_quick_actions.html.erb +1 -1
- data/config/routes.rb +4 -4
- data/lib/generators/solidstats/clean/clean_generator.rb +24 -0
- data/lib/generators/solidstats/clean/templates/README +8 -0
- data/lib/generators/solidstats/install/install_generator.rb +32 -17
- data/lib/generators/solidstats/templates/initializer.rb +112 -0
- data/lib/solidstats/asset_compatibility.rb +238 -0
- data/lib/solidstats/asset_manifest.rb +205 -0
- data/lib/solidstats/engine.rb +49 -9
- data/lib/solidstats/version.rb +1 -1
- data/lib/solidstats.rb +24 -11
- data/lib/tasks/solidstats.rake +67 -0
- data/lib/tasks/solidstats_performance.rake +6 -29
- data/lib/tasks/solidstats_tasks.rake +16 -0
- metadata +14 -5
- data/lib/tasks/solidstats_install.rake +0 -13
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "find"
|
4
|
+
require "json"
|
5
5
|
|
6
6
|
module Solidstats
|
7
7
|
# Enhanced TODO service for comprehensive project scanning
|
8
8
|
class MyTodoService
|
9
|
-
CACHE_FILE = Rails.root.join(
|
9
|
+
CACHE_FILE = Rails.root.join("solidstats", "todos.json")
|
10
10
|
CACHE_DURATION = 24.hours
|
11
|
-
|
11
|
+
|
12
12
|
TODO_PATTERNS = [
|
13
13
|
/TODO:?\s*(.+)/i,
|
14
14
|
/FIXME:?\s*(.+)/i,
|
@@ -16,19 +16,19 @@ module Solidstats
|
|
16
16
|
/NOTE:?\s*(.+)/i,
|
17
17
|
/BUG:?\s*(.+)/i
|
18
18
|
].freeze
|
19
|
-
|
19
|
+
|
20
20
|
SCAN_EXTENSIONS = %w[.rb .js .html .erb .yml .yaml .json .css .scss .vue .jsx .tsx .ts].freeze
|
21
21
|
EXCLUDE_DIRS = %w[node_modules vendor tmp log public/assets .git coverage pkg app/assets/builds solidstats].freeze
|
22
|
-
|
22
|
+
|
23
23
|
def self.collect_todos(force_refresh: false)
|
24
24
|
return cached_todos unless force_refresh || cache_expired?
|
25
|
-
|
25
|
+
|
26
26
|
todos = scan_project_files
|
27
27
|
cache_todos(todos)
|
28
28
|
update_summary_json(todos)
|
29
29
|
todos
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
def self.get_summary
|
33
33
|
todos = collect_todos
|
34
34
|
{
|
@@ -44,31 +44,31 @@ module Solidstats
|
|
44
44
|
last_updated: Time.current.iso8601
|
45
45
|
}
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
private
|
49
|
-
|
49
|
+
|
50
50
|
def self.scan_project_files
|
51
51
|
todos = []
|
52
52
|
exclude_patterns = load_gitignore_patterns
|
53
|
-
|
53
|
+
|
54
54
|
Find.find(Rails.root) do |path|
|
55
55
|
if File.directory?(path)
|
56
56
|
Find.prune if should_exclude_directory?(path, exclude_patterns)
|
57
57
|
next
|
58
58
|
end
|
59
|
-
|
59
|
+
|
60
60
|
next unless should_scan_file?(path)
|
61
61
|
next if excluded_by_gitignore?(path, exclude_patterns)
|
62
|
-
|
62
|
+
|
63
63
|
scan_file_for_todos(path, todos)
|
64
64
|
end
|
65
|
-
|
66
|
-
todos.sort_by { |todo| [todo[:file], todo[:line_number]] }
|
65
|
+
|
66
|
+
todos.sort_by { |todo| [ todo[:file], todo[:line_number] ] }
|
67
67
|
end
|
68
|
-
|
68
|
+
|
69
69
|
def self.scan_file_for_todos(file_path, todos)
|
70
|
-
relative_path = file_path.sub("#{Rails.root}/",
|
71
|
-
|
70
|
+
relative_path = file_path.sub("#{Rails.root}/", "")
|
71
|
+
|
72
72
|
File.readlines(file_path, chomp: true).each_with_index do |line, index|
|
73
73
|
TODO_PATTERNS.each do |pattern|
|
74
74
|
if match = line.match(pattern)
|
@@ -87,117 +87,117 @@ module Solidstats
|
|
87
87
|
rescue => e
|
88
88
|
Rails.logger.warn "Error scanning file #{file_path}: #{e.message}"
|
89
89
|
end
|
90
|
-
|
90
|
+
|
91
91
|
def self.extract_todo_type(line)
|
92
92
|
case line.upcase
|
93
|
-
when /TODO/ then
|
94
|
-
when /FIXME/ then
|
95
|
-
when /HACK/ then
|
96
|
-
when /NOTE/ then
|
97
|
-
when /BUG/ then
|
98
|
-
else
|
93
|
+
when /TODO/ then "todo"
|
94
|
+
when /FIXME/ then "fixme"
|
95
|
+
when /HACK/ then "hack"
|
96
|
+
when /NOTE/ then "note"
|
97
|
+
when /BUG/ then "bug"
|
98
|
+
else "todo"
|
99
99
|
end
|
100
100
|
end
|
101
|
-
|
101
|
+
|
102
102
|
def self.should_exclude_directory?(path, exclude_patterns)
|
103
|
-
relative_path = path.sub("#{Rails.root}/",
|
104
|
-
|
103
|
+
relative_path = path.sub("#{Rails.root}/", "")
|
104
|
+
|
105
105
|
# Check standard exclude directories
|
106
106
|
EXCLUDE_DIRS.each do |dir|
|
107
107
|
# Check if this directory or any parent directory matches
|
108
|
-
path_parts = relative_path.split(
|
108
|
+
path_parts = relative_path.split("/")
|
109
109
|
return true if path_parts.include?(dir)
|
110
110
|
return true if relative_path == dir
|
111
111
|
return true if relative_path.start_with?("#{dir}/")
|
112
112
|
end
|
113
|
-
|
113
|
+
|
114
114
|
# Check gitignore patterns
|
115
115
|
return true if exclude_patterns.any? { |pattern| File.fnmatch(pattern, relative_path, File::FNM_PATHNAME) }
|
116
|
-
|
116
|
+
|
117
117
|
false
|
118
118
|
end
|
119
|
-
|
119
|
+
|
120
120
|
def self.should_scan_file?(path)
|
121
121
|
SCAN_EXTENSIONS.any? { |ext| path.end_with?(ext) }
|
122
122
|
end
|
123
|
-
|
123
|
+
|
124
124
|
def self.excluded_by_gitignore?(file_path, exclude_patterns)
|
125
|
-
relative_path = file_path.sub("#{Rails.root}/",
|
125
|
+
relative_path = file_path.sub("#{Rails.root}/", "")
|
126
126
|
exclude_patterns.any? { |pattern| File.fnmatch(pattern, relative_path, File::FNM_PATHNAME) }
|
127
127
|
end
|
128
|
-
|
128
|
+
|
129
129
|
def self.load_gitignore_patterns
|
130
|
-
gitignore_path = Rails.root.join(
|
130
|
+
gitignore_path = Rails.root.join(".gitignore")
|
131
131
|
return [] unless File.exist?(gitignore_path)
|
132
|
-
|
132
|
+
|
133
133
|
patterns = []
|
134
134
|
File.readlines(gitignore_path, chomp: true).each do |line|
|
135
135
|
line = line.strip
|
136
|
-
next if line.empty? || line.start_with?(
|
137
|
-
|
136
|
+
next if line.empty? || line.start_with?("#")
|
137
|
+
|
138
138
|
# Convert gitignore patterns to fnmatch patterns
|
139
|
-
pattern = line.gsub(/\*\*/,
|
140
|
-
pattern = pattern.chomp(
|
139
|
+
pattern = line.gsub(/\*\*/, "*")
|
140
|
+
pattern = pattern.chomp("/")
|
141
141
|
patterns << pattern
|
142
|
-
patterns << "#{pattern}/*" if !pattern.include?(
|
142
|
+
patterns << "#{pattern}/*" if !pattern.include?("*")
|
143
143
|
end
|
144
|
-
|
144
|
+
|
145
145
|
patterns
|
146
146
|
end
|
147
|
-
|
147
|
+
|
148
148
|
def self.cached_todos
|
149
149
|
return [] unless File.exist?(CACHE_FILE)
|
150
|
-
|
150
|
+
|
151
151
|
JSON.parse(File.read(CACHE_FILE), symbolize_names: true)
|
152
152
|
rescue JSON::ParserError
|
153
153
|
[]
|
154
154
|
end
|
155
|
-
|
155
|
+
|
156
156
|
def self.cache_todos(todos)
|
157
157
|
FileUtils.mkdir_p(File.dirname(CACHE_FILE))
|
158
158
|
File.write(CACHE_FILE, todos.to_json)
|
159
159
|
end
|
160
|
-
|
160
|
+
|
161
161
|
def self.cache_expired?
|
162
162
|
return true unless File.exist?(CACHE_FILE)
|
163
|
-
|
163
|
+
|
164
164
|
File.mtime(CACHE_FILE) < CACHE_DURATION.ago
|
165
165
|
end
|
166
|
-
|
166
|
+
|
167
167
|
def self.update_summary_json(todos)
|
168
|
-
summary_file_path = Rails.root.join(
|
169
|
-
|
168
|
+
summary_file_path = Rails.root.join("solidstats", "summary.json")
|
169
|
+
|
170
170
|
# Ensure directory exists
|
171
171
|
FileUtils.mkdir_p(File.dirname(summary_file_path))
|
172
|
-
|
172
|
+
|
173
173
|
# Read existing summary or create new one
|
174
174
|
begin
|
175
175
|
existing_summary = File.exist?(summary_file_path) ? JSON.parse(File.read(summary_file_path)) : {}
|
176
176
|
rescue JSON::ParserError
|
177
177
|
existing_summary = {}
|
178
178
|
end
|
179
|
-
|
179
|
+
|
180
180
|
# Calculate TODO statistics
|
181
181
|
todo_count = todos.length
|
182
182
|
status = determine_status(todos)
|
183
183
|
type_counts = todos.group_by { |t| t[:type] }.transform_values(&:count)
|
184
|
-
|
184
|
+
|
185
185
|
# Create badges based on TODO types and counts
|
186
186
|
badges = []
|
187
187
|
badges << { "text" => "#{todo_count} Items", "color" => "info" }
|
188
|
-
|
188
|
+
|
189
189
|
%w[todo fixme hack note bug].each do |type|
|
190
190
|
count = type_counts[type] || 0
|
191
191
|
if count > 0
|
192
192
|
color = case type
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
193
|
+
when "fixme" then "warning"
|
194
|
+
when "hack", "bug" then "error"
|
195
|
+
else "info"
|
196
|
+
end
|
197
197
|
badges << { "text" => "#{type.upcase}: #{count}", "color" => color }
|
198
198
|
end
|
199
199
|
end
|
200
|
-
|
200
|
+
|
201
201
|
# Update the TODO items entry
|
202
202
|
existing_summary["TODO Items"] = {
|
203
203
|
"icon" => "list-todo",
|
@@ -207,21 +207,21 @@ module Solidstats
|
|
207
207
|
"url" => "/solidstats/productivity/my_todos",
|
208
208
|
"badges" => badges
|
209
209
|
}
|
210
|
-
|
210
|
+
|
211
211
|
# Write updated summary
|
212
212
|
File.write(summary_file_path, JSON.pretty_generate(existing_summary))
|
213
213
|
Rails.logger.info("Updated summary.json with TODO items")
|
214
214
|
rescue => e
|
215
215
|
Rails.logger.error("Failed to update summary.json: #{e.message}")
|
216
216
|
end
|
217
|
-
|
217
|
+
|
218
218
|
def self.determine_status(todos)
|
219
219
|
return "success" if todos.empty?
|
220
|
-
|
221
|
-
fixme_count = todos.count { |t| t[:type] ==
|
222
|
-
hack_count = todos.count { |t| t[:type] ==
|
223
|
-
bug_count = todos.count { |t| t[:type] ==
|
224
|
-
|
220
|
+
|
221
|
+
fixme_count = todos.count { |t| t[:type] == "fixme" }
|
222
|
+
hack_count = todos.count { |t| t[:type] == "hack" }
|
223
|
+
bug_count = todos.count { |t| t[:type] == "bug" }
|
224
|
+
|
225
225
|
if bug_count > 0 || hack_count > 0
|
226
226
|
"error"
|
227
227
|
elsif fixme_count > 0 || todos.length > 20
|
@@ -230,7 +230,7 @@ module Solidstats
|
|
230
230
|
"success"
|
231
231
|
end
|
232
232
|
end
|
233
|
-
|
233
|
+
|
234
234
|
def self.generate_message(count)
|
235
235
|
case count
|
236
236
|
when 0 then "No items found"
|
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "fileutils"
|
4
|
+
require "json"
|
5
5
|
|
6
6
|
module Solidstats
|
7
7
|
class StylePatrolService
|
8
8
|
CACHE_KEY = "style_patrol_data"
|
9
9
|
CACHE_DURATION = 6.hours
|
10
|
-
STANDARD_JSON_FILE = Rails.root.join(
|
10
|
+
STANDARD_JSON_FILE = Rails.root.join("solidstats", "standard.json")
|
11
11
|
|
12
12
|
def self.collect_data(force_refresh: false)
|
13
13
|
return cached_data unless force_refresh || cache_expired?
|
14
|
-
|
14
|
+
|
15
15
|
analysis_data = analyze_code_quality
|
16
16
|
cache_data(analysis_data)
|
17
17
|
save_to_standard_json(analysis_data)
|
@@ -40,10 +40,10 @@ module Solidstats
|
|
40
40
|
|
41
41
|
def self.analyze_code_quality
|
42
42
|
result = `standardrb --format json 2>&1`
|
43
|
-
|
43
|
+
|
44
44
|
# Extract JSON from output (may contain debug info or other text)
|
45
45
|
json_content = extract_json_from_output(result)
|
46
|
-
|
46
|
+
|
47
47
|
if json_content.nil?
|
48
48
|
# No JSON found - could be clean result or error
|
49
49
|
if $?.success?
|
@@ -51,9 +51,9 @@ module Solidstats
|
|
51
51
|
{
|
52
52
|
status: "clean",
|
53
53
|
issues: [],
|
54
|
-
summary: {
|
55
|
-
total_files: 0,
|
56
|
-
total_offenses: 0,
|
54
|
+
summary: {
|
55
|
+
total_files: 0,
|
56
|
+
total_offenses: 0,
|
57
57
|
correctable_count: 0,
|
58
58
|
target_file_count: 0,
|
59
59
|
inspected_file_count: 0
|
@@ -90,10 +90,10 @@ module Solidstats
|
|
90
90
|
|
91
91
|
def self.process_standard_output(json_data)
|
92
92
|
issues = []
|
93
|
-
|
93
|
+
|
94
94
|
# Handle StandardRB JSON format
|
95
95
|
files = json_data["files"] || []
|
96
|
-
|
96
|
+
|
97
97
|
files.each do |file_data|
|
98
98
|
file_data["offenses"]&.each do |offense|
|
99
99
|
issues << {
|
@@ -110,7 +110,7 @@ module Solidstats
|
|
110
110
|
|
111
111
|
# Get summary from StandardRB output
|
112
112
|
summary_data = json_data["summary"] || {}
|
113
|
-
|
113
|
+
|
114
114
|
{
|
115
115
|
status: issues.any? ? "issues_found" : "clean",
|
116
116
|
issues: issues,
|
@@ -140,7 +140,7 @@ module Solidstats
|
|
140
140
|
def self.cache_expired?
|
141
141
|
cached_entry = Rails.cache.read(CACHE_KEY)
|
142
142
|
return true if cached_entry.nil?
|
143
|
-
|
143
|
+
|
144
144
|
cached_time = Time.parse(cached_entry[:analyzed_at])
|
145
145
|
Time.current > cached_time + CACHE_DURATION
|
146
146
|
rescue
|
@@ -150,46 +150,46 @@ module Solidstats
|
|
150
150
|
def self.calculate_health_score(data)
|
151
151
|
return 100 if data[:status] == "clean"
|
152
152
|
return 0 if data[:status] == "error"
|
153
|
-
|
153
|
+
|
154
154
|
total_offenses = data.dig(:summary, :total_offenses) || 0
|
155
155
|
total_files = data.dig(:summary, :total_files) || 1
|
156
|
-
|
156
|
+
|
157
157
|
# Health score: 100 - (offenses per file * 10), minimum 0
|
158
158
|
score = 100 - ((total_offenses.to_f / total_files) * 10).round
|
159
|
-
[score, 0].max
|
159
|
+
[ score, 0 ].max
|
160
160
|
end
|
161
161
|
|
162
162
|
def self.update_summary_json(data)
|
163
|
-
summary_file_path = Rails.root.join(
|
164
|
-
|
163
|
+
summary_file_path = Rails.root.join("solidstats", "summary.json")
|
164
|
+
|
165
165
|
# Ensure directory exists
|
166
166
|
FileUtils.mkdir_p(File.dirname(summary_file_path))
|
167
|
-
|
167
|
+
|
168
168
|
# Read existing summary or create new one
|
169
169
|
begin
|
170
170
|
existing_summary = File.exist?(summary_file_path) ? JSON.parse(File.read(summary_file_path)) : {}
|
171
171
|
rescue JSON::ParserError
|
172
172
|
existing_summary = {}
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
# Calculate style patrol statistics
|
176
176
|
total_offenses = data.dig(:summary, :total_offenses) || 0
|
177
177
|
correctable_count = data.dig(:summary, :correctable_count) || 0
|
178
178
|
status = determine_dashboard_status(data[:status], total_offenses)
|
179
179
|
health_score = calculate_health_score(data)
|
180
|
-
|
180
|
+
|
181
181
|
# Create badges based on code quality metrics
|
182
182
|
badges = []
|
183
183
|
badges << { "text" => "Health: #{health_score}%", "color" => health_badge_color(health_score) }
|
184
|
-
|
184
|
+
|
185
185
|
if total_offenses > 0
|
186
186
|
badges << { "text" => "#{total_offenses} Issues", "color" => "warning" }
|
187
|
-
|
187
|
+
|
188
188
|
if correctable_count > 0
|
189
189
|
badges << { "text" => "#{correctable_count} Auto-fixable", "color" => "info" }
|
190
190
|
end
|
191
191
|
end
|
192
|
-
|
192
|
+
|
193
193
|
# Update the StylePatrol entry
|
194
194
|
existing_summary["StylePatrol"] = {
|
195
195
|
"icon" => "code",
|
@@ -199,7 +199,7 @@ module Solidstats
|
|
199
199
|
"url" => "/solidstats/quality/style_patrol",
|
200
200
|
"badges" => badges
|
201
201
|
}
|
202
|
-
|
202
|
+
|
203
203
|
# Write updated summary
|
204
204
|
File.write(summary_file_path, JSON.pretty_generate(existing_summary))
|
205
205
|
Rails.logger.info("Updated summary.json with StylePatrol data")
|
@@ -246,7 +246,7 @@ module Solidstats
|
|
246
246
|
def self.save_to_standard_json(data)
|
247
247
|
# Ensure directory exists
|
248
248
|
FileUtils.mkdir_p(File.dirname(STANDARD_JSON_FILE))
|
249
|
-
|
249
|
+
|
250
250
|
# Prepare data for standard.json file
|
251
251
|
standard_data = {
|
252
252
|
last_updated: data[:analyzed_at],
|
@@ -257,7 +257,7 @@ module Solidstats
|
|
257
257
|
processed_summary: data[:summary] || {},
|
258
258
|
issues_count: data[:issues]&.count || 0
|
259
259
|
}
|
260
|
-
|
260
|
+
|
261
261
|
# Write to standard.json file
|
262
262
|
File.write(STANDARD_JSON_FILE, JSON.pretty_generate(standard_data))
|
263
263
|
Rails.logger.info("Saved StandardRB data to #{STANDARD_JSON_FILE}")
|
@@ -268,47 +268,47 @@ module Solidstats
|
|
268
268
|
def self.extract_json_from_output(output)
|
269
269
|
# Look for JSON content - should start with { and end with }
|
270
270
|
# Handle cases where debug info or other text appears before/after JSON
|
271
|
-
|
271
|
+
|
272
272
|
return nil if output.nil? || output.strip.empty?
|
273
|
-
|
273
|
+
|
274
274
|
# Split by lines and look for the line that starts with JSON
|
275
275
|
lines = output.split("\n")
|
276
276
|
json_lines = []
|
277
277
|
json_started = false
|
278
278
|
brace_count = 0
|
279
|
-
|
279
|
+
|
280
280
|
lines.each do |line|
|
281
281
|
# Skip debug lines that mention "Subprocess Debugger", "ruby-debug-ide", etc.
|
282
|
-
next if line.include?("Subprocess Debugger") ||
|
283
|
-
line.include?("ruby-debug-ide") ||
|
282
|
+
next if line.include?("Subprocess Debugger") ||
|
283
|
+
line.include?("ruby-debug-ide") ||
|
284
284
|
line.include?("debase") ||
|
285
285
|
line.include?("listens on")
|
286
|
-
|
286
|
+
|
287
287
|
# Look for the start of JSON (line starting with {)
|
288
|
-
if !json_started && line.strip.start_with?(
|
288
|
+
if !json_started && line.strip.start_with?("{")
|
289
289
|
json_started = true
|
290
290
|
end
|
291
|
-
|
291
|
+
|
292
292
|
if json_started
|
293
293
|
json_lines << line
|
294
|
-
|
294
|
+
|
295
295
|
# Count braces to know when JSON ends
|
296
|
-
brace_count += line.count(
|
297
|
-
|
296
|
+
brace_count += line.count("{") - line.count("}")
|
297
|
+
|
298
298
|
# If we've closed all braces, we've reached the end of JSON
|
299
299
|
break if brace_count == 0
|
300
300
|
end
|
301
301
|
end
|
302
|
-
|
302
|
+
|
303
303
|
return nil if json_lines.empty?
|
304
|
-
|
304
|
+
|
305
305
|
json_content = json_lines.join("\n")
|
306
|
-
|
306
|
+
|
307
307
|
# Validate that it looks like StandardRB JSON by checking for expected structure
|
308
|
-
return nil unless json_content.include?('"metadata"') ||
|
309
|
-
json_content.include?('"files"') ||
|
308
|
+
return nil unless json_content.include?('"metadata"') ||
|
309
|
+
json_content.include?('"files"') ||
|
310
310
|
json_content.include?('"summary"')
|
311
|
-
|
311
|
+
|
312
312
|
json_content
|
313
313
|
rescue => e
|
314
314
|
Rails.logger.error("Error extracting JSON from StandardRB output: #{e.message}")
|
@@ -10,7 +10,7 @@
|
|
10
10
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
|
11
11
|
<% quick_actions.each do |action| %>
|
12
12
|
<button class="btn btn-outline btn-sm group flex flex-col items-center p-4 h-auto min-h-16
|
13
|
-
hover:btn-<%= action[:color] %> transition-all duration-200"
|
13
|
+
hover:btn-<%= action[:color] %> transition-all duration-200">
|
14
14
|
<div class="w-10 h-10 bg-gradient-to-r from-<%= action[:color] %>-500 to-<%= action[:color] %>-600
|
15
15
|
rounded-lg flex items-center justify-center mb-2
|
16
16
|
group-hover:scale-110 transition-transform duration-200 shadow-lg">
|
data/config/routes.rb
CHANGED
@@ -7,17 +7,17 @@ Solidstats::Engine.routes.draw do
|
|
7
7
|
get "logs/size", to: "logs#logs_size", as: :logs_size
|
8
8
|
post "logs/truncate/:filename", to: "logs#truncate", as: :truncate_log, constraints: { filename: /.+/ }
|
9
9
|
post "logs/refresh", to: "logs#refresh", as: :refresh_logs
|
10
|
-
|
10
|
+
|
11
11
|
# Security-related routes
|
12
12
|
get "securities/bundler_audit", to: "securities#bundler_audit", as: :securities_bundler_audit
|
13
13
|
post "securities/refresh_bundler_audit", to: "securities#refresh_bundler_audit", as: :refresh_securities_bundler_audit
|
14
|
-
|
14
|
+
|
15
15
|
# Quality-related routes
|
16
16
|
get "quality/style_patrol", to: "quality#style_patrol", as: :quality_style_patrol
|
17
17
|
post "quality/refresh_style_patrol", to: "quality#refresh_style_patrol", as: :refresh_quality_style_patrol
|
18
18
|
get "quality/coverage_compass", to: "quality#coverage_compass", as: :quality_coverage_compass
|
19
19
|
post "quality/refresh_coverage_compass", to: "quality#refresh_coverage_compass", as: :refresh_quality_coverage_compass
|
20
|
-
|
20
|
+
|
21
21
|
# Productivity-related routes
|
22
22
|
resources :productivity, only: [] do
|
23
23
|
collection do
|
@@ -25,7 +25,7 @@ Solidstats::Engine.routes.draw do
|
|
25
25
|
post :refresh_todos
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
# Performance-related routes
|
30
30
|
resources :performance, only: [] do
|
31
31
|
collection do
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rails/generators/base"
|
2
|
+
|
3
|
+
module Solidstats
|
4
|
+
module Generators
|
5
|
+
# This generator cleans up Solidstats data by invoking the Rake task
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# $ rails generate solidstats:clean
|
9
|
+
# or
|
10
|
+
# $ rake solidstats:clean
|
11
|
+
class CleanGenerator < Rails::Generators::Base
|
12
|
+
source_root File.expand_path("templates", __dir__)
|
13
|
+
desc "Cleans up Solidstats data in your Rails application"
|
14
|
+
|
15
|
+
def run_clean_task
|
16
|
+
rake "solidstats:clean"
|
17
|
+
end
|
18
|
+
|
19
|
+
def show_readme
|
20
|
+
readme "README" if behavior == :invoke
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
The Solidstats cleanup task has been executed.
|
2
|
+
|
3
|
+
All generated data, including JSON files and caches in the 'solidstats' directory, have been removed.
|
4
|
+
|
5
|
+
You can run this task again at any time by executing:
|
6
|
+
`rails generate solidstats:clean`
|
7
|
+
or
|
8
|
+
`rake solidstats:clean`
|
@@ -2,7 +2,7 @@ require "rails/generators/base"
|
|
2
2
|
|
3
3
|
module Solidstats
|
4
4
|
module Generators
|
5
|
-
# This generator installs Solidstats
|
5
|
+
# This generator installs Solidstats in the host application
|
6
6
|
#
|
7
7
|
# @example
|
8
8
|
# $ rails generate solidstats:install
|
@@ -17,13 +17,19 @@ module Solidstats
|
|
17
17
|
desc "Installs Solidstats in your Rails application"
|
18
18
|
|
19
19
|
def add_routes
|
20
|
-
|
20
|
+
route_string = %Q(mount Solidstats::Engine => "/solidstats")
|
21
|
+
route_code = %Q(
|
21
22
|
# Solidstats Routes (development only)
|
22
|
-
|
23
|
-
|
23
|
+
#{route_string} if Rails.env.development?
|
24
|
+
)
|
24
25
|
|
25
|
-
route
|
26
|
-
|
26
|
+
# Check if the route already exists
|
27
|
+
if File.read(File.join(destination_root, "config", "routes.rb")).include?(route_string)
|
28
|
+
say_status :skip, "Solidstats route already exists", :yellow
|
29
|
+
else
|
30
|
+
route route_code.strip
|
31
|
+
say_status :routes, "Mounting Solidstats engine at /solidstats (development only)", :green
|
32
|
+
end
|
27
33
|
end
|
28
34
|
|
29
35
|
def create_solidstats_directory
|
@@ -35,23 +41,32 @@ module Solidstats
|
|
35
41
|
def add_to_gitignore
|
36
42
|
gitignore_path = File.join(destination_root, ".gitignore")
|
37
43
|
gitignore_content = "\n# Solidstats data directory\nsolidstats/\n"
|
38
|
-
|
44
|
+
|
39
45
|
if File.exist?(gitignore_path)
|
40
|
-
|
41
|
-
unless existing_content.include?("solidstats/")
|
42
|
-
append_to_file ".gitignore", gitignore_content
|
43
|
-
say_status :update, ".gitignore to exclude solidstats directory", :green
|
44
|
-
else
|
45
|
-
say_status :skip, ".gitignore already contains solidstats exclusion", :yellow
|
46
|
-
end
|
46
|
+
append_to_file ".gitignore", gitignore_content unless File.read(gitignore_path).include?("solidstats/")
|
47
47
|
else
|
48
48
|
create_file ".gitignore", gitignore_content.strip
|
49
|
-
say_status :create, ".gitignore with solidstats exclusion", :green
|
50
49
|
end
|
50
|
+
say_status :update, ".gitignore to exclude solidstats directory", :green
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
|
53
|
+
def show_next_steps
|
54
|
+
say ""
|
55
|
+
say "✅ Solidstats has been installed successfully!", :green
|
56
|
+
say ""
|
57
|
+
say "To get started, you need to prime the data for the dashboard."
|
58
|
+
say "You can prime data for all services at once by running:", :yellow
|
59
|
+
say " rake solidstats:prime:all", :cyan
|
60
|
+
say ""
|
61
|
+
say "Or, you can run them individually:", :yellow
|
62
|
+
say " rake solidstats:prime:log_size"
|
63
|
+
say " rake solidstats:prime:bundler_audit"
|
64
|
+
say " rake solidstats:prime:todos"
|
65
|
+
say " rake solidstats:prime:style_patrol"
|
66
|
+
say " rake solidstats:prime:coverage"
|
67
|
+
say " rake solidstats:prime:load_lens"
|
68
|
+
say ""
|
69
|
+
say "After the tasks complete, start your server and visit http://localhost:3000/solidstats to see the dashboard."
|
55
70
|
end
|
56
71
|
end
|
57
72
|
end
|