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

eval_gemfile + LTS support #249

Closed
wants to merge 4 commits into from
Closed

Conversation

pboling
Copy link
Contributor

@pboling pboling commented Feb 20, 2025

Adds back support for Ruby 1.8 - 2.2, but enhances the support with real, automated, tools to ensure syntax remains compatible with Ruby 1.8.

Also fixes several bugs / problem areas around the codebase that became apparent when adding back support. I'll go through this PR and annotate with comments, as it is very large. As much as I'd love to see this merged, I understand it may not be.

For my use case, with my version_gem gem, I need to use appraisal to test compatibility with Ruby v2.2 (and many of my other gems I plan to add appraisal to, particularly the RSpec plugins, support back to Ruby 1.8.7). I've tried a few other things, but they were all much worse solutions than doing this work. So if this doesn't get merged, an appraisal-lts gem fork might appear in the wild... :)

This is a combinataion of #248 and #250, which can be merged separately instead of this one.

NOTE: Hound is not paying attention to the RuboCop config. :(. Not very helpful.

"Gemfile:3311641552": [
[19, 3, 4, "Security/Eval: The use of `eval` is a serious security risk.", 2087429787],
[21, 3, 4, "Security/Eval: The use of `eval` is a serious security risk.", 2087429787],
[23, 3, 4, "Security/Eval: The use of `eval` is a serious security risk.", 2087429787]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm sure you are familiar with lockfiles, and how they are useful. Using a lockfile for RuboCop violations allows for gradually fixing violations in a way that the rubocop TODO pattern does not.

It's almost a perfect tool, and every project should be using it, IMO:
https://github.com/skryukov/rubocop-gradual

# We run rubocop on the latest version of Ruby,
# but in support of the oldest supported version of Ruby

gem "rubocop-lts", "~> 0.1", ">= 0.1.1" # Style and Linting support for Ruby >= 1.8
Copy link
Contributor Author

Choose a reason for hiding this comment

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

rubocop-lts is how I package all of the rules I've found to support every version of Ruby since v1.8, where every even version of the gem corresponds to a specific minor version of Ruby, (odd releases are deprecated, or non-extant).

See: https://rubocop-lts.gitlab.io/

@@ -150,7 +150,7 @@ def lockfile_path
end

def clean_name
name.gsub(/[^\w\.]/, "_")
Copy link
Contributor Author

Choose a reason for hiding this comment

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

. does not need to be escaped inside [] brakcets.

end
raise AppraisalsNotFound unless File.exist?(path)

run(File.read(path))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

File.read is safer than IO.read (according to whoever made the rule for RuboCop).

@@ -114,7 +139,7 @@ def ruby_version_entry

case @ruby_version
when String then "ruby #{@ruby_version.inspect}"
else "ruby(#{@ruby_version.inspect})"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a minor bug, as it was not calling Utils.format_string, as many of the other similar methods in this class do (and they are correct for so doing). Some of the variability in output between Ruby versions was due to this.

end
end
ERROR
exit(1)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The control flow is much easier to understand now.

@@single_quotes = single_quotes
end

def self.heading(gemfile)
def self.heading(gemfile = nil)
Copy link
Contributor Author

@pboling pboling Feb 20, 2025

Choose a reason for hiding this comment

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

The RSPec tests for this method were failing, and marked as pending because this was a bug!

def self.customize(heading, gemfile)
return nil unless heading
def self.customize(topper, gemfile)
return unless topper
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Stop shadowing the heading method.

@@ -14,7 +14,7 @@ module Appraisal
# Load bundler Gemfiles and merge dependencies
class Gemfile < BundlerDSL
def load(path)
run(IO.read(path), path) if File.exist?(path)
run(File.read(path), path) if File.exist?(path)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

File.read is safer than IO.read (again).

# a complete implementation, but it covers Appraisal's specific needs.
class OrderedHash < ::Hash
# Hashes are ordered in Ruby 1.9.
if RUBY_VERSION < "1.9"
Copy link
Contributor Author

@pboling pboling Feb 20, 2025

Choose a reason for hiding this comment

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

This is the only RUBY_VERSION check of any kind in the runtime code in this PR.

There are still some checks in the test code, so that the old versions of ruby can be properly tested.

Maintaining support for Ruby v1.8+ isn't (currently) hard at all, especially with the new syntax and linting support from rubocop-lts that autocorrects code (like hashes to hash rockets) to enforce Ruby 1.8 syntax. I run it on many gems supporting Ruby 1.8, and have submitted a number of related fixes upstream to both standard, and rubocop.

platforms #{Utils.format_arguments(@platform_names)} do
#{output_dependencies}
end
<<-OUTPUT.strip
Copy link
Contributor Author

Choose a reason for hiding this comment

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

<<~ Squiggly HEREDOC support was added in Ruby 2.3, so for now HEREDOCS are a bit ugly.

end

def self.join_parts(parts)
parts.reject(&:nil?).reject(&:empty?).join("\n\n").strip
end

def self.prefix_path(path)
if path !~ /^(?:\/|\S:)/ && path !~ /^\S+:\/\// && path !~ /^\S+@\S+:/
cleaned_path = path.gsub(/(^|\/)\.(?:\/|$)/, "\\1")
if path !~ %r{^(?:/|\S:)} && path !~ %r{^\S+://} && path !~ /^\S+@\S+:/
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Much simplified regexes, thanks to the %r{} syntax.

it "passes --without flag to Bundler on install" do
RSpec.describe "Bundle without group" do
it "config set --local without group is honored by Bundler" do
pending "config set --local without group support seems broken, see: https://github.com/rubygems/rubygems/issues/8518"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Literally this is broken, reported bug to bundler:
rubygems/rubygems#8518

%x[git init . --initial-branch=master]
%x[git config user.email "appraisal@thoughtbot.com"]
%x[git config user.name "Appraisal"]
%x[git config commit.gpgsign false]
Copy link
Contributor Author

@pboling pboling Feb 20, 2025

Choose a reason for hiding this comment

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

On my local machine I have git configured to sign all commits, which was breaking the test suite here, because it couldn't sign the commit two lines below. Adding this config to prevent signing for this ephemeral gem fixed the issue.

@n-rodriguez
Copy link
Contributor

@pboling thanks for your amazing work! 👍

Also fixes several bugs / problem areas around the codebase that became apparent when adding back support. I'll go through this PR and annotate with comments, as it is very large. As much as I'd love to see this merged, I understand it may not be.

Is it possible to split this PR in 2? one for eval_gemfile and one for LTS? thank you!

@pboling
Copy link
Contributor Author

pboling commented Feb 20, 2025

It sort of already is. This one sits on top of theeval_gemfile PR. The build failures in the other PR are not related to the PR, and are actually just a broken CI build, which I am sure would fail similarly on master branch. If that other PR is merged, then this one will shrink.

I'll make a new PR with the LTS changes that is not stacked. Then perhaps it could be merged first, and improve the CI build.

@pboling
Copy link
Contributor Author

pboling commented Feb 20, 2025

@n-rodriguez done, see: #248 & #250

puts "building gem: #{gem_name} #{version}" if ENV["VERBOSE"]
`gem build #{gemspec} #{redirect}`
puts "building gem: #{gem_name} #{version}" if ENV["VERBOSE"]
%x[gem build #{gemspec} #{redirect}]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you please use %x()? It reads better than %x[] or %x{}. Thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #250

puts "installing gem: #{gem_name} #{version}" if ENV["VERBOSE"]
`gem install -lN #{gem_name}-#{version}.gem -v #{version} #{redirect}`
puts "installing gem: #{gem_name} #{version}" if ENV["VERBOSE"]
%x[gem install -lN #{gem_name}-#{version}.gem -v #{version} #{redirect}]

Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #250

%x[git config user.name "Appraisal"]
%x[git config commit.gpgsign false]
%x[git add .]
%x[git commit --all --no-verify --message "initial commit"]
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in #250

@pboling pboling closed this Feb 20, 2025
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.

2 participants