From 7fe7c2c91095d49a3cadb68a0ab0a138660998d2 Mon Sep 17 00:00:00 2001 From: Peter Boling Date: Thu, 20 Feb 2025 19:18:36 +0700 Subject: [PATCH] Revert "Drop MRI 1.8.7-2.2 support (#158)" This reverts commit 92d5134994003d6f17e874117483bac7e8fb7829. --- .rubocop.yml | 14 ++ .rubocop_gradual.lock | 132 ++++++++++++++++++ .tool-versions | 1 + Gemfile | 26 +++- Gemfile-1.8 | 6 + Gemfile-2.0 | 4 + Gemfile-2.1 | 4 + README.md | 42 +++--- Rakefile | 13 +- appraisal.gemspec | 59 +++++--- bin/bundle | 30 ++-- exe/appraisal | 2 +- gemfiles/modular/style.gemfile | 12 ++ lib/appraisal/appraisal.rb | 28 ++-- lib/appraisal/appraisal_file.rb | 8 +- lib/appraisal/bundler_dsl.rb | 44 +++--- lib/appraisal/cli.rb | 52 ++++--- lib/appraisal/command.rb | 22 ++- lib/appraisal/customize.rb | 26 ++-- lib/appraisal/dependency.rb | 12 +- lib/appraisal/dependency_list.rb | 15 +- lib/appraisal/gemfile.rb | 2 +- lib/appraisal/gemspec.rb | 2 +- lib/appraisal/git.rb | 4 +- lib/appraisal/group.rb | 12 +- lib/appraisal/ordered_hash.rb | 22 +++ lib/appraisal/path.rb | 4 +- lib/appraisal/platform.rb | 12 +- lib/appraisal/source.rb | 12 +- lib/appraisal/task.rb | 50 +++---- lib/appraisal/utils.rb | 26 ++-- ...als_file_bundler_dsl_compatibility_spec.rb | 22 ++- .../bundle_with_custom_path_spec.rb | 22 ++- spec/acceptance/bundle_without_spec.rb | 1 + spec/acceptance/cli/install_spec.rb | 14 +- spec/acceptance/cli/update_spec.rb | 4 +- spec/acceptance/eval_gemfile_spec.rb | 15 +- spec/acceptance/gemspec_spec.rb | 9 +- spec/appraisal/appraisal_file_spec.rb | 14 +- spec/appraisal/appraisal_spec.rb | 40 +++--- spec/appraisal/customize_spec.rb | 85 +++++------ spec/appraisal/dependency_list_spec.rb | 4 +- spec/appraisal/gemfile_spec.rb | 107 +++++++------- spec/appraisal/utils_spec.rb | 39 +++--- spec/spec_helper.rb | 6 +- spec/support/acceptance_test_helpers.rb | 39 ++++-- spec/support/dependency_helpers.rb | 49 +++---- spec/support/stream_helpers.rb | 2 +- 48 files changed, 742 insertions(+), 428 deletions(-) create mode 100644 .rubocop.yml create mode 100644 .rubocop_gradual.lock create mode 100644 .tool-versions create mode 100644 Gemfile-1.8 create mode 100644 Gemfile-2.0 create mode 100644 Gemfile-2.1 create mode 100644 gemfiles/modular/style.gemfile create mode 100644 lib/appraisal/ordered_hash.rb diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 00000000..292ba165 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,14 @@ +inherit_gem: + rubocop-lts: config/rubygem_rspec.yml + +RSpec/ExampleLength: + Enabled: false + +RSpec/MultipleExpectations: + 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 diff --git a/.rubocop_gradual.lock b/.rubocop_gradual.lock new file mode 100644 index 00000000..31bb06a7 --- /dev/null +++ b/.rubocop_gradual.lock @@ -0,0 +1,132 @@ +{ + "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] + ], + "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/appraisals_file_bundler_dsl_compatibility_spec.rb:2353843955": [ + [5, 16, 43, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 911757094] + ], + "spec/acceptance/bundle_with_custom_path_spec.rb:3433031704": [ + [5, 16, 25, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 4182422797] + ], + "spec/acceptance/bundle_without_spec.rb:4272966736": [ + [5, 16, 22, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 2294013635] + ], + "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/run_spec.rb:439366539": [ + [5, 16, 32, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 3238989605] + ], + "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/acceptance/cli/with_no_arguments_spec.rb:378638472": [ + [5, 16, 35, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 1965211524] + ], + "spec/acceptance/eval_gemfile_spec.rb:2596300425": [ + [5, 16, 14, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 987272877] + ], + "spec/acceptance/gemfile_dsl_compatibility_spec.rb:4168565060": [ + [5, 16, 27, "RSpec/DescribeClass: The first argument to describe should be the class or module being tested.", 2610159673] + ], + "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:4269934475": [ + [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] + ] +} diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..ae5ecdb2 --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +ruby 3.4.2 diff --git a/Gemfile b/Gemfile index a0dda8f5..e73905ca 100644 --- a/Gemfile +++ b/Gemfile @@ -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+. + # This means we can develop on modern Ruby but remain compatible with ancient Ruby. + eval_gemfile "gemfiles/modular/style.gemfile" end diff --git a/Gemfile-1.8 b/Gemfile-1.8 new file mode 100644 index 00000000..976c8496 --- /dev/null +++ b/Gemfile-1.8 @@ -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 diff --git a/Gemfile-2.0 b/Gemfile-2.0 new file mode 100644 index 00000000..75ff4f5d --- /dev/null +++ b/Gemfile-2.0 @@ -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 diff --git a/Gemfile-2.1 b/Gemfile-2.1 new file mode 100644 index 00000000..71901a8d --- /dev/null +++ b/Gemfile-2.1 @@ -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 diff --git a/README.md b/README.md index 590e22fc..02afaadb 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ 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. +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 @@ -147,30 +147,30 @@ gem "bundler-audit", "~> 0.9.2" **Appraisals** ```ruby -appraise 'ruby-2-7' do +appraise "ruby-2-7" do gem "dummy" end -appraise 'ruby-3-0' do +appraise "ruby-3-0" do gem "dummy" end -appraise 'ruby-3-1' do +appraise "ruby-3-1" do gem "dummy" eval_gemfile "modular/audit.gemfile" end -appraise 'ruby-3-2' do +appraise "ruby-3-2" do gem "dummy" eval_gemfile "modular/audit.gemfile" end -appraise 'ruby-3-3' do +appraise "ruby-3-3" do gem "dummy" eval_gemfile "modular/audit.gemfile" end -appraise 'ruby-3-4' do +appraise "ruby-3-4" do gem "dummy" eval_gemfile "modular/audit.gemfile" end @@ -188,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 ``` @@ -240,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 @@ -262,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 diff --git a/Rakefile b/Rakefile index f8d79dbc..44f0463b 100644 --- a/Rakefile +++ b/Rakefile @@ -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] diff --git a/appraisal.gemspec b/appraisal.gemspec index d136a37f..6a037762 100644 --- a/appraisal.gemspec +++ b/appraisal.gemspec @@ -1,26 +1,51 @@ # frozen_string_literal: true -require_relative "lib/appraisal/version" +# TODO: Switch to require_relative once support for Ruby < 2 is dropped. +# require_relative "lib/appraisal/version" + +$:.push(File.expand_path("lib", __dir__)) +require "appraisal/version" Gem::Specification.new do |s| - s.name = "appraisal" - s.version = Appraisal::VERSION.dup - s.platform = Gem::Platform::RUBY - s.authors = ["Joe Ferris", "Prem Sichanugrist"] - s.email = ["jferris@thoughtbot.com", "prem@thoughtbot.com"] - s.homepage = "http://github.com/thoughtbot/appraisal" - s.summary = "Find out what your Ruby gems are worth" + s.name = "appraisal" + s.version = Appraisal::VERSION.dup + s.platform = Gem::Platform::RUBY + s.authors = ["Joe Ferris", "Prem Sichanugrist"] + s.email = ["jferris@thoughtbot.com", "prem@thoughtbot.com"] + s.homepage = "http://github.com/thoughtbot/appraisal" + s.summary = "Find out what your Ruby gems are worth" s.description = 'Appraisal integrates with bundler and rake to test your library against different versions of dependencies in repeatable scenarios called "appraisals."' - s.license = "MIT" + s.license = "MIT" + + # specify which files should be added to the gem when it is released. + s.files = Dir[ + # Splats (keep alphabetical) + "lib/**/*.rb", + ] + + # automatically included with gem package, no need to list twice (i.e. do not list in files above). + s.extra_rdoc_files = Dir[ + # Files (keep alphabetical) + "CONTRIBUTING.md", + "MIT-LICENSE", + "README.md", + "SECURITY.md", + ] + + # bin/ is scripts, in any available language, for development of this specific gem + # exe/ is for ruby scripts that will ship with this gem to be used by other tools + s.bindir = "exe" + # files listed are relative paths from bindir above. + s.executables = [ + "appraisal", + ] - s.files = `git ls-files`.split("\n") - s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - s.executables = `git ls-files -- exe/*`.split("\n").map { |f| File.basename(f) } - s.bindir = "exe" + s.required_ruby_version = ">= 1.8.7" - s.required_ruby_version = ">= 2.3.0" + s.add_runtime_dependency("bundler", ">= 1.17.3") # Last version supporting Ruby 1.8.7 + s.add_runtime_dependency("rake", ">= 10") # Last version supporting Ruby 1.8.7 + s.add_runtime_dependency("thor", ">= 0.14.0") - s.add_dependency("rake") - s.add_dependency("bundler") - s.add_dependency("thor", ">= 0.14.0") + s.add_development_dependency("activesupport", ">= 3.2.21") + s.add_development_dependency("rspec", "~> 3.0") end diff --git a/bin/bundle b/bin/bundle index 50da5fdf..8f20a586 100755 --- a/bin/bundle +++ b/bin/bundle @@ -24,14 +24,14 @@ m = Module.new do def cli_arg_version return unless invoked_as_script? # don't want to hijack other binstubs return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` + bundler_version = nil update_index = nil ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) - bundler_version = a - end - next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 + bundler_version = a if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) + next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o + + bundler_version = Regexp.last_match(1) update_index = i end bundler_version @@ -55,8 +55,10 @@ m = Module.new do def lockfile_version return unless File.file?(lockfile) + lockfile_contents = File.read(lockfile) - return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ + return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o + Regexp.last_match(1) end @@ -83,15 +85,19 @@ m = Module.new do def activate_bundler gem_error = activation_error_handling do - gem "bundler", bundler_requirement + gem("bundler", bundler_requirement) end return if gem_error.nil? + require_error = activation_error_handling do require "bundler/version" end - return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) - warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" - exit 42 + if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) + return + end + + warn("Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`") + exit(42) end def activation_error_handling @@ -104,6 +110,4 @@ end m.load_bundler! -if m.invoked_as_script? - load Gem.bin_path("bundler", "bundle") -end +load Gem.bin_path("bundler", "bundle") if m.invoked_as_script? diff --git a/exe/appraisal b/exe/appraisal index 4e963840..16e4087a 100755 --- a/exe/appraisal +++ b/exe/appraisal @@ -10,5 +10,5 @@ begin Appraisal::CLI.start rescue Appraisal::AppraisalsNotFound => e puts e.message - exit 127 + exit(127) end diff --git a/gemfiles/modular/style.gemfile b/gemfiles/modular/style.gemfile new file mode 100644 index 00000000..c9cf8473 --- /dev/null +++ b/gemfiles/modular/style.gemfile @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +# 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 +gem "rubocop-packaging", "~> 0.5", ">= 0.5.2" +gem "rubocop-rspec", "~> 3.2" +gem "standard", ">= 1.35.1", "!= 1.41.1", "!= 1.42.0" + +# Std Lib extractions +gem "benchmark", "~> 0.4" # Removed from Std Lib in Ruby 3.5 diff --git a/lib/appraisal/appraisal.rb b/lib/appraisal/appraisal.rb index 6c4118a9..ccbed470 100644 --- a/lib/appraisal/appraisal.rb +++ b/lib/appraisal/appraisal.rb @@ -10,7 +10,7 @@ module Appraisal # Represents one appraisal and its dependencies class Appraisal - DEFAULT_INSTALL_OPTIONS = { "jobs" => 1 }.freeze + DEFAULT_INSTALL_OPTIONS = {"jobs" => 1}.freeze attr_reader :name, :gemfile @@ -78,28 +78,24 @@ def write_gemfile def install(options = {}) commands = [install_command(options).join(" ")] - if options["without"].nil? || options["without"].empty? - commands.unshift(check_command.join(" ")) - end + commands.unshift(check_command.join(" ")) if options["without"].nil? || options["without"].empty? command = commands.join(" || ") if Bundler.settings[:path] - env = { "BUNDLE_DISABLE_SHARED_GEMS" => "1" } - Command.new(command, env: env).run + env = {"BUNDLE_DISABLE_SHARED_GEMS" => "1"} + Command.new(command, :env => env).run else Command.new(command).run end end def update(gems = []) - Command.new(update_command(gems), gemfile: gemfile_path).run + Command.new(update_command(gems), :gemfile => gemfile_path).run end def gemfile_path - unless gemfile_root.exist? - gemfile_root.mkdir - end + gemfile_root.mkdir unless gemfile_root.exist? gemfile_root.join(gemfile_name).to_s end @@ -114,10 +110,10 @@ def relativize lockfile_content = File.read(lockfile_path) File.open(lockfile_path, "w") do |file| - file.write lockfile_content.gsub( + file.write(lockfile_content.gsub( / #{current_directory}/, - " #{relative_path}" - ) + " #{relative_path}", + )) end end @@ -154,7 +150,7 @@ def lockfile_path end def clean_name - name.gsub(/[^\w\.]/, "_") + name.gsub(/[^\w.]/, "_") end def bundle_options(options) @@ -165,8 +161,8 @@ def bundle_options(options) if Utils.support_parallel_installation? options_strings << "--jobs=#{jobs}" else - warn "Your current version of Bundler does not support parallel installation. Please " + - "upgrade Bundler to version >= 1.4.0, or invoke `appraisal` without `--jobs` option." + warn("Your current version of Bundler does not support parallel installation. Please " \ + "upgrade Bundler to version >= 1.4.0, or invoke `appraisal` without `--jobs` option.") end end diff --git a/lib/appraisal/appraisal_file.rb b/lib/appraisal/appraisal_file.rb index 2d182038..316673fb 100644 --- a/lib/appraisal/appraisal_file.rb +++ b/lib/appraisal/appraisal_file.rb @@ -19,11 +19,9 @@ def initialize @gemfile = Gemfile.new @gemfile.load(ENV["BUNDLE_GEMFILE"] || "Gemfile") - if File.exist? path - run IO.read(path) - else - raise AppraisalsNotFound - end + raise AppraisalsNotFound unless File.exist?(path) + + run(File.read(path)) end def each(&block) diff --git a/lib/appraisal/bundler_dsl.rb b/lib/appraisal/bundler_dsl.rb index 65e85ad8..8b76b510 100644 --- a/lib/appraisal/bundler_dsl.rb +++ b/lib/appraisal/bundler_dsl.rb @@ -1,24 +1,36 @@ # frozen_string_literal: true require "appraisal/dependency_list" +require "appraisal/ordered_hash" module Appraisal class BundlerDSL attr_reader :dependencies - PARTS = %w[source ruby_version gits paths dependencies groups - platforms source_blocks install_if gemspec eval_gemfile] + PARTS = %w[ + source + ruby_version + gits + paths + dependencies + groups + platforms + source_blocks + install_if + gemspec + eval_gemfile + ] def initialize @sources = [] @ruby_version = nil @dependencies = DependencyList.new @gemspecs = [] - @groups = {} - @platforms = {} - @gits = {} - @paths = {} - @source_blocks = {} + @groups = OrderedHash.new + @platforms = OrderedHash.new + @gits = OrderedHash.new + @paths = OrderedHash.new + @source_blocks = OrderedHash.new @git_sources = {} @install_if = {} @eval_gemfile = [] @@ -87,11 +99,11 @@ def path(source, options = {}, &block) end def to_s - Utils.join_parts(PARTS.map { |part| send("#{part}_entry") }) + Utils.join_parts(PARTS.map { |part| send(:"#{part}_entry") }) end def for_dup - Utils.join_parts(PARTS.map { |part| send("#{part}_entry_for_dup") }) + Utils.join_parts(PARTS.map { |part| send(:"#{part}_entry_for_dup") }) end def gemspec(options = {}) @@ -127,7 +139,7 @@ def ruby_version_entry case @ruby_version when String then "ruby #{@ruby_version.inspect}" - else "ruby(#{@ruby_version.inspect})" + else "ruby(#{Utils.format_string(@ruby_version)})" end end @@ -150,7 +162,7 @@ def dependencies_entry_for_dup end %i[gits paths platforms groups source_blocks install_if].each do |method_name| - class_eval <<-METHODS, __FILE__, __LINE__ + class_eval <<-METHODS, __FILE__, __LINE__ + 1 private def #{method_name}_entry @@ -169,11 +181,11 @@ def indent(string) def substitute_git_source(requirements) requirements.each do |requirement| - if requirement.is_a?(Hash) - (requirement.keys & @git_sources.keys).each do |matching_source| - value = requirement.delete(matching_source) - requirement[:git] = @git_sources[matching_source].call(value) - end + next unless requirement.is_a?(Hash) + + (requirement.keys & @git_sources.keys).each do |matching_source| + value = requirement.delete(matching_source) + requirement[:git] = @git_sources[matching_source].call(value) end end end diff --git a/lib/appraisal/cli.rb b/lib/appraisal/cli.rb index 3c8a56f1..f5a966a2 100644 --- a/lib/appraisal/cli.rb +++ b/lib/appraisal/cli.rb @@ -11,7 +11,7 @@ class CLI < Thor class << self # Override help command to print out usage def help(shell, subcommand = false) - shell.say strip_heredoc(<<-HELP) + shell.say(strip_heredoc(<<-HELP)) Appraisal: Find out what your Ruby gems are worth. Usage: @@ -23,10 +23,10 @@ def help(shell, subcommand = false) if File.exist?("Appraisals") shell.say - shell.say "Available Appraisal(s):" + shell.say("Available Appraisal(s):") AppraisalFile.each do |appraisal| - shell.say " - #{appraisal.name}" + shell.say(" - #{appraisal.name}") end end @@ -48,23 +48,31 @@ def strip_heredoc(string) end desc "install", "Resolve and install dependencies for each appraisal" - method_option "jobs", aliases: "j", type: :numeric, default: 1, - banner: "SIZE", - desc: "Install gems in parallel using the given number of workers." - method_option "retry", type: :numeric, default: 1, - desc: "Retry network and git requests that have failed" - method_option "without", banner: "GROUP_NAMES", - desc: "A space-separated list of groups referencing gems to skip " + + method_option "jobs", + :aliases => "j", + :type => :numeric, + :default => 1, + :banner => "SIZE", + :desc => "Install gems in parallel using the given number of workers." + method_option "retry", + :type => :numeric, + :default => 1, + :desc => "Retry network and git requests that have failed" + method_option "without", + :banner => "GROUP_NAMES", + :desc => "A space-separated list of groups referencing gems to skip " \ "during installation. Bundler will remember this option." - method_option "full-index", type: :boolean, - desc: "Run bundle install with the " \ - "full-index argument." - method_option "path", type: :string, - desc: "Install gems in the specified directory. " \ - "Bundler will remember this option." + method_option "full-index", + :type => :boolean, + :desc => "Run bundle install with the " \ + "full-index argument." + method_option "path", + :type => :string, + :desc => "Install gems in the specified directory. " \ + "Bundler will remember this option." def install - invoke :generate, [], {} + invoke(:generate, [], {}) AppraisalFile.each do |appraisal| appraisal.install(options) @@ -81,12 +89,12 @@ def generate desc "clean", "Remove all generated gemfiles and lockfiles from gemfiles folder" def clean - FileUtils.rm_f Dir["gemfiles/*.{gemfile,gemfile.lock}"] + FileUtils.rm_f(Dir["gemfiles/*.{gemfile,gemfile.lock}"]) end desc "update [LIST_OF_GEMS]", "Remove all generated gemfiles and lockfiles, resolve, and install dependencies again" def update(*gems) - invoke :generate, [] + invoke(:generate, []) AppraisalFile.each do |appraisal| appraisal.update(gems) @@ -105,16 +113,16 @@ def version private - def method_missing(name, *args, &block) + def method_missing(name, *args) matching_appraisal = AppraisalFile.new.appraisals.detect do |appraisal| appraisal.name == name.to_s end if matching_appraisal - Command.new(args, gemfile: matching_appraisal.gemfile_path).run + Command.new(args, :gemfile => matching_appraisal.gemfile_path).run else AppraisalFile.each do |appraisal| - Command.new(ARGV, gemfile: appraisal.gemfile_path).run + Command.new(ARGV, :gemfile => appraisal.gemfile_path).run end end end diff --git a/lib/appraisal/command.rb b/lib/appraisal/command.rb index facadfd9..76ac69f2 100644 --- a/lib/appraisal/command.rb +++ b/lib/appraisal/command.rb @@ -26,9 +26,7 @@ def run ENV[key] = value end - unless Kernel.system(command_as_string) - exit(1) - end + exit(1) unless Kernel.system(command_as_string) end end @@ -36,20 +34,20 @@ def run def ensure_bundler_is_available version = Utils.bundler_version - unless system %(gem list --silent -i bundler -v #{version}) - puts ">> Reinstall Bundler into #{ENV["GEM_HOME"]}" + return if system(%(gem list --silent -i bundler -v #{version})) + + puts ">> Reinstall Bundler into #{ENV["GEM_HOME"]}" + + return if system("gem install bundler --version #{version}") - unless system "gem install bundler --version #{version}" - puts - puts <<-ERROR.strip.gsub(/\s+/, " ") + puts + puts <<-ERROR.strip.gsub(/\s+/, " ") Bundler installation failed. Please try running: `GEM_HOME="#{ENV["GEM_HOME"]}" gem install bundler --version #{version}` manually. - ERROR - exit(1) - end - end + ERROR + exit(1) end def announce diff --git a/lib/appraisal/customize.rb b/lib/appraisal/customize.rb index 20cb99ac..8d9e4a75 100644 --- a/lib/appraisal/customize.rb +++ b/lib/appraisal/customize.rb @@ -3,12 +3,14 @@ module Appraisal class Customize def initialize(heading: nil, single_quotes: false) - @@heading = heading&.chomp + @@heading = !heading.nil? && heading.chomp @@single_quotes = single_quotes end - def self.heading(gemfile) + def self.heading(gemfile = nil) @@heading ||= nil + return @@heading unless gemfile + customize(@@heading, gemfile) end @@ -16,18 +18,18 @@ def self.single_quotes @@single_quotes ||= false end - def self.customize(heading, gemfile) - return nil unless heading + def self.customize(topper, gemfile) + return unless topper format( - heading.to_s, - appraisal: gemfile.send("clean_name"), - gemfile: gemfile.send("gemfile_name"), - gemfile_path: gemfile.gemfile_path, - lockfile: "#{gemfile.send('gemfile_name')}.lock", - lockfile_path: gemfile.send("lockfile_path"), - relative_gemfile_path: gemfile.relative_gemfile_path, - relative_lockfile_path: "#{gemfile.relative_gemfile_path}.lock" + topper.to_s, + :appraisal => gemfile.send(:clean_name), + :gemfile => gemfile.send(:gemfile_name), + :gemfile_path => gemfile.gemfile_path, + :lockfile => "#{gemfile.send(:gemfile_name)}.lock", + :lockfile_path => gemfile.send(:lockfile_path), + :relative_gemfile_path => gemfile.relative_gemfile_path, + :relative_lockfile_path => "#{gemfile.relative_gemfile_path}.lock", ) end diff --git a/lib/appraisal/dependency.rb b/lib/appraisal/dependency.rb index 37302ab8..367749df 100644 --- a/lib/appraisal/dependency.rb +++ b/lib/appraisal/dependency.rb @@ -14,12 +14,12 @@ def initialize(name, requirements) end def to_s - formatted_output Utils.format_arguments(path_prefixed_requirements) + formatted_output(Utils.format_arguments(path_prefixed_requirements)) end # :nodoc: def for_dup - formatted_output Utils.format_arguments(requirements) + formatted_output(Utils.format_arguments(requirements)) end private @@ -27,13 +27,9 @@ def for_dup def path_prefixed_requirements requirements.map do |requirement| if requirement.is_a?(Hash) - if requirement[:path] - requirement[:path] = Utils.prefix_path(requirement[:path]) - end + requirement[:path] = Utils.prefix_path(requirement[:path]) if requirement[:path] - if requirement[:git] - requirement[:git] = Utils.prefix_path(requirement[:git]) - end + requirement[:git] = Utils.prefix_path(requirement[:git]) if requirement[:git] end requirement diff --git a/lib/appraisal/dependency_list.rb b/lib/appraisal/dependency_list.rb index 9b6fb8d9..8a42c938 100644 --- a/lib/appraisal/dependency_list.rb +++ b/lib/appraisal/dependency_list.rb @@ -1,25 +1,26 @@ # frozen_string_literal: true require "appraisal/dependency" +require "appraisal/ordered_hash" require "set" module Appraisal class DependencyList def initialize - @dependencies = {} + @dependencies = OrderedHash.new @removed_dependencies = Set.new end def add(name, requirements) - unless @removed_dependencies.include?(name) - @dependencies[name] = Dependency.new(name, requirements) - end + return if @removed_dependencies.include?(name) + + @dependencies[name] = Dependency.new(name, requirements) end def remove(name) - if @removed_dependencies.add?(name) - @dependencies.delete(name) - end + return unless @removed_dependencies.add?(name) + + @dependencies.delete(name) end def to_s diff --git a/lib/appraisal/gemfile.rb b/lib/appraisal/gemfile.rb index bfbb4580..93911b57 100644 --- a/lib/appraisal/gemfile.rb +++ b/lib/appraisal/gemfile.rb @@ -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) end def run(definitions, path, line = 1) diff --git a/lib/appraisal/gemspec.rb b/lib/appraisal/gemspec.rb index 8c4d6a0a..dfdef6de 100644 --- a/lib/appraisal/gemspec.rb +++ b/lib/appraisal/gemspec.rb @@ -24,7 +24,7 @@ def for_dup def exported_options @options.merge( - path: Utils.prefix_path(@options[:path]) + :path => Utils.prefix_path(@options[:path]), ) end end diff --git a/lib/appraisal/git.rb b/lib/appraisal/git.rb index 5849cca1..791429df 100644 --- a/lib/appraisal/git.rb +++ b/lib/appraisal/git.rb @@ -15,7 +15,7 @@ def to_s if @options.empty? "git #{Utils.prefix_path(@source).inspect} do\n#{indent(super)}\nend" else - "git #{Utils.prefix_path(@source).inspect}, #{Utils.format_string(@options)} do\n" + + "git #{Utils.prefix_path(@source).inspect}, #{Utils.format_string(@options)} do\n" \ "#{indent(super)}\nend" end end @@ -25,7 +25,7 @@ def for_dup if @options.empty? "git #{@source.inspect} do\n#{indent(super)}\nend" else - "git #{@source.inspect}, #{Utils.format_string(@options)} do\n" + + "git #{@source.inspect}, #{Utils.format_string(@options)} do\n" \ "#{indent(super)}\nend" end end diff --git a/lib/appraisal/group.rb b/lib/appraisal/group.rb index 2327298c..50351a88 100644 --- a/lib/appraisal/group.rb +++ b/lib/appraisal/group.rb @@ -11,21 +11,21 @@ def initialize(group_names) end def to_s - formatted_output indent(super) + formatted_output(indent(super)) end # :nodoc: def for_dup - formatted_output indent(super) + formatted_output(indent(super)) end private def formatted_output(output_dependencies) - <<~OUTPUT.strip - group #{Utils.format_arguments(@group_names)} do - #{output_dependencies} - end + <<-OUTPUT.strip +group #{Utils.format_arguments(@group_names)} do +#{output_dependencies} +end OUTPUT end end diff --git a/lib/appraisal/ordered_hash.rb b/lib/appraisal/ordered_hash.rb new file mode 100644 index 00000000..b460f9d5 --- /dev/null +++ b/lib/appraisal/ordered_hash.rb @@ -0,0 +1,22 @@ +module Appraisal + # An ordered hash implementation for Ruby 1.8.7 compatibility. This is not + # 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" + def initialize(*args, &block) + super + @keys = [] + end + + def []=(key, value) + @keys << key unless has_key?(key) + super + end + + def values + @keys.collect { |key| self[key] } + end + end + end +end diff --git a/lib/appraisal/path.rb b/lib/appraisal/path.rb index f06d2737..c33b60c6 100644 --- a/lib/appraisal/path.rb +++ b/lib/appraisal/path.rb @@ -15,7 +15,7 @@ def to_s if @options.empty? "path #{Utils.prefix_path(@source).inspect} do\n#{indent(super)}\nend" else - "path #{Utils.prefix_path(@source).inspect}, #{Utils.format_string(@options)} do\n" + + "path #{Utils.prefix_path(@source).inspect}, #{Utils.format_string(@options)} do\n" \ "#{indent(super)}\nend" end end @@ -25,7 +25,7 @@ def for_dup if @options.empty? "path #{@source.inspect} do\n#{indent(super)}\nend" else - "path #{@source.inspect}, #{Utils.format_string(@options)} do\n" + + "path #{@source.inspect}, #{Utils.format_string(@options)} do\n" \ "#{indent(super)}\nend" end end diff --git a/lib/appraisal/platform.rb b/lib/appraisal/platform.rb index ad98ce1f..ae0e0849 100644 --- a/lib/appraisal/platform.rb +++ b/lib/appraisal/platform.rb @@ -11,21 +11,21 @@ def initialize(platform_names) end def to_s - formatted_output indent(super) + formatted_output(indent(super)) end # :nodoc: def for_dup - formatted_output indent(super) + formatted_output(indent(super)) end private def formatted_output(output_dependencies) - <<~OUTPUT.strip - platforms #{Utils.format_arguments(@platform_names)} do - #{output_dependencies} - end + <<-OUTPUT.strip +platforms #{Utils.format_arguments(@platform_names)} do +#{output_dependencies} +end OUTPUT end end diff --git a/lib/appraisal/source.rb b/lib/appraisal/source.rb index c5905617..ebbe71e9 100644 --- a/lib/appraisal/source.rb +++ b/lib/appraisal/source.rb @@ -11,21 +11,21 @@ def initialize(source) end def to_s - formatted_output indent(super) + formatted_output(indent(super)) end # :nodoc: def for_dup - formatted_output indent(super) + formatted_output(indent(super)) end private def formatted_output(output_dependencies) - <<~OUTPUT.strip - source #{@source.inspect} do - #{output_dependencies} - end + <<-OUTPUT.strip +source #{@source.inspect} do +#{output_dependencies} +end OUTPUT end end diff --git a/lib/appraisal/task.rb b/lib/appraisal/task.rb index 5c649925..cdca7b3d 100644 --- a/lib/appraisal/task.rb +++ b/lib/appraisal/task.rb @@ -8,49 +8,49 @@ module Appraisal # for a given appraisal. class Task < Rake::TaskLib def initialize - namespace :appraisal do - desc "DEPRECATED: Generate a Gemfile for each appraisal" - task :gemfiles do - warn "`rake appraisal:gemfile` task is deprecated and will be removed soon. " + - "Please use `appraisal generate`." - exec "bundle exec appraisal generate" + namespace(:appraisal) do + desc("DEPRECATED: Generate a Gemfile for each appraisal") + task(:gemfiles) do + warn("`rake appraisal:gemfile` task is deprecated and will be removed soon. " \ + "Please use `appraisal generate`.") + exec("bundle exec appraisal generate") end - desc "DEPRECATED: Resolve and install dependencies for each appraisal" - task :install do - warn "`rake appraisal:install` task is deprecated and will be removed soon. " + - "Please use `appraisal install`." - exec "bundle exec appraisal install" + desc("DEPRECATED: Resolve and install dependencies for each appraisal") + task(:install) do + warn("`rake appraisal:install` task is deprecated and will be removed soon. " \ + "Please use `appraisal install`.") + exec("bundle exec appraisal install") end - desc "DEPRECATED: Remove all generated gemfiles from gemfiles/ folder" - task :cleanup do - warn "`rake appraisal:cleanup` task is deprecated and will be removed soon. " + - "Please use `appraisal clean`." - exec "bundle exec appraisal clean" + desc("DEPRECATED: Remove all generated gemfiles from gemfiles/ folder") + task(:cleanup) do + warn("`rake appraisal:cleanup` task is deprecated and will be removed soon. " \ + "Please use `appraisal clean`.") + exec("bundle exec appraisal clean") end begin AppraisalFile.each do |appraisal| - desc "DEPRECATED: Run the given task for appraisal #{appraisal.name}" - task appraisal.name do + desc("DEPRECATED: Run the given task for appraisal #{appraisal.name}") + task(appraisal.name) do ARGV.shift - warn "`rake appraisal:#{appraisal.name}` task is deprecated and will be removed soon. " + - "Please use `appraisal #{appraisal.name} rake #{ARGV.join(' ')}`." - exec "bundle exec appraisal #{appraisal.name} rake #{ARGV.join(' ')}" + warn("`rake appraisal:#{appraisal.name}` task is deprecated and will be removed soon. " \ + "Please use `appraisal #{appraisal.name} rake #{ARGV.join(" ")}`.") + exec("bundle exec appraisal #{appraisal.name} rake #{ARGV.join(" ")}") end end rescue AppraisalsNotFound end - task :all do + task(:all) do ARGV.shift - exec "bundle exec appraisal rake #{ARGV.join(' ')}" + exec("bundle exec appraisal rake #{ARGV.join(" ")}") end end - desc "Run the given task for all appraisals" - task appraisal: "appraisal:all" + desc("Run the given task for all appraisals") + task(:appraisal => "appraisal:all") end end end diff --git a/lib/appraisal/utils.rb b/lib/appraisal/utils.rb index 40e6c31e..a9cf68e0 100644 --- a/lib/appraisal/utils.rb +++ b/lib/appraisal/utils.rb @@ -7,6 +7,9 @@ def self.support_parallel_installation? Gem::Version.create(Bundler::VERSION) >= Gem::Version.create("1.4.0.pre.1") end + # Appraisal needs to print Gemfiles in the oldest Ruby syntax that is supported by Appraisal. + # Otherwise, a project would not be able to use Appraisal to test compatibility + # with older versions of Ruby, which is a core use case for Appraisal. def self.format_string(object, enclosing_object = false) case object when Hash @@ -15,7 +18,7 @@ def self.format_string(object, enclosing_object = false) end if enclosing_object - "{ #{items.join(', ')} }" + "{ #{items.join(", ")} }" else items.join(", ") end @@ -24,21 +27,22 @@ def self.format_string(object, enclosing_object = false) end end + # Appraisal needs to print Gemfiles in the oldest Ruby syntax that is supported by Appraisal. + # This means formatting Hashes as Rockets, until support for Ruby 1.8 is dropped. + # Regardless of what Ruby is used to generate appraisals, + # generated appraisals may need to run on a different Ruby version. + # Generated appraisals should use a syntax compliant with the oldest supported Ruby version. def self.format_hash_value(key, value) key = format_string(key, true) value = format_string(value, true) - if key.start_with?(":") - "#{key.sub(/^:/, "")}: #{value}" - else - "#{key} => #{value}" - end + "#{key} => #{value}" end def self.format_arguments(arguments) - unless arguments.empty? - arguments.map { |object| format_string(object, false) }.join(", ") - end + return if arguments.empty? + + arguments.map { |object| format_string(object, false) }.join(", ") end def self.join_parts(parts) @@ -46,8 +50,8 @@ def self.join_parts(parts) 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+:/ + cleaned_path = path.gsub(%r{(^|/)\.(?:/|$)}, '\\1') File.join("..", cleaned_path) else path diff --git a/spec/acceptance/appraisals_file_bundler_dsl_compatibility_spec.rb b/spec/acceptance/appraisals_file_bundler_dsl_compatibility_spec.rb index 41e6cdd6..b5a46e6b 100644 --- a/spec/acceptance/appraisals_file_bundler_dsl_compatibility_spec.rb +++ b/spec/acceptance/appraisals_file_bundler_dsl_compatibility_spec.rb @@ -4,8 +4,18 @@ RSpec.describe "Appraisals file Bundler DSL compatibility" do it "supports all Bundler DSL in Appraisals file" do - build_gems %w[bagel orange_juice milk waffle coffee ham - sausage pancake rotten_egg mayonnaise] + build_gems %w[ + bagel + orange_juice + milk + waffle + coffee + ham + sausage + pancake + rotten_egg + mayonnaise + ] build_git_gems %w[egg croissant pain_au_chocolat omelette] build_gemfile <<-GEMFILE @@ -52,7 +62,7 @@ build_appraisal_file <<-APPRAISALS appraise 'breakfast' do source 'http://some-other-source.com' - ruby "2.3.0" + ruby "1.8.7" gem 'bread' gem "pain_au_chocolat", :custom_git_source => "pain_au_chocolat" @@ -100,7 +110,7 @@ source "https://rubygems.org" source "http://some-other-source.com" - ruby "2.3.0" + ruby "1.8.7" git "../../build/egg" do gem "egg" @@ -163,7 +173,7 @@ build_appraisal_file <<-APPRAISALS appraise 'ruby-version' do - ruby file: ".ruby-version" + ruby({:file => ".ruby-version"}) end APPRAISALS @@ -175,7 +185,7 @@ source "https://rubygems.org" - ruby({:file=>".ruby-version"}) + ruby(:file => ".ruby-version") gem "appraisal", :path => #{PROJECT_ROOT.inspect} GEMFILE diff --git a/spec/acceptance/bundle_with_custom_path_spec.rb b/spec/acceptance/bundle_with_custom_path_spec.rb index b0d2157b..cdb92c0a 100644 --- a/spec/acceptance/bundle_with_custom_path_spec.rb +++ b/spec/acceptance/bundle_with_custom_path_spec.rb @@ -6,12 +6,18 @@ let(:gem_name) { "rack" } let(:path) { "vendor/bundle" } - shared_examples :gemfile_dependencies_are_satisfied do + shared_examples "gemfile dependencies are satisfied" do it "installs gems in the --path directory" do build_gemfile <<-GEMFILE source "https://rubygems.org" gem 'appraisal', :path => #{PROJECT_ROOT.inspect} + + if RUBY_VERSION < "1.9" + #{File.read(File.join(PROJECT_ROOT, "Gemfile-1.8"))} + elsif RUBY_VERSION < "2.2" + #{File.read(File.join(PROJECT_ROOT, "Gemfile-2.1"))} + end GEMFILE build_appraisal_file <<-APPRAISALS @@ -25,8 +31,8 @@ run "bundle exec appraisal install" installed_gem = Dir.glob("tmp/stage/#{path}/#{Gem.ruby_engine}/*/gems/*") - .map { |path| path.split("/").last } - .select { |gem| gem.include?(gem_name) } + .map { |path| path.split("/").last } + .select { |gem| gem.include?(gem_name) } expect(installed_gem).not_to be_empty bundle_output = run "bundle check" @@ -39,14 +45,18 @@ end end - include_examples :gemfile_dependencies_are_satisfied + include_examples "gemfile dependencies are satisfied" context "when already installed in vendor/another" do before do build_gemfile <<-GEMFILE source "https://rubygems.org" - gem '#{gem_name}' + if RUBY_VERSION <= "1.9" + gem '#{gem_name}', '~> 1.6.5' + else + gem '#{gem_name}' + end GEMFILE run "bundle config set --local path vendor/another" @@ -54,6 +64,6 @@ run "bundle config unset --local path" end - include_examples :gemfile_dependencies_are_satisfied + include_examples "gemfile dependencies are satisfied" end end diff --git a/spec/acceptance/bundle_without_spec.rb b/spec/acceptance/bundle_without_spec.rb index 7eb1b71f..4d9b3431 100644 --- a/spec/acceptance/bundle_without_spec.rb +++ b/spec/acceptance/bundle_without_spec.rb @@ -4,6 +4,7 @@ RSpec.describe "Bundler without flag" do it "passes --without flag to Bundler on install" do + pending "--without group support seems broken, see: https://github.com/rubygems/rubygems/issues/8518" build_gems %w[pancake orange_juice waffle coffee sausage soda] build_gemfile <<-GEMFILE diff --git a/spec/acceptance/cli/install_spec.rb b/spec/acceptance/cli/install_spec.rb index 09dcc106..1e93029f 100644 --- a/spec/acceptance/cli/install_spec.rb +++ b/spec/acceptance/cli/install_spec.rb @@ -60,7 +60,7 @@ expect(content_of("gemfiles/1.0.0.gemfile.lock")).to include("file://#{uri_dummy_path}") end - context "with job size", parallel: true do + context "with job size", :parallel do before do build_appraisal_file <<-APPRAISAL appraise '1.0.0' do @@ -72,15 +72,15 @@ it "accepts --jobs option to set job size" do output = run "appraisal install --jobs=2" - expect(output).to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}' --jobs=2") + expect(output).to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}' --jobs=2") end it "ignores --jobs option if the job size is less than or equal to 1" do output = run "appraisal install --jobs=0" - expect(output).to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}'") - expect(output).not_to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}' --jobs=0") - expect(output).not_to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}' --jobs=1") + expect(output).to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}'") + expect(output).not_to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}' --jobs=0") + expect(output).not_to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}' --jobs=1") end end @@ -96,7 +96,7 @@ it "accepts --full-index option to pull the full RubyGems index" do output = run("appraisal install --full-index") - expect(output).to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}' --retry 1 --full-index true") + expect(output).to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}' --retry 1 --full-index true") end end @@ -112,7 +112,7 @@ it "accepts --path option to specify the location to install gems into" do output = run("appraisal install --path vendor/appraisal") - expect(output).to include("bundle install --gemfile='#{file('gemfiles/1.0.0.gemfile')}' --path #{file('vendor/appraisal')} --retry 1") + expect(output).to include("bundle install --gemfile='#{file("gemfiles/1.0.0.gemfile")}' --path #{file("vendor/appraisal")} --retry 1") end end end diff --git a/spec/acceptance/cli/update_spec.rb b/spec/acceptance/cli/update_spec.rb index 8929c89c..3819a81c 100644 --- a/spec/acceptance/cli/update_spec.rb +++ b/spec/acceptance/cli/update_spec.rb @@ -20,8 +20,8 @@ after do in_test_directory do - `gem uninstall dummy -v 1.0.1` - `gem uninstall dummy2 -a` + %x{gem uninstall dummy -v 1.0.1} + %x{gem uninstall dummy2 -a} end end diff --git a/spec/acceptance/eval_gemfile_spec.rb b/spec/acceptance/eval_gemfile_spec.rb index 152e8a00..03654d5f 100644 --- a/spec/acceptance/eval_gemfile_spec.rb +++ b/spec/acceptance/eval_gemfile_spec.rb @@ -27,7 +27,11 @@ end def build_modular_gemfile - Dir.mkdir("tmp/stage/gemfiles") rescue nil + begin + Dir.mkdir("tmp/stage/gemfiles") + rescue StandardError + nil + end write_file File.join("gemfiles", "im_with_dummy"), <<-GEMFILE # No source needed because this is a modular gemfile intended to be loaded into another gemfile, @@ -37,12 +41,13 @@ def build_modular_gemfile end def build_appraisal_file - super <<-APPRAISALS + appraisals = <<-APPRAISALS appraise 'stock' do gem 'rake' eval_gemfile "im_with_dummy" end APPRAISALS + super(appraisals) end def build_rakefile @@ -59,7 +64,11 @@ def build_rakefile end def build_gemspec(path = ".") - Dir.mkdir("tmp/stage/#{path}") rescue nil + begin + Dir.mkdir("tmp/stage/#{path}") + rescue StandardError + nil + end write_file File.join(path, "gemspec_project.gemspec"), <<-GEMSPEC Gem::Specification.new do |s| diff --git a/spec/acceptance/gemspec_spec.rb b/spec/acceptance/gemspec_spec.rb index d85fdf08..fd93a11d 100644 --- a/spec/acceptance/gemspec_spec.rb +++ b/spec/acceptance/gemspec_spec.rb @@ -45,11 +45,12 @@ end def build_appraisal_file - super <<-APPRAISALS + appraisals = <<-APPRAISALS appraise 'stock' do gem 'rake' end APPRAISALS + super(appraisals) end def build_rakefile @@ -66,7 +67,11 @@ def build_rakefile end def build_gemspec(path = ".") - Dir.mkdir("tmp/stage/#{path}") rescue nil + begin + Dir.mkdir("tmp/stage/#{path}") + rescue StandardError + nil + end write_file File.join(path, "gemspec_project.gemspec"), <<-GEMSPEC Gem::Specification.new do |s| diff --git a/spec/appraisal/appraisal_file_spec.rb b/spec/appraisal/appraisal_file_spec.rb index 8b583ea8..c90a390a 100644 --- a/spec/appraisal/appraisal_file_spec.rb +++ b/spec/appraisal/appraisal_file_spec.rb @@ -16,7 +16,7 @@ end describe "#customize_gemfiles" do - before(:each) do + before do allow(File).to receive(:exist?).with(anything).and_return(true) allow(IO).to receive(:read).with(anything).and_return("") end @@ -33,13 +33,11 @@ context "when the block returns a hash with :heading key" do subject do described_class.new.customize_gemfiles do - { heading: "foo" } + {:heading => "foo"} end end it "sets the heading" do - pending("test is broken: wrong number of arguments (given 0, expected 1)") - expect { subject }.to change { Appraisal::Customize.heading }.to("foo") end end @@ -47,7 +45,7 @@ context "when the block returns a hash with :single_quotes key" do subject do described_class.new.customize_gemfiles do - { single_quotes: true } + {:single_quotes => true} end end @@ -59,16 +57,14 @@ context "when the block returns a hash with :heading and :single_quotes keys" do subject do described_class.new.customize_gemfiles do - { heading: "foo", single_quotes: true } + {:heading => "foo", :single_quotes => true} end end it "sets the heading and single_quotes" do - pending("test is broken: wrong number of arguments (given 0, expected 1)") - subject expect(Appraisal::Customize.heading).to eq("foo") - expect(Appraisal::Customize.single_quotes).to eq(true) + expect(Appraisal::Customize.single_quotes).to be(true) end end end diff --git a/spec/appraisal/appraisal_spec.rb b/spec/appraisal/appraisal_spec.rb index d68bfd57..031c871e 100644 --- a/spec/appraisal/appraisal_spec.rb +++ b/spec/appraisal/appraisal_spec.rb @@ -6,23 +6,23 @@ RSpec.describe Appraisal::Appraisal do it "converts spaces to underscores in the gemfile path" do - appraisal = Appraisal::Appraisal.new("one two", "Gemfile") + appraisal = described_class.new("one two", "Gemfile") expect(appraisal.gemfile_path).to match(/one_two\.gemfile$/) end - it "converts punctuation to underscores in the gemfile path" do - appraisal = Appraisal::Appraisal.new("o&ne!", "Gemfile") + it "converts punctuation to underscores in the gemfile path" do + appraisal = described_class.new("o&ne!", "Gemfile") expect(appraisal.gemfile_path).to match(/o_ne_\.gemfile$/) end it "keeps dots in the gemfile path" do - appraisal = Appraisal::Appraisal.new("rails3.0", "Gemfile") + appraisal = described_class.new("rails3.0", "Gemfile") expect(appraisal.gemfile_path).to match(/rails3\.0\.gemfile$/) end it "generates a gemfile with a newline at the end of file" do output_file = Tempfile.new("gemfile") - appraisal = Appraisal::Appraisal.new("fake", "fake") + appraisal = described_class.new("fake", "fake") allow(appraisal).to receive(:gemfile_path).and_return(output_file.path) appraisal.write_gemfile @@ -33,9 +33,9 @@ context "gemfile customization" do it "generates a gemfile with a custom heading" do heading = "This file was generated with a custom heading!" - Appraisal::Customize.new(heading: heading) + Appraisal::Customize.new(:heading => heading) output_file = Tempfile.new("gemfile") - appraisal = Appraisal::Appraisal.new("custom", "Gemfile") + appraisal = described_class.new("custom", "Gemfile") allow(appraisal).to receive(:gemfile_path).and_return(output_file.path) appraisal.write_gemfile @@ -45,28 +45,28 @@ end it "generates a gemfile with multiple lines of custom heading" do - heading = <<~HEADING - frozen_string_literal: true\n - This file was generated with a custom heading! + heading = <<-HEADING +frozen_string_literal: true\n +This file was generated with a custom heading! HEADING - Appraisal::Customize.new(heading: heading) + Appraisal::Customize.new(:heading => heading) output_file = Tempfile.new("gemfile") - appraisal = Appraisal::Appraisal.new("custom", "Gemfile") + appraisal = described_class.new("custom", "Gemfile") allow(appraisal).to receive(:gemfile_path).and_return(output_file.path) appraisal.write_gemfile - expected_output = <<~HEADING - # frozen_string_literal: true\n - # This file was generated with a custom heading! + expected_output = <<-HEADING +# frozen_string_literal: true\n +# This file was generated with a custom heading! HEADING expect(output_file.read).to start_with(expected_output) end it "generates a gemfile with single quotes rather than doubles" do - Appraisal::Customize.new(single_quotes: true) + Appraisal::Customize.new(:single_quotes => true) output_file = Tempfile.new("gemfile") - appraisal = Appraisal::Appraisal.new("quotes", 'gem "foo"') + appraisal = described_class.new("quotes", 'gem "foo"') allow(appraisal).to receive(:gemfile_path).and_return(output_file.path) appraisal.write_gemfile @@ -77,7 +77,7 @@ it "does not customize anything by default" do Appraisal::Customize.new output_file = Tempfile.new("gemfile") - appraisal = Appraisal::Appraisal.new("fake", 'gem "foo"') + appraisal = described_class.new("fake", 'gem "foo"') allow(appraisal).to receive(:gemfile_path).and_return(output_file.path) appraisal.write_gemfile @@ -91,10 +91,10 @@ include StreamHelpers before do - @appraisal = Appraisal::Appraisal.new("fake", "fake") + @appraisal = described_class.new("fake", "fake") allow(@appraisal).to receive(:gemfile_path).and_return("/home/test/test directory") allow(@appraisal).to receive(:project_root).and_return(Pathname.new("/home/test")) - allow(Appraisal::Command).to receive(:new).and_return(double(run: true)) + allow(Appraisal::Command).to receive(:new).and_return(double(:run => true)) end it "runs single install command on Bundler < 1.4.0" do diff --git a/spec/appraisal/customize_spec.rb b/spec/appraisal/customize_spec.rb index 98e77f36..cc9e50f8 100644 --- a/spec/appraisal/customize_spec.rb +++ b/spec/appraisal/customize_spec.rb @@ -8,24 +8,24 @@ let(:appraisal) { Appraisal::Appraisal.new("test", "Gemfile") } let(:single_line_heading) { "This file was generated with a custom heading!" } let(:multi_line_heading) do - <<~HEADING - frozen_string_literal: true + <<-HEADING +frozen_string_literal: true - This file was generated with a custom heading! +This file was generated with a custom heading! HEADING end let(:subject) { described_class.new } let(:single_line_subject) do - described_class.new(heading: single_line_heading) + described_class.new(:heading => single_line_heading) end let(:multi_line_single_quotes_subject) do - described_class.new(heading: multi_line_heading, single_quotes: true) + described_class.new(:heading => multi_line_heading, :single_quotes => true) end describe ".heading" do it "returns nil if no heading is set" do subject - expect(described_class.heading(appraisal)).to eq(nil) + expect(described_class.heading(appraisal)).to be_nil end it "returns the heading if set" do @@ -36,19 +36,19 @@ it "returns the heading without an trailing newline" do multi_line_single_quotes_subject expect(described_class.heading(appraisal)).to eq(multi_line_heading.chomp) - expect(described_class.heading(appraisal)).to_not end_with("\n") + expect(described_class.heading(appraisal)).not_to end_with("\n") end end describe ".single_quotes" do it "returns false if not set" do subject - expect(described_class.single_quotes).to eq(false) + expect(described_class.single_quotes).to be(false) end it "returns true if set" do multi_line_single_quotes_subject - expect(described_class.single_quotes).to eq(true) + expect(described_class.single_quotes).to be(true) end end @@ -60,6 +60,7 @@ let(:lockfile_relative_path) { "gemfiles/#{lockfile}" } let(:gemfile_full_path) { "/path/to/project/#{gemfile_relative_path}" } let(:lockfile_full_path) { "/path/to/project/#{lockfile_relative_path}" } + before do allow(appraisal).to receive(:gemfile_name).and_return(gemfile) allow(appraisal).to receive(:gemfile_path).and_return(gemfile_full_path) @@ -69,72 +70,72 @@ it "returns nil if no heading is set" do subject - expect(described_class.send(:customize, nil, appraisal)).to eq(nil) + expect(described_class.send(:customize, nil, appraisal)).to be_nil end it "returns the heading unchanged" do single_line_subject expect(described_class.send( - :customize, - single_line_heading, - appraisal - )).to eq(single_line_heading) + :customize, + single_line_heading, + appraisal, + )).to eq(single_line_heading) end it "returns the heading with the appraisal name" do expect(described_class.send( - :customize, - "Appraisal: %{appraisal}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Appraisal: #{appraisal_name}") + :customize, + "Appraisal: %{appraisal}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Appraisal: #{appraisal_name}") end it "returns the heading with the gemfile name" do expect(described_class.send( - :customize, - "Gemfile: %{gemfile}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Gemfile: #{gemfile}") + :customize, + "Gemfile: %{gemfile}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Gemfile: #{gemfile}") end it "returns the heading with the gemfile path" do expect(described_class.send( - :customize, - "Gemfile: %{gemfile_path}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Gemfile: #{gemfile_full_path}") + :customize, + "Gemfile: %{gemfile_path}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Gemfile: #{gemfile_full_path}") end it "returns the heading with the lockfile name" do expect(described_class.send( - :customize, - "Lockfile: %{lockfile}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Lockfile: #{lockfile}") + :customize, + "Lockfile: %{lockfile}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Lockfile: #{lockfile}") end it "returns the heading with the lockfile path" do expect(described_class.send( - :customize, - "Lockfile: %{lockfile_path}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Lockfile: #{lockfile_full_path}") + :customize, + "Lockfile: %{lockfile_path}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Lockfile: #{lockfile_full_path}") end it "returns the heading with the relative gemfile path" do expect(described_class.send( - :customize, - "Gemfile: %{relative_gemfile_path}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Gemfile: #{gemfile_relative_path}") + :customize, + "Gemfile: %{relative_gemfile_path}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Gemfile: #{gemfile_relative_path}") end it "returns the heading with the relative lockfile path" do expect(described_class.send( - :customize, - "Gemfile: %{relative_lockfile_path}", # rubocop:disable Style/FormatStringToken - appraisal - )).to eq("Gemfile: #{lockfile_relative_path}") + :customize, + "Gemfile: %{relative_lockfile_path}", # rubocop:disable Style/FormatStringToken + appraisal, + )).to eq("Gemfile: #{lockfile_relative_path}") end end end diff --git a/spec/appraisal/dependency_list_spec.rb b/spec/appraisal/dependency_list_spec.rb index 8b322984..7aa0c4a2 100644 --- a/spec/appraisal/dependency_list_spec.rb +++ b/spec/appraisal/dependency_list_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Appraisal::DependencyList do describe "#add" do - let(:dependency_list) { Appraisal::DependencyList.new } + let(:dependency_list) { described_class.new } it "adds dependency to the list" do dependency_list.add("rails", ["4.1.4"]) @@ -32,7 +32,7 @@ end describe "#remove" do - let(:dependency_list) { Appraisal::DependencyList.new } + let(:dependency_list) { described_class.new } before do dependency_list.add("rails", ["4.1.4"]) diff --git a/spec/appraisal/gemfile_spec.rb b/spec/appraisal/gemfile_spec.rb index 666c07ba..2e88f728 100644 --- a/spec/appraisal/gemfile_spec.rb +++ b/spec/appraisal/gemfile_spec.rb @@ -8,26 +8,26 @@ include StreamHelpers it "supports gemfiles without sources" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new expect(gemfile.to_s.strip).to eq "" end it "supports multiple sources" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.source "one" gemfile.source "two" expect(gemfile.to_s.strip).to eq %(source "one"\nsource "two") end it "ignores duplicate sources" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.source "one" gemfile.source "one" expect(gemfile.to_s.strip).to eq %(source "one") end it "preserves dependency order" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.gem "one" gemfile.gem "two" gemfile.gem "three" @@ -35,13 +35,13 @@ end it "supports symbol sources" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.source :one expect(gemfile.to_s.strip).to eq %(source :one) end it "supports group syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.group :development, :test do gem "one" @@ -55,7 +55,7 @@ end it "supports nested DSL within group syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.group :development, :test do platforms :jruby do @@ -87,7 +87,7 @@ end it "supports platform syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.platform :jruby do gem "one" @@ -101,7 +101,7 @@ end it "supports nested DSL within platform syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.platform :jruby do group :development, :test do @@ -133,7 +133,7 @@ end it "supports git syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "git://example.com/repo.git" do gem "one" @@ -147,7 +147,7 @@ end it "supports nested DSL within git syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "git://example.com/repo.git" do group :development, :test do @@ -179,7 +179,7 @@ end it "supports path syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.path "../path" do gem "one" @@ -193,7 +193,7 @@ end it "supports nested DSL within path syntax" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.path "../path" do group :development, :test do @@ -227,14 +227,14 @@ context "excess new line" do context "no contents" do it "shows empty string" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new expect(gemfile.to_s).to eq "" end end context "full contents" do it "does not show newline at end" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.source "source" gemfile.gem "gem" gemfile.gemspec @@ -244,7 +244,7 @@ context "no gemspec" do it "does not show newline at end" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.source "source" gemfile.gem "gem" expect(gemfile.to_s).to match(/[^\n]\z/m) @@ -253,64 +253,65 @@ end context "relative path handling" do - before { stub_const("RUBY_VERSION", "2.3.0") } + before { stub_const("RUBY_VERSION", "1.8.7") } context "in :path option" do it "handles dot path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", path: "." + gemfile = described_class.new + gemfile.gem "bacon", :path => "." - expect(gemfile.to_s).to eq %(gem "bacon", path: "../") + expect(gemfile.to_s).to eq %(gem "bacon", :path => "../") end it "handles relative path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", path: "../bacon" + gemfile = described_class.new + gemfile.gem "bacon", :path => "../bacon" - expect(gemfile.to_s).to eq %(gem "bacon", path: "../../bacon") + expect(gemfile.to_s).to eq %(gem "bacon", :path => "../../bacon") end it "handles absolute path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", path: "/tmp" + gemfile = described_class.new + gemfile.gem "bacon", :path => "/tmp" - expect(gemfile.to_s).to eq %(gem "bacon", path: "/tmp") + expect(gemfile.to_s).to eq %(gem "bacon", :path => "/tmp") end end context "in :git option" do it "handles dot git path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", git: "." + gemfile = described_class.new + gemfile.gem "bacon", :git => "." - expect(gemfile.to_s).to eq %(gem "bacon", git: "../") + expect(gemfile.to_s).to eq %(gem "bacon", :git => "../") end it "handles relative git path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", git: "../bacon" + gemfile = described_class.new + gemfile.gem "bacon", :git => "../bacon" - expect(gemfile.to_s).to eq %(gem "bacon", git: "../../bacon") + expect(gemfile.to_s).to eq %(gem "bacon", :git => "../../bacon") end it "handles absolute git path" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", git: "/tmp" + gemfile = described_class.new + gemfile.gem "bacon", :git => "/tmp" - expect(gemfile.to_s).to eq %(gem "bacon", git: "/tmp") + expect(gemfile.to_s).to eq %(gem "bacon", :git => "/tmp") end it "handles git uri" do - gemfile = Appraisal::Gemfile.new - gemfile.gem "bacon", git: "git@github.com:bacon/bacon.git" + gemfile = described_class.new + gemfile.gem "bacon", :git => "git@github.com:bacon/bacon.git" - expect(gemfile.to_s).to eq %(gem "bacon", git: "git@github.com:bacon/bacon.git") + expect(gemfile.to_s) + .to eq %(gem "bacon", :git => "git@github.com:bacon/bacon.git") end end context "in path block" do it "handles dot path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.path "." do gem "bacon" @@ -324,7 +325,7 @@ end it "handles relative path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.path "../bacon" do gem "bacon" @@ -338,7 +339,7 @@ end it "handles absolute path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.path "/tmp" do gem "bacon" @@ -354,7 +355,7 @@ context "in git block" do it "handles dot git path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "." do gem "bacon" @@ -368,7 +369,7 @@ end it "handles relative git path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "../bacon" do gem "bacon" @@ -382,7 +383,7 @@ end it "handles absolute git path" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "/tmp" do gem "bacon" @@ -396,7 +397,7 @@ end it "handles git uri" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git "git@github.com:bacon/bacon.git" do gem "bacon" @@ -412,28 +413,28 @@ context "in gemspec directive" do it "handles gemspec path" do - gemfile = Appraisal::Gemfile.new - gemfile.gemspec path: "." + gemfile = described_class.new + gemfile.gemspec :path => "." - expect(gemfile.to_s).to eq %(gemspec path: "../") + expect(gemfile.to_s).to eq %(gemspec :path => "../") end end end context "git_source support" do - before { stub_const("RUBY_VERSION", "2.3.0") } + before { stub_const("RUBY_VERSION", "1.8.7") } it "stores git_source declaration and apply it as git option" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new gemfile.git_source(:custom_source) { |repo| "path/#{repo}" } - gemfile.gem "bacon", custom_source: "bacon_pancake" + gemfile.gem "bacon", :custom_source => "bacon_pancake" - expect(gemfile.to_s).to eq %(gem "bacon", git: "../path/bacon_pancake") + expect(gemfile.to_s).to eq %(gem "bacon", :git => "../path/bacon_pancake") end end it "preserves the Gemfile's __FILE__" do - gemfile = Appraisal::Gemfile.new + gemfile = described_class.new Tempfile.open do |tmpfile| tmpfile.write "__FILE__" tmpfile.rewind diff --git a/spec/appraisal/utils_spec.rb b/spec/appraisal/utils_spec.rb index 04dfe910..743f8683 100644 --- a/spec/appraisal/utils_spec.rb +++ b/spec/appraisal/utils_spec.rb @@ -5,61 +5,64 @@ RSpec.describe Appraisal::Utils do describe ".format_string" do - it "prints out a nice looking hash without brackets with new syntax" do - hash = { foo: "bar" } - expect(Appraisal::Utils.format_string(hash)).to eq('foo: "bar"') + it "prints out a nice looking hash without brackets with old syntax" do + hash = {:foo => "bar"} + expect(described_class.format_string(hash)).to eq(':foo => "bar"') - hash = { "baz" => { ball: "boo" } } - expect(Appraisal::Utils.format_string(hash)).to eq('"baz" => { ball: "boo" }') + hash = {"baz" => {:ball => "boo"}} + expect(described_class.format_string(hash)) + .to eq('"baz" => { :ball => "boo" }') end end describe ".format_arguments" do - before { stub_const("RUBY_VERSION", "2.3.0") } + before { stub_const("RUBY_VERSION", "1.8.7") } it "prints out arguments without enclosing square brackets" do - arguments = [:foo, { bar: { baz: "ball" } }] + arguments = [:foo, {:bar => {:baz => "ball"}}] - expect(Appraisal::Utils.format_arguments(arguments)).to eq(':foo, bar: { baz: "ball" }') + expect(described_class.format_arguments(arguments)).to eq( + ':foo, :bar => { :baz => "ball" }', + ) end it "returns nil if arguments is empty" do arguments = [] - expect(Appraisal::Utils.format_arguments(arguments)).to eq(nil) + expect(described_class.format_arguments(arguments)).to be_nil end end describe ".prefix_path" do it "prepends two dots in front of relative path" do - expect(Appraisal::Utils.prefix_path("test")).to eq "../test" + expect(described_class.prefix_path("test")).to eq "../test" end it "replaces single dot with two dots" do - expect(Appraisal::Utils.prefix_path(".")).to eq "../" + expect(described_class.prefix_path(".")).to eq "../" end it "ignores absolute path" do - expect(Appraisal::Utils.prefix_path("/tmp")).to eq "/tmp" + expect(described_class.prefix_path("/tmp")).to eq "/tmp" end it "strips out './' from path" do - expect(Appraisal::Utils.prefix_path("./tmp/./appraisal././")).to eq "../tmp/appraisal./" + expect(described_class.prefix_path("./tmp/./appraisal././")).to eq "../tmp/appraisal./" end it "does not prefix Git uri" do - expect(Appraisal::Utils.prefix_path("git@github.com:bacon/bacon.git")).to eq "git@github.com:bacon/bacon.git" - expect(Appraisal::Utils.prefix_path("git://github.com/bacon/bacon.git")).to eq "git://github.com/bacon/bacon.git" - expect(Appraisal::Utils.prefix_path("https://github.com/bacon/bacon.git")).to eq("https://github.com/bacon/bacon.git") + expect(described_class.prefix_path("git@github.com:bacon/bacon.git")).to eq "git@github.com:bacon/bacon.git" + expect(described_class.prefix_path("git://github.com/bacon/bacon.git")).to eq "git://github.com/bacon/bacon.git" + expect(described_class.prefix_path("https://github.com/bacon/bacon.git")).to eq("https://github.com/bacon/bacon.git") end end describe ".bundler_version" do it "returns the bundler version" do - bundler = double("Bundler", name: "bundler", version: "a.b.c") + bundler = double("Bundler", :name => "bundler", :version => "a.b.c") allow(Gem::Specification).to receive(:detect).and_return(bundler) - version = Appraisal::Utils.bundler_version + version = described_class.bundler_version expect(version).to eq "a.b.c" expect(Gem::Specification).to have_received(:detect) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 20b036de..24416362 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "rubygems" -require "bundler/setup" +require "byebug" if ENV["CI"].nil? && ENV["DEBUG"] == "true" require "./spec/support/acceptance_test_helpers" require "./spec/support/stream_helpers" @@ -13,11 +13,11 @@ RSpec.configure do |config| config.raise_errors_for_deprecations! - config.define_derived_metadata(file_path: %r{spec\/acceptance\/}) do |metadata| + config.define_derived_metadata(:file_path => %r{spec/acceptance/}) do |metadata| metadata[:type] = :acceptance end - config.include AcceptanceTestHelpers, type: :acceptance + config.include AcceptanceTestHelpers, :type => :acceptance # disable monkey patching # see: https://relishapp.com/rspec/rspec-core/v/3-8/docs/configuration/zero-monkey-patching-mode diff --git a/spec/support/acceptance_test_helpers.rb b/spec/support/acceptance_test_helpers.rb index 52e0c60a..ff5b1219 100644 --- a/spec/support/acceptance_test_helpers.rb +++ b/spec/support/acceptance_test_helpers.rb @@ -22,7 +22,7 @@ module AcceptanceTestHelpers included do metadata[:type] = :acceptance - before parallel: true do + before :parallel => true do unless Appraisal::Utils.support_parallel_installation? pending "This Bundler version does not support --jobs flag." end @@ -58,7 +58,7 @@ def unset_bundler_environment_variables end def add_binstub_path - ENV["PATH"] = "bin:#{ENV['PATH']}" + ENV["PATH"] = "bin:#{ENV["PATH"]}" end def restore_environment_variables @@ -94,7 +94,7 @@ def build_gemspec def content_of(path) file(path).read.tap do |content| - content.gsub!(/(\S+): /, ":\\1 => ") + content.gsub!(/(\S+): /, ':\\1 => ') if RUBY_VERSION >= "1.9" end end @@ -130,15 +130,15 @@ def build_default_dummy_gems def ensure_bundler_is_available run "bundle -v 2>&1", false - if $?.exitstatus != 0 - puts <<-WARNING.squish.strip_heredoc + return unless $?.exitstatus != 0 + + puts <<-WARNING.squish.strip_heredoc Reinstall Bundler to #{TMP_GEM_ROOT} as `BUNDLE_DISABLE_SHARED_GEMS` is enabled. - WARNING - version = Utils.bundler_version + WARNING + version = Utils.bundler_version - run "gem install bundler --version #{version} --install-dir '#{TMP_GEM_ROOT}'" - end + run "gem install bundler --version #{version} --install-dir '#{TMP_GEM_ROOT}'" end def build_default_gemfile @@ -146,9 +146,20 @@ def build_default_gemfile source 'https://rubygems.org' gem 'appraisal', :path => '#{PROJECT_ROOT}' + + if RUBY_VERSION < "1.9" + #{File.read(File.join(PROJECT_ROOT, "Gemfile-1.8"))} + elsif RUBY_VERSION < "2.2" + #{File.read(File.join(PROJECT_ROOT, "Gemfile-2.1"))} + end GEMFILE run "bundle install --local" + # Support for binstubs --all was added to bundler's 1-17-stable branch + # and released with bundler v1.17.0.pre.2 (2018-10-13) + # See: + # - https://github.com/rubygems/bundler/pull/6450 + # - https://github.com/rubygems/bundler/commit/9d59fa41ef43aaccc6cf867a69a49648510c4df7#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4edR10 run "bundle binstubs --all" end @@ -159,17 +170,15 @@ def in_test_directory(&block) def run(command, raise_on_error = true) in_test_directory do - `#{command}`.tap do |output| + %x(#{command}).tap do |output| exitstatus = $?.exitstatus - if ENV["VERBOSE"] - puts output - end + puts output if ENV["VERBOSE"] if raise_on_error && exitstatus != 0 - raise RuntimeError, <<-ERROR_MESSAGE.strip_heredoc + raise <<-ERROR_MESSAGE.strip_heredoc.to_s Command #{command.inspect} exited with status #{exitstatus}. Output: - #{output.gsub(/^/, ' ')} + #{output.gsub(/^/, " ")} ERROR_MESSAGE end end diff --git a/spec/support/dependency_helpers.rb b/spec/support/dependency_helpers.rb index 54752d20..3214d7cb 100644 --- a/spec/support/dependency_helpers.rb +++ b/spec/support/dependency_helpers.rb @@ -4,15 +4,16 @@ module DependencyHelpers def build_gem(gem_name, version = "1.0.0") ENV["GEM_HOME"] = TMP_GEM_ROOT - unless File.exist? "#{TMP_GEM_ROOT}/gems/#{gem_name}-#{version}" - FileUtils.mkdir_p "#{TMP_GEM_BUILD}/#{gem_name}/lib" + return if File.exist? "#{TMP_GEM_ROOT}/gems/#{gem_name}-#{version}" - FileUtils.cd "#{TMP_GEM_BUILD}/#{gem_name}" do - gemspec = "#{gem_name}.gemspec" - lib_file = "lib/#{gem_name}.rb" + FileUtils.mkdir_p "#{TMP_GEM_BUILD}/#{gem_name}/lib" - File.open gemspec, "w" do |file| - file.puts <<-GEMSPEC + FileUtils.cd "#{TMP_GEM_BUILD}/#{gem_name}" do + gemspec = "#{gem_name}.gemspec" + lib_file = "lib/#{gem_name}.rb" + + File.open gemspec, "w" do |file| + file.puts <<-GEMSPEC Gem::Specification.new do |s| s.name = #{gem_name.inspect} s.version = #{version.inspect} @@ -23,23 +24,22 @@ def build_gem(gem_name, version = "1.0.0") s.homepage = 'http://github.com/thoughtbot/#{gem_name}' s.required_ruby_version = '>= 2.3.0' end - GEMSPEC - end + GEMSPEC + end - File.open lib_file, "w" do |file| - file.puts "$#{gem_name}_version = '#{version}'" - end + File.open lib_file, "w" do |file| + file.puts "$#{gem_name}_version = '#{version}'" + end - redirect = ENV["VERBOSE"] ? "" : "2>&1" + redirect = ENV["VERBOSE"] ? "" : "2>&1" - 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}] - 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}] - puts "" if ENV["VERBOSE"] - end + puts "" if ENV["VERBOSE"] end end @@ -52,11 +52,12 @@ def build_git_gem(gem_name, version = "1.0.0") build_gem gem_name, version Dir.chdir "#{TMP_GEM_BUILD}/#{gem_name}" do - `git init . --initial-branch=master` - `git config user.email "appraisal@thoughtbot.com"` - `git config user.name "Appraisal"` - `git add .` - `git commit --all --no-verify --message "initial commit"` + %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] + %x[git add .] + %x[git commit --all --no-verify --message "initial commit"] end # Cleanup Bundler cache path manually for now diff --git a/spec/support/stream_helpers.rb b/spec/support/stream_helpers.rb index ca0eb436..97c0240d 100644 --- a/spec/support/stream_helpers.rb +++ b/spec/support/stream_helpers.rb @@ -13,7 +13,7 @@ def capture(stream) yield stream_io.rewind - return captured_stream.read + captured_stream.read ensure captured_stream.close captured_stream.unlink