upgrade_radar 0.1.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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +537 -0
  5. data/exe/upgrade_radar +5 -0
  6. data/lib/upgrade_radar/audit_visitor.rb +121 -0
  7. data/lib/upgrade_radar/cli.rb +77 -0
  8. data/lib/upgrade_radar/configuration.rb +31 -0
  9. data/lib/upgrade_radar/effort_estimator.rb +63 -0
  10. data/lib/upgrade_radar/file_finder.rb +40 -0
  11. data/lib/upgrade_radar/finding.rb +43 -0
  12. data/lib/upgrade_radar/formatters/base.rb +20 -0
  13. data/lib/upgrade_radar/formatters/html_formatter.rb +20 -0
  14. data/lib/upgrade_radar/formatters/json_formatter.rb +26 -0
  15. data/lib/upgrade_radar/formatters/text_formatter.rb +96 -0
  16. data/lib/upgrade_radar/matchers/base.rb +32 -0
  17. data/lib/upgrade_radar/matchers/block_parameter_matcher.rb +19 -0
  18. data/lib/upgrade_radar/matchers/constant_reference_matcher.rb +43 -0
  19. data/lib/upgrade_radar/matchers/gemfile_matcher.rb +70 -0
  20. data/lib/upgrade_radar/matchers/method_call_matcher.rb +202 -0
  21. data/lib/upgrade_radar/matchers/string_mutation_matcher.rb +32 -0
  22. data/lib/upgrade_radar/result.rb +47 -0
  23. data/lib/upgrade_radar/rule.rb +52 -0
  24. data/lib/upgrade_radar/rule_registry.rb +43 -0
  25. data/lib/upgrade_radar/runtime/collector.rb +74 -0
  26. data/lib/upgrade_radar/runtime/middleware.rb +51 -0
  27. data/lib/upgrade_radar/runtime/rails_hook.rb +24 -0
  28. data/lib/upgrade_radar/runtime/ruby_warning_hook.rb +22 -0
  29. data/lib/upgrade_radar/scanner.rb +71 -0
  30. data/lib/upgrade_radar/templates/report.html.erb +125 -0
  31. data/lib/upgrade_radar/version.rb +5 -0
  32. data/lib/upgrade_radar/version_range.rb +38 -0
  33. data/lib/upgrade_radar.rb +33 -0
  34. data/rules/rails/rails_7_1.yml +17 -0
  35. data/rules/rails/rails_7_2.yml +49 -0
  36. data/rules/rails/rails_8_0.yml +81 -0
  37. data/rules/ruby/ruby_3_3.yml +91 -0
  38. data/rules/ruby/ruby_3_4.yml +84 -0
  39. metadata +113 -0
@@ -0,0 +1,125 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Upgrade Radar Report</title>
7
+ <style>
8
+ :root {
9
+ --bg: #ffffff;
10
+ --fg: #1a1a2e;
11
+ --card-bg: #f8f9fa;
12
+ --border: #dee2e6;
13
+ --breaking-bg: #fff5f5;
14
+ --breaking-fg: #c53030;
15
+ --breaking-badge: #e53e3e;
16
+ --warning-bg: #fffff0;
17
+ --warning-fg: #b7791f;
18
+ --warning-badge: #d69e2e;
19
+ --success: #38a169;
20
+ --code-bg: #edf2f7;
21
+ --link: #3182ce;
22
+ }
23
+ @media (prefers-color-scheme: dark) {
24
+ :root {
25
+ --bg: #1a1a2e;
26
+ --fg: #e2e8f0;
27
+ --card-bg: #2d2d44;
28
+ --border: #4a5568;
29
+ --breaking-bg: #3b1a1a;
30
+ --breaking-fg: #fc8181;
31
+ --breaking-badge: #e53e3e;
32
+ --warning-bg: #3b3520;
33
+ --warning-fg: #fbd38d;
34
+ --warning-badge: #d69e2e;
35
+ --success: #68d391;
36
+ --code-bg: #2d3748;
37
+ --link: #63b3ed;
38
+ }
39
+ }
40
+ * { margin: 0; padding: 0; box-sizing: border-box; }
41
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--fg); line-height: 1.6; padding: 2rem; max-width: 960px; margin: 0 auto; }
42
+ h1 { font-size: 1.8rem; margin-bottom: 0.5rem; }
43
+ .meta { color: var(--warning-fg); margin-bottom: 2rem; }
44
+ .stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin-bottom: 2rem; }
45
+ .stat { background: var(--card-bg); border: 1px solid var(--border); border-radius: 8px; padding: 1rem; text-align: center; }
46
+ .stat-value { font-size: 1.8rem; font-weight: 700; }
47
+ .stat-label { font-size: 0.85rem; opacity: 0.7; }
48
+ .breaking .stat-value { color: var(--breaking-fg); }
49
+ .warning .stat-value { color: var(--warning-fg); }
50
+ .success .stat-value { color: var(--success); }
51
+ .file-group { margin-bottom: 1.5rem; }
52
+ .file-name { font-weight: 600; font-size: 0.95rem; padding: 0.5rem 0; border-bottom: 1px solid var(--border); margin-bottom: 0.5rem; }
53
+ .finding { padding: 0.75rem; margin-bottom: 0.5rem; border-radius: 6px; border-left: 4px solid; }
54
+ .finding.breaking { background: var(--breaking-bg); border-color: var(--breaking-badge); }
55
+ .finding.warning { background: var(--warning-bg); border-color: var(--warning-badge); }
56
+ .badge { display: inline-block; font-size: 0.7rem; font-weight: 700; padding: 0.1rem 0.4rem; border-radius: 3px; color: white; text-transform: uppercase; }
57
+ .badge.breaking { background: var(--breaking-badge); }
58
+ .badge.warning { background: var(--warning-badge); }
59
+ .finding-message { margin-top: 0.25rem; font-size: 0.9rem; }
60
+ .finding-meta { font-size: 0.8rem; opacity: 0.7; margin-top: 0.25rem; }
61
+ code { background: var(--code-bg); padding: 0.1rem 0.3rem; border-radius: 3px; font-size: 0.85rem; }
62
+ a { color: var(--link); }
63
+ footer { margin-top: 3rem; padding-top: 1rem; border-top: 1px solid var(--border); font-size: 0.8rem; opacity: 0.6; }
64
+ </style>
65
+ </head>
66
+ <body>
67
+ <h1>Upgrade Radar Report</h1>
68
+ <%- if @result.from_version && @result.to_version -%>
69
+ <p class="meta"><%= h(@result.from_version) %> → <%= h(@result.to_version) %></p>
70
+ <%- end -%>
71
+
72
+ <div class="stats">
73
+ <div class="stat breaking">
74
+ <div class="stat-value"><%= @summary[:breaking] %></div>
75
+ <div class="stat-label">Breaking</div>
76
+ </div>
77
+ <div class="stat warning">
78
+ <div class="stat-value"><%= @summary[:warnings] %></div>
79
+ <div class="stat-label">Warnings</div>
80
+ </div>
81
+ <div class="stat">
82
+ <div class="stat-value"><%= @summary[:files_affected] %></div>
83
+ <div class="stat-label">Files Affected</div>
84
+ </div>
85
+ <div class="stat">
86
+ <div class="stat-value"><%= @summary[:total_effort_human] %></div>
87
+ <div class="stat-label">Estimated Effort</div>
88
+ </div>
89
+ </div>
90
+
91
+ <%- if @result.findings.empty? -%>
92
+ <div class="stat success" style="text-align:center; padding:2rem;">
93
+ <div class="stat-value">✓</div>
94
+ <div class="stat-label">No deprecations or breaking changes found</div>
95
+ </div>
96
+ <%- else -%>
97
+ <%- @result.findings_by_file.each do |filepath, findings| -%>
98
+ <div class="file-group">
99
+ <div class="file-name"><%= h(filepath) %></div>
100
+ <%- findings.sort_by(&:line).each do |f| -%>
101
+ <div class="finding <%= f.severity %>">
102
+ <span class="badge <%= f.severity %>"><%= f.severity %></span>
103
+ <strong>Line <%= f.line %></strong>
104
+ <div class="finding-message"><%= h(f.message) %></div>
105
+ <div class="finding-meta">
106
+ <code><%= h(f.source.length > 80 ? f.source[0...80] + "..." : f.source) %></code>
107
+ <%- if f.rule.replacement -%>
108
+ <br>Fix: <code><%= h(f.rule.replacement) %></code>
109
+ <%- end -%>
110
+ <%- if f.rule.docs -%>
111
+ <br><a href="<%= h(f.rule.docs) %>" target="_blank" rel="noopener noreferrer">Documentation</a>
112
+ <%- end -%>
113
+ </div>
114
+ </div>
115
+ <%- end -%>
116
+ </div>
117
+ <%- end -%>
118
+ <%- end -%>
119
+
120
+ <footer>
121
+ Generated by Upgrade Radar v<%= UpgradeRadar::VERSION %> on <%= Time.now.strftime("%Y-%m-%d %H:%M:%S") %>
122
+ — Scanned <%= @result.files_scanned %> files in <%= @result.scan_duration.round(2) %>s
123
+ </footer>
124
+ </body>
125
+ </html>
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UpgradeRadar
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module UpgradeRadar
4
+ class VersionRange
5
+ attr_reader :framework, :version
6
+
7
+ VALID_FRAMEWORKS = %w[rails ruby].freeze
8
+
9
+ def initialize(descriptor)
10
+ parts = descriptor.strip.split("-", 2)
11
+ raise ArgumentError, "Invalid version descriptor: #{descriptor.inspect}. Expected format: 'rails-7.2' or 'ruby-3.3'" unless parts.length == 2
12
+
13
+ @framework = parts[0].downcase
14
+ raise ArgumentError, "Unknown framework: #{@framework.inspect}. Supported: #{VALID_FRAMEWORKS.join(', ')}" unless VALID_FRAMEWORKS.include?(@framework)
15
+
16
+ @version = Gem::Version.new(parts[1])
17
+ rescue ArgumentError => e
18
+ raise e if e.message.start_with?("Invalid version", "Unknown framework")
19
+ raise ArgumentError, "Invalid version number in: #{descriptor.inspect}"
20
+ end
21
+
22
+ def self.parse(from_str, to_str)
23
+ from = new(from_str)
24
+ to = new(to_str)
25
+ unless from.framework == to.framework
26
+ raise ArgumentError, "Framework mismatch: --from is #{from.framework} but --to is #{to.framework}"
27
+ end
28
+ unless to.version > from.version
29
+ raise ArgumentError, "--to version (#{to.version}) must be greater than --from version (#{from.version})"
30
+ end
31
+ [from, to]
32
+ end
33
+
34
+ def to_s
35
+ "#{framework}-#{version}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "prism"
4
+ require "yaml"
5
+ require "json"
6
+ require "erb"
7
+
8
+ require_relative "upgrade_radar/version"
9
+ require_relative "upgrade_radar/version_range"
10
+ require_relative "upgrade_radar/rule"
11
+ require_relative "upgrade_radar/rule_registry"
12
+ require_relative "upgrade_radar/finding"
13
+ require_relative "upgrade_radar/result"
14
+ require_relative "upgrade_radar/effort_estimator"
15
+ require_relative "upgrade_radar/file_finder"
16
+ require_relative "upgrade_radar/matchers/base"
17
+ require_relative "upgrade_radar/matchers/method_call_matcher"
18
+ require_relative "upgrade_radar/matchers/constant_reference_matcher"
19
+ require_relative "upgrade_radar/matchers/block_parameter_matcher"
20
+ require_relative "upgrade_radar/matchers/gemfile_matcher"
21
+ require_relative "upgrade_radar/matchers/string_mutation_matcher"
22
+ require_relative "upgrade_radar/audit_visitor"
23
+ require_relative "upgrade_radar/scanner"
24
+ require_relative "upgrade_radar/formatters/base"
25
+ require_relative "upgrade_radar/formatters/text_formatter"
26
+ require_relative "upgrade_radar/formatters/json_formatter"
27
+ require_relative "upgrade_radar/formatters/html_formatter"
28
+ require_relative "upgrade_radar/configuration"
29
+ require_relative "upgrade_radar/cli"
30
+
31
+ module UpgradeRadar
32
+ RULES_PATH = File.expand_path("../../rules", __FILE__)
33
+ end
@@ -0,0 +1,17 @@
1
+ ---
2
+ - id: rails-7.1-serialize-coder
3
+ framework: rails
4
+ introduced_in: "7.1"
5
+ breaking_in: "8.0"
6
+ pattern:
7
+ type: method_call
8
+ name: serialize
9
+ receiver: null
10
+ conditions:
11
+ - no_keyword: coder
12
+ message: >
13
+ `serialize` requires an explicit `coder:` keyword argument in Rails 7.1+.
14
+ Without it, Rails 8.0 raises ArgumentError.
15
+ replacement: "serialize :attr, coder: JSON"
16
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#new-activerecord-serialize-api"
17
+ effort_minutes: 5
@@ -0,0 +1,49 @@
1
+ ---
2
+ - id: rails-7.2-enum-keyword-args
3
+ framework: rails
4
+ introduced_in: "7.2"
5
+ breaking_in: "8.0"
6
+ pattern:
7
+ type: method_call
8
+ name: enum
9
+ receiver: null
10
+ conditions:
11
+ - first_arg_type: hash
12
+ message: >
13
+ Positional hash argument to `enum` is deprecated in Rails 7.2 and removed in 8.0.
14
+ Use `enum :status, { active: 0, inactive: 1 }` with a positional symbol instead.
15
+ replacement: "enum :status, { active: 0, inactive: 1 }"
16
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#enumerations"
17
+ effort_minutes: 5
18
+
19
+ - id: rails-7.2-secrets-removed
20
+ framework: rails
21
+ introduced_in: "7.2"
22
+ breaking_in: "7.2"
23
+ pattern:
24
+ type: method_call
25
+ name: secrets
26
+ receiver_chain: "Rails.application"
27
+ conditions: []
28
+ message: >
29
+ `Rails.application.secrets` has been removed in Rails 7.2.
30
+ Use `Rails.application.credentials` instead.
31
+ replacement: "Rails.application.credentials"
32
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#removal-of-secrets"
33
+ effort_minutes: 30
34
+
35
+ - id: rails-7.2-alias-attribute
36
+ framework: rails
37
+ introduced_in: "7.2"
38
+ breaking_in: "7.2"
39
+ pattern:
40
+ type: method_call
41
+ name: alias_attribute
42
+ receiver: null
43
+ conditions: []
44
+ message: >
45
+ `alias_attribute` in Rails 7.2 now defines actual attribute methods that bypass custom accessors.
46
+ If the aliased attribute has a custom getter/setter, behavior may change silently.
47
+ replacement: "Verify custom accessor logic; consider alias_method if needed"
48
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
49
+ effort_minutes: 20
@@ -0,0 +1,81 @@
1
+ ---
2
+ - id: rails-8.0-connection-pool
3
+ framework: rails
4
+ introduced_in: "8.0"
5
+ breaking_in: "8.0"
6
+ pattern:
7
+ type: method_call
8
+ name: connection
9
+ receiver_chain: "**.connection_pool"
10
+ conditions: []
11
+ message: >
12
+ `connection_pool.connection` is removed in Rails 8.0.
13
+ Use `connection_pool.with_connection { |conn| ... }` or `lease_connection` instead.
14
+ replacement: "connection_pool.with_connection { |conn| ... }"
15
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
16
+ effort_minutes: 15
17
+
18
+ - id: rails-8.0-params-expect
19
+ framework: rails
20
+ introduced_in: "8.0"
21
+ breaking_in: "8.0"
22
+ pattern:
23
+ type: method_call
24
+ name: permit
25
+ receiver_method: require
26
+ conditions: []
27
+ message: >
28
+ `params.require(:x).permit(...)` pattern is deprecated in Rails 8.0.
29
+ Use `params.expect(x: [...])` for safer parameter filtering that returns 400 on malformed input.
30
+ replacement: "params.expect(user: [:name, :email])"
31
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
32
+ effort_minutes: 5
33
+
34
+ - id: rails-8.0-proxy-object
35
+ framework: rails
36
+ introduced_in: "8.0"
37
+ breaking_in: "8.0"
38
+ pattern:
39
+ type: constant_reference
40
+ name: "ActiveSupport::ProxyObject"
41
+ message: >
42
+ `ActiveSupport::ProxyObject` has been removed in Rails 8.0.
43
+ Use `ActiveSupport::BasicObject` instead.
44
+ replacement: "ActiveSupport::BasicObject"
45
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
46
+ effort_minutes: 5
47
+
48
+ - id: rails-8.0-form-with-nil
49
+ framework: rails
50
+ introduced_in: "8.0"
51
+ breaking_in: "8.0"
52
+ pattern:
53
+ type: method_call
54
+ name: form_with
55
+ receiver: null
56
+ conditions:
57
+ - keyword_value:
58
+ key: model
59
+ value: "nil"
60
+ message: >
61
+ `form_with model: nil` is no longer valid in Rails 8.0.
62
+ Omit the `model:` option entirely when not using a model.
63
+ replacement: "form_with url: some_path"
64
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
65
+ effort_minutes: 5
66
+
67
+ - id: rails-8.0-read-encrypted-secrets
68
+ framework: rails
69
+ introduced_in: "8.0"
70
+ breaking_in: "8.0"
71
+ pattern:
72
+ type: method_call
73
+ name: read_encrypted_secrets=
74
+ receiver: null
75
+ conditions: []
76
+ message: >
77
+ `config.read_encrypted_secrets` is removed in Rails 8.0.
78
+ Use the credentials system instead.
79
+ replacement: "Remove config.read_encrypted_secrets; use Rails.application.credentials"
80
+ docs: "https://guides.rubyonrails.org/upgrading_ruby_on_rails.html"
81
+ effort_minutes: 30
@@ -0,0 +1,91 @@
1
+ ---
2
+ - id: ruby-3.3-lambda-proc
3
+ framework: ruby
4
+ introduced_in: "3.3"
5
+ breaking_in: "3.3"
6
+ pattern:
7
+ type: method_call
8
+ name: lambda
9
+ receiver: null
10
+ conditions:
11
+ - has_block_pass: true
12
+ message: >
13
+ `lambda(&proc_variable)` raises ArgumentError in Ruby 3.3 if the Proc is not a lambda.
14
+ Passing a non-lambda Proc to `lambda` no longer silently converts it.
15
+ replacement: "Use lambda { |a, b| ... } directly, or -> (a, b) { ... }"
16
+ docs: "https://docs.ruby-lang.org/en/3.3/NEWS.html"
17
+ effort_minutes: 15
18
+
19
+ - id: ruby-3.3-time-new-string
20
+ framework: ruby
21
+ introduced_in: "3.3"
22
+ breaking_in: "3.3"
23
+ pattern:
24
+ type: method_call
25
+ name: new
26
+ receiver: "Time"
27
+ conditions:
28
+ - first_arg_type: string
29
+ message: >
30
+ `Time.new` with a partial date string (e.g. 'YYYY-MM-DD') raises ArgumentError in Ruby 3.3.
31
+ Use integer positional arguments: Time.new(year, month, day).
32
+ replacement: "Time.new(2024, 1, 15)"
33
+ docs: "https://docs.ruby-lang.org/en/3.3/NEWS.html"
34
+ effort_minutes: 10
35
+
36
+ - id: ruby-3.3-io-pipe
37
+ framework: ruby
38
+ introduced_in: "3.3"
39
+ breaking_in: "3.3"
40
+ pattern:
41
+ type: method_call
42
+ name:
43
+ - read
44
+ - open
45
+ receiver: "IO"
46
+ conditions:
47
+ - first_arg_starts_with: "|"
48
+ message: >
49
+ `IO.read('| command')` and `IO.open('| command')` with pipe prefix are deprecated in Ruby 3.3.
50
+ Use `IO.popen` instead.
51
+ replacement: "IO.popen('command') { |io| io.read }"
52
+ docs: "https://docs.ruby-lang.org/en/3.3/NEWS.html"
53
+ effort_minutes: 10
54
+
55
+ - id: ruby-3.3-it-block-param
56
+ framework: ruby
57
+ introduced_in: "3.3"
58
+ breaking_in: "3.4"
59
+ pattern:
60
+ type: block_parameter
61
+ bare_it: true
62
+ message: >
63
+ Bare `it` inside a block refers to the first block argument in Ruby 3.4.
64
+ In Ruby 3.3 this emits a warning. If `it` is a method call, use explicit `self.it` or `it()`.
65
+ replacement: "self.it or it() for method calls; use _1 or named params"
66
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
67
+ effort_minutes: 10
68
+
69
+ - id: ruby-3.3-default-gems-bundled
70
+ framework: ruby
71
+ introduced_in: "3.3"
72
+ breaking_in: "3.4"
73
+ pattern:
74
+ type: gemfile_dependency
75
+ gems:
76
+ - csv
77
+ - base64
78
+ - bigdecimal
79
+ - mutex_m
80
+ - drb
81
+ - nkf
82
+ - syslog
83
+ - observer
84
+ - abbrev
85
+ - getoptlong
86
+ message: >
87
+ These default gems became bundled gems in Ruby 3.4 and must be declared in your Gemfile.
88
+ In Ruby 3.3 this emits a warning; in 3.4 it raises LoadError.
89
+ replacement: "Add gem 'name' to your Gemfile"
90
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
91
+ effort_minutes: 5
@@ -0,0 +1,84 @@
1
+ ---
2
+ - id: ruby-3.4-kwargs-index
3
+ framework: ruby
4
+ introduced_in: "3.4"
5
+ breaking_in: "3.4"
6
+ pattern:
7
+ type: method_call
8
+ name: "[]"
9
+ receiver: "any"
10
+ conditions:
11
+ - has_keywords: true
12
+ - min_positional_args: 1
13
+ message: >
14
+ Keyword arguments in indexing operators (`[]`, `[]=`) raise SyntaxError in Ruby 3.4.
15
+ Wrap keyword args in an explicit hash or refactor to a method call.
16
+ replacement: "obj.fetch(key, **opts) or obj[key, { opt: val }]"
17
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
18
+ effort_minutes: 15
19
+
20
+ - id: ruby-3.4-range-size
21
+ framework: ruby
22
+ introduced_in: "3.4"
23
+ breaking_in: "3.4"
24
+ confidence: low
25
+ pattern:
26
+ type: method_call
27
+ name: size
28
+ receiver: "any"
29
+ conditions: []
30
+ message: >
31
+ `Range#size` raises TypeError for non-iterable ranges (Float endpoints, beginless) in Ruby 3.4.
32
+ Note: this rule has low confidence statically — receiver type cannot always be determined.
33
+ Verify manually if the receiver is a Range with Float or nil bounds.
34
+ replacement: "Use Range#count for iterable ranges, or calculate manually"
35
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
36
+ effort_minutes: 10
37
+
38
+ - id: ruby-3.4-socket-resolution-error
39
+ framework: ruby
40
+ introduced_in: "3.4"
41
+ breaking_in: "3.4"
42
+ pattern:
43
+ type: constant_reference
44
+ name: "SocketError"
45
+ context: rescue
46
+ message: >
47
+ Ruby 3.4 introduces `Socket::ResolutionError` for DNS failures (subclass of SocketError).
48
+ Existing `rescue SocketError` still catches it, but if you need to handle DNS failures
49
+ specifically, use `rescue Socket::ResolutionError`.
50
+ replacement: "rescue SocketError, Socket::ResolutionError"
51
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
52
+ effort_minutes: 10
53
+
54
+ - id: ruby-3.4-chilled-string
55
+ framework: ruby
56
+ introduced_in: "3.4"
57
+ breaking_in: "3.4"
58
+ pattern:
59
+ type: string_mutation
60
+ detect_literal_mutation: true
61
+ message: >
62
+ Mutating a string literal emits a deprecation warning in Ruby 3.4 ('chilled strings').
63
+ This prepares for frozen string literals becoming the default in a future Ruby version.
64
+ replacement: "Use String.new('str') or 'str'.dup for mutable strings, or +'str'"
65
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
66
+ effort_minutes: 5
67
+
68
+ - id: ruby-3.4-hash-inspect
69
+ framework: ruby
70
+ introduced_in: "3.4"
71
+ breaking_in: "3.4"
72
+ confidence: low
73
+ pattern:
74
+ type: method_call
75
+ name: inspect
76
+ receiver: "any"
77
+ conditions: []
78
+ message: >
79
+ Hash#inspect in Ruby 3.4 uses colon syntax for symbol keys: `{a: 1}` instead of `{:a=>1}`.
80
+ Note: low confidence statically — cannot determine if receiver is a Hash.
81
+ Check tests that assert on Hash#inspect or Hash#to_s output.
82
+ replacement: "Update test expectations to match new format"
83
+ docs: "https://docs.ruby-lang.org/en/3.4/NEWS.html"
84
+ effort_minutes: 10
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: upgrade_radar
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Samarth Kulkarni
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: prism
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: thor
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.3'
40
+ description: Static and runtime analysis for Ruby/Rails deprecations and breaking
41
+ changes. Scans your codebase with Prism AST analysis and reports what will break,
42
+ where, and why.
43
+ email:
44
+ - svkulkarni23@gmail.com
45
+ executables:
46
+ - upgrade_radar
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - CHANGELOG.md
51
+ - LICENSE.txt
52
+ - README.md
53
+ - exe/upgrade_radar
54
+ - lib/upgrade_radar.rb
55
+ - lib/upgrade_radar/audit_visitor.rb
56
+ - lib/upgrade_radar/cli.rb
57
+ - lib/upgrade_radar/configuration.rb
58
+ - lib/upgrade_radar/effort_estimator.rb
59
+ - lib/upgrade_radar/file_finder.rb
60
+ - lib/upgrade_radar/finding.rb
61
+ - lib/upgrade_radar/formatters/base.rb
62
+ - lib/upgrade_radar/formatters/html_formatter.rb
63
+ - lib/upgrade_radar/formatters/json_formatter.rb
64
+ - lib/upgrade_radar/formatters/text_formatter.rb
65
+ - lib/upgrade_radar/matchers/base.rb
66
+ - lib/upgrade_radar/matchers/block_parameter_matcher.rb
67
+ - lib/upgrade_radar/matchers/constant_reference_matcher.rb
68
+ - lib/upgrade_radar/matchers/gemfile_matcher.rb
69
+ - lib/upgrade_radar/matchers/method_call_matcher.rb
70
+ - lib/upgrade_radar/matchers/string_mutation_matcher.rb
71
+ - lib/upgrade_radar/result.rb
72
+ - lib/upgrade_radar/rule.rb
73
+ - lib/upgrade_radar/rule_registry.rb
74
+ - lib/upgrade_radar/runtime/collector.rb
75
+ - lib/upgrade_radar/runtime/middleware.rb
76
+ - lib/upgrade_radar/runtime/rails_hook.rb
77
+ - lib/upgrade_radar/runtime/ruby_warning_hook.rb
78
+ - lib/upgrade_radar/scanner.rb
79
+ - lib/upgrade_radar/templates/report.html.erb
80
+ - lib/upgrade_radar/version.rb
81
+ - lib/upgrade_radar/version_range.rb
82
+ - rules/rails/rails_7_1.yml
83
+ - rules/rails/rails_7_2.yml
84
+ - rules/rails/rails_8_0.yml
85
+ - rules/ruby/ruby_3_3.yml
86
+ - rules/ruby/ruby_3_4.yml
87
+ homepage: https://github.com/samrocks03/upgrade_radar
88
+ licenses:
89
+ - MIT
90
+ metadata:
91
+ homepage_uri: https://github.com/samrocks03/upgrade_radar
92
+ source_code_uri: https://github.com/samrocks03/upgrade_radar
93
+ changelog_uri: https://github.com/samrocks03/upgrade_radar/blob/main/CHANGELOG.md
94
+ bug_tracker_uri: https://github.com/samrocks03/upgrade_radar/issues
95
+ rubygems_mfa_required: 'true'
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.2.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.7.2
111
+ specification_version: 4
112
+ summary: Know your Ruby/Rails upgrade cost before you start
113
+ test_files: []