ruby-reforge 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/.rspec +4 -0
- data/.ruby-version +2 -0
- data/CHANGELOG.md +27 -0
- data/Gemfile +14 -0
- data/LICENSE.txt +22 -0
- data/QUICKSTART.md +77 -0
- data/README.md +115 -0
- data/Rakefile +9 -0
- data/exe/ruby-reforge +7 -0
- data/lib/ruby/reforge/cli.rb +146 -0
- data/lib/ruby/reforge/git_integration.rb +52 -0
- data/lib/ruby/reforge/migration_rules.rb +79 -0
- data/lib/ruby/reforge/rails_rules.rb +47 -0
- data/lib/ruby/reforge/reporter.rb +83 -0
- data/lib/ruby/reforge/rewriter.rb +96 -0
- data/lib/ruby/reforge/scanner.rb +178 -0
- data/lib/ruby/reforge/version.rb +8 -0
- data/lib/ruby/reforge/version_updater.rb +112 -0
- data/lib/ruby/reforge.rb +18 -0
- data/ruby-reforge.gemspec +48 -0
- metadata +212 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rainbow"
|
|
4
|
+
|
|
5
|
+
module Ruby
|
|
6
|
+
module Reforge
|
|
7
|
+
class Reporter
|
|
8
|
+
def report(issues, current_version, target_version)
|
|
9
|
+
say "\n" + "=" * 80, :cyan
|
|
10
|
+
say "📊 Upgrade Report: Ruby #{current_version || 'unknown'} → #{target_version}", :cyan
|
|
11
|
+
say "=" * 80, :cyan
|
|
12
|
+
say ""
|
|
13
|
+
|
|
14
|
+
if issues.empty?
|
|
15
|
+
say "✅ No issues found! Your code is ready for Ruby #{target_version}.", :green
|
|
16
|
+
return
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Group issues by type
|
|
20
|
+
by_type = issues.group_by(&:type)
|
|
21
|
+
by_severity = issues.group_by(&:severity)
|
|
22
|
+
|
|
23
|
+
# Summary
|
|
24
|
+
say "Summary:", :yellow
|
|
25
|
+
say " Total issues found: #{issues.size}", :white
|
|
26
|
+
say " Errors: #{by_severity[:error]&.size || 0}", :red
|
|
27
|
+
say " Warnings: #{by_severity[:warning]&.size || 0}", :yellow
|
|
28
|
+
say " Info: #{by_severity[:info]&.size || 0}", :blue
|
|
29
|
+
say ""
|
|
30
|
+
|
|
31
|
+
# Issues by file
|
|
32
|
+
by_file = issues.group_by(&:file)
|
|
33
|
+
say "Issues by file:", :yellow
|
|
34
|
+
say ""
|
|
35
|
+
|
|
36
|
+
by_file.each do |file, file_issues|
|
|
37
|
+
relative_path = file.start_with?("/") ? file : file
|
|
38
|
+
say " #{relative_path} (#{file_issues.size} issue(s))", :white
|
|
39
|
+
file_issues.each do |issue|
|
|
40
|
+
severity_color = case issue.severity
|
|
41
|
+
when :error then :red
|
|
42
|
+
when :warning then :yellow
|
|
43
|
+
else :blue
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
say " Line #{issue.line}: #{issue.message}", severity_color
|
|
47
|
+
if issue.old_code && issue.new_code
|
|
48
|
+
say " Old: #{issue.old_code}", :red
|
|
49
|
+
say " New: #{issue.new_code}", :green
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
say ""
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Breaking changes
|
|
56
|
+
if by_type[:breaking_change]
|
|
57
|
+
say "⚠️ Breaking Changes:", :red
|
|
58
|
+
by_type[:breaking_change].each do |issue|
|
|
59
|
+
say " - #{issue.message}", :red
|
|
60
|
+
end
|
|
61
|
+
say ""
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Recommendations
|
|
65
|
+
say "Recommendations:", :yellow
|
|
66
|
+
say " 1. Review all deprecated method calls", :white
|
|
67
|
+
say " 2. Test thoroughly after applying fixes", :white
|
|
68
|
+
say " 3. Update dependencies: bundle update", :white
|
|
69
|
+
say " 4. Check for gem compatibility with Ruby #{target_version}", :white
|
|
70
|
+
say ""
|
|
71
|
+
|
|
72
|
+
say "Run 'ruby-reforge upgrade #{target_version}' to apply automatic fixes.", :green
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def say(message, color = nil)
|
|
78
|
+
puts Rainbow(message).color(color)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Ruby
|
|
6
|
+
module Reforge
|
|
7
|
+
class Rewriter
|
|
8
|
+
def initialize(root_path)
|
|
9
|
+
@root_path = root_path
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def rewrite(issues, interactive: false)
|
|
13
|
+
grouped_issues = issues.group_by(&:file)
|
|
14
|
+
fixed_count = 0
|
|
15
|
+
|
|
16
|
+
grouped_issues.each do |file_path, file_issues|
|
|
17
|
+
next unless File.exist?(file_path)
|
|
18
|
+
|
|
19
|
+
content = File.read(file_path)
|
|
20
|
+
lines = content.lines
|
|
21
|
+
modified = false
|
|
22
|
+
|
|
23
|
+
# Sort issues by line number (descending) to avoid offset issues
|
|
24
|
+
file_issues.sort_by { |i| -i.line }.each do |issue|
|
|
25
|
+
case issue.type
|
|
26
|
+
when :deprecated_method, :deprecated_pattern
|
|
27
|
+
if interactive
|
|
28
|
+
next unless confirm_fix?(issue)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
line_index = issue.line - 1
|
|
32
|
+
if line_index < lines.length
|
|
33
|
+
old_line = lines[line_index]
|
|
34
|
+
new_line = apply_fix(old_line, issue)
|
|
35
|
+
if old_line != new_line
|
|
36
|
+
lines[line_index] = new_line
|
|
37
|
+
modified = true
|
|
38
|
+
fixed_count += 1
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if modified
|
|
45
|
+
File.write(file_path, lines.join)
|
|
46
|
+
say "✓ Fixed #{file_issues.size} issue(s) in #{File.basename(file_path)}", :green
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
fixed_count
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def apply_fix(line, issue)
|
|
56
|
+
case issue.type
|
|
57
|
+
when :deprecated_method
|
|
58
|
+
# Replace deprecated method calls
|
|
59
|
+
if issue.old_code && issue.new_code
|
|
60
|
+
# Try exact match first
|
|
61
|
+
if line.include?(issue.old_code)
|
|
62
|
+
line.gsub(issue.old_code, issue.new_code)
|
|
63
|
+
else
|
|
64
|
+
# Try with method call syntax
|
|
65
|
+
line.gsub(/#{Regexp.escape(issue.old_code)}/, issue.new_code)
|
|
66
|
+
end
|
|
67
|
+
else
|
|
68
|
+
line
|
|
69
|
+
end
|
|
70
|
+
when :deprecated_pattern
|
|
71
|
+
# Apply pattern-based fixes - use the new_code which should be the replacement
|
|
72
|
+
# The issue.new_code contains the full line replacement
|
|
73
|
+
if issue.new_code
|
|
74
|
+
issue.new_code
|
|
75
|
+
else
|
|
76
|
+
line
|
|
77
|
+
end
|
|
78
|
+
else
|
|
79
|
+
line
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def confirm_fix?(issue)
|
|
84
|
+
require "tty-prompt"
|
|
85
|
+
prompt = TTY::Prompt.new
|
|
86
|
+
prompt.yes?("Fix: #{issue.message} at #{File.basename(issue.file)}:#{issue.line}?")
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def say(message, color = nil)
|
|
90
|
+
require "rainbow"
|
|
91
|
+
puts Rainbow(message).color(color)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "prism"
|
|
4
|
+
require "find"
|
|
5
|
+
|
|
6
|
+
module Ruby
|
|
7
|
+
module Reforge
|
|
8
|
+
class Scanner
|
|
9
|
+
def initialize(root_path)
|
|
10
|
+
@root_path = root_path
|
|
11
|
+
@issues = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def scan(target_version)
|
|
15
|
+
@target_version = normalize_version(target_version)
|
|
16
|
+
@issues = []
|
|
17
|
+
|
|
18
|
+
# Get migration rules for target version
|
|
19
|
+
rules = MigrationRules.for_version(@target_version)
|
|
20
|
+
|
|
21
|
+
# Add Rails rules if Rails project detected
|
|
22
|
+
if RailsRules.detect_rails_project?(@root_path)
|
|
23
|
+
rules = merge_rails_rules(rules)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Scan Ruby files
|
|
27
|
+
scan_ruby_files(rules)
|
|
28
|
+
|
|
29
|
+
@issues
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def scan_ruby_files(rules)
|
|
35
|
+
ruby_files.each do |file_path|
|
|
36
|
+
begin
|
|
37
|
+
content = File.read(file_path)
|
|
38
|
+
parse_result = Prism.parse(content, file_path)
|
|
39
|
+
|
|
40
|
+
if parse_result.success?
|
|
41
|
+
scan_ast(parse_result.value, file_path, rules)
|
|
42
|
+
else
|
|
43
|
+
# File has syntax errors, but we can still scan for patterns
|
|
44
|
+
scan_file_content(content, file_path, rules)
|
|
45
|
+
end
|
|
46
|
+
rescue => e
|
|
47
|
+
# Skip files that can't be parsed
|
|
48
|
+
next
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def scan_ast(node, file_path, rules)
|
|
54
|
+
# Scan for keyword argument issues (Ruby 3.0+)
|
|
55
|
+
if @target_version >= "3.0.0"
|
|
56
|
+
scan_keyword_arguments(node, file_path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Scan for deprecated method calls
|
|
60
|
+
scan_deprecated_methods(node, file_path, rules)
|
|
61
|
+
|
|
62
|
+
# Recursively scan child nodes
|
|
63
|
+
node.child_nodes.each do |child|
|
|
64
|
+
scan_ast(child, file_path, rules) if child.respond_to?(:child_nodes)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def scan_keyword_arguments(node, file_path)
|
|
69
|
+
# Detect method definitions that might need **args
|
|
70
|
+
if node.is_a?(Prism::DefNode)
|
|
71
|
+
# Check if method accepts keyword arguments without **
|
|
72
|
+
# This is a simplified check - real implementation would be more sophisticated
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def scan_deprecated_methods(node, file_path, rules)
|
|
77
|
+
rules.deprecated_methods.each do |deprecated_method, replacement|
|
|
78
|
+
if node.is_a?(Prism::CallNode) && node.name == deprecated_method.to_sym
|
|
79
|
+
@issues << Issue.new(
|
|
80
|
+
type: :deprecated_method,
|
|
81
|
+
file: file_path,
|
|
82
|
+
line: node.location.start_line,
|
|
83
|
+
message: "#{deprecated_method} is deprecated, use #{replacement} instead",
|
|
84
|
+
old_code: deprecated_method.to_s,
|
|
85
|
+
new_code: replacement.to_s
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def scan_file_content(content, file_path, rules)
|
|
92
|
+
lines = content.lines
|
|
93
|
+
|
|
94
|
+
rules.deprecated_patterns.each do |pattern, replacement|
|
|
95
|
+
lines.each_with_index do |line, index|
|
|
96
|
+
if line.match?(pattern)
|
|
97
|
+
# Preserve indentation when replacing
|
|
98
|
+
indent = line[/\A\s*/]
|
|
99
|
+
new_line = line.gsub(pattern, replacement)
|
|
100
|
+
# Ensure new line ends properly
|
|
101
|
+
new_line = new_line.chomp + "\n" unless new_line.end_with?("\n")
|
|
102
|
+
|
|
103
|
+
@issues << Issue.new(
|
|
104
|
+
type: :deprecated_pattern,
|
|
105
|
+
file: file_path,
|
|
106
|
+
line: index + 1,
|
|
107
|
+
message: "Deprecated pattern found: #{pattern.source}",
|
|
108
|
+
old_code: line,
|
|
109
|
+
new_code: new_line
|
|
110
|
+
)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def ruby_files
|
|
118
|
+
@ruby_files ||= begin
|
|
119
|
+
files = []
|
|
120
|
+
Find.find(@root_path) do |path|
|
|
121
|
+
next if File.directory?(path)
|
|
122
|
+
next if path.include?("/vendor/")
|
|
123
|
+
next if path.include?("/node_modules/")
|
|
124
|
+
next if path.include?("/.git/")
|
|
125
|
+
next if path.include?("/tmp/")
|
|
126
|
+
next if path.include?("/log/")
|
|
127
|
+
|
|
128
|
+
files << path if path.end_with?(".rb")
|
|
129
|
+
end
|
|
130
|
+
files
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def normalize_version(version)
|
|
135
|
+
parts = version.to_s.split(".").map(&:to_i)
|
|
136
|
+
parts << 0 while parts.size < 3
|
|
137
|
+
parts[0..2].join(".")
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def merge_rails_rules(rules)
|
|
141
|
+
# Merge Rails deprecations into existing rules
|
|
142
|
+
merged_methods = rules.deprecated_methods.merge(RailsRules.deprecated_methods)
|
|
143
|
+
merged_patterns = rules.deprecated_patterns.merge(RailsRules.deprecated_patterns)
|
|
144
|
+
|
|
145
|
+
merged_rules = MigrationRules.new
|
|
146
|
+
merged_rules.instance_variable_set(:@deprecated_methods, merged_methods)
|
|
147
|
+
merged_rules.instance_variable_set(:@deprecated_patterns, merged_patterns)
|
|
148
|
+
merged_rules.instance_variable_set(:@breaking_changes, rules.breaking_changes)
|
|
149
|
+
merged_rules
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Issue
|
|
154
|
+
attr_reader :type, :file, :line, :message, :old_code, :new_code
|
|
155
|
+
|
|
156
|
+
def initialize(type:, file:, line:, message:, old_code: nil, new_code: nil)
|
|
157
|
+
@type = type
|
|
158
|
+
@file = file
|
|
159
|
+
@line = line
|
|
160
|
+
@message = message
|
|
161
|
+
@old_code = old_code
|
|
162
|
+
@new_code = new_code
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def severity
|
|
166
|
+
case @type
|
|
167
|
+
when :deprecated_method, :deprecated_pattern
|
|
168
|
+
:warning
|
|
169
|
+
when :breaking_change
|
|
170
|
+
:error
|
|
171
|
+
else
|
|
172
|
+
:info
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
|
|
5
|
+
module Ruby
|
|
6
|
+
module Reforge
|
|
7
|
+
class VersionUpdater
|
|
8
|
+
def initialize(root_path)
|
|
9
|
+
@root_path = root_path
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def update(target_version)
|
|
13
|
+
updated_files = []
|
|
14
|
+
|
|
15
|
+
# Update .ruby-version
|
|
16
|
+
ruby_version_path = File.join(@root_path, ".ruby-version")
|
|
17
|
+
if File.exist?(ruby_version_path)
|
|
18
|
+
File.write(ruby_version_path, "#{target_version}\n")
|
|
19
|
+
updated_files << ".ruby-version"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Update Gemfile
|
|
23
|
+
gemfile_path = File.join(@root_path, "Gemfile")
|
|
24
|
+
if File.exist?(gemfile_path)
|
|
25
|
+
update_gemfile(gemfile_path, target_version)
|
|
26
|
+
updated_files << "Gemfile"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Update gemspec files
|
|
30
|
+
Dir.glob(File.join(@root_path, "**/*.gemspec")).each do |gemspec_path|
|
|
31
|
+
update_gemspec(gemspec_path, target_version)
|
|
32
|
+
updated_files << gemspec_path
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
updated_files
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def detect_current_version
|
|
39
|
+
# Try .ruby-version first
|
|
40
|
+
ruby_version_path = File.join(@root_path, ".ruby-version")
|
|
41
|
+
if File.exist?(ruby_version_path)
|
|
42
|
+
version = File.read(ruby_version_path).strip
|
|
43
|
+
return version if version.match?(/^\d+\.\d+\.\d+/)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Try Gemfile
|
|
47
|
+
gemfile_path = File.join(@root_path, "Gemfile")
|
|
48
|
+
if File.exist?(gemfile_path)
|
|
49
|
+
content = File.read(gemfile_path)
|
|
50
|
+
if match = content.match(/ruby\s+['"]([\d.]+)['"]/)
|
|
51
|
+
return normalize_version(match[1])
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Try gemspec
|
|
56
|
+
Dir.glob(File.join(@root_path, "**/*.gemspec")).each do |gemspec_path|
|
|
57
|
+
content = File.read(gemspec_path)
|
|
58
|
+
if match = content.match(/required_ruby_version\s*[>=<]+\s*['"]([\d.]+)['"]/)
|
|
59
|
+
return normalize_version(match[1])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def update_gemfile(gemfile_path, target_version)
|
|
69
|
+
content = File.read(gemfile_path)
|
|
70
|
+
updated_content = content.gsub(
|
|
71
|
+
/ruby\s+['"][\d.]+['"]/,
|
|
72
|
+
"ruby '#{target_version}'"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# If no ruby version specified, add it after source
|
|
76
|
+
unless content.match(/ruby\s+['"][\d.]+['"]/)
|
|
77
|
+
updated_content = updated_content.sub(
|
|
78
|
+
/(source\s+['"][^'"]+['"])/,
|
|
79
|
+
"\\1\nruby '#{target_version}'"
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
File.write(gemfile_path, updated_content)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def update_gemspec(gemspec_path, target_version)
|
|
87
|
+
content = File.read(gemspec_path)
|
|
88
|
+
updated_content = content.gsub(
|
|
89
|
+
/required_ruby_version\s*[>=<]+\s*['"][\d.]+['"]/,
|
|
90
|
+
"required_ruby_version = \">= #{target_version}\""
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# If no required_ruby_version specified, add it
|
|
94
|
+
unless content.match(/required_ruby_version/)
|
|
95
|
+
updated_content = updated_content.sub(
|
|
96
|
+
/(spec\.version\s*=.*)/,
|
|
97
|
+
"\\1\n spec.required_ruby_version = \">= #{target_version}\""
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
File.write(gemspec_path, updated_content)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def normalize_version(version)
|
|
105
|
+
parts = version.split(".").map(&:to_i)
|
|
106
|
+
parts << 0 while parts.size < 3
|
|
107
|
+
parts[0..2].join(".")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
data/lib/ruby/reforge.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "reforge/version"
|
|
4
|
+
require_relative "reforge/cli"
|
|
5
|
+
require_relative "reforge/scanner"
|
|
6
|
+
require_relative "reforge/rewriter"
|
|
7
|
+
require_relative "reforge/version_updater"
|
|
8
|
+
require_relative "reforge/migration_rules"
|
|
9
|
+
require_relative "reforge/rails_rules"
|
|
10
|
+
require_relative "reforge/reporter"
|
|
11
|
+
require_relative "reforge/git_integration"
|
|
12
|
+
|
|
13
|
+
module Ruby
|
|
14
|
+
module Reforge
|
|
15
|
+
class Error < StandardError; end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/ruby/reforge/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "ruby-reforge"
|
|
7
|
+
spec.version = Ruby::Reforge::VERSION
|
|
8
|
+
spec.authors = ["Afshin Amini"]
|
|
9
|
+
spec.email = ["afshmini@gmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Automatically upgrade Ruby projects to newer versions and fix deprecated code"
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
ruby-reforge is a gem that scans a Ruby or Rails project, detects incompatible code
|
|
14
|
+
for a target Ruby version, and automatically rewrites or suggests fixes. It updates
|
|
15
|
+
version files, fixes deprecated syntax, rewrites code for new Ruby syntax, and
|
|
16
|
+
suggests Rails-level incompatibilities.
|
|
17
|
+
DESC
|
|
18
|
+
spec.homepage = "https://github.com/afshmini/ruby-reforge"
|
|
19
|
+
spec.license = "MIT"
|
|
20
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
21
|
+
|
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
24
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
25
|
+
|
|
26
|
+
spec.files = Dir.chdir(__dir__) do
|
|
27
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
28
|
+
(File.expand_path(f) == __FILE__) ||
|
|
29
|
+
f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
spec.bindir = "exe"
|
|
33
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
34
|
+
spec.require_paths = ["lib"]
|
|
35
|
+
|
|
36
|
+
spec.add_dependency "thor", "~> 1.0"
|
|
37
|
+
spec.add_dependency "prism", "~> 0.24"
|
|
38
|
+
spec.add_dependency "parser", "~> 3.3"
|
|
39
|
+
spec.add_dependency "unparser", "~> 0.6"
|
|
40
|
+
spec.add_dependency "rainbow", "~> 3.1"
|
|
41
|
+
spec.add_dependency "tty-prompt", "~> 0.23"
|
|
42
|
+
|
|
43
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
|
44
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
45
|
+
spec.add_development_dependency "rspec", "~> 3.12"
|
|
46
|
+
spec.add_development_dependency "rubocop", "~> 1.50"
|
|
47
|
+
end
|
|
48
|
+
|