Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find latest compatible version when checking compatibility #49

Merged
merged 3 commits into from
May 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# main [(unreleased)](https://github.com/fastruby/next_rails/compare/v1.0.4...main)

* [FEATURE: Try to find the latest **compatible** version of a gem if the latest version is not compatible with the desired Rails version when checking compatibility](https://github.com/fastruby/next_rails/pull/49)

# v1.0.5 / 2022-03-29 [(commits)](https://github.com/fastruby/next_rails/compare/v1.0.4...v1.0.5)

* [FEATURE: Initialize the Gemfile.next.lock to avoid major version jumps when used without an initial Gemfile.next.lock](https://github.com/fastruby/next_rails/pull/25)
Expand All @@ -14,6 +16,7 @@

* [BUGFIX: Update README.md to better document this `ten_years_rails` fork](https://github.com/fastruby/next_rails/pull/11)
* [BUGFIX: Make ActionView an optional dependency](https://github.com/fastruby/next_rails/pull/6)

# v1.0.2 / 2020-01-20

# v1.0.1 / 2019-07-26
Expand Down
15 changes: 6 additions & 9 deletions lib/next_rails/bundle_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,19 @@ class BundleReport
def self.compatibility(rails_version:, include_rails_gems:)
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 do |gem|
[
gem.latest_version.compatible_with_rails?(rails_version: rails_version) ? 0 : 1,
gem.name
].join("-")
end
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) }

template = <<~ERB
<% if incompatible_gems_by_state[:latest_compatible] -%>
<% if incompatible_gems_by_state[:found_compatible] -%>
<%= "=> Incompatible with Rails #{rails_version} (with new versions that are compatible):".white.bold %>
<%= "These gems will need to be upgraded before upgrading to Rails #{rails_version}.".italic %>

<% incompatible_gems_by_state[:latest_compatible].each do |gem| -%>
<%= gem_header(gem) %> - upgrade to <%= gem.latest_version.version %>
<% incompatible_gems_by_state[:found_compatible].each do |gem| -%>
<%= gem_header(gem) %> - upgrade to <%= gem.latest_compatible_version.version %>
<% end -%>

<% end -%>
Expand Down
97 changes: 61 additions & 36 deletions lib/next_rails/gem_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,28 @@ def state(_)
end
end

RAILS_GEMS = [
"rails",
"activemodel",
"activerecord",
"actionmailer",
"actioncable",
"actionpack",
"actionview",
"activejob",
"activestorage",
"activesupport",
"railties",
].freeze

def self.all
Gem::Specification.each.map do |gem_specification|
new(gem_specification)
end
end

attr_reader :gem_specification, :version, :name
attr_reader :gem_specification, :version, :name, :latest_compatible_version

def initialize(gem_specification)
@gem_specification = gem_specification
@version = gem_specification.version
Expand All @@ -57,59 +72,69 @@ def up_to_date?
version == latest_version.version
end

def from_rails?
RAILS_GEMS.include?(name)
end

def state(rails_version)
if compatible_with_rails?(rails_version: rails_version)
:compatible
elsif latest_version.compatible_with_rails?(rails_version: rails_version)
:latest_compatible
elsif latest_version.version == "NOT FOUND"
elsif latest_compatible_version.version == "NOT FOUND"
:no_new_version
elsif latest_compatible_version
:found_compatible
else
:incompatible
end
end

def latest_version
@latest_version ||= begin
latest_gem_specification = Gem.latest_spec_for(name)
if latest_gem_specification
GemInfo.new(latest_gem_specification)
else
NullGemInfo.new
end
end
end

def compatible_with_rails?(rails_version: Gem::Version.new("5.0"))
def compatible_with_rails?(rails_version:)
unsatisfied_rails_dependencies(rails_version: rails_version).empty?
end

def unsatisfied_rails_dependencies(rails_version:)
rails_dependencies = gem_specification.runtime_dependencies.select {|dependency| rails_gems.include?(dependency.name) }
spec_compatible_with_rails?(specification: gem_specification, rails_version: rails_version)
end

rails_dependencies.reject do |rails_dependency|
rails_dependency.requirement.satisfied_by?(Gem::Version.new(rails_version))
def find_latest_compatible(rails_version:)
dependency = Gem::Dependency.new(@name)
fetcher = Gem::SpecFetcher.new

# list all available data for released gems
list, errors = fetcher.available_specs(:released)

specs = []
# filter only specs for the current gem and older versions
list.each do |source, gem_tuples|
gem_tuples.each do |gem_tuple|
if gem_tuple.name == @name && gem_tuple.version > @version
specs << source.fetch_spec(gem_tuple)
end
end
end
end

def from_rails?
rails_gems.include?(name)
# if nothing is found, consider gem incompatible
if specs.empty?
@latest_compatible_version = NullGemInfo.new
return
end

# if specs are found, look for the first one from that is compatible
# with the desired rails version starting from the end
specs.reverse.each do |spec|
if spec_compatible_with_rails?(specification: spec, rails_version: rails_version).empty?
@latest_compatible_version = spec
break
end
end
end

private def rails_gems
[
"rails",
"activemodel",
"activerecord",
"actionmailer",
"actioncable",
"actionpack",
"actionview",
"activejob",
"activestorage",
"activesupport",
"railties",
]
def spec_compatible_with_rails?(specification:, rails_version:)
rails_dependencies = specification.runtime_dependencies.select {|dependency| RAILS_GEMS.include?(dependency.name) }

rails_dependencies.reject do |rails_dependency|
rails_dependency.requirement.satisfied_by?(Gem::Version.new(rails_version))
end
end
end
end