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
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
7 changes: 6 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
fail-fast: false
matrix:
ruby:
- '3.4'
- '3.3'
- '3.2'
- '3.1'
Expand All @@ -41,7 +42,11 @@ jobs:
run: |
case ${RUBY_VERSION} in
truffleruby|truffleruby-head)
gem install bundler -v 2.5.18
gem install bundler -v 2.5.23
;;

3.0)
gem install bundler -v 2.5.23
;;

*)
Expand Down
17 changes: 17 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
inherit_gem:
rubocop-lts: config/rubygem_rspec.yml

RSpec/ExampleLength:
Enabled: false

RSpec/MultipleExpectations:
Enabled: false

RSpec/DescribeClass:
Enabled: false

# TODO: We would need to implement Mutexes in order to make violations thread safe.
# But even then they would still trigger the violation.
# See: https://coderscat.com/ruby-change-current-working-directory/
ThreadSafety/DirChdir:
Enabled: false
111 changes: 111 additions & 0 deletions .rubocop_gradual.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
{
"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

],
"bin/bundle:3123891436": [
[66, 5, 20, "ThreadSafety/ClassInstanceVariable: Avoid class instance variables.", 2485198147]
],
"lib/appraisal/appraisal_file.rb:3486777149": [
[13, 5, 52, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 946226050]
],
"lib/appraisal/cli.rb:435288507": [
[116, 5, 410, "Style/MissingRespondToMissing: When using `method_missing`, define `respond_to_missing?`.", 1969529734]
],
"lib/appraisal/customize.rb:190316824": [
[10, 5, 138, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2682968747],
[17, 5, 62, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 4082816720],
[21, 5, 516, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2780620929]
],
"lib/appraisal/utils.rb:2308689703": [
[6, 5, 129, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 733338411],
[13, 5, 354, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 119295155],
[35, 5, 152, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1306460482],
[42, 5, 151, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1120325158],
[48, 5, 97, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 2744794681],
[52, 5, 244, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1461524090],
[61, 5, 111, "Style/ClassMethodsDefinitions: Use `class << self` to define a class method.", 1909626476]
],
"spec/acceptance/cli/clean_spec.rb:1398207731": [
[5, 23, 17, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 1606893221]
],
"spec/acceptance/cli/generate_spec.rb:1599585127": [
[5, 23, 20, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 3905826443]
],
"spec/acceptance/cli/help_spec.rb:3832542771": [
[5, 23, 16, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 2148351217]
],
"spec/acceptance/cli/install_spec.rb:2827187127": [
[5, 23, 19, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 3284642881]
],
"spec/acceptance/cli/list_spec.rb:1943273700": [
[5, 23, 16, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 2152687586]
],
"spec/acceptance/cli/update_spec.rb:2524405540": [
[5, 23, 18, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 3752509585]
],
"spec/acceptance/cli/version_spec.rb:4076882072": [
[5, 23, 19, "RSpec/DescribeMethod: The second argument to describe should be the method being tested. '#instance' or '.class'.", 316256858]
],
"spec/appraisal/appraisal_file_spec.rb:1896479557": [
[28, 18, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441],
[33, 7, 57, "RSpec/NestedGroups: Maximum example group nesting exceeded [4/3].", 511689843],
[41, 20, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441],
[41, 33, 39, "RSpec/ExpectChange: Prefer `change(Appraisal::Customize, :heading)`.", 513325635],
[45, 7, 63, "RSpec/NestedGroups: Maximum example group nesting exceeded [4/3].", 1527582647],
[53, 20, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441],
[53, 33, 45, "RSpec/ExpectChange: Prefer `change(Appraisal::Customize, :single_quotes)`.", 627072007],
[57, 7, 77, "RSpec/NestedGroups: Maximum example group nesting exceeded [4/3].", 1960750461],
[65, 11, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441]
],
"spec/appraisal/appraisal_spec.rb:1242607413": [
[33, 11, 23, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2511581329],
[90, 11, 23, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1120511218],
[95, 7, 83, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [96].", 1453890366],
[95, 13, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192],
[96, 7, 82, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [95].", 1614459968],
[96, 13, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192],
[97, 61, 20, "RSpec/VerifiedDoubles: Prefer using verifying doubles over normal doubles.", 2281802167],
[104, 9, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192],
[114, 7, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192],
[120, 7, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192],
[126, 7, 10, "RSpec/InstanceVariable: Avoid instance variables - use let, a method call, or a local variable (if possible).", 4097172192]
],
"spec/appraisal/customize_spec.rb:1577209079": [
[7, 1, 4439, "RSpec/MultipleMemoizedHelpers: Example group has too many memoized helpers [6/5]", 3030165271],
[17, 3, 13, "RSpec/SubjectDeclaration: Use subject explicitly rather than using let", 3282510975],
[25, 3, 564, "RSpec/MultipleMemoizedHelpers: Example group has too many memoized helpers [6/5]", 3336288078],
[27, 7, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441],
[43, 3, 286, "RSpec/MultipleMemoizedHelpers: Example group has too many memoized helpers [6/5]", 4222920637],
[45, 7, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441],
[55, 3, 2985, "RSpec/MultipleMemoizedHelpers: Example group has too many memoized helpers [13/5]", 1576524985],
[65, 7, 62, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [66, 67, 68].", 123979604],
[66, 7, 72, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [65, 67, 68].", 3240119264],
[67, 7, 74, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [65, 66, 68].", 3701550880],
[68, 7, 85, "RSpec/ReceiveMessages: Use `receive_messages` instead of multiple stubs on lines [65, 66, 67].", 3874694796],
[72, 7, 7, "RSpec/NamedSubject: Name your test subject if you need to reference it explicitly.", 1892732441]
],
"spec/appraisal/gemfile_spec.rb:3323881535": [
[227, 11, 17, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3608128140],
[228, 13, 13, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 420381022],
[235, 13, 15, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3511037132],
[245, 13, 12, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3100155342],
[255, 11, 24, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3242693747],
[258, 13, 17, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 105800534],
[281, 13, 16, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 179066369],
[312, 13, 15, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 3912615366],
[356, 13, 14, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 2832187185],
[414, 13, 22, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 772581599],
[424, 11, 20, "RSpec/ContextWording: Context description should match /^when\\b/, /^with\\b/, or /^without\\b/.", 1076107314]
],
"spec/appraisal/utils_spec.rb:2286225770": [
[62, 17, 58, "RSpec/VerifiedDoubles: Prefer using verifying doubles over normal doubles.", 1573223692]
],
"spec/support/acceptance_test_helpers.rb:1261310616": [
[133, 5, 32, "Style/InvertibleUnlessCondition: Prefer `if $?.exitstatus == 0` over `unless $?.exitstatus != 0`.", 4187517264]
],
"spec/support/stream_helpers.rb:3273310040": [
[9, 17, 4, "Security/Eval: The use of `eval` is a serious security risk.", 2087429787]
]
}
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ruby 3.4.2
26 changes: 23 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,30 @@ source "https://rubygems.org"

gemspec

platform :mri do
# Debugging
gem "byebug", ">= 11"

# Dev Console - Binding.pry - Irb replacement
gem "pry", "~> 0.14" # ruby >= 2.0
end

# This here to make sure appraisal works with Rails 3.0.0.
gem "thor", "~> 0.14.0"

group :development, :test do
gem "activesupport", ">= 3.2.21"
gem "rspec", "~> 3.0"
# Ruby version specific dependencies
ruby_version = Gem::Version.new(RUBY_VERSION)
if ruby_version < Gem::Version.new("1.9")
eval File.read("Gemfile-1.8")
elsif ruby_version < Gem::Version.new("2.1")
eval File.read("Gemfile-2.0")
elsif ruby_version < Gem::Version.new("2.2")
eval File.read("Gemfile-2.1")
elsif ruby_version < Gem::Version.new("2.7")
# Std Lib extractions
gem "benchmark", "~> 0.4" # Removed from Std Lib in Ruby 3.5
else
# Ruby >= 2.7 we can run style / lint checks via rubocop-gradual with rubocop-lts rules for Ruby 1.8+.
Copy link

Choose a reason for hiding this comment

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

Metrics/LineLength: Line is too long. [104/80]

# This means we can develop on modern Ruby but remain compatible with ancient Ruby.
Copy link

Choose a reason for hiding this comment

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

Metrics/LineLength: Line is too long. [85/80]

eval_gemfile "gemfiles/modular/style.gemfile"
end
6 changes: 6 additions & 0 deletions Gemfile-1.8
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# These gems are locked for Ruby 1.8.7 compatibility
gem "i18n", "~> 0.6.0"
gem "activesupport", "~> 3.2.21"
gem "rake", "~> 10.5"
gem "rack", "~> 1.6.5"
gem "benchmark", "~> 0.3" # Removed from Std Lib in Ruby 3.5
4 changes: 4 additions & 0 deletions Gemfile-2.0
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These gems are locked for Ruby 1.9 & 2.0 compatibility
gem "activesupport", "~> 4.2.7"
gem "rack", "~> 1.6.5"
gem "benchmark", "~> 0.3" # Removed from Std Lib in Ruby 3.5
4 changes: 4 additions & 0 deletions Gemfile-2.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# These gems are locked for Ruby 2.1 compatibility
gem "activesupport", "~> 4.2.7"
gem "rack", "~> 1.6.5"
gem "benchmark", "~> 0.4" # Removed from Std Lib in Ruby 3.5
94 changes: 80 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,72 @@ When you prefix a command with `appraisal`, the command is run with the
appropriate Gemfile for that appraisal, ensuring the correct dependencies
are used.

Sharing Modular Gemfiles between Appraisals
-------

_New for version 3.0_

It is common for Appraisals to duplicate sets of gems, and sometimes it
makes sense to DRY this up into a shared, modular, gemfile.
In a scenario where you do not load your main Gemfile in your Appraisals,
but you want to declare your various gem sets for e.g.
`%w(coverage test documentation audit)` once each, you can re-use the same
modular gemfiles for local development by referencing them from the main
Gemfile.

To do this, use the `eval_gemfile` declaration within the necessary
`appraise` block in your `Appraisals` file, which will behave the same as
`eval_gemfile` does in a normal Gemfile.

### Example Usage

You could put your modular gemfiles in the `gemfiles` directory, or nest
them in `gemfiles/modular/*`, which will be used for this example.

**Gemfile**
```ruby
eval_gemfile "gemfiles/modular/audit.gemfile"
```

**gemfiles/modular/audit.gemfile**
```ruby
# Many gems are dropping support for Ruby < 3.1,
# so we only want to run our security audit in CI on Ruby 3.1+
gem "bundler-audit", "~> 0.9.2"
# And other security audit gems...
```

**Appraisals**
```ruby
appraise "ruby-2-7" do
gem "dummy"
end

appraise "ruby-3-0" do
gem "dummy"
end

appraise "ruby-3-1" do
gem "dummy"
eval_gemfile "modular/audit.gemfile"
end

appraise "ruby-3-2" do
gem "dummy"
eval_gemfile "modular/audit.gemfile"
end

appraise "ruby-3-3" do
gem "dummy"
eval_gemfile "modular/audit.gemfile"
end

appraise "ruby-3-4" do
gem "dummy"
eval_gemfile "modular/audit.gemfile"
end
```

Removing Gems using Appraisal
-------

Expand All @@ -122,31 +188,31 @@ To do this, use the `remove_gem` declaration within the necessary `appraise` blo

**Gemfile**
```ruby
gem 'rails', '~> 4.2'
gem "rails", "~> 4.2"

group :test do
gem 'rspec', '~> 4.0'
gem 'test_after_commit'
gem "rspec", "~> 4.0"
gem "test_after_commit"
end
```

**Appraisals**
```ruby
appraise 'rails-5' do
gem 'rails', '~> 5.2'
appraise "rails-5" do
gem "rails", "~> 5.2"

group :test do
remove_gem 'test_after_commit'
remove_gem "test_after_commit"
end
end
```

Using the `Appraisals` file defined above, this is what the resulting `Gemfile` will look like:
```ruby
gem 'rails', '~> 5.2'
gem "rails", "~> 5.2"

group :test do
gem 'rspec', '~> 4.0'
gem "rspec", "~> 4.0"
end
```

Expand Down Expand Up @@ -174,12 +240,12 @@ You can also provide variables for substitution in the heading, based on each ap
```ruby
customize_gemfiles do
{
single_quotes: true,
heading: <<~HEADING
frozen_string_literal: true
:single_quotes => true,
:heading => <<-HEADING,
frozen_string_literal: true

`%{gemfile}` has been generated by Appraisal, do NOT modify it or `%{lockfile}` directly!
Make the changes to the "%{appraisal}" block in `Appraisals` instead. See the conventions at https://example.com/
`%{gemfile}` has been generated by Appraisal, do NOT modify it or `%{lockfile}` directly!
Make the changes to the "%{appraisal}" block in `Appraisals` instead. See the conventions at https://example.com/
HEADING
}
end
Expand All @@ -196,7 +262,7 @@ Using the `Appraisals` file defined above, this is what the resulting `Gemfile`
# `rails-3.gemfile` has been generated by Appraisal, do NOT modify it or `rails-3.gemfile.lock` directly!
# Make the changes to the "rails-3" block in `Appraisals` instead. See the conventions at https://example.com/

gem 'rails', '3.2.14'
gem "rails", "3.2.14"
```

Version Control
Expand Down
13 changes: 11 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,14 @@ RSpec::Core::RakeTask.new do |t|
t.verbose = false
end

desc "Default: run the rspec examples"
task default: [:spec]
begin
require "rubocop/lts"
Rubocop::Lts.install_tasks
rescue LoadError
task(:rubocop_gradual) do
warn("RuboCop (Gradual) is disabled")
end
end

desc "Default: rubocop_gradual's autocorrect and run the rspec examples"
task :default => ["rubocop_gradual:autocorrect", :spec]
Loading
Loading