rails_code_health 0.3.1 ā 0.3.3
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 +22 -1
- data/README.md +37 -0
- data/lib/rails_code_health/cli.rb +48 -4
- data/lib/rails_code_health/configuration.rb +1 -7
- data/lib/rails_code_health/report_generator.rb +5 -13
- data/lib/rails_code_health/version.rb +1 -1
- metadata +1 -2
- data/config/tresholds.json +0 -80
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b357d8913c61f87fa0edb2c9f672963c90a56a1b9679e0ee41a80c17d69883f
|
|
4
|
+
data.tar.gz: 55f62937933e657fe285dfddf64238ab39ef19114118c265f849773023ee56b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47aa3d974735472e21adc083cd66237582241d71e12e6801615915277de1bb221e3f97080e2166da32ec6a91276f0f1070e0c0f73678c866e73400de2e2fa8a7
|
|
7
|
+
data.tar.gz: e7709324ea411f35eddfeed0fa0c4070cb902a1a87761a76d27d21d0f0aa7bcb7e6f30588dc6d0d65a351990c8bbcf570965b0adfae760aa2b52d0fe7ca937ad
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.3] - 2026-05-18
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Removed the canned "Focus on these improvement areas" block from the recommendations report. It was a static fortune-cookie message printed on every run regardless of the actual findings, making reports look templated. The "Most Common Issues (by frequency)" section above it already provides evidence-based, data-driven recommendations derived from real per-file findings.
|
|
14
|
+
- The "šÆ Priority Actions:" header is now only emitted when there are actually critical files to act on (no more orphaned header).
|
|
15
|
+
|
|
16
|
+
## [0.3.2] - 2026-05-18
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- `--fail-under SCORE` CLI flag: exits with code `2` when the average health score is below the given threshold. Lets teams gate CI builds on overall code health.
|
|
20
|
+
- `--max-critical N` CLI flag: exits with code `2` when more than `N` files fall into the Critical category. Useful for blocking PRs that introduce severely unhealthy files.
|
|
21
|
+
- GitHub Actions example in the README and landing page.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- Removed dead `config/tresholds.json` file (misspelled, never loaded by the gem) and the unreachable file-loading branch in `Configuration#load_default_thresholds`. The hardcoded defaults in `Configuration#default_hardcoded_thresholds` are now the single source of truth for the gem's built-in thresholds. User-supplied configs via `--config` continue to work unchanged.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Removed `config/**/*` from `gemspec`'s shipped files (the directory no longer exists).
|
|
28
|
+
|
|
10
29
|
## [0.3.1] - 2026-05-18
|
|
11
30
|
|
|
12
31
|
### Fixed
|
|
@@ -81,7 +100,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
81
100
|
- **Reporting**: Detailed console output with health categories and JSON export
|
|
82
101
|
- **CLI**: `rails-health` command with options for format, output file, and custom configuration
|
|
83
102
|
|
|
84
|
-
[Unreleased]: https://github.com/gkosmo/rails_code_health/compare/v0.3.
|
|
103
|
+
[Unreleased]: https://github.com/gkosmo/rails_code_health/compare/v0.3.3...HEAD
|
|
104
|
+
[0.3.3]: https://github.com/gkosmo/rails_code_health/compare/v0.3.2...v0.3.3
|
|
105
|
+
[0.3.2]: https://github.com/gkosmo/rails_code_health/compare/v0.3.1...v0.3.2
|
|
85
106
|
[0.3.1]: https://github.com/gkosmo/rails_code_health/compare/v0.3.0...v0.3.1
|
|
86
107
|
[0.3.0]: https://github.com/gkosmo/rails_code_health/compare/v0.2.0...v0.3.0
|
|
87
108
|
[0.2.0]: https://github.com/gkosmo/rails_code_health/compare/v0.1.0...v0.2.0
|
data/README.md
CHANGED
|
@@ -55,6 +55,43 @@ Use custom configuration:
|
|
|
55
55
|
rails-health --config custom_thresholds.json
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
+
### Continuous Integration
|
|
59
|
+
|
|
60
|
+
Use `--fail-under` and/or `--max-critical` to gate CI builds on code health:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Fail the build if the average health score drops below 7.0
|
|
64
|
+
rails-health --fail-under 7.0
|
|
65
|
+
|
|
66
|
+
# Fail the build if more than 5 files are in the Critical category
|
|
67
|
+
rails-health --max-critical 5
|
|
68
|
+
|
|
69
|
+
# Combine both ā fail if either condition is violated
|
|
70
|
+
rails-health --fail-under 7.0 --max-critical 5
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Exit codes:
|
|
74
|
+
- `0` ā analysis succeeded and any configured gates passed
|
|
75
|
+
- `1` ā analysis itself failed (e.g. not a Rails project, parse error)
|
|
76
|
+
- `2` ā analysis succeeded but a `--fail-under` or `--max-critical` gate failed
|
|
77
|
+
|
|
78
|
+
#### GitHub Actions example
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
name: Code Health
|
|
82
|
+
on: [pull_request]
|
|
83
|
+
jobs:
|
|
84
|
+
health:
|
|
85
|
+
runs-on: ubuntu-latest
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/checkout@v4
|
|
88
|
+
- uses: ruby/setup-ruby@v1
|
|
89
|
+
with:
|
|
90
|
+
ruby-version: '3.3'
|
|
91
|
+
- run: gem install rails_code_health
|
|
92
|
+
- run: rails-health --fail-under 7.0 --max-critical 5
|
|
93
|
+
```
|
|
94
|
+
|
|
58
95
|
### Programmatic Usage
|
|
59
96
|
|
|
60
97
|
```ruby
|
|
@@ -13,7 +13,9 @@ module RailsCodeHealth
|
|
|
13
13
|
format: :console,
|
|
14
14
|
output: nil,
|
|
15
15
|
config: nil,
|
|
16
|
-
verbose: false
|
|
16
|
+
verbose: false,
|
|
17
|
+
fail_under: nil,
|
|
18
|
+
max_critical: nil
|
|
17
19
|
}
|
|
18
20
|
end
|
|
19
21
|
|
|
@@ -33,13 +35,17 @@ module RailsCodeHealth
|
|
|
33
35
|
puts "š Using format: #{@options[:format]}" if @options[:verbose]
|
|
34
36
|
|
|
35
37
|
# Run the analysis
|
|
36
|
-
|
|
38
|
+
results = analyze_project
|
|
37
39
|
|
|
38
40
|
# Output the report
|
|
39
|
-
output_report(
|
|
41
|
+
output_report(results)
|
|
40
42
|
|
|
41
43
|
puts "\nā
Analysis complete!" if @options[:verbose]
|
|
42
44
|
|
|
45
|
+
# Apply CI gating thresholds (after report is emitted so the user still sees output)
|
|
46
|
+
exit_code = gate_exit_code(results)
|
|
47
|
+
exit exit_code unless exit_code.zero?
|
|
48
|
+
|
|
43
49
|
rescue RailsCodeHealth::Error => e
|
|
44
50
|
puts "ā Error: #{e.message}"
|
|
45
51
|
exit 1
|
|
@@ -75,11 +81,21 @@ module RailsCodeHealth
|
|
|
75
81
|
@options[:config] = file
|
|
76
82
|
end
|
|
77
83
|
|
|
78
|
-
opts.on("-v", "--verbose",
|
|
84
|
+
opts.on("-v", "--verbose",
|
|
79
85
|
"Verbose output") do
|
|
80
86
|
@options[:verbose] = true
|
|
81
87
|
end
|
|
82
88
|
|
|
89
|
+
opts.on("--fail-under SCORE", Float,
|
|
90
|
+
"Exit non-zero if average health score is below SCORE (e.g. 7.0)") do |score|
|
|
91
|
+
@options[:fail_under] = score
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
opts.on("--max-critical N", Integer,
|
|
95
|
+
"Exit non-zero if more than N files fall into the Critical category") do |n|
|
|
96
|
+
@options[:max_critical] = n
|
|
97
|
+
end
|
|
98
|
+
|
|
83
99
|
opts.on_tail("-h", "--help", "Show this message") do
|
|
84
100
|
puts opts
|
|
85
101
|
exit
|
|
@@ -149,6 +165,34 @@ module RailsCodeHealth
|
|
|
149
165
|
end
|
|
150
166
|
end
|
|
151
167
|
|
|
168
|
+
def gate_exit_code(results)
|
|
169
|
+
return 0 if results.nil? || results.empty?
|
|
170
|
+
return 0 if @options[:fail_under].nil? && @options[:max_critical].nil?
|
|
171
|
+
|
|
172
|
+
failures = []
|
|
173
|
+
|
|
174
|
+
if (threshold = @options[:fail_under])
|
|
175
|
+
average = results.sum { |r| r[:health_score] || 0 } / results.size.to_f
|
|
176
|
+
if average < threshold
|
|
177
|
+
failures << "average health score #{average.round(2)} is below --fail-under #{threshold}"
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
if (max = @options[:max_critical])
|
|
182
|
+
critical_count = results.count { |r| r[:health_category] == :critical }
|
|
183
|
+
if critical_count > max
|
|
184
|
+
failures << "#{critical_count} critical file(s) exceeds --max-critical #{max}"
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
return 0 if failures.empty?
|
|
189
|
+
|
|
190
|
+
warn ""
|
|
191
|
+
warn "ā Health gate failed:"
|
|
192
|
+
failures.each { |msg| warn " - #{msg}" }
|
|
193
|
+
2
|
|
194
|
+
end
|
|
195
|
+
|
|
152
196
|
def capture_console_output(report_generator)
|
|
153
197
|
original_stdout = $stdout
|
|
154
198
|
$stdout = StringIO.new
|
|
@@ -23,13 +23,7 @@ module RailsCodeHealth
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def load_default_thresholds
|
|
26
|
-
|
|
27
|
-
if File.exist?(config_file)
|
|
28
|
-
JSON.parse(File.read(config_file))
|
|
29
|
-
else
|
|
30
|
-
# Fallback to hardcoded defaults if config file is missing
|
|
31
|
-
default_hardcoded_thresholds
|
|
32
|
-
end
|
|
26
|
+
default_hardcoded_thresholds
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
def default_excluded_paths
|
|
@@ -226,14 +226,13 @@ module RailsCodeHealth
|
|
|
226
226
|
recommendations << "#{index + 1}. #{rec} (#{count} occurrence#{'s' if count > 1})"
|
|
227
227
|
end
|
|
228
228
|
|
|
229
|
-
recommendations << ""
|
|
230
|
-
recommendations << "šÆ Priority Actions:"
|
|
231
|
-
recommendations << ""
|
|
232
|
-
|
|
233
229
|
# Find files with lowest scores and their recommendations
|
|
234
230
|
critical_files = @results.select { |r| r[:health_score] && r[:health_score] < 4.0 }
|
|
235
231
|
if critical_files.any?
|
|
236
|
-
recommendations << "
|
|
232
|
+
recommendations << ""
|
|
233
|
+
recommendations << "šÆ Priority Actions:"
|
|
234
|
+
recommendations << ""
|
|
235
|
+
recommendations << "šØ Address critical files immediately:"
|
|
237
236
|
critical_files.first(3).each do |file|
|
|
238
237
|
recommendations << " - #{file[:relative_path]} (score: #{file[:health_score]})"
|
|
239
238
|
if file[:recommendations] && file[:recommendations].any?
|
|
@@ -242,15 +241,8 @@ module RailsCodeHealth
|
|
|
242
241
|
end
|
|
243
242
|
end
|
|
244
243
|
end
|
|
245
|
-
recommendations << ""
|
|
246
244
|
end
|
|
247
|
-
|
|
248
|
-
recommendations << "2. š§ Focus on these improvement areas:"
|
|
249
|
-
recommendations << " - Reduce method and class lengths"
|
|
250
|
-
recommendations << " - Lower cyclomatic complexity"
|
|
251
|
-
recommendations << " - Follow Rails conventions"
|
|
252
|
-
recommendations << " - Extract business logic from controllers and views"
|
|
253
|
-
|
|
245
|
+
|
|
254
246
|
recommendations.join("\n")
|
|
255
247
|
end
|
|
256
248
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails_code_health
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- George Kosmopoulos
|
|
@@ -148,7 +148,6 @@ files:
|
|
|
148
148
|
- LICENSE.txt
|
|
149
149
|
- README.md
|
|
150
150
|
- bin/rails-health
|
|
151
|
-
- config/tresholds.json
|
|
152
151
|
- lib/rails_code_health.rb
|
|
153
152
|
- lib/rails_code_health/ast_helpers.rb
|
|
154
153
|
- lib/rails_code_health/cli.rb
|
data/config/tresholds.json
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"ruby_thresholds": {
|
|
3
|
-
"method_length": {
|
|
4
|
-
"green": 15,
|
|
5
|
-
"yellow": 25,
|
|
6
|
-
"red": 40
|
|
7
|
-
},
|
|
8
|
-
"class_length": {
|
|
9
|
-
"green": 100,
|
|
10
|
-
"yellow": 200,
|
|
11
|
-
"red": 400
|
|
12
|
-
},
|
|
13
|
-
"cyclomatic_complexity": {
|
|
14
|
-
"green": 6,
|
|
15
|
-
"yellow": 10,
|
|
16
|
-
"red": 15
|
|
17
|
-
},
|
|
18
|
-
"abc_complexity": {
|
|
19
|
-
"green": 15,
|
|
20
|
-
"yellow": 25,
|
|
21
|
-
"red": 40
|
|
22
|
-
},
|
|
23
|
-
"nesting_depth": {
|
|
24
|
-
"green": 3,
|
|
25
|
-
"yellow": 5,
|
|
26
|
-
"red": 8
|
|
27
|
-
},
|
|
28
|
-
"parameter_count": {
|
|
29
|
-
"green": 3,
|
|
30
|
-
"yellow": 5,
|
|
31
|
-
"red": 8
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
"rails_specific": {
|
|
35
|
-
"controller_actions": {
|
|
36
|
-
"green": 5,
|
|
37
|
-
"yellow": 10,
|
|
38
|
-
"red": 20
|
|
39
|
-
},
|
|
40
|
-
"controller_length": {
|
|
41
|
-
"green": 50,
|
|
42
|
-
"yellow": 100,
|
|
43
|
-
"red": 200
|
|
44
|
-
},
|
|
45
|
-
"model_public_methods": {
|
|
46
|
-
"green": 7,
|
|
47
|
-
"yellow": 12,
|
|
48
|
-
"red": 20
|
|
49
|
-
},
|
|
50
|
-
"view_length": {
|
|
51
|
-
"green": 30,
|
|
52
|
-
"yellow": 50,
|
|
53
|
-
"red": 100
|
|
54
|
-
},
|
|
55
|
-
"migration_complexity": {
|
|
56
|
-
"green": 10,
|
|
57
|
-
"yellow": 20,
|
|
58
|
-
"red": 40
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"file_type_multipliers": {
|
|
62
|
-
"controllers": 1.2,
|
|
63
|
-
"models": 1.0,
|
|
64
|
-
"views": 0.8,
|
|
65
|
-
"helpers": 0.9,
|
|
66
|
-
"lib": 1.1,
|
|
67
|
-
"specs": 0.7,
|
|
68
|
-
"migrations": 0.6
|
|
69
|
-
},
|
|
70
|
-
"scoring_weights": {
|
|
71
|
-
"method_length": 0.15,
|
|
72
|
-
"class_length": 0.12,
|
|
73
|
-
"cyclomatic_complexity": 0.20,
|
|
74
|
-
"nesting_depth": 0.18,
|
|
75
|
-
"parameter_count": 0.10,
|
|
76
|
-
"duplication": 0.25,
|
|
77
|
-
"rails_conventions": 0.15,
|
|
78
|
-
"code_smells": 0.25
|
|
79
|
-
}
|
|
80
|
-
}
|