require "rainbow" require "cgi" require "erb" require "json" require "net/http" module NextRails module BundleReport extend self def compatibility(rails_version: nil, ruby_version: nil, include_rails_gems: nil) return puts RubyVersionCompatibility.new(options: { ruby_version: ruby_version }).generate if ruby_version incompatible_gems = NextRails::GemInfo.all.reject do |gem| gem.compatible_with_rails?(rails_version: rails_version) || (!include_rails_gems && gem.from_rails?) end.sort_by { |gem| gem.name } incompatible_gems.each { |gem| gem.find_latest_compatible(rails_version: rails_version) } incompatible_gems_by_state = incompatible_gems.group_by { |gem| gem.state(rails_version) } puts erb_output(incompatible_gems_by_state, incompatible_gems, rails_version) end def erb_output(incompatible_gems_by_state, incompatible_gems, rails_version) template = <<-ERB <% if incompatible_gems_by_state[:found_compatible] -%> <%= Rainbow("=> Incompatible with Rails #{rails_version} (with new versions that are compatible):").white.bold %> <%= Rainbow("These gems will need to be upgraded before upgrading to Rails #{rails_version}.").italic %> <% incompatible_gems_by_state[:found_compatible].each do |gem| -%> <%= gem_header(gem) %> - upgrade to <%= gem.latest_compatible_version.version %> <% end -%> <% end -%> <% if incompatible_gems_by_state[:incompatible] -%> <%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new compatible versions):").white.bold %> <%= Rainbow("These gems will need to be removed or replaced before upgrading to Rails #{rails_version}.").italic %> <% incompatible_gems_by_state[:incompatible].each do |gem| -%> <%= gem_header(gem) %> - new version, <%= gem.latest_version.version %>, is not compatible with Rails #{rails_version} <% end -%> <% end -%> <% if incompatible_gems_by_state[:no_new_version] -%> <%= Rainbow("=> Incompatible with Rails #{rails_version} (with no new versions):").white.bold %> <%= Rainbow("These gems will need to be upgraded by us or removed before upgrading to Rails #{rails_version}.").italic %> <%= Rainbow("This list is likely to contain internal gems, like Cuddlefish.").italic %> <% incompatible_gems_by_state[:no_new_version].each do |gem| -%> <%= gem_header(gem) %> - new version not found <% end -%> <% end -%> <%= Rainbow(incompatible_gems.length.to_s).red %> gems incompatible with Rails <%= rails_version %> ERB erb_version = ERB.version if erb_version =~ /erb.rb \[([\d\.]+) .*\]/ erb_version = $1 end if Gem::Version.new(erb_version) < Gem::Version.new("2.2") ERB.new(template, nil, "-").result(binding) else ERB.new(template, trim_mode: "-").result(binding) end end def gem_header(_gem) header = Rainbow("#{_gem.name} #{_gem.version}").bold header << Rainbow(" (loaded from git)").magenta if _gem.sourced_from_git? header end def compatible_ruby_version(rails_version) # find all the versions of rails gem uri = URI('https://rubygems.org/api/v1/versions/rails.json') res = Net::HTTP.get_response(uri) all_versions_res = JSON.parse(res.body) # push all the versions in an array all_versions = [] all_versions_res.each { |rv| all_versions << rv['number'] } rv = rails_version[:rails_version] matched_versions = all_versions.select { |h| h.start_with?(rv) } # the list can either have the exact version or the latest version in the series of versions # you are looking at # ex: matched_versions = ["6.1.4.2", "6.1.4.1", "6.1.4"] # if you have passed "6.1.4" and the list has the exact version, it will match and send # the ruby version for it bu tif you had passed "6.1", then it will look for the # latest version matching "6.1" which is "6.1.4.2" in this case and will return ruby # version for it. exact_version = matched_versions.include?(rv) ? rv : matched_versions[0] if exact_version uri = URI("https://rubygems.org/api/v2/rubygems/rails/versions/#{exact_version}.json") res = Net::HTTP.get_response(uri) ruby_version = JSON.parse(res.body)["ruby_version"] else ruby_version = nil end if ruby_version puts "The required ruby version is #{ruby_version} for matched rails version #{exact_version}" ruby_version else puts "Could not find a compatible ruby version" end end def outdated(format = nil) gems = NextRails::GemInfo.all out_of_date_gems = gems.reject(&:up_to_date?).sort_by(&:created_at) sourced_from_git = gems.select(&:sourced_from_git?) if format == 'json' output_to_json(out_of_date_gems, gems.count, sourced_from_git.count) else output_to_stdout(out_of_date_gems, gems.count, sourced_from_git.count) end end def output_to_json(out_of_date_gems, total_gem_count, sourced_from_git_count) obj = build_json(out_of_date_gems, total_gem_count, sourced_from_git_count) puts JSON.pretty_generate(obj) end def build_json(out_of_date_gems, total_gem_count, sourced_from_git_count) output = Hash.new { [] } out_of_date_gems.each do |gem| output[:outdated_gems] += [ { name: gem.name, installed_version: gem.version, installed_age: gem.age, latest_version: gem.latest_version.version, latest_age: gem.latest_version.age } ] end output.merge( { sourced_from_git_count: sourced_from_git_count, total_gem_count: total_gem_count } ) end def output_to_stdout(out_of_date_gems, total_gem_count, sourced_from_git_count) out_of_date_gems.each do |gem| header = "#{gem.name} #{gem.version}" puts <<-MESSAGE #{Rainbow(header.bold.white)}: released #{gem.age} (latest version, #{gem.latest_version.version}, released #{gem.latest_version.age}) MESSAGE end percentage_out_of_date = ((out_of_date_gems.count / total_gem_count.to_f) * 100).round footer = <<-MESSAGE #{Rainbow(sourced_from_git_count.to_s).yellow} gems are sourced from git #{Rainbow(out_of_date_gems.count.to_s).red} of the #{total_gem_count} gems are out-of-date (#{percentage_out_of_date}%) MESSAGE puts '' puts footer end end end