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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +537 -0
- data/exe/upgrade_radar +5 -0
- data/lib/upgrade_radar/audit_visitor.rb +121 -0
- data/lib/upgrade_radar/cli.rb +77 -0
- data/lib/upgrade_radar/configuration.rb +31 -0
- data/lib/upgrade_radar/effort_estimator.rb +63 -0
- data/lib/upgrade_radar/file_finder.rb +40 -0
- data/lib/upgrade_radar/finding.rb +43 -0
- data/lib/upgrade_radar/formatters/base.rb +20 -0
- data/lib/upgrade_radar/formatters/html_formatter.rb +20 -0
- data/lib/upgrade_radar/formatters/json_formatter.rb +26 -0
- data/lib/upgrade_radar/formatters/text_formatter.rb +96 -0
- data/lib/upgrade_radar/matchers/base.rb +32 -0
- data/lib/upgrade_radar/matchers/block_parameter_matcher.rb +19 -0
- data/lib/upgrade_radar/matchers/constant_reference_matcher.rb +43 -0
- data/lib/upgrade_radar/matchers/gemfile_matcher.rb +70 -0
- data/lib/upgrade_radar/matchers/method_call_matcher.rb +202 -0
- data/lib/upgrade_radar/matchers/string_mutation_matcher.rb +32 -0
- data/lib/upgrade_radar/result.rb +47 -0
- data/lib/upgrade_radar/rule.rb +52 -0
- data/lib/upgrade_radar/rule_registry.rb +43 -0
- data/lib/upgrade_radar/runtime/collector.rb +74 -0
- data/lib/upgrade_radar/runtime/middleware.rb +51 -0
- data/lib/upgrade_radar/runtime/rails_hook.rb +24 -0
- data/lib/upgrade_radar/runtime/ruby_warning_hook.rb +22 -0
- data/lib/upgrade_radar/scanner.rb +71 -0
- data/lib/upgrade_radar/templates/report.html.erb +125 -0
- data/lib/upgrade_radar/version.rb +5 -0
- data/lib/upgrade_radar/version_range.rb +38 -0
- data/lib/upgrade_radar.rb +33 -0
- data/rules/rails/rails_7_1.yml +17 -0
- data/rules/rails/rails_7_2.yml +49 -0
- data/rules/rails/rails_8_0.yml +81 -0
- data/rules/ruby/ruby_3_3.yml +91 -0
- data/rules/ruby/ruby_3_4.yml +84 -0
- 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,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: []
|