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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 806988266c00efaa8181b4379e6baa5f5ff9e5913fa4a0801fd993ededf3a580
4
- data.tar.gz: 61e1909e93309e22265b961038f87da7c0367f38cf6cf9a763449e144e39c73f
3
+ metadata.gz: 4b357d8913c61f87fa0edb2c9f672963c90a56a1b9679e0ee41a80c17d69883f
4
+ data.tar.gz: 55f62937933e657fe285dfddf64238ab39ef19114118c265f849773023ee56b5
5
5
  SHA512:
6
- metadata.gz: 37d498b553e834f76811b405f6be6b967fec45512170098b70b818bc952de8321cf7790dce0f7b93b1353fc373b12614ba9f7be98466e871d26f25b22ad75938
7
- data.tar.gz: 1ca51781fa1cccfdabd3ef5e138d2cd2aea2e0a64432ea7e26802464c4e5c732185c09a8622ca4a558f8ccef7762653a168982dcc1db9f6fe29633ded8feb673
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.1...HEAD
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
- report = analyze_project
38
+ results = analyze_project
37
39
 
38
40
  # Output the report
39
- output_report(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
- config_file = File.join(File.dirname(__FILE__), '..', '..', 'config', 'thresholds.json')
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 << "1. 🚨 Address critical files immediately:"
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
 
@@ -1,3 +1,3 @@
1
1
  module RailsCodeHealth
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.3"
3
3
  end
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.1
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
@@ -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
- }