rails_code_health 0.3.1 → 0.3.2

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: 5497b3b246fd2ade723213f812d3b182930bbb5b330842095c9da5e6d209d95e
4
+ data.tar.gz: ebd140330230966a6d8077376c2372238b4ef9ca8cc683a5a8eabc0d102d8ab3
5
5
  SHA512:
6
- metadata.gz: 37d498b553e834f76811b405f6be6b967fec45512170098b70b818bc952de8321cf7790dce0f7b93b1353fc373b12614ba9f7be98466e871d26f25b22ad75938
7
- data.tar.gz: 1ca51781fa1cccfdabd3ef5e138d2cd2aea2e0a64432ea7e26802464c4e5c732185c09a8622ca4a558f8ccef7762653a168982dcc1db9f6fe29633ded8feb673
6
+ metadata.gz: a8c22b599aa60e8376efc1a690f64f4b6bd9b45d1df0511e5d92a620798e49f41c1524e71a8bfab6422626fecbdf6b328ff92fd9002a13bd6ad9f1e5ad2bd9aa
7
+ data.tar.gz: 873bac085a21edf62a52c8174f5208b86bc6852b922ce0c2a0d67f7d1d0ee3a1e242713a4f8a121f399afb5b1686a569ca1c8e1c861cf1350cba22629c0d0cd2
data/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.2] - 2026-05-18
11
+
12
+ ### Added
13
+ - `--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.
14
+ - `--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.
15
+ - GitHub Actions example in the README and landing page.
16
+
17
+ ### Fixed
18
+ - 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.
19
+
20
+ ### Changed
21
+ - Removed `config/**/*` from `gemspec`'s shipped files (the directory no longer exists).
22
+
10
23
  ## [0.3.1] - 2026-05-18
11
24
 
12
25
  ### Fixed
@@ -81,7 +94,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
81
94
  - **Reporting**: Detailed console output with health categories and JSON export
82
95
  - **CLI**: `rails-health` command with options for format, output file, and custom configuration
83
96
 
84
- [Unreleased]: https://github.com/gkosmo/rails_code_health/compare/v0.3.1...HEAD
97
+ [Unreleased]: https://github.com/gkosmo/rails_code_health/compare/v0.3.2...HEAD
98
+ [0.3.2]: https://github.com/gkosmo/rails_code_health/compare/v0.3.1...v0.3.2
85
99
  [0.3.1]: https://github.com/gkosmo/rails_code_health/compare/v0.3.0...v0.3.1
86
100
  [0.3.0]: https://github.com/gkosmo/rails_code_health/compare/v0.2.0...v0.3.0
87
101
  [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
@@ -1,3 +1,3 @@
1
1
  module RailsCodeHealth
2
- VERSION = "0.3.1"
2
+ VERSION = "0.3.2"
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.2
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
- }