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

Required ruby version fixes #9515

Merged
merged 4 commits into from
Feb 20, 2021

Conversation

HeroProtagonist
Copy link
Contributor

Closes #9482 and #9329

The class to match required_ruby_version from .gemspec files could return invalid ruby versions.

  • This PR makes it so that it will only return a value from the KNOWN_RUBIES array. Specifically the minimal known ruby will be returned.
  • The version parsing is also greatly improved by leveraging existing classes from the Gem library.

Before submitting the PR make sure the following are checked:

  • The PR relates to only one subject with a clear title and description in grammatically correct, complete sentences.
  • Wrote good commit messages.
  • Commit message starts with [Fix #issue-number] (if the related issue exists).
  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Ran bundle exec rake default. It executes all tests and runs RuboCop on its own code.
  • Added an entry (file) to the changelog folder named {change_type}_{change_description}.md if the new code introduces user-observable changes. See changelog entry format for details.

@HeroProtagonist HeroProtagonist force-pushed the required-ruby-version-fixes branch 2 times, most recently from 38e6f2c to bb076a8 Compare February 14, 2021 04:32
Add cases that include
- numeric version
- approximate version
- not equal version
- array of many version types
…equired_ruby_version`

- Create `Gem::Requirement` from required_ruby_version
- Return first `KNOWN_RUBY` that satisfies the requirement when one exists
- Support more types of version entries
Copy link
Contributor

@marcandre marcandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Much better!
I think it can be improved, simplified, and avoid parsing the requirement ourselves


# Helper to check if the requirement is satisfied by a given known ruby version
# @api private
class Matcher
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be best to do without this class and use the Gem::Requirement as a matcher. Note that matchers typically better constructed without the value to match; that value is passed to the matching method (like satisfied_by?).

def version_from_str(str)
str.match(/^(?:>=|<=)?\s*(?<version>\d+(?:\.\d+)*)/) do |md|
md[:version].to_f
def remove_patch_from_version(str)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you went through the trouble of parsing the requirement nevertheless... How about instead mapping our versions like 2.7 => '2.7.999'? This would have the desired effect, except in the case of exact Ruby version requirements (which are a mistake), simplify the code and avoid redoing the parsing that we shouldn't be doing.

s.licenses = ['MIT']
end
HEREDOC

create_file(gemspec_file_path, content)
expect(target_ruby.version).to eq default_version
end

it 'sets target_ruby from inclusive range' do
content =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No obligation, but consider using let or before_each to avoid duplicating all these lines so many times.

content =
<<-HEREDOC
Gem::Specification.new do |s|
s.name = 'test'
s.required_ruby_version = '>= 3.0.0'
s.required_ruby_version = '2.7.4'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't even test this. There's is no reason I can think of to specify such a requirement. We could even consider raising an offense in one of our cops for this, but I doubt it happens in practice.

content =
<<-HEREDOC
Gem::Specification.new do |s|
s.name = 'test'
s.required_ruby_version = '< 3.0.0'
s.required_ruby_version = '2.3.0'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, testing for < 2.3.0 is fine, but not = 2.3.0

@HeroProtagonist
Copy link
Contributor Author

@marcandre Thanks for the review. Updated the PR and I think its ready now 🤞

Copy link
Contributor

@marcandre marcandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is almost ready 😅

Comment on lines 208 to 210
gem_version = ->(ruby_version) { Gem::Version.new(ruby_version) }

match = KNOWN_RUBIES.each.lazy.map(&gem_version).detect { |v| requirement.satisfied_by?(v) }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't these lines be replaced by:

match = KNOWN_RUBIES.detect { |v| requirement.satisfied_by?(Gem::Version.new(v) }

I'd actually use Gem::Version.new("#{v}.99") so that it works ok for >= '2.6.1'

In general, storing a lambda in a local variable is an anti-pattern. Also, it's typically best to use lazy only when actually needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only one question. The .99 would cause a not equal check like the ones I have here and here to fail. Maybe that is ok? Not sure how common it is to be specifying not equal versions

Copy link
Contributor

@marcandre marcandre Feb 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to me it is an error to use !=. In any case, if someone did specify != 2.4.0, that doesn't exclude 2.4.1 etc which are in the 2.4 series, so it seems like an ok target.

The main scenario we have to get right is >, >= and potentially ~> even though that seems a bit strange.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok 1 more thing if we had '> 2.4.1' then it would match to 2.4 due to the .99, that seems wrong?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's the best behavior. Imagine that there is a bug in 2.4.1 that is fixed in Ruby 2.4.2+; the gem author wants to make sure we don't try to run the gem in 2.4.1 or below as the bug can be triggered by code in the gem. The gem still uses the "2.4.x" features and syntax, so target of 2.4 is best.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok pushed the updates 😃

content =
<<-HEREDOC
Gem::Specification.new do |s|
s.name = 'test'
s.required_ruby_version = '2.7.4'
s.required_ruby_version = '>= 2.6.0'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should also work for >= 2.6.1

Copy link
Contributor

@marcandre marcandre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff! 💪
Thanks 😄

@marcandre marcandre merged commit 3ade276 into rubocop:master Feb 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TargetRuby and required_ruby_version crash
3 participants