diff options
author | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2019-04-14 06:01:35 +0000 |
---|---|---|
committer | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2019-04-14 06:01:35 +0000 |
commit | 68ddd4d300e9a88737c4f37af74e1a0312949b2f (patch) | |
tree | 787e1e83d76934ce039eb336995a8d5bb53a89e6 /lib/bundler | |
parent | d636809c057432e8d42abe30c6c6785eb0721d77 (diff) | |
download | ruby-68ddd4d300e9a88737c4f37af74e1a0312949b2f.tar.gz |
Merge Bundler 2.1.0.pre.1 as developed version from upstream.
https://github.com/bundler/bundler/commit/a53709556b95a914e874b22ed2116a46b0528852
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67539 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/bundler')
95 files changed, 1177 insertions, 840 deletions
diff --git a/lib/bundler/bundler.gemspec b/lib/bundler/bundler.gemspec index b229ab224d..d8abb2b16f 100644 --- a/lib/bundler/bundler.gemspec +++ b/lib/bundler/bundler.gemspec @@ -1,4 +1,3 @@ -# coding: utf-8 # frozen_string_literal: true begin @@ -19,16 +18,16 @@ Gem::Specification.new do |s| "Yehuda Katz" ] s.email = ["team@bundler.io"] - s.homepage = "http://bundler.io" + s.homepage = "https://bundler.io" s.summary = "The best way to manage your application's dependencies" s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably" if s.respond_to?(:metadata=) s.metadata = { - "bug_tracker_uri" => "http://github.com/bundler/bundler/issues", + "bug_tracker_uri" => "https://github.com/bundler/bundler/issues", "changelog_uri" => "https://github.com/bundler/bundler/blob/master/CHANGELOG.md", "homepage_uri" => "https://bundler.io/", - "source_code_uri" => "http://github.com/bundler/bundler/", + "source_code_uri" => "https://github.com/bundler/bundler/", } end @@ -41,14 +40,12 @@ Gem::Specification.new do |s| end s.add_development_dependency "automatiek", "~> 0.1.0" - s.add_development_dependency "mustache", "0.99.6" - s.add_development_dependency "rake", "~> 10.0" - s.add_development_dependency "rdiscount", "~> 2.2" + s.add_development_dependency "rake", "~> 12.0" s.add_development_dependency "ronn", "~> 0.7.3" s.add_development_dependency "rspec", "~> 3.6" + s.add_development_dependency "rubocop", "= 0.65.0" - # base_dir = File.dirname(__FILE__).gsub(%r{([^A-Za-z0-9_\-.,:\/@\n])}, "\\\\\\1") - # s.files = IO.popen("git -C #{base_dir} ls-files -z", &:read).split("\x0").select {|f| f.match(%r{^(lib|exe)/}) } + # s.files = Dir.glob("{lib,exe}/**/*", File::FNM_DOTMATCH).reject {|f| File.directory?(f) } # we don't check in man pages, but we need to ship them because # we use them to generate the long-form help for each command. diff --git a/lib/bundler/capistrano.rb b/lib/bundler/capistrano.rb index 40e2e5dbe8..1b7145b72b 100644 --- a/lib/bundler/capistrano.rb +++ b/lib/bundler/capistrano.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 3, +Bundler::SharedHelpers.major_deprecation 2, "The Bundler task for Capistrano. Please use http://github.com/capistrano/bundler" # Capistrano task for Bundler. diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 3efe193613..4e3735847e 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -16,7 +16,7 @@ module Bundler def self.start(*) super - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException Bundler.ui = UI::Shell.new raise e ensure @@ -61,11 +61,6 @@ module Bundler end end - def self.deprecated_option(*args, &blk) - return if Bundler.feature_flag.forget_cli_options? - method_option(*args, &blk) - end - check_unknown_options!(:except => [:config, :exec]) stop_on_unknown_option! :exec @@ -142,7 +137,7 @@ module Bundler Gemfile to a gem with a gemspec, the --gemspec option will automatically add each dependency listed in the gemspec file to the newly created Gemfile. D - deprecated_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile" + method_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile" def init require "bundler/cli/init" Init.new(options.dup).run @@ -188,13 +183,13 @@ module Bundler If the bundle has already been installed, bundler will tell you so and then exit. D - deprecated_option "binstubs", :type => :string, :lazy_default => "bin", :banner => + method_option "binstubs", :type => :string, :lazy_default => "bin", :banner => "Generate bin stubs for bundled gems to ./bin" - deprecated_option "clean", :type => :boolean, :banner => + method_option "clean", :type => :boolean, :banner => "Run bundle clean automatically after install" - deprecated_option "deployment", :type => :boolean, :banner => + method_option "deployment", :type => :boolean, :banner => "Install using defaults tuned for deployment environments" - deprecated_option "frozen", :type => :boolean, :banner => + method_option "frozen", :type => :boolean, :banner => "Do not allow the Gemfile.lock to be updated after this install" method_option "full-index", :type => :boolean, :banner => "Fall back to using the single-file index of all gems" @@ -204,32 +199,37 @@ module Bundler "Specify the number of jobs to run in parallel" method_option "local", :type => :boolean, :banner => "Do not attempt to fetch gems remotely and use the gem cache instead" - deprecated_option "no-cache", :type => :boolean, :banner => + method_option "no-cache", :type => :boolean, :banner => "Don't update the existing gem cache." method_option "redownload", :type => :boolean, :aliases => "--force", :banner => "Force downloading every gem." - deprecated_option "no-prune", :type => :boolean, :banner => + method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache." - deprecated_option "path", :type => :string, :banner => + method_option "path", :type => :string, :banner => "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine" method_option "quiet", :type => :boolean, :banner => "Only output warnings and errors." - deprecated_option "shebang", :type => :string, :banner => + method_option "shebang", :type => :string, :banner => "Specify a different shebang executable name than the default (usually 'ruby')" method_option "standalone", :type => :array, :lazy_default => [], :banner => "Make a bundle that can work without the Bundler runtime" - deprecated_option "system", :type => :boolean, :banner => + method_option "system", :type => :boolean, :banner => "Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application" method_option "trust-policy", :alias => "P", :type => :string, :banner => "Gem trust policy (like gem install -P). Must be one of " + Bundler.rubygems.security_policy_keys.join("|") - deprecated_option "without", :type => :array, :banner => + method_option "without", :type => :array, :banner => "Exclude gems that are part of the specified named group." - deprecated_option "with", :type => :array, :banner => + method_option "with", :type => :array, :banner => "Include gems that are part of the specified named group." map "i" => "install" def install - SharedHelpers.major_deprecation(3, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") + SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") + + %w[clean deployment frozen no-cache no-prune path shebang system without with].each do |option| + remembered_flag_deprecation(option) + end + require "bundler/cli/install" Bundler.settings.temporary(:no_install => false) do Install.new(options.dup).run @@ -275,57 +275,60 @@ module Bundler method_option "all", :type => :boolean, :banner => "Update everything." def update(*gems) - SharedHelpers.major_deprecation(3, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") + SharedHelpers.major_deprecation(2, "The `--force` option has been renamed to `--redownload`") if ARGV.include?("--force") require "bundler/cli/update" - Update.new(options, gems).run + Bundler.settings.temporary(:no_install => false) do + Update.new(options, gems).run + end end - desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem" - long_desc <<-D - Show lists the names and versions of all gems that are required by your Gemfile. - Calling show with [GEM] will list the exact location of that gem on your machine. - D - method_option "paths", :type => :boolean, - :banner => "List the paths of all gems that are required by your Gemfile." - method_option "outdated", :type => :boolean, - :banner => "Show verbose output including whether gems are outdated." - def show(gem_name = nil) - if ARGV[0] == "show" - rest = ARGV[1..-1] - - new_command = rest.find {|arg| !arg.start_with?("--") } ? "info" : "list" - - new_arguments = rest.map do |arg| - next arg if arg != "--paths" - next "--path" if new_command == "info" + unless Bundler.feature_flag.bundler_3_mode? + desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem" + long_desc <<-D + Show lists the names and versions of all gems that are required by your Gemfile. + Calling show with [GEM] will list the exact location of that gem on your machine. + D + method_option "paths", :type => :boolean, + :banner => "List the paths of all gems that are required by your Gemfile." + method_option "outdated", :type => :boolean, + :banner => "Show verbose output including whether gems are outdated." + def show(gem_name = nil) + if ARGV[0] == "show" + rest = ARGV[1..-1] + + if flag = rest.find{|arg| ["--verbose", "--outdated"].include?(arg) } + Bundler::SharedHelpers.major_deprecation(2, "the `#{flag}` flag to `bundle show` was undocumented and will be removed without replacement") + else + new_command = rest.find {|arg| !arg.start_with?("--") } ? "info" : "list" + + new_arguments = rest.map do |arg| + next arg if arg != "--paths" + next "--path" if new_command == "info" + end + + old_argv = ARGV.join(" ") + new_argv = [new_command, *new_arguments.compact].join(" ") + + Bundler::SharedHelpers.major_deprecation(2, "use `bundle #{new_argv}` instead of `bundle #{old_argv}`") + end end - - old_argv = ARGV.join(" ") - new_argv = [new_command, *new_arguments.compact].join(" ") - - Bundler::SharedHelpers.major_deprecation(3, "use `bundle #{new_argv}` instead of `bundle #{old_argv}`") - end - require "bundler/cli/show" - Show.new(options, gem_name).run - end - # TODO: 2.0 remove `bundle show` - - if Bundler.feature_flag.list_command? - desc "list", "List all gems in the bundle" - method_option "name-only", :type => :boolean, :banner => "print only the gem names" - method_option "only-group", :type => :string, :banner => "print gems from a particular group" - method_option "without-group", :type => :string, :banner => "print all gems expect from a group" - method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle" - def list - require "bundler/cli/list" - List.new(options).run + require "bundler/cli/show" + Show.new(options, gem_name).run end + end - map %w[ls] => "list" - else - map %w[list] => "show" + desc "list", "List all gems in the bundle" + method_option "name-only", :type => :boolean, :banner => "print only the gem names" + method_option "only-group", :type => :string, :banner => "print gems from a particular group" + method_option "without-group", :type => :string, :banner => "print all gems except from a group" + method_option "paths", :type => :boolean, :banner => "print the path to each gem in the bundle" + def list + require "bundler/cli/list" + List.new(options).run end + map %w[ls] => "list" + desc "info GEM [OPTIONS]", "Show information for the given gem" method_option "path", :type => :boolean, :banner => "Print full path to gem" def info(gem_name) @@ -386,9 +389,10 @@ module Bundler "Do not attempt to fetch gems remotely and use the gem cache instead" method_option "pre", :type => :boolean, :banner => "Check for newer pre-release gems" method_option "source", :type => :array, :banner => "Check against a specific source" - method_option "strict", :type => :boolean, :banner => + strict_is_update = Bundler.feature_flag.forget_cli_options? + method_option "filter-strict", :type => :boolean, :aliases => strict_is_update ? [] : %w[--strict], :banner => "Only list newer versions allowed by your Gemfile requirements" - method_option "update-strict", :type => :boolean, :banner => + method_option "update-strict", :type => :boolean, :aliases => strict_is_update ? %w[--strict] : [], :banner => "Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major" method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version" method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)" @@ -475,11 +479,8 @@ module Bundler will show the current value, as well as any superceded values and where they were specified. D - method_option "parseable", :type => :boolean, :banner => "Use minimal formatting for more parseable output" - def config(*args) - require "bundler/cli/config" - Config.new(options, args, self).run - end + require "bundler/cli/config" + subcommand "config", Config desc "open GEM", "Opens the source directory of the given bundled gem" def open(name) @@ -487,7 +488,7 @@ module Bundler Open.new(options, name).run end - if Bundler.feature_flag.console_command? + unless Bundler.feature_flag.bundler_3_mode? desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded" def console(group = nil) require "bundler/cli/console" @@ -524,7 +525,7 @@ module Bundler end end - if Bundler.feature_flag.viz_command? + unless Bundler.feature_flag.bundler_3_mode? desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true long_desc <<-D Viz generates a PNG file of the current Gemfile as a dependency graph. @@ -537,7 +538,7 @@ module Bundler method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version." method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group." def viz - SharedHelpers.major_deprecation 3, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz" + SharedHelpers.major_deprecation 2, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz" require "bundler/cli/viz" Viz.new(options.dup).run end @@ -547,14 +548,14 @@ module Bundler desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem" method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library." - method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config gem.coc true`." + method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config set gem.coc true`." method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR", :lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }, :desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)" method_option :ext, :type => :boolean, :default => false, :desc => "Generate the boilerplate for C extension code" - method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config gem.mit true`." + method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config set gem.mit true`." method_option :test, :type => :string, :lazy_default => "rspec", :aliases => "-t", :banner => "rspec", - :desc => "Generate a test directory for your library, either rspec or minitest. Set a default with `bundle config gem.test rspec`." + :desc => "Generate a test directory for your library, either rspec or minitest. Set a default with `bundle config set gem.test rspec`." def gem(name) end @@ -608,7 +609,7 @@ module Bundler method_option "group", :type => :string, :banner => "Install gem into a bundler group" def inject(name, version) - SharedHelpers.major_deprecation 3, "The `inject` command has been replaced by the `add` command" + SharedHelpers.major_deprecation 2, "The `inject` command has been replaced by the `add` command" require "bundler/cli/inject" Inject.new(options.dup, name, version).run end @@ -620,6 +621,8 @@ module Bundler "do not attempt to fetch remote gemspecs and use the local gem cache only" method_option "print", :type => :boolean, :default => false, :banner => "print the lockfile to STDOUT instead of writing to the file system" + method_option "gemfile", :type => :string, :banner => + "Use the specified gemfile instead of Gemfile" method_option "lockfile", :type => :string, :default => nil, :banner => "the path the lockfile should be written to" method_option "full-index", :type => :boolean, :default => false, :banner => @@ -714,7 +717,7 @@ module Bundler # Automatically invoke `bundle install` and resume if # Bundler.settings[:auto_install] exists. This is set through config cmd - # `bundle config auto_install 1`. + # `bundle config set auto_install 1`. # # Note that this method `nil`s out the global Definition object, so it # should be called first, before you instantiate anything like an @@ -786,5 +789,22 @@ module Bundler rescue RuntimeError nil end + + def remembered_flag_deprecation(name) + option = current_command.options[name] + flag_name = option.switch_name + + name_index = ARGV.find {|arg| flag_name == arg } + return unless name_index + + value = options[name] + value = value.join(" ").to_s if option.type == :array + + Bundler::SharedHelpers.major_deprecation 2,\ + "The `#{flag_name}` flag is deprecated because it relies on being " \ + "remembered accross bundler invokations, which bundler will no longer " \ + "do in future versions. Instead please use `bundle config #{name} " \ + "'#{value}'`, and stop using this flag" + end end end diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb index 9d2ba87d34..cb7958e5d0 100644 --- a/lib/bundler/cli/cache.rb +++ b/lib/bundler/cli/cache.rb @@ -29,7 +29,7 @@ module Bundler if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all? Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \ "to package them as well, please pass the --all flag. This will be the default " \ - "on Bundler 2.0." + "on Bundler 3.0." end end end diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb index 9d40ee9dfd..9ea52baa6b 100644 --- a/lib/bundler/cli/common.rb +++ b/lib/bundler/cli/common.rb @@ -23,7 +23,7 @@ module Bundler groups = Bundler.settings[:without] group_list = [groups[0...-1].join(", "), groups[-1..-1]]. reject {|s| s.to_s.empty? }.join(" and ") - group_str = (groups.size == 1) ? "group" : "groups" + group_str = groups.size == 1 ? "group" : "groups" "Gems in the #{group_str} #{group_list} were not installed." end @@ -49,10 +49,6 @@ module Bundler end def self.ask_for_spec_from(specs) - if !$stdout.tty? && ENV["BUNDLE_SPEC_RUN"].nil? - raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies) - end - specs.each_with_index do |spec, index| Bundler.ui.info "#{index.succ} : #{spec.name}", true end @@ -72,7 +68,7 @@ module Bundler end def self.ensure_all_gems_in_lockfile!(names, locked_gems = Bundler.locked_gems) - locked_names = locked_gems.specs.map(&:name) + locked_names = locked_gems.specs.map(&:name).uniq names.-(locked_names).each do |g| raise GemNotFound, gem_not_found_message(g, locked_names) end @@ -80,10 +76,12 @@ module Bundler def self.configure_gem_version_promoter(definition, options) patch_level = patch_level_options(options) + patch_level << :patch if patch_level.empty? && Bundler.settings[:prefer_patch] raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1 + definition.gem_version_promoter.tap do |gvp| gvp.level = patch_level.first || :major - gvp.strict = options[:strict] || options["update-strict"] + gvp.strict = options[:strict] || options["update-strict"] || options["filter-strict"] end end diff --git a/lib/bundler/cli/config.rb b/lib/bundler/cli/config.rb index 12f71ea8fe..1df2a55d9a 100644 --- a/lib/bundler/cli/config.rb +++ b/lib/bundler/cli/config.rb @@ -1,119 +1,194 @@ # frozen_string_literal: true module Bundler - class CLI::Config - attr_reader :name, :options, :scope, :thor - attr_accessor :args - - def initialize(options, args, thor) - @options = options - @args = args - @thor = thor - @name = peek = args.shift - @scope = "global" - return unless peek && peek.start_with?("--") - @name = args.shift - @scope = peek[2..-1] + class CLI::Config < Thor + class_option :parseable, :type => :boolean, :banner => "Use minimal formatting for more parseable output" + + def self.scope_options + method_option :global, :type => :boolean, :banner => "Only change the global config" + method_option :local, :type => :boolean, :banner => "Only change the local config" end + private_class_method :scope_options - def run - unless name - confirm_all - return - end + desc "base NAME [VALUE]", "The Bundler 1 config interface", :hide => true + scope_options + method_option :delete, :type => :boolean, :banner => "delete" + def base(name = nil, *value) + new_args = + if ARGV.size == 1 + ["config", "list"] + elsif ARGV.include?("--delete") + ARGV.map {|arg| arg == "--delete" ? "unset" : arg } + elsif ARGV.include?("--global") || ARGV.include?("--local") || ARGV.size == 3 + ["config", "set", *ARGV[1..-1]] + else + ["config", "get", ARGV[1]] + end - unless valid_scope?(scope) - Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global." - exit 1 - end + SharedHelpers.major_deprecation 2, + "Using the `config` command without a subcommand [list, get, set, unset] is deprecated and will be removed in the future. Use `bundle #{new_args.join(" ")}` instead." + + Base.new(options, name, value, self).run + end + + desc "list", "List out all configured settings" + def list + Base.new(options, nil, nil, self).run + end + + desc "get NAME", "Returns the value for the given key" + def get(name) + Base.new(options, name, nil, self).run + end + + desc "set NAME VALUE", "Sets the given value for the given key" + scope_options + def set(name, value, *value_) + Base.new(options, name, value_.unshift(value), self).run + end - if scope == "delete" - Bundler.settings.set_local(name, nil) - Bundler.settings.set_global(name, nil) - return + desc "unset NAME", "Unsets the value for the given key" + scope_options + def unset(name) + options[:delete] = true + Base.new(options, name, nil, self).run + end + + default_task :base + + class Base + attr_reader :name, :value, :options, :scope, :thor + + def initialize(options, name, value, thor) + @options = options + @name = name + value = Array(value) + @value = value.empty? ? nil : value.join(" ") + @thor = thor + validate_scope! end - if args.empty? - if options[:parseable] - if value = Bundler.settings[name] - Bundler.ui.info("#{name}=#{value}") + def run + unless name + warn_unused_scope "Ignoring --#{scope}" + confirm_all + return + end + + if options[:delete] + if !explicit_scope? || scope != "global" + Bundler.settings.set_local(name, nil) + end + if !explicit_scope? || scope != "local" + Bundler.settings.set_global(name, nil) end return end - confirm(name) - return - end + if value.nil? + warn_unused_scope "Ignoring --#{scope} since no value to set was given" - Bundler.ui.info(message) if message - Bundler.settings.send("set_#{scope}", name, new_value) - end + if options[:parseable] + if value = Bundler.settings[name] + Bundler.ui.info("#{name}=#{value}") + end + return + end - private + confirm(name) + return + end - def confirm_all - if @options[:parseable] - thor.with_padding do + Bundler.ui.info(message) if message + Bundler.settings.send("set_#{scope}", name, new_value) + end + + def confirm_all + if @options[:parseable] + thor.with_padding do + Bundler.settings.all.each do |setting| + val = Bundler.settings[setting] + Bundler.ui.info "#{setting}=#{val}" + end + end + else + Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n" Bundler.settings.all.each do |setting| - val = Bundler.settings[setting] - Bundler.ui.info "#{setting}=#{val}" + Bundler.ui.confirm "#{setting}" + show_pretty_values_for(setting) + Bundler.ui.confirm "" end end - else - Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n" - Bundler.settings.all.each do |setting| - Bundler.ui.confirm "#{setting}" - show_pretty_values_for(setting) - Bundler.ui.confirm "" - end end - end - def confirm(name) - Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used" - show_pretty_values_for(name) - end + def confirm(name) + Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used" + show_pretty_values_for(name) + end - def new_value - pathname = Pathname.new(args.join(" ")) - if name.start_with?("local.") && pathname.directory? - pathname.expand_path.to_s - else - args.join(" ") + def new_value + pathname = Pathname.new(value) + if name.start_with?("local.") && pathname.directory? + pathname.expand_path.to_s + else + value + end end - end - def message - locations = Bundler.settings.locations(name) - if @options[:parseable] - "#{name}=#{new_value}" if new_value - elsif scope == "global" - if locations[:local] - "Your application has set #{name} to #{locations[:local].inspect}. " \ - "This will override the global value you are currently setting" - elsif locations[:env] - "You have a bundler environment variable for #{name} set to " \ - "#{locations[:env].inspect}. This will take precedence over the global value you are setting" - elsif locations[:global] && locations[:global] != args.join(" ") - "You are replacing the current global value of #{name}, which is currently " \ - "#{locations[:global].inspect}" + def message + locations = Bundler.settings.locations(name) + if @options[:parseable] + "#{name}=#{new_value}" if new_value + elsif scope == "global" + if !locations[:local].nil? + "Your application has set #{name} to #{locations[:local].inspect}. " \ + "This will override the global value you are currently setting" + elsif locations[:env] + "You have a bundler environment variable for #{name} set to " \ + "#{locations[:env].inspect}. This will take precedence over the global value you are setting" + elsif !locations[:global].nil? && locations[:global] != value + "You are replacing the current global value of #{name}, which is currently " \ + "#{locations[:global].inspect}" + end + elsif scope == "local" && !locations[:local].nil? && locations[:local] != value + "You are replacing the current local value of #{name}, which is currently " \ + "#{locations[:local].inspect}" end - elsif scope == "local" && locations[:local] != args.join(" ") - "You are replacing the current local value of #{name}, which is currently " \ - "#{locations[:local].inspect}" end - end - def show_pretty_values_for(setting) - thor.with_padding do - Bundler.settings.pretty_values_for(setting).each do |line| - Bundler.ui.info line + def show_pretty_values_for(setting) + thor.with_padding do + Bundler.settings.pretty_values_for(setting).each do |line| + Bundler.ui.info line + end end end - end - def valid_scope?(scope) - %w[delete local global].include?(scope) + def explicit_scope? + @explicit_scope + end + + def warn_unused_scope(msg) + return unless explicit_scope? + return if options[:parseable] + + Bundler.ui.warn(msg) + end + + def validate_scope! + @explicit_scope = true + scopes = %w[global local].select {|s| options[s] } + case scopes.size + when 0 + @scope = "global" + @explicit_scope = false + when 1 + @scope = scopes.first + else + raise InvalidOption, + "The options #{scopes.join " and "} were specified. Please only use one of the switches at a time." + end + end end end end diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb index d45f30cdcf..6e0dfe28af 100644 --- a/lib/bundler/cli/console.rb +++ b/lib/bundler/cli/console.rb @@ -9,10 +9,10 @@ module Bundler end def run - Bundler::SharedHelpers.major_deprecation 3, "bundle console will be replaced " \ + Bundler::SharedHelpers.major_deprecation 2, "bundle console will be replaced " \ "by `bin/console` generated by `bundle gem <name>`" - group ? Bundler.require(:default, *(group.split.map!(&:to_sym))) : Bundler.require + group ? Bundler.require(:default, *group.split.map!(&:to_sym)) : Bundler.require ARGV.clear console = get_console(Bundler.settings[:console] || "irb") diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb index 3e0898ff8a..6d038937c0 100644 --- a/lib/bundler/cli/doctor.rb +++ b/lib/bundler/cli/doctor.rb @@ -4,8 +4,8 @@ require "rbconfig" module Bundler class CLI::Doctor - DARWIN_REGEX = /\s+(.+) \(compatibility / - LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/ + DARWIN_REGEX = /\s+(.+) \(compatibility /.freeze + LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/.freeze attr_reader :options diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb index 58e2f8a3fd..3db1ec7843 100644 --- a/lib/bundler/cli/gem.rb +++ b/lib/bundler/cli/gem.rb @@ -11,7 +11,7 @@ module Bundler class CLI::Gem TEST_FRAMEWORK_VERSIONS = { "rspec" => "3.0", - "minitest" => "5.0" + "minitest" => "5.0", }.freeze attr_reader :options, :gem_name, :thor, :name, :target @@ -57,7 +57,7 @@ module Bundler :ext => options[:ext], :exe => options[:exe], :bundler_version => bundler_dependency_version, - :github_username => github_username.empty? ? "[USERNAME]" : github_username + :github_username => github_username.empty? ? "[USERNAME]" : github_username, } ensure_safe_gem_name(name, constant_array) @@ -69,7 +69,7 @@ module Bundler "Rakefile.tt" => "Rakefile", "README.md.tt" => "README.md", "bin/console.tt" => "bin/console", - "bin/setup.tt" => "bin/setup" + "bin/setup.tt" => "bin/setup", } executables = %w[ diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb index 958b525067..4733675e8c 100644 --- a/lib/bundler/cli/info.rb +++ b/lib/bundler/cli/info.rb @@ -9,18 +9,24 @@ module Bundler end def run + Bundler.ui.silence do + Bundler.definition.validate_runtime! + Bundler.load.lock + end + spec = spec_for_gem(gem_name) - spec_not_found(gem_name) unless spec - return print_gem_path(spec) if @options[:path] - print_gem_info(spec) + if spec + return print_gem_path(spec) if @options[:path] + print_gem_info(spec) + end end private def spec_for_gem(gem_name) spec = Bundler.definition.specs.find {|s| s.name == gem_name } - spec || default_gem_spec(gem_name) + spec || default_gem_spec(gem_name) || Bundler::CLI::Common.select_spec(gem_name, :regex_match) end def default_gem_spec(gem_name) @@ -34,7 +40,13 @@ module Bundler end def print_gem_path(spec) - Bundler.ui.info spec.full_gem_path + path = if spec.name == "bundler" + File.expand_path("../../../..", __FILE__) + else + spec.full_gem_path + end + + Bundler.ui.info path end def print_gem_info(spec) diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb index 40df797269..65dd08dfe9 100644 --- a/lib/bundler/cli/init.rb +++ b/lib/bundler/cli/init.rb @@ -41,7 +41,7 @@ module Bundler private def gemfile - @gemfile ||= Bundler.feature_flag.init_gems_rb? ? "gems.rb" : "Gemfile" + @gemfile ||= Bundler.settings[:init_gems_rb] ? "gems.rb" : "Gemfile" end end end diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 55e90ead0e..03edc7fbd2 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -53,7 +53,7 @@ module Bundler Bundler::Fetcher.disable_endpoint = options["full-index"] if options["binstubs"] - Bundler::SharedHelpers.major_deprecation 3, + Bundler::SharedHelpers.major_deprecation 2, "The --binstubs option will be removed in favor of `bundle binstubs`" end @@ -202,15 +202,16 @@ module Bundler end def warn_ambiguous_gems + # TODO: remove this when we drop Bundler 1.x support Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris| - Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources." - Bundler.ui.error "Installed from: #{installed_from_uri}" - Bundler.ui.error "Also found in:" - also_found_in_uris.each {|uri| Bundler.ui.error " * #{uri}" } - Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source." - Bundler.ui.error "For example:" - Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'" - Bundler.ui.error "Then uninstall the gem '#{name}' (or delete all bundled gems) and then install again." + Bundler.ui.warn "Warning: the gem '#{name}' was found in multiple sources." + Bundler.ui.warn "Installed from: #{installed_from_uri}" + Bundler.ui.warn "Also found in:" + also_found_in_uris.each {|uri| Bundler.ui.warn " * #{uri}" } + Bundler.ui.warn "You should add a source requirement to restrict this gem to your preferred source." + Bundler.ui.warn "For example:" + Bundler.ui.warn " gem '#{name}', :source => '#{installed_from_uri}'" + Bundler.ui.warn "Then uninstall the gem '#{name}' (or delete all bundled gems) and then install again." end end end diff --git a/lib/bundler/cli/issue.rb b/lib/bundler/cli/issue.rb index 91f827ea99..bba1619340 100644 --- a/lib/bundler/cli/issue.rb +++ b/lib/bundler/cli/issue.rb @@ -13,10 +13,10 @@ module Bundler https://github.com/bundler/bundler/blob/master/doc/TROUBLESHOOTING.md 2. Instructions for common Bundler uses can be found on the documentation - site: http://bundler.io/ + site: https://bundler.io/ 3. Information about each Bundler command can be found in the Bundler - man pages: http://bundler.io/man/bundle.1.html + man pages: https://bundler.io/man/bundle.1.html Hopefully the troubleshooting steps above resolved your problem! If things still aren't working the way you expect them to, please let us know so diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb index 2ca90293db..3a0521de1e 100644 --- a/lib/bundler/cli/outdated.rb +++ b/lib/bundler/cli/outdated.rb @@ -41,7 +41,7 @@ module Bundler # the patch level options imply strict is also true. It wouldn't make # sense otherwise. - strict = options[:strict] || + strict = options["filter-strict"] || Bundler::CLI::Common.patch_level_options(options).any? filter_options_patch = options.keys & @@ -124,7 +124,7 @@ module Bundler [nil, ordered_groups].flatten.each do |groups| gems = outdated_gems_by_groups[groups] contains_group = if groups - groups.split(",").include?(options[:group]) + groups.split(", ").include?(options[:group]) else options[:group] == "group" end @@ -221,9 +221,9 @@ module Bundler def check_for_deployment_mode return unless Bundler.frozen_bundle? suggested_command = if Bundler.settings.locations("frozen")[:global] - "bundle config --delete frozen" + "bundle config unset frozen" elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any? - "bundle config --delete deployment" + "bundle config unset deployment" else "bundle install --no-deployment" end diff --git a/lib/bundler/cli/package.rb b/lib/bundler/cli/package.rb index 2dcd0e1e29..cd01ce422e 100644 --- a/lib/bundler/cli/package.rb +++ b/lib/bundler/cli/package.rb @@ -42,7 +42,7 @@ module Bundler if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all? Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \ "to package them as well, please pass the --all flag. This will be the default " \ - "on Bundler 2.0." + "on Bundler 3.0." end end end diff --git a/lib/bundler/cli/plugin.rb b/lib/bundler/cli/plugin.rb index 5488a9f28d..b5dd5b6d4b 100644 --- a/lib/bundler/cli/plugin.rb +++ b/lib/bundler/cli/plugin.rb @@ -5,7 +5,7 @@ module Bundler class CLI::Plugin < Thor desc "install PLUGINS", "Install the plugin from the source" long_desc <<-D - Install plugins either from the rubygems source provided (with --source option) or from a git source provided with (--git option). If no sources are provided, it uses Gem.sources + Install plugins either from the rubygems source provided (with --source option) or from a git source provided with --git (for remote repos) or --local_git (for local repos). If no sources are provided, it uses Gem.sources D method_option "source", :type => :string, :default => nil, :banner => "URL of the RubyGems source to fetch the plugin from" @@ -13,6 +13,8 @@ module Bundler "The version of the plugin to fetch" method_option "git", :type => :string, :default => nil, :banner => "URL of the git repo to fetch from" + method_option "local_git", :type => :string, :default => nil, :banner => + "Path of the local git repo to fetch from" method_option "branch", :type => :string, :default => nil, :banner => "The git branch to checkout" method_option "ref", :type => :string, :default => nil, :banner => @@ -20,5 +22,10 @@ module Bundler def install(*plugins) Bundler::Plugin.install(plugins, options) end + + desc "list", "List the installed plugins and available commands" + def list + Bundler::Plugin.list + end end end diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb index 4a411a83fc..532b3e0b5b 100644 --- a/lib/bundler/cli/pristine.rb +++ b/lib/bundler/cli/pristine.rb @@ -33,7 +33,7 @@ module Bundler if extension_cache_path = source.extension_cache_path(spec) FileUtils.rm_rf extension_cache_path end - FileUtils.rm_rf spec.extension_dir if spec.respond_to?(:extension_dir) + FileUtils.rm_rf spec.extension_dir FileUtils.rm_rf spec.full_gem_path else Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb index 61756801b2..3748c25b89 100644 --- a/lib/bundler/cli/show.rb +++ b/lib/bundler/cli/show.rb @@ -24,7 +24,7 @@ module Bundler return unless spec path = spec.full_gem_path unless File.directory?(path) - Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:" + return Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at: #{path}" end end return Bundler.ui.info(path) diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb index bf300a8437..b088853768 100644 --- a/lib/bundler/cli/update.rb +++ b/lib/bundler/cli/update.rb @@ -22,7 +22,7 @@ module Bundler if Bundler.feature_flag.update_requires_all_flag? raise InvalidOption, "To update everything, pass the `--all` flag." end - SharedHelpers.major_deprecation 3, "Pass --all to `bundle update` to update everything" + SharedHelpers.major_deprecation 2, "Pass --all to `bundle update` to update everything" elsif !full_update && options[:all] raise InvalidOption, "Cannot specify --all along with specific options." end diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb index 6c241ca07a..2f713041c8 100644 --- a/lib/bundler/compact_index_client.rb +++ b/lib/bundler/compact_index_client.rb @@ -18,11 +18,6 @@ module Bundler attr_reader :directory - # @return [Lambda] A lambda that takes an array of inputs and a block, and - # maps the inputs with the block in parallel. - # - attr_accessor :in_parallel - def initialize(directory, fetcher) @directory = Pathname.new(directory) @updater = Updater.new(fetcher) @@ -31,7 +26,28 @@ module Bundler @info_checksums_by_name = {} @parsed_checksums = false @mutex = Mutex.new - @in_parallel = lambda do |inputs, &blk| + end + + def execution_mode=(block) + Bundler::CompactIndexClient.debug { "execution_mode=" } + @endpoints = Set.new + + @execution_mode = block + end + + # @return [Lambda] A lambda that takes an array of inputs and a block, and + # maps the inputs with the block in parallel. + # + def execution_mode + @execution_mode || sequentially + end + + def sequential_execution_mode! + self.execution_mode = sequentially + end + + def sequentially + @sequentially ||= lambda do |inputs, &blk| inputs.map(&blk) end end @@ -51,7 +67,7 @@ module Bundler def dependencies(names) Bundler::CompactIndexClient.debug { "dependencies(#{names})" } - in_parallel.call(names) do |name| + execution_mode.call(names) do |name| update_info(name) @cache.dependencies(name).map {|d| d.unshift(name) } end.flatten(1) diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb index 4d6eb80044..d77285072c 100644 --- a/lib/bundler/compact_index_client/updater.rb +++ b/lib/bundler/compact_index_client/updater.rb @@ -95,11 +95,7 @@ module Bundler end def slice_body(body, range) - if body.respond_to?(:byteslice) - body.byteslice(range) - else # pre-1.9.3 - body.unpack("@#{range.first}a#{range.end + 1}").first - end + body.byteslice(range) end def checksum_for_file(path) diff --git a/lib/bundler/compatibility_guard.rb b/lib/bundler/compatibility_guard.rb index 750a1db04f..958116ce55 100644 --- a/lib/bundler/compatibility_guard.rb +++ b/lib/bundler/compatibility_guard.rb @@ -1,14 +1,9 @@ # frozen_string_literal: false -require "rubygems" require "bundler/version" if Bundler::VERSION.split(".").first.to_i >= 2 if Gem::Version.new(Object::RUBY_VERSION.dup) < Gem::Version.new("2.3") abort "Bundler 2 requires Ruby 2.3 or later. Either install bundler 1 or update to a supported Ruby version." end - - if Gem::Version.new(Gem::VERSION.dup) < Gem::Version.new("2.5") - abort "Bundler 2 requires RubyGems 2.5 or later. Either install bundler 1 or update to a supported RubyGems version." - end end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index c5e94c7123..f9daae067c 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -331,7 +331,7 @@ module Bundler # i.e., Windows with `git config core.autocrlf=true` contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n") - if @locked_bundler_version && Bundler.feature_flag.lockfile_upgrade_warning? + if @locked_bundler_version locked_major = @locked_bundler_version.segments.first current_major = Gem::Version.create(Bundler::VERSION).segments.first @@ -397,9 +397,9 @@ module Bundler unless explicit_flag suggested_command = if Bundler.settings.locations("frozen")[:global] - "bundle config --delete frozen" + "bundle config unset frozen" elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any? - "bundle config --delete deployment" + "bundle config unset deployment" else "bundle install --no-deployment" end @@ -643,7 +643,7 @@ module Bundler end def converge_rubygems_sources - return false if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + return false if Bundler.feature_flag.disable_multisource? changes = false @@ -855,8 +855,8 @@ module Bundler concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby] end [ - Dependency.new("ruby\0", ruby_versions), - Dependency.new("rubygems\0", Gem::VERSION), + Dependency.new("Ruby\0", ruby_versions), + Dependency.new("RubyGems\0", Gem::VERSION), ] end end @@ -915,7 +915,7 @@ module Bundler # look for that gemspec (or its dependencies) default = sources.default_source source_requirements = { :default => default } - default = nil unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + default = nil unless Bundler.feature_flag.disable_multisource? dependencies.each do |dep| next unless source = dep.source || default source_requirements[dep.name] = source @@ -929,7 +929,7 @@ module Bundler def pinned_spec_names(skip = nil) pinned_names = [] - default = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? && sources.default_source + default = Bundler.feature_flag.disable_multisource? && sources.default_source @dependencies.each do |dep| next unless dep_source = dep.source || default next if dep_source == skip @@ -977,7 +977,9 @@ module Bundler dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) } @locked_gems.specs.reduce({}) do |requirements, locked_spec| name = locked_spec.name - next requirements if @locked_gems.dependencies[name] != dependencies_by_name[name] + dependency = dependencies_by_name[name] + next requirements if @locked_gems.dependencies[name] != dependency + next requirements if dependency && dependency.source.is_a?(Source::Path) dep = Gem::Dependency.new(name, ">= #{locked_spec.version}") requirements[name] = DepProxy.new(dep, locked_spec.platform) requirements diff --git a/lib/bundler/deployment.rb b/lib/bundler/deployment.rb index 4c8f48d405..291e158ca0 100644 --- a/lib/bundler/deployment.rb +++ b/lib/bundler/deployment.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 3, "Bundler no longer integrates with " \ +Bundler::SharedHelpers.major_deprecation 2, "Bundler no longer integrates with " \ "Capistrano, but Capistrano provides its own integration with " \ "Bundler via the capistrano-bundler gem. Use it instead." diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 90ac073c36..7424a5c8a4 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -45,7 +45,7 @@ module Bundler @gemfiles << expanded_gemfile_path contents ||= Bundler.read_file(@gemfile.to_s) instance_eval(contents.dup.untaint, gemfile.to_s, 1) - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException message = "There was an error " \ "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \ "`#{File.basename gemfile.to_s}`: #{e.message}" @@ -194,7 +194,7 @@ module Bundler " end\n\n" raise DeprecatedError, msg if Bundler.feature_flag.disable_multisource? - SharedHelpers.major_deprecation(3, msg.strip) + SharedHelpers.major_deprecation(2, msg.strip) end source_options = normalize_hash(options).merge( @@ -290,37 +290,21 @@ module Bundler warn_deprecated_git_source(:github, <<-'RUBY'.strip, 'Change any "reponame" :github sources to "username/reponame".') "https://github.com/#{repo_name}.git" RUBY - # It would be better to use https instead of the git protocol, but this - # can break deployment of existing locked bundles when switching between - # different versions of Bundler. The change will be made in 2.0, which - # does not guarantee compatibility with the 1.x series. - # - # See https://github.com/bundler/bundler/pull/2569 for discussion - # - # This can be overridden by adding this code to your Gemfiles: - # - # git_source(:github) do |repo_name| - # repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - # "https://github.com/#{repo_name}.git" - # end repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - # TODO: 2.0 upgrade this setting to the default - if Bundler.settings["github.https"] - Bundler::SharedHelpers.major_deprecation 3, "The `github.https` setting will be removed" + if Bundler.feature_flag.github_https? "https://github.com/#{repo_name}.git" else + Bundler::SharedHelpers.major_deprecation 2, "Setting `github.https` to false is deprecated and won't be supported in the future." "git://github.com/#{repo_name}.git" end end - # TODO: 2.0 remove this deprecated git source git_source(:gist) do |repo_name| warn_deprecated_git_source(:gist, '"https://gist.github.com/#{repo_name}.git"') "https://gist.github.com/#{repo_name}.git" end - # TODO: 2.0 remove this deprecated git source git_source(:bitbucket) do |repo_name| warn_deprecated_git_source(:bitbucket, <<-'RUBY'.strip) user_name, repo_name = repo_name.split("/") @@ -363,9 +347,7 @@ repo_name ||= user_name if name =~ /\s/ raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace) end - if name.empty? - raise GemfileError, %(an empty gem name is not valid) - end + raise GemfileError, %(an empty gem name is not valid) if name.empty? normalize_hash(opts) @@ -443,10 +425,10 @@ repo_name ||= user_name message = String.new message << "You passed #{invalid_keys.map {|k| ":" + k }.join(", ")} " message << if invalid_keys.size > 1 - "as options for #{command}, but they are invalid." - else - "as an option for #{command}, but it is invalid." - end + "as options for #{command}, but they are invalid." + else + "as an option for #{command}, but it is invalid." + end message << " Valid options are: #{valid_keys.join(", ")}." message << " You may be able to resolve this by upgrading Bundler to the newest version." @@ -456,7 +438,7 @@ repo_name ||= user_name def normalize_source(source) case source when :gemcutter, :rubygems, :rubyforge - Bundler::SharedHelpers.major_deprecation 3, "The source :#{source} is deprecated because HTTP " \ + Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \ "requests are insecure.\nPlease change your source to 'https://" \ "rubygems.org' if possible, or 'http://rubygems.org' if not." "http://rubygems.org" @@ -474,23 +456,22 @@ repo_name ||= user_name msg = "This Gemfile contains multiple primary sources. " \ "Each source after the first must include a block to indicate which gems " \ "should come from that source" - unless Bundler.feature_flag.bundler_3_mode? + unless Bundler.feature_flag.bundler_2_mode? msg += ". To downgrade this error to a warning, run " \ - "`bundle config --delete disable_multisource`" + "`bundle config unset disable_multisource`" end raise GemfileEvalError, msg else - Bundler::SharedHelpers.major_deprecation 3, "Your Gemfile contains multiple primary sources. " \ + Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \ "Using `source` more than once without a block is a security risk, and " \ "may result in installing unexpected gems. To resolve this warning, use " \ "a block to indicate which gems should come from the secondary source. " \ - "To upgrade this warning to an error, run `bundle config " \ + "To upgrade this warning to an error, run `bundle config set " \ "disable_multisource true`." end end def warn_deprecated_git_source(name, replacement, additional_message = nil) - # TODO: 2.0 remove deprecation additional_message &&= " #{additional_message}" replacement = if replacement.count("\n").zero? "{|repo_name| #{replacement} }" @@ -499,7 +480,7 @@ repo_name ||= user_name end Bundler::SharedHelpers.major_deprecation 3, <<-EOS -The :#{name} git source is deprecated, and will be removed in Bundler 3.0.#{additional_message} Add this code to the top of your Gemfile to ensure it continues to work: +The :#{name} git source is deprecated, and will be removed in the future.#{additional_message} Add this code to the top of your Gemfile to ensure it continues to work: git_source(:#{name}) #{replacement} @@ -601,7 +582,7 @@ The :#{name} git source is deprecated, and will be removed in Bundler 3.0.#{addi description = self.description if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/ trace_line = Regexp.last_match[1] - description = description.sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ") + description = description.sub(/\n.*\n(\.\.\.)? *\^~+$/, "").sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ") end [trace_line, description] end diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb index 51738139fa..9cd9b8baca 100644 --- a/lib/bundler/env.rb +++ b/lib/bundler/env.rb @@ -70,14 +70,8 @@ module Bundler def self.ruby_version str = String.new("#{RUBY_VERSION}") - if RUBY_VERSION < "1.9" - str << " (#{RUBY_RELEASE_DATE}" - str << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL - str << ") [#{RUBY_PLATFORM}]" - else - str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL - str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{RUBY_PLATFORM}]" - end + str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL + str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{RUBY_PLATFORM}]" end def self.git_version diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb index e5b4e84063..982f0fa540 100644 --- a/lib/bundler/feature_flag.rb +++ b/lib/bundler/feature_flag.rb @@ -19,7 +19,7 @@ module Bundler def self.settings_method(name, key, &default) define_method(name) do value = Bundler.settings[key] - value = instance_eval(&default) if value.nil? && !default.nil? + value = instance_eval(&default) if value.nil? value end end @@ -33,7 +33,6 @@ module Bundler settings_flag(:auto_config_jobs) { bundler_3_mode? } settings_flag(:cache_all) { bundler_3_mode? } settings_flag(:cache_command_is_package) { bundler_3_mode? } - settings_flag(:console_command) { !bundler_3_mode? } settings_flag(:default_install_uses_path) { bundler_3_mode? } settings_flag(:deployment_means_frozen) { bundler_3_mode? } settings_flag(:disable_multisource) { bundler_3_mode? } @@ -41,23 +40,17 @@ module Bundler settings_flag(:forget_cli_options) { bundler_3_mode? } settings_flag(:global_path_appends_ruby_scope) { bundler_3_mode? } settings_flag(:global_gem_cache) { bundler_3_mode? } - settings_flag(:init_gems_rb) { bundler_3_mode? } - settings_flag(:list_command) { bundler_3_mode? } - settings_flag(:lockfile_uses_separate_rubygems_sources) { bundler_3_mode? } - settings_flag(:lockfile_upgrade_warning) { bundler_3_mode? } settings_flag(:only_update_to_newer_versions) { bundler_3_mode? } settings_flag(:path_relative_to_cwd) { bundler_3_mode? } settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") } - settings_flag(:prefer_gems_rb) { bundler_3_mode? } settings_flag(:print_only_version_number) { bundler_3_mode? } settings_flag(:setup_makes_kernel_gem_public) { !bundler_3_mode? } - settings_flag(:skip_default_git_sources) { bundler_3_mode? } + settings_flag(:skip_default_git_sources) { bundler_4_mode? } settings_flag(:specific_platform) { bundler_3_mode? } settings_flag(:suppress_install_using_messages) { bundler_3_mode? } settings_flag(:unlock_source_unlocks_spec) { !bundler_3_mode? } settings_flag(:update_requires_all_flag) { bundler_3_mode? } settings_flag(:use_gem_version_promoter_for_major_updates) { bundler_3_mode? } - settings_flag(:viz_command) { !bundler_3_mode? } settings_option(:default_cli_command) { bundler_3_mode? ? :cli_help : :install } diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb index 4dd42e42ff..8397f7b72b 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -4,6 +4,7 @@ require "bundler/vendored_persistent" require "cgi" require "securerandom" require "zlib" +require "rubygems/request" module Bundler # Handles all the fetching with the rubygems server @@ -15,6 +16,8 @@ module Bundler # This error is raised when it looks like the network is down class NetworkDownError < HTTPError; end + # This error is raised if we should rate limit our requests to the API + class TooManyRequestsError < HTTPError; end # This error is raised if the API returns a 413 (only printed in verbose) class FallbackError < HTTPError; end # This is the error raised if OpenSSL fails the cert verification @@ -44,7 +47,7 @@ module Bundler remote_uri = filter_uri(remote_uri) super "Authentication is required for #{remote_uri}.\n" \ "Please supply credentials for this source. You can do this by running:\n" \ - " bundle config #{remote_uri} username:password" + " bundle config set #{remote_uri} username:password" end end # This error is raised if HTTP authentication is provided, but incorrect. @@ -226,7 +229,7 @@ module Bundler "GO_SERVER_URL" => "go", "SNAP_CI" => "snap", "CI_NAME" => ENV["CI_NAME"], - "CI" => "ci" + "CI" => "ci", } env_cis.find_all {|env, _| ENV[env] }.map {|_, ci| ci } end @@ -293,8 +296,7 @@ module Bundler end else store.set_default_paths - certs = File.expand_path("../ssl_certs/*/*.pem", __FILE__) - Dir.glob(certs).each {|c| store.add_file c } + Gem::Request.get_cert_files.each {|c| store.add_file c } end store end diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb index cfc74d642c..a117af72fa 100644 --- a/lib/bundler/fetcher/compact_index.rb +++ b/lib/bundler/fetcher/compact_index.rb @@ -39,7 +39,13 @@ module Bundler until remaining_gems.empty? log_specs "Looking up gems #{remaining_gems.inspect}" - deps = compact_index_client.dependencies(remaining_gems) + deps = begin + parallel_compact_index_client.dependencies(remaining_gems) + rescue TooManyRequestsError + @bundle_worker.stop if @bundle_worker + @bundle_worker = nil # reset it. Not sure if necessary + serial_compact_index_client.dependencies(remaining_gems) + end next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq deps.each {|dep| gem_info << dep } complete_gems.concat(deps.map(&:first)).uniq! @@ -80,18 +86,26 @@ module Bundler private def compact_index_client - @compact_index_client ||= begin + @compact_index_client ||= SharedHelpers.filesystem_access(cache_path) do CompactIndexClient.new(cache_path, client_fetcher) - end.tap do |client| - client.in_parallel = lambda do |inputs, &blk| - func = lambda {|object, _index| blk.call(object) } - worker = bundle_worker(func) - inputs.each {|input| worker.enq(input) } - inputs.map { worker.deq } - end end + end + + def parallel_compact_index_client + compact_index_client.execution_mode = lambda do |inputs, &blk| + func = lambda {|object, _index| blk.call(object) } + worker = bundle_worker(func) + inputs.each {|input| worker.enq(input) } + inputs.map { worker.deq } end + + compact_index_client + end + + def serial_compact_index_client + compact_index_client.sequential_execution_mode! + compact_index_client end def bundle_worker(func = nil) diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb index e0e0cbf1c9..2aeb9962c4 100644 --- a/lib/bundler/fetcher/downloader.rb +++ b/lib/bundler/fetcher/downloader.rb @@ -34,10 +34,12 @@ module Bundler fetch(uri, new_headers) when Net::HTTPRequestEntityTooLarge raise FallbackError, response.body + when Net::HTTPTooManyRequests + raise TooManyRequestsError, response.body when Net::HTTPUnauthorized raise AuthenticationRequiredError, uri.host when Net::HTTPNotFound - raise FallbackError, "Net::HTTPNotFound" + raise FallbackError, "Net::HTTPNotFound: #{URICredentialsFilter.credential_filtered_uri(uri)}" else raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}" end diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb index ae3299a7c8..dd9b847f10 100644 --- a/lib/bundler/friendly_errors.rb +++ b/lib/bundler/friendly_errors.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 # frozen_string_literal: true require "cgi" @@ -45,7 +44,7 @@ module Bundler "Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)." else request_issue_report_for(error) end - rescue + rescue StandardError raise error end @@ -124,7 +123,7 @@ module Bundler yield rescue SignalException raise - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException FriendlyErrors.log_error(e) exit FriendlyErrors.exit_status(e) end diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb index 55f484f723..e9ee03b8a2 100644 --- a/lib/bundler/gem_helper.rb +++ b/lib/bundler/gem_helper.rb @@ -2,6 +2,7 @@ require "bundler/vendored_thor" unless defined?(Thor) require "bundler" +require "shellwords" module Bundler class GemHelper @@ -75,7 +76,7 @@ module Bundler def build_gem file_name = nil gem = ENV["BUNDLE_GEM"] ? ENV["BUNDLE_GEM"] : "gem" - sh("#{gem} build -V '#{spec_path}'") do + sh(%W[#{gem} build -V #{spec_path}]) do file_name = File.basename(built_gem_path) SharedHelpers.filesystem_access(File.join(base, "pkg")) {|p| FileUtils.mkdir_p(p) } FileUtils.mv(built_gem_path, "pkg") @@ -87,17 +88,21 @@ module Bundler def install_gem(built_gem_path = nil, local = false) built_gem_path ||= build_gem gem = ENV["BUNDLE_GEM"] ? ENV["BUNDLE_GEM"] : "gem" - out, _ = sh_with_code("#{gem} install '#{built_gem_path}'#{" --local" if local}") - raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/] + cmd = %W[#{gem} install #{built_gem_path}] + cmd << "--local" if local + out, status = sh_with_status(cmd) + unless status.success? && out[/Successfully installed/] + raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" + end Bundler.ui.confirm "#{name} (#{version}) installed." end protected def rubygem_push(path) - gem_command = "gem push '#{path}'" - gem_command += " --key #{gem_key}" if gem_key - gem_command += " --host #{allowed_push_host}" if allowed_push_host + gem_command = %W[gem push #{path}] + gem_command << "--key" << gem_key if gem_key + gem_command << "--host" << allowed_push_host if allowed_push_host unless allowed_push_host || Bundler.user_home.join(".gem/credentials").file? raise "Your rubygems.org credentials aren't set. Run `gem push` to set them." end @@ -129,12 +134,14 @@ module Bundler def perform_git_push(options = "") cmd = "git push #{options}" - out, code = sh_with_code(cmd) - raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0 + out, status = sh_with_status(cmd) + return if status.success? + cmd = cmd.shelljoin if cmd.respond_to?(:shelljoin) + raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" end def already_tagged? - return false unless sh("git tag").split(/\n/).include?(version_tag) + return false unless sh(%w[git tag]).split(/\n/).include?(version_tag) Bundler.ui.confirm "Tag #{version_tag} has already been created." true end @@ -144,20 +151,20 @@ module Bundler end def clean? - sh_with_code("git diff --exit-code")[1] == 0 + sh_with_status(%w[git diff --exit-code])[1].success? end def committed? - sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0 + sh_with_status(%w[git diff-index --quiet --cached HEAD])[1].success? end def tag_version - sh "git tag -m \"Version #{version}\" #{version_tag}" + sh %W[git tag -m Version\ #{version} #{version_tag}] Bundler.ui.confirm "Tagged #{version_tag}." yield if block_given? rescue RuntimeError Bundler.ui.error "Untagging #{version_tag} due to error." - sh_with_code "git tag -d #{version_tag}" + sh_with_status %W[git tag -d #{version_tag}] raise end @@ -174,21 +181,20 @@ module Bundler end def sh(cmd, &block) - out, code = sh_with_code(cmd, &block) - unless code.zero? + out, status = sh_with_status(cmd, &block) + unless status.success? + cmd = cmd.shelljoin if cmd.respond_to?(:shelljoin) raise(out.empty? ? "Running `#{cmd}` failed. Run this command directly for more detailed output." : out) end out end - def sh_with_code(cmd, &block) - cmd += " 2>&1" - outbuf = String.new + def sh_with_status(cmd, &block) Bundler.ui.debug(cmd) SharedHelpers.chdir(base) do - outbuf = `#{cmd}` - status = $?.exitstatus - block.call(outbuf) if status.zero? && block + outbuf = IO.popen(cmd, :err => [:child, :out], &:read) + status = $? + block.call(outbuf) if status.success? && block [outbuf, status] end end diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index 019ae10c66..9d35169b99 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -10,7 +10,7 @@ module Bundler [Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")], [Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")], [Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")], - [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")] + [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")], ].freeze def generic(p) diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb index adb951a7a0..ed950ad28b 100644 --- a/lib/bundler/gem_version_promoter.rb +++ b/lib/bundler/gem_version_promoter.rb @@ -109,7 +109,7 @@ module Bundler must_match = minor? ? [0] : [0, 1] matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] } - (matches.uniq == [true]) ? (gsv >= lsv) : false + matches.uniq == [true] ? (gsv >= lsv) : false else true end diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb index de6bba0214..5644e41079 100644 --- a/lib/bundler/graph.rb +++ b/lib/bundler/graph.rb @@ -117,7 +117,7 @@ module Bundler :style => "filled", :fillcolor => "#B9B9D5", :shape => "box3d", - :fontsize => 16 + :fontsize => 16, }.merge(@node_options[group]) ) end @@ -142,7 +142,7 @@ module Bundler g.output @output_format.to_sym => "#{@output_file}.#{@output_format}" Bundler.ui.info "#{@output_file}.#{@output_format}" rescue ArgumentError => e - $stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb" + warn "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb" raise e end end diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb index 1bb29f0b36..e67469f2dd 100644 --- a/lib/bundler/injector.rb +++ b/lib/bundler/injector.rb @@ -123,7 +123,7 @@ module Bundler end end - # evalutes a gemfile to remove the specified gem + # evaluates a gemfile to remove the specified gem # from it. def remove_deps(gemfile_path) initial_gemfile = IO.readlines(gemfile_path) @@ -136,8 +136,8 @@ module Bundler removed_deps = remove_gems_from_dependencies(builder, @deps, gemfile_path) - # abort the opertion if no gems were removed - # no need to operate on gemfile furthur + # abort the operation if no gems were removed + # no need to operate on gemfile further return [] if removed_deps.empty? cleaned_gemfile = remove_gems_from_gemfile(@deps, gemfile_path) @@ -153,8 +153,8 @@ module Bundler # @param [Dsl] builder Dsl object of current Gemfile. # @param [Array] gems Array of names of gems to be removed. - # @param [Pathname] path of the Gemfile - # @return [Array] removed_deps Array of removed dependencies. + # @param [Pathname] gemfile_path Path of the Gemfile. + # @return [Array] Array of removed dependencies. def remove_gems_from_dependencies(builder, gems, gemfile_path) removed_deps = [] @@ -206,7 +206,7 @@ module Bundler nested_blocks -= 1 gemfile.each_with_index do |line, index| - next unless !line.nil? && line.include?(block_name) + next unless !line.nil? && line.strip.start_with?(block_name) if gemfile[index + 1] =~ /^\s*end\s*$/ gemfile[index] = nil gemfile[index + 1] = nil @@ -222,7 +222,7 @@ module Bundler # @param [Array] removed_deps Array of removed dependencies. # @param [Array] initial_gemfile Contents of original Gemfile before any operation. def cross_check_for_errors(gemfile_path, original_deps, removed_deps, initial_gemfile) - # evalute the new gemfile to look for any failure cases + # evaluate the new gemfile to look for any failure cases builder = Dsl.new builder.eval_gemfile(gemfile_path) diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb index 9d25f3261a..93355c9460 100644 --- a/lib/bundler/inline.rb +++ b/lib/bundler/inline.rb @@ -36,6 +36,7 @@ def gemfile(install = false, options = {}, &gemfile) opts = options.dup ui = opts.delete(:ui) { Bundler::UI::Shell.new } + ui.level = "silent" if opts.delete(:quiet) raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty? old_root = Bundler.method(:root) diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index b49cfb6703..49143b38f9 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -221,7 +221,7 @@ module Bundler def processor_count require "etc" Etc.nprocessors - rescue + rescue StandardError 1 end @@ -275,14 +275,7 @@ module Bundler end def can_install_in_parallel? - if Bundler.rubygems.provides?(">= 2.1.0") - true - else - Bundler.ui.warn "RubyGems #{Gem::VERSION} is not threadsafe, so your "\ - "gems will be installed one at a time. Upgrade to RubyGems 2.1.0 " \ - "or higher to enable parallel gem installation." - false - end + true end def install_in_parallel(size, standalone, force = false) diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb index e5e245f970..9689911d6c 100644 --- a/lib/bundler/installer/gem_installer.rb +++ b/lib/bundler/installer/gem_installer.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "shellwords" + module Bundler class GemInstaller attr_reader :spec, :standalone, :worker, :force, :installer @@ -56,7 +58,9 @@ module Bundler def spec_settings # Fetch the build settings, if there are any - Bundler.settings["build.#{spec.name}"] + if settings = Bundler.settings["build.#{spec.name}"] + Shellwords.shellsplit(settings) + end end def install diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb index f8a849ccfc..469b15b96c 100644 --- a/lib/bundler/installer/parallel_installer.rb +++ b/lib/bundler/installer/parallel_installer.rb @@ -91,10 +91,6 @@ module Bundler end def call - # Since `autoload` has the potential for threading issues on 1.8.7 - # TODO: remove in bundler 2.0 - require "bundler/gem_remote_fetcher" if RUBY_VERSION < "1.9" - check_for_corrupt_lockfile if @size > 1 @@ -115,7 +111,7 @@ module Bundler s, s.missing_lockfile_dependencies(@specs.map(&:name)), ] - end.reject { |a| a.last.empty? } + end.reject {|a| a.last.empty? } return if missing_dependencies.empty? warning = [] @@ -150,7 +146,7 @@ module Bundler end def worker_pool - @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num| + @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda {|spec_install, worker_num| do_install(spec_install, worker_num) } end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index d9cb01f810..69a721c120 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -77,7 +77,7 @@ module Bundler if search && Gem::Platform.new(search.platform) != Gem::Platform.new(platform) && !search.runtime_dependencies.-(dependencies.reject {|d| d.type == :development }).empty? Bundler.ui.warn "Unable to use the platform-specific (#{search.platform}) version of #{name} (#{version}) " \ "because it has different dependencies from the #{platform} version. " \ - "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again." + "To use the platform-specific version of the gem, run `bundle config set specific_platform true` and install again." search = source.specs.search(self).last end search.dependencies = dependencies if search && (search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification)) diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index ff706fca1d..50dc1381fe 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -23,16 +23,14 @@ module Bundler PATH = "PATH".freeze PLUGIN = "PLUGIN SOURCE".freeze SPECS = " specs:".freeze - OPTIONS = /^ ([a-z]+): (.*)$/i + OPTIONS = /^ ([a-z]+): (.*)$/i.freeze SOURCE = [GIT, GEM, PATH, PLUGIN].freeze SECTIONS_BY_VERSION_INTRODUCED = { - # The strings have to be dup'ed for old RG on Ruby 2.3+ - # TODO: remove dup in Bundler 2.0 - Gem::Version.create("1.0".dup) => [DEPENDENCIES, PLATFORMS, GIT, GEM, PATH].freeze, - Gem::Version.create("1.10".dup) => [BUNDLED].freeze, - Gem::Version.create("1.12".dup) => [RUBY].freeze, - Gem::Version.create("1.13".dup) => [PLUGIN].freeze, + Gem::Version.create("1.0") => [DEPENDENCIES, PLATFORMS, GIT, GEM, PATH].freeze, + Gem::Version.create("1.10") => [BUNDLED].freeze, + Gem::Version.create("1.12") => [RUBY].freeze, + Gem::Version.create("1.13") => [PLUGIN].freeze, }.freeze KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze @@ -90,7 +88,7 @@ module Bundler send("parse_#{@state}", line) end end - @sources << @rubygems_aggregate unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + @sources << @rubygems_aggregate unless Bundler.feature_flag.disable_multisource? @specs = @specs.values.sort_by(&:identifier) warn_for_outdated_bundler_version rescue ArgumentError => e @@ -109,9 +107,9 @@ module Bundler when 0 if current_version < bundler_version Bundler.ui.warn "Warning: the running version of Bundler (#{current_version}) is older " \ - "than the version that created the lockfile (#{bundler_version}). We suggest you " \ - "upgrade to the latest version of Bundler by running `gem " \ - "install bundler#{prerelease_text}`.\n" + "than the version that created the lockfile (#{bundler_version}). We suggest you to " \ + "upgrade to the version that created the lockfile by running `gem install " \ + "bundler:#{bundler_version}#{prerelease_text}`.\n" end end end @@ -141,7 +139,7 @@ module Bundler @sources << @current_source end when GEM - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + if Bundler.feature_flag.disable_multisource? @opts["remotes"] = @opts.delete("remote") @current_source = TYPES[@type].from_lock(@opts) @sources << @current_source @@ -185,7 +183,7 @@ module Bundler (?:-(.*))?\))? # Optional platform (!)? # Optional pinned marker $ # Line end - /xo + /xo.freeze def parse_dependency(line) return unless line =~ NAME_VERSION diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb index 53f9806b73..996d29aafb 100644 --- a/lib/bundler/plugin.rb +++ b/lib/bundler/plugin.rb @@ -47,26 +47,48 @@ module Bundler Bundler.ui.error "Failed to install plugin #{name}: #{e.message}\n #{e.backtrace.join("\n ")}" end + # List installed plugins and commands + # + def list + installed_plugins = index.installed_plugins + if installed_plugins.any? + output = String.new + installed_plugins.each do |plugin| + output << "#{plugin}\n" + output << "-----\n" + index.plugin_commands(plugin).each do |command| + output << " #{command}\n" + end + output << "\n" + end + else + output = "No plugins installed" + end + Bundler.ui.info output + end + # Evaluates the Gemfile with a limited DSL and installs the plugins # specified by plugin method # # @param [Pathname] gemfile path # @param [Proc] block that can be evaluated for (inline) Gemfile def gemfile_install(gemfile = nil, &inline) - builder = DSL.new - if block_given? - builder.instance_eval(&inline) - else - builder.eval_gemfile(gemfile) - end - definition = builder.to_definition(nil, true) + Bundler.settings.temporary(:frozen => false, :deployment => false) do + builder = DSL.new + if block_given? + builder.instance_eval(&inline) + else + builder.eval_gemfile(gemfile) + end + definition = builder.to_definition(nil, true) - return if definition.dependencies.empty? + return if definition.dependencies.empty? - plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p } - installed_specs = Installer.new.install_definition(definition) + plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p } + installed_specs = Installer.new.install_definition(definition) - save_plugins plugins, installed_specs, builder.inferred_plugins + save_plugins plugins, installed_specs, builder.inferred_plugins + end rescue RuntimeError => e unless e.is_a?(GemfileError) Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}" diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb index 586477efb5..b33926a181 100644 --- a/lib/bundler/plugin/api/source.rb +++ b/lib/bundler/plugin/api/source.rb @@ -37,7 +37,7 @@ module Bundler # # @!attribute [rw] dependency_names # @return [Array<String>] Names of dependencies that the source should - # try to resolve. It is not necessary to use this list intenally. This + # try to resolve. It is not necessary to use this list internally. This # is present to be compatible with `Definition` and is used by # rubygems source. module Source diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb index f09587dfda..faabf3a8d1 100644 --- a/lib/bundler/plugin/index.rb +++ b/lib/bundler/plugin/index.rb @@ -58,7 +58,10 @@ module Bundler raise SourceConflict.new(name, common) unless common.empty? sources.each {|k| @sources[k] = name } - hooks.each {|e| (@hooks[e] ||= []) << name } + hooks.each do |event| + event_hooks = (@hooks[event] ||= []) << name + event_hooks.uniq! + end @plugin_paths[name] = path @load_paths[name] = load_paths @@ -100,6 +103,14 @@ module Bundler @plugin_paths[name] end + def installed_plugins + @plugin_paths.keys + end + + def plugin_commands(plugin) + @commands.find_all {|_, n| n == plugin }.map(&:first) + end + def source?(source) @sources.key? source end diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index 5379c38979..4a262efac2 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -12,10 +12,15 @@ module Bundler autoload :Git, "bundler/plugin/installer/git" def install(names, options) + check_sources_consistency!(options) + version = options[:version] || [">= 0"] - Bundler.settings.temporary(:lockfile_uses_separate_rubygems_sources => false, :disable_multisource => false) do + + Bundler.settings.temporary(:disable_multisource => false) do if options[:git] install_git(names, version, options) + elsif options[:local_git] + install_local_git(names, version, options) else sources = options[:source] || Bundler.rubygems.sources install_rubygems(names, version, sources) @@ -38,22 +43,24 @@ module Bundler private + def check_sources_consistency!(options) + if options.key?(:git) && options.key?(:local_git) + raise InvalidOption, "Remote and local plugin git sources can't be both specified" + end + end + def install_git(names, version, options) uri = options.delete(:git) options["uri"] = uri - source_list = SourceList.new - source_list.add_git_source(options) - - # To support both sources - if options[:source] - source_list.add_rubygems_source("remotes" => options[:source]) - end + install_all_sources(names, version, options, options[:source]) + end - deps = names.map {|name| Dependency.new name, version } + def install_local_git(names, version, options) + uri = options.delete(:local_git) + options["uri"] = uri - definition = Definition.new(nil, deps, source_list, true) - install_definition(definition) + install_all_sources(names, version, options, options[:source]) end # Installs the plugin from rubygems source and returns the path where the @@ -65,10 +72,16 @@ module Bundler # # @return [Hash] map of names to the specs of plugins installed def install_rubygems(names, version, sources) - deps = names.map {|name| Dependency.new name, version } + install_all_sources(names, version, nil, sources) + end + def install_all_sources(names, version, git_source_options, rubygems_source) source_list = SourceList.new - source_list.add_rubygems_source("remotes" => sources) + + source_list.add_git_source(git_source_options) if git_source_options + source_list.add_rubygems_source("remotes" => rubygems_source) if rubygems_source + + deps = names.map {|name| Dependency.new name, version } definition = Definition.new(nil, deps, source_list, true) install_definition(definition) diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 545b4cc88a..90f833bef1 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -38,8 +38,8 @@ module Bundler @platforms = platforms @gem_version_promoter = gem_version_promoter @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts? - @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? @use_gvp = Bundler.feature_flag.use_gem_version_promoter_for_major_updates? || !@gem_version_promoter.major? + @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.disable_multisource? end def start(requirements) @@ -48,9 +48,12 @@ module Bundler verify_gemfile_dependencies_are_found!(requirements) dg = @resolver.resolve(requirements, @base_dg) - dg.map(&:payload). + dg. + tap {|resolved| validate_resolved_specs!(resolved) }. + map(&:payload). reject {|sg| sg.name.end_with?("\0") }. - map(&:to_specs).flatten + map(&:to_specs). + flatten rescue Molinillo::VersionConflict => e message = version_conflict_message(e) raise VersionConflict.new(e.conflicts.keys.uniq, message) @@ -169,13 +172,13 @@ module Bundler def name_for_explicit_dependency_source Bundler.default_gemfile.basename.to_s - rescue + rescue StandardError "Gemfile" end def name_for_locking_dependency_source Bundler.default_lockfile.basename.to_s - rescue + rescue StandardError "Gemfile.lock" end @@ -276,10 +279,10 @@ module Bundler versions_with_platforms = specs.map {|s| [s.version, s.platform] } message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n") message << if versions_with_platforms.any? - "The source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}" - else - "The source does not contain any versions of '#{name}'" - end + "The source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}" + else + "The source does not contain any versions of '#{name}'" + end else message = "Could not find gem '#{requirement}' in any of the gem sources " \ "listed in your Gemfile#{cache_message}." @@ -300,9 +303,19 @@ module Bundler end def version_conflict_message(e) + # only show essential conflicts, if possible + conflicts = e.conflicts.dup + conflicts.delete_if do |_name, conflict| + deps = conflict.requirement_trees.map(&:last).flatten(1) + !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) + end + e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty? + + solver_name = "Bundler" + possibility_type = "gem" e.message_with_trees( - :solver_name => "Bundler", - :possibility_type => "gem", + :solver_name => solver_name, + :possibility_type => possibility_type, :reduce_trees => lambda do |trees| # called first, because we want to reduce the amount of work required to find maximal empty sets trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } @@ -314,10 +327,8 @@ module Bundler end.flatten(1).select do |deps| Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) end.min_by(&:size) - trees.reject! {|t| !maximal.include?(t.last) } if maximal - trees = trees.sort_by {|t| t.flatten.map(&:to_s) } - trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } + trees.reject! {|t| !maximal.include?(t.last) } if maximal trees.sort_by {|t| t.reverse.map(&:name) } end, @@ -325,7 +336,7 @@ module Bundler :additional_message_for_conflict => lambda do |o, name, conflict| if name == "bundler" o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION})) - other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new Bundler::VERSION) + other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new(Bundler::VERSION)) end if name == "bundler" && other_bundler_required @@ -352,7 +363,11 @@ module Bundler [] end.compact.map(&:to_s).uniq.sort - o << "Could not find gem '#{SharedHelpers.pretty_dependency(conflict.requirement)}'" + metadata_requirement = name.end_with?("\0") + + o << "Could not find gem '" unless metadata_requirement + o << SharedHelpers.pretty_dependency(conflict.requirement) + o << "'" unless metadata_requirement if conflict.requirement_trees.first.size > 1 o << ", which is required by " o << "gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}'," @@ -360,14 +375,47 @@ module Bundler o << " " o << if relevant_sources.empty? - "in any of the sources.\n" - else - "in any of the relevant sources:\n #{relevant_sources * "\n "}\n" - end + "in any of the sources.\n" + elsif metadata_requirement + "is not available in #{relevant_sources.join(" or ")}" + else + "in any of the relevant sources:\n #{relevant_sources * "\n "}\n" + end end end, - :version_for_spec => lambda {|spec| spec.version } + :version_for_spec => lambda {|spec| spec.version }, + :incompatible_version_message_for_conflict => lambda do |name, _conflict| + if name.end_with?("\0") + %(#{solver_name} found conflicting requirements for the #{name} version:) + else + %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":) + end + end ) end + + def validate_resolved_specs!(resolved_specs) + resolved_specs.each do |v| + name = v.name + next unless sources = relevant_sources_for_vertex(v) + sources.compact! + if default_index = sources.index(@source_requirements[:default]) + sources.delete_at(default_index) + end + sources.reject! {|s| s.specs[name].empty? } + sources.uniq! + next if sources.size <= 1 + + multisource_disabled = Bundler.feature_flag.disable_multisource? + + msg = ["The gem '#{name}' was found in multiple relevant sources."] + msg.concat sources.map {|s| " * #{s}" }.sort + msg << "You #{multisource_disabled ? :must : :should} add this gem to the source block for the source you wish it to be installed from." + msg = msg.join("\n") + + raise SecurityError, msg if multisource_disabled + Bundler.ui.warn "Warning: #{msg}" + end + end end end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 34d043aed7..119f63b5c8 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -94,10 +94,10 @@ module Bundler return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("ruby\0", spec.required_ruby_version), platform) + dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) end if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("rubygems\0", spec.required_rubygems_version), platform) + dependencies << DepProxy.new(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) end dependencies end diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb index 244606dcc9..d64958ba70 100644 --- a/lib/bundler/retry.rb +++ b/lib/bundler/retry.rb @@ -35,10 +35,10 @@ module Bundler private def run(&block) - @failed = false + @failed = false @current_run += 1 @result = block.call - rescue => e + rescue StandardError => e fail_attempt(e) end diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb index e6c31a94c9..80dc444f93 100644 --- a/lib/bundler/ruby_version.rb +++ b/lib/bundler/ruby_version.rb @@ -49,7 +49,7 @@ module Bundler ([\d.]+) # ruby version (?:p(-?\d+))? # optional patchlevel (?:\s\((\S+)\s(.+)\))? # optional engine info - /xo + /xo.freeze # Returns a RubyVersion from the given string. # @param [String] the version string to match. @@ -74,7 +74,7 @@ module Bundler @host ||= [ RbConfig::CONFIG["host_cpu"], RbConfig::CONFIG["host_vendor"], - RbConfig::CONFIG["host_os"] + RbConfig::CONFIG["host_os"], ].join("-") end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index e9f0eac355..ccf71c8c00 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -2,12 +2,6 @@ require "pathname" -if defined?(Gem::QuickLoader) - # Gem Prelude makes me a sad panda :'( - Gem::QuickLoader.load_full_rubygems_library -end - -require "rubygems" require "rubygems/specification" begin @@ -27,14 +21,16 @@ module Gem class Specification attr_accessor :remote, :location, :relative_loaded_from - if instance_methods(false).map(&:to_sym).include?(:source) + if instance_methods(false).include?(:source) remove_method :source attr_writer :source def source (defined?(@source) && @source) || Gem::Source::Installed.new end else + # rubocop:disable Lint/DuplicateMethods attr_accessor :source + # rubocop:enable Lint/DuplicateMethods end alias_method :rg_full_gem_path, :full_gem_path @@ -85,10 +81,7 @@ module Gem end end - # RubyGems 1.8+ used only. - methods = instance_methods(false) - gem_dir = methods.first.is_a?(String) ? "gem_dir" : :gem_dir - remove_method :gem_dir if methods.include?(gem_dir) + remove_method :gem_dir if instance_methods(false).include?(:gem_dir) def gem_dir full_gem_path end diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb index 2b7fa8e0f6..b1076b4554 100644 --- a/lib/bundler/rubygems_gem_installer.rb +++ b/lib/bundler/rubygems_gem_installer.rb @@ -66,7 +66,7 @@ module Bundler If you wish to continue installing the downloaded gem, and are certain it does not pose a \ security issue despite the mismatching checksum, do the following: - 1. run `bundle config disable_checksum_validation true` to turn off checksum verification + 1. run `bundle config set disable_checksum_validation true` to turn off checksum verification 2. run `bundle install` (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \ diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index f088c2fdfb..a4519246a9 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true require "monitor" -require "rubygems" -require "rubygems/config_file" module Bundler class RubygemsIntegration @@ -307,7 +305,7 @@ module Bundler gem_from_path(path, security_policies[policy]).spec rescue Gem::Package::FormatError raise GemspecError, "Could not read gem at #{path}. It may be corrupted." - rescue Exception, Gem::Exception, Gem::Security::Exception => e + rescue Exception, Gem::Exception, Gem::Security::Exception => e # rubocop:disable Lint/RescueException if e.is_a?(Gem::Security::Exception) || e.message =~ /unknown trust policy|unsigned gem/i || e.message =~ /couldn't verify (meta)?data signature/i @@ -436,40 +434,42 @@ module Bundler # Used to make bin stubs that are not created by bundler work # under bundler. The new Gem.bin_path only considers gems in # +specs+ - def replace_bin_path(specs, specs_by_name) + def replace_bin_path(specs_by_name) gem_class = (class << Gem; self; end) redefine_method(gem_class, :find_spec_for_exe) do |gem_name, *args| exec_name = args.first + raise ArgumentError, "you must supply exec_name" unless exec_name spec_with_name = specs_by_name[gem_name] - spec = if exec_name - if spec_with_name && spec_with_name.executables.include?(exec_name) - spec_with_name - else - specs.find {|s| s.executables.include?(exec_name) } - end - else - spec_with_name - end + matching_specs_by_exec_name = specs_by_name.values.select {|s| s.executables.include?(exec_name) } + spec = matching_specs_by_exec_name.delete(spec_with_name) - unless spec + unless spec || !matching_specs_by_exec_name.empty? message = "can't find executable #{exec_name} for gem #{gem_name}" - if !exec_name || spec_with_name.nil? + if spec_with_name.nil? message += ". #{gem_name} is not currently included in the bundle, " \ "perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?" end raise Gem::Exception, message end - raise Gem::Exception, "no default executable for #{spec.full_name}" unless exec_name ||= spec.default_executable - - unless spec.name == gem_name - Bundler::SharedHelpers.major_deprecation 3, + unless spec + spec = matching_specs_by_exec_name.shift + warn \ "Bundler is using a binstub that was created for a different gem (#{spec.name}).\n" \ "You should run `bundle binstub #{gem_name}` " \ "to work around a system/bundle conflict." end + + unless matching_specs_by_exec_name.empty? + conflicting_names = matching_specs_by_exec_name.map(&:name).join(", ") + warn \ + "The `#{exec_name}` executable in the `#{spec.name}` gem is being loaded, but it's also present in other gems (#{conflicting_names}).\n" \ + "If you meant to run the executable for another gem, make sure you use a project specific binstub (`bundle binstub <gem_name>`).\n" \ + "If you plan to use multiple conflicting executables, generate binstubs for them and disambiguate their names." + end + spec end @@ -514,9 +514,18 @@ module Bundler h end + Bundler.rubygems.default_stubs.each do |stub| + default_spec = stub.to_spec + default_spec_name = default_spec.name + next if specs_by_name.key?(default_spec_name) + + specs << default_spec + specs_by_name[default_spec_name] = default_spec + end + replace_gem(specs, specs_by_name) stub_rubygems(specs) - replace_bin_path(specs, specs_by_name) + replace_bin_path(specs_by_name) replace_refresh Gem.clear_paths @@ -850,6 +859,16 @@ module Bundler end end + if Gem::Specification.respond_to?(:default_stubs) + def default_stubs + Gem::Specification.default_stubs("*.gemspec") + end + else + def default_stubs + Gem::Specification.send(:default_stubs, "*.gemspec") + end + end + def use_gemdeps(gemfile) ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile) require "bundler/gemdeps" diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index 762e7b3ec6..83945868f9 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -163,7 +163,7 @@ module Bundler gem_dirs = Dir["#{Gem.dir}/gems/*"] gem_files = Dir["#{Gem.dir}/cache/*.gem"] gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"] - extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + extension_dirs = Dir["#{Gem.dir}/extensions/*/*/*"] + Dir["#{Gem.dir}/bundler/gems/extensions/*/*/*"] spec_gem_paths = [] # need to keep git sources around spec_git_paths = @definition.spec_git_paths diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 66af31dab2..5bc190865f 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -18,7 +18,6 @@ module Bundler cache_all cache_all_platforms cache_command_is_package - console_command default_install_uses_path deployment deployment_means_frozen @@ -35,23 +34,21 @@ module Bundler frozen gem.coc gem.mit + github.https global_path_appends_ruby_scope global_gem_cache ignore_messages init_gems_rb - list_command - lockfile_upgrade_warning - lockfile_uses_separate_rubygems_sources - major_deprecations no_install no_prune only_update_to_newer_versions path_relative_to_cwd path.system plugins - prefer_gems_rb + prefer_patch print_only_version_number setup_makes_kernel_gem_public + silence_deprecations silence_root_warning skip_default_git_sources specific_platform @@ -59,7 +56,6 @@ module Bundler unlock_source_unlocks_spec update_requires_all_flag use_gem_version_promoter_for_major_updates - viz_command ].freeze NUMBER_KEYS = %w[ @@ -76,7 +72,9 @@ module Bundler ].freeze DEFAULT_CONFIG = { + :silence_deprecations => false, :disable_version_check => true, + :prefer_patch => false, :redirect => 5, :retry => 3, :timeout => 10, @@ -107,18 +105,6 @@ module Bundler temporary(key => value) value else - command = if value.nil? - "bundle config --delete #{key}" - else - "bundle config #{key} #{Array(value).join(":")}" - end - - Bundler::SharedHelpers.major_deprecation 3,\ - "flags passed to commands " \ - "will no longer be automatically remembered. Instead please set flags " \ - "you want remembered between commands using `bundle config " \ - "<setting name> <setting value>`, i.e. `#{command}`" - set_local(key, value) end end @@ -407,20 +393,6 @@ module Bundler Pathname.new(@root).join("config") if @root end - CONFIG_REGEX = %r{ # rubocop:disable Style/RegexpLiteral - ^ - (BUNDLE_.+):\s # the key - (?: !\s)? # optional exclamation mark found with ruby 1.9.3 - (['"]?) # optional opening quote - (.* # contents of the value - (?: # optionally, up until the next key - (\n(?!BUNDLE).+)* - ) - ) - \2 # matching closing quote - $ - }xo - def load_config(config_file) return {} if !config_file || ignore_config? SharedHelpers.filesystem_access(config_file, :read) do |file| @@ -442,7 +414,7 @@ module Bundler (https?.*?) # URI (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key \z - /ix + /ix.freeze # TODO: duplicates Rubygems#normalize_uri # TODO: is this the correct place to validate mirror URIs? diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb index ac6a5bf861..52a5b8889a 100644 --- a/lib/bundler/setup.rb +++ b/lib/bundler/setup.rb @@ -6,13 +6,14 @@ if Bundler::SharedHelpers.in_bundle? require "bundler" if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"] + Bundler.ui = Bundler::UI::Shell.new begin Bundler.setup rescue Bundler::BundlerError => e - puts "\e[31m#{e.message}\e[0m" - puts e.backtrace.join("\n") if ENV["DEBUG"] + Bundler.ui.warn "\e[31m#{e.message}\e[0m" + Bundler.ui.warn e.backtrace.join("\n") if ENV["DEBUG"] if e.is_a?(Bundler::GemNotFound) - puts "\e[33mRun `bundle install` to install missing gems.\e[0m" + Bundler.ui.warn "\e[33mRun `bundle install` to install missing gems.\e[0m" end exit e.status_code end diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb index d6ef25dc95..1b703c5cbc 100644 --- a/lib/bundler/shared_helpers.rb +++ b/lib/bundler/shared_helpers.rb @@ -3,6 +3,7 @@ require "bundler/compatibility_guard" require "pathname" +require "rbconfig" require "rubygems" require "bundler/version" @@ -10,17 +11,6 @@ require "bundler/constants" require "bundler/rubygems_integration" require "bundler/current_ruby" -module Gem - class Dependency - # This is only needed for RubyGems < 1.4 - unless method_defined? :requirement - def requirement - version_requirements - end - end - end -end - module Bundler module SharedHelpers def root @@ -140,33 +130,28 @@ module Bundler end def major_deprecation(major_version, message) - if Bundler.bundler_major_version >= major_version + bundler_major_version = Bundler.bundler_major_version + if bundler_major_version > major_version require "bundler/errors" - raise DeprecatedError, "[REMOVED FROM #{major_version}.0] #{message}" + raise DeprecatedError, "[REMOVED] #{message}" end - return unless prints_major_deprecations? + return unless bundler_major_version >= major_version && prints_major_deprecations? @major_deprecation_ui ||= Bundler::UI::Shell.new("no-color" => true) ui = Bundler.ui.is_a?(@major_deprecation_ui.class) ? Bundler.ui : @major_deprecation_ui - ui.warn("[DEPRECATED FOR #{major_version}.0] #{message}") + ui.warn("[DEPRECATED] #{message}") end def print_major_deprecations! multiple_gemfiles = search_up(".") do |dir| gemfiles = gemfile_names.select {|gf| File.file? File.expand_path(gf, dir) } next if gemfiles.empty? - break false if gemfiles.size == 1 + break gemfiles.size != 1 end - if multiple_gemfiles && Bundler.bundler_major_version == 2 - Bundler::SharedHelpers.major_deprecation 3, \ - "gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock." - end - - if RUBY_VERSION < "2" - major_deprecation(2, "Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}") - end - return if Bundler.rubygems.provides?(">= 2") - major_deprecation(2, "Bundler will only support rubygems >= 2.0, you are running #{Bundler.rubygems.version}") + return unless multiple_gemfiles + message = "Multiple gemfiles (gems.rb and Gemfile) detected. " \ + "Make sure you remove Gemfile and Gemfile.lock since bundler is ignoring them in favor of gems.rb and gems.rb.locked." + Bundler.ui.warn message end def trap(signal, override = false, &block) @@ -246,13 +231,11 @@ module Bundler def find_gemfile(order_matters = false) given = ENV["BUNDLE_GEMFILE"] return given if given && !given.empty? - names = gemfile_names - names.reverse! if order_matters && Bundler.feature_flag.prefer_gems_rb? - find_file(*names) + find_file(*gemfile_names) end def gemfile_names - ["Gemfile", "gems.rb"] + ["gems.rb", "Gemfile"] end def find_file(*names) @@ -349,9 +332,6 @@ module Bundler end def clean_load_path - # handle 1.9 where system gems are always on the load path - return unless defined?(::Gem) - bundler_lib = bundler_ruby_lib loaded_gem_paths = Bundler.rubygems.loaded_gem_paths @@ -372,8 +352,7 @@ module Bundler def prints_major_deprecations? require "bundler" - deprecation_release = Bundler::VERSION.split(".").drop(1).include?("99") - return false if !deprecation_release && !Bundler.settings[:major_deprecations] + return false if Bundler.settings[:silence_deprecations] require "bundler/deprecate" return false if Bundler::Deprecate.skip true diff --git a/lib/bundler/similarity_detector.rb b/lib/bundler/similarity_detector.rb index b7f3ee7afa..f698f46a4c 100644 --- a/lib/bundler/similarity_detector.rb +++ b/lib/bundler/similarity_detector.rb @@ -51,7 +51,7 @@ module Bundler dm[i][j] = [ dm[i - 1][j - 1] + (this[j - 1] == that[i - 1] ? 0 : sub), dm[i][j - 1] + ins, - dm[i - 1][j] + del + dm[i - 1][j] + del, ].min end end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index 0b00608bdd..a8afb341d8 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -118,18 +118,19 @@ module Bundler def local_override!(path) return false if local? + original_path = path path = Pathname.new(path) path = path.expand_path(Bundler.root) unless path.relative? unless options["branch"] || Bundler.settings[:disable_local_branch_check] raise GitError, "Cannot use local override for #{name} at #{path} because " \ - ":branch is not specified in Gemfile. Specify a branch or use " \ - "`bundle config --delete` to remove the local override" + ":branch is not specified in Gemfile. Specify a branch or run " \ + "`bundle config unset local.#{override_for(original_path)}` to remove the local override" end unless path.exist? raise GitError, "Cannot use local override for #{name} because #{path} " \ - "does not exist. Check `bundle config --delete` to remove the local override" + "does not exist. Run `bundle config unset local.#{override_for(original_path)}` to remove the local override" end set_local!(path) @@ -260,7 +261,11 @@ module Bundler end def requires_checkout? - allow_git_ops? && !local? + allow_git_ops? && !local? && !cached_revision_checked_out? + end + + def cached_revision_checked_out? + cached_revision && cached_revision == revision && install_path.exist? end def base_name @@ -324,6 +329,10 @@ module Bundler def extension_cache_slug(_) extension_dir_name end + + def override_for(path) + Bundler.settings.local_overrides.key(path) + end end end end diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index cd964f7e56..3db31f0237 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -253,7 +253,7 @@ module Bundler ensure STDERR.reopen backup_stderr end - $stderr.puts URICredentialsFilter.credential_filtered_string(captured_err, uri) if uri && !captured_err.empty? + Bundler.ui.warn URICredentialsFilter.credential_filtered_string(captured_err, uri) if uri && !captured_err.empty? return_value end end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index d9c93bfb67..559b912ffd 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -5,8 +5,10 @@ module Bundler class Metadata < Source def specs @specs ||= Index.build do |idx| - idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) - idx << Gem::Specification.new("rubygems\0", Gem::VERSION) + idx << Gem::Specification.new("Ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) + idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s| + s.required_rubygems_version = Gem::Requirement.default + end idx << Gem::Specification.new do |s| s.name = "bundler" diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index ed734bf549..5f3f1bc2e4 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -191,10 +191,10 @@ module Bundler else message = String.new("The path `#{expanded_path}` ") message << if File.exist?(expanded_path) - "is not a directory." - else - "does not exist." - end + "is not a directory." + else + "does not exist." + end raise PathError, message end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 485b388a32..86fd329089 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -51,7 +51,7 @@ module Bundler end def can_lock?(spec) - return super if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + return super if Bundler.feature_flag.disable_multisource? spec.source.is_a?(Rubygems) end @@ -106,7 +106,7 @@ module Bundler end end - if installed?(spec) && !force + if (installed?(spec) || Plugin.installed?(spec.name)) && !force print_using_message "Using #{version_message(spec)}" return nil # no post-install message end @@ -120,8 +120,14 @@ module Bundler uris.uniq! Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1 - s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"]) - spec.__swap__(s) + path = fetch_gem(spec) + begin + s = Bundler.rubygems.spec_from_gem(path, Bundler.settings["trust-policy"]) + spec.__swap__(s) + rescue StandardError + Bundler.rm_rf(path) + raise + end end unless Bundler.settings[:no_install] diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index ac2adacb3d..d3f649a12c 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "set" + module Bundler class SourceList attr_reader :path_sources, @@ -41,17 +43,14 @@ module Bundler end def global_rubygems_source=(uri) - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + if Bundler.feature_flag.disable_multisource? @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri) end add_rubygems_remote(uri) end def add_rubygems_remote(uri) - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - return if Bundler.feature_flag.disable_multisource? - raise InvalidOption, "`lockfile_uses_separate_rubygems_sources` cannot be set without `disable_multisource` being set" - end + return if Bundler.feature_flag.disable_multisource? @rubygems_aggregate.add_remote(uri) @rubygems_aggregate end @@ -77,12 +76,10 @@ module Bundler end def lock_sources - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - [[default_source], @rubygems_sources, git_sources, path_sources, plugin_sources].map do |sources| - sources.sort_by(&:to_s) - end.flatten(1) + lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s) + if Bundler.feature_flag.disable_multisource? + lock_sources + rubygems_sources.sort_by(&:to_s) else - lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s) lock_sources << combine_rubygems_sources end end @@ -97,7 +94,7 @@ module Bundler end end - replacement_rubygems = !Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? && + replacement_rubygems = !Bundler.feature_flag.disable_multisource? && replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } @rubygems_aggregate = replacement_rubygems if replacement_rubygems @@ -150,7 +147,7 @@ module Bundler if source.uri =~ /^git\:/ Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \ "which transmits data without encryption. Disable this warning with " \ - "`bundle config git.allow_insecure true`, or switch to the `https` " \ + "`bundle config set git.allow_insecure true`, or switch to the `https` " \ "protocol to keep your data secure." end end diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index bbdf04a7d6..2c5d9e6580 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -60,7 +60,6 @@ module Bundler @specs << value @lookup = nil @sorted = nil - value end def sort! diff --git a/lib/bundler/ssl_certs/.document b/lib/bundler/ssl_certs/.document deleted file mode 100644 index fb66f13c33..0000000000 --- a/lib/bundler/ssl_certs/.document +++ /dev/null @@ -1 +0,0 @@ -# Ignore all files in this directory diff --git a/lib/bundler/ssl_certs/certificate_manager.rb b/lib/bundler/ssl_certs/certificate_manager.rb deleted file mode 100644 index 26fc38ec18..0000000000 --- a/lib/bundler/ssl_certs/certificate_manager.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_fileutils" -require "net/https" -require "openssl" - -module Bundler - module SSLCerts - class CertificateManager - attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs - - def self.update_from!(rubygems_path) - new(rubygems_path).update! - end - - def initialize(rubygems_path = nil) - if rubygems_path - rubygems_cert_path = File.join(rubygems_path, "lib/rubygems/ssl_certs") - @rubygems_certs = certificates_in(rubygems_cert_path) - end - - @bundler_cert_path = File.expand_path("..", __FILE__) - @bundler_certs = certificates_in(bundler_cert_path) - end - - def up_to_date? - rubygems_certs.all? do |rc| - bundler_certs.find do |bc| - File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc) - end - end - end - - def update! - return if up_to_date? - - FileUtils.rm bundler_certs - FileUtils.cp rubygems_certs, bundler_cert_path - end - - def connect_to(host) - http = Net::HTTP.new(host, 443) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_PEER - http.cert_store = store - http.head("/") - end - - private - - def certificates_in(path) - Dir[File.join(path, "**/*.pem")].sort - end - - def store - @store ||= begin - store = OpenSSL::X509::Store.new - bundler_certs.each do |cert| - store.add_file cert - end - store - end - end - end - end -end diff --git a/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem deleted file mode 100644 index f4ce4ca43d..0000000000 --- a/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- diff --git a/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem b/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem deleted file mode 100644 index 9e6810ab70..0000000000 --- a/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- diff --git a/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem b/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem deleted file mode 100644 index 20585f1c01..0000000000 --- a/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb index 0dd024024a..bef94b505e 100644 --- a/lib/bundler/stub_specification.rb +++ b/lib/bundler/stub_specification.rb @@ -13,17 +13,13 @@ module Bundler attr_accessor :stub, :ignored - # Pre 2.2.0 did not include extension_dir - # https://github.com/rubygems/rubygems/commit/9485ca2d101b82a946d6f327f4bdcdea6d4946ea - if Bundler.rubygems.provides?(">= 2.2.0") - def source=(source) - super - # Stub has no concept of source, which means that extension_dir may be wrong - # This is the case for git-based gems. So, instead manually assign the extension dir - return unless source.respond_to?(:extension_dir_name) - path = File.join(stub.extensions_dir, source.extension_dir_name) - stub.extension_dir = File.expand_path(path) - end + def source=(source) + super + # Stub has no concept of source, which means that extension_dir may be wrong + # This is the case for git-based gems. So, instead manually assign the extension dir + return unless source.respond_to?(:extension_dir_name) + path = File.join(stub.extensions_dir, source.extension_dir_name) + stub.extension_dir = File.expand_path(path) end def to_yaml @@ -32,11 +28,9 @@ module Bundler # @!group Stub Delegates - if Bundler.rubygems.provides?(">= 2.3") - # This is defined directly to avoid having to load every installed spec - def missing_extensions? - stub.missing_extensions? - end + # This is defined directly to avoid having to load every installed spec + def missing_extensions? + stub.missing_extensions? end def activated @@ -57,16 +51,14 @@ module Bundler stub.full_gem_path || method_missing(:full_gem_path) end - if Bundler.rubygems.provides?(">= 2.2.0") - def full_require_paths - stub.full_require_paths - end + def full_require_paths + stub.full_require_paths + end - # This is what we do in bundler/rubygems_ext - # full_require_paths is always implemented in >= 2.2.0 - def load_paths - full_require_paths - end + # This is what we do in bundler/rubygems_ext + # full_require_paths is always implemented + def load_paths + full_require_paths end def loaded_from diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler index eeda90b584..3adac41e74 100644 --- a/lib/bundler/templates/Executable.bundler +++ b/lib/bundler/templates/Executable.bundler @@ -11,7 +11,7 @@ require "rubygems" m = Module.new do - module_function + module_function def invoked_as_script? File.expand_path($0) == File.expand_path(__FILE__) diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt index 868a0afe67..22ddeaa91a 100644 --- a/lib/bundler/templates/newgem/README.md.tt +++ b/lib/bundler/templates/newgem/README.md.tt @@ -14,7 +14,7 @@ gem '<%= config[:name] %>' And then execute: - $ bundle + $ bundle install Or install it yourself as: diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt index faf6f7bbc5..5505540b9e 100644 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ b/lib/bundler/templates/newgem/newgem.gemspec.tt @@ -1,8 +1,4 @@ -<%- if RUBY_VERSION < "2.0.0" -%> -# coding: utf-8 -<%- end -%> - -lib = File.expand_path("../lib", __FILE__) +lib = File.expand_path("lib", __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "<%= config[:namespaced_path] %>/version" @@ -19,18 +15,11 @@ Gem::Specification.new do |spec| spec.license = "MIT" <%- end -%> - # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' - # to allow pushing to a single host or delete this section to allow pushing to any host. - if spec.respond_to?(:metadata) - spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" + spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" - spec.metadata["homepage_uri"] = spec.homepage - spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." - spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." - else - raise "RubyGems 2.0 or newer is required to protect against " \ - "public gem pushes." - end + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here." + spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here." # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. @@ -45,7 +34,7 @@ Gem::Specification.new do |spec| <%- end -%> spec.add_development_dependency "bundler", "~> <%= config[:bundler_version] %>" - spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rake", "~> 12.0" <%- if config[:ext] -%> spec.add_development_dependency "rake-compiler" <%- end -%> diff --git a/lib/bundler/templates/newgem/test/test_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt index 725e3e4647..7d7db165ec 100644 --- a/lib/bundler/templates/newgem/test/test_helper.rb.tt +++ b/lib/bundler/templates/newgem/test/test_helper.rb.tt @@ -1,4 +1,4 @@ -$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) +$LOAD_PATH.unshift File.expand_path("../lib", __dir__) require "<%= config[:namespaced_path] %>" require "minitest/autorun" diff --git a/lib/bundler/templates/newgem/travis.yml.tt b/lib/bundler/templates/newgem/travis.yml.tt index 7a3381a889..eab16addca 100644 --- a/lib/bundler/templates/newgem/travis.yml.tt +++ b/lib/bundler/templates/newgem/travis.yml.tt @@ -1,5 +1,4 @@ --- -sudo: false language: ruby cache: bundler rvm: diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb index cc69740845..fb7777eb49 100644 --- a/lib/bundler/vendor/fileutils/lib/fileutils.rb +++ b/lib/bundler/vendor/fileutils/lib/fileutils.rb @@ -1,4 +1,13 @@ # frozen_string_literal: true + +begin + require 'rbconfig' +rescue LoadError + # for make mjit-headers +end + +require "bundler/vendor/fileutils/lib/fileutils/version" + # # = fileutils.rb # @@ -56,7 +65,7 @@ # # There are some `low level' methods, which do not accept any option: # -# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false) +# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) # Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) # Bundler::FileUtils.copy_stream(srcstream, deststream) # Bundler::FileUtils.remove_entry(path, force = false) @@ -84,7 +93,6 @@ # files/directories. This equates to passing the <tt>:noop</tt> and # <tt>:verbose</tt> flags to methods in Bundler::FileUtils. # - module Bundler::FileUtils def self.private_module_function(name) #:nodoc: @@ -106,19 +114,22 @@ module Bundler::FileUtils # # Changes the current directory to the directory +dir+. # - # If this method is called with block, resumes to the old - # working directory after the block execution finished. + # If this method is called with block, resumes to the previous + # working directory after the block execution has finished. # - # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it + # Bundler::FileUtils.cd('/') # change directory # - # Bundler::FileUtils.cd('/') do # chdir + # Bundler::FileUtils.cd('/', :verbose => true) # change directory and report it + # + # Bundler::FileUtils.cd('/') do # change directory # # ... # do something # end # return to original directory # def cd(dir, verbose: nil, &block) # :yield: dir fu_output_message "cd #{dir}" if verbose - Dir.chdir(dir, &block) + result = Dir.chdir(dir, &block) fu_output_message 'cd -' if verbose and block + result end module_function :cd @@ -245,15 +256,15 @@ module Bundler::FileUtils fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose return if noop list.each do |dir| - begin - Dir.rmdir(dir = remove_trailing_slash(dir)) - if parents + Dir.rmdir(dir = remove_trailing_slash(dir)) + if parents + begin until (parent = File.dirname(dir)) == '.' or parent == dir dir = parent Dir.rmdir(dir) end + rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT end - rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT end end end @@ -295,6 +306,39 @@ module Bundler::FileUtils # # :call-seq: + # Bundler::FileUtils.cp_lr(src, dest, noop: nil, verbose: nil, dereference_root: true, remove_destination: false) + # + # Hard link +src+ to +dest+. If +src+ is a directory, this method links + # all its contents recursively. If +dest+ is a directory, links + # +src+ to +dest/src+. + # + # +src+ can be a list of files. + # + # # Installing the library "mylib" under the site_ruby directory. + # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force => true + # Bundler::FileUtils.cp_lr 'lib/', site_ruby + '/mylib' + # + # # Examples of linking several files to target directory. + # Bundler::FileUtils.cp_lr %w(mail.rb field.rb debug/), site_ruby + '/tmail' + # Bundler::FileUtils.cp_lr Dir.glob('*.rb'), '/home/aamine/lib/ruby', :noop => true, :verbose => true + # + # # If you want to link all contents of a directory instead of the + # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, + # # use the following code. + # Bundler::FileUtils.cp_lr 'src/.', 'dest' # cp_lr('src', 'dest') makes dest/src, but this doesn't. + # + def cp_lr(src, dest, noop: nil, verbose: nil, + dereference_root: true, remove_destination: false) + fu_output_message "cp -lr#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose + return if noop + fu_each_src_dest(src, dest) do |s, d| + link_entry s, d, dereference_root, remove_destination + end + end + module_function :cp_lr + + # + # :call-seq: # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil) # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil) # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil) @@ -340,6 +384,26 @@ module Bundler::FileUtils module_function :ln_sf # + # Hard links a file system entry +src+ to +dest+. + # If +src+ is a directory, this method links its contents recursively. + # + # Both of +src+ and +dest+ must be a path name. + # +src+ must exist, +dest+ must not exist. + # + # If +dereference_root+ is true, this method dereferences the tree root. + # + # If +remove_destination+ is true, this method removes each destination file before copy. + # + def link_entry(src, dest, dereference_root = false, remove_destination = false) + Entry_.new(src, nil, dereference_root).traverse do |ent| + destent = Entry_.new(dest, ent.rel, false) + File.unlink destent.path if remove_destination && File.file?(destent.path) + ent.link destent.path + end + end + module_function :link_entry + + # # Copies a file content +src+ to +dest+. If +dest+ is a directory, # copies +src+ to +dest/src+. # @@ -412,7 +476,7 @@ module Bundler::FileUtils def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent| destent = Entry_.new(dest, ent.rel, false) - File.unlink destent.path if remove_destination && File.file?(destent.path) + File.unlink destent.path if remove_destination && (File.file?(destent.path) || File.symlink?(destent.path)) ent.copy destent.path end, proc do |ent| destent = Entry_.new(dest, ent.rel, false) @@ -461,13 +525,12 @@ module Bundler::FileUtils if destent.exist? if destent.directory? raise Errno::EEXIST, d - else - destent.remove_file if rename_cannot_overwrite_file? end end begin File.rename s, d - rescue Errno::EXDEV + rescue Errno::EXDEV, + Errno::EPERM # move from unencrypted to encrypted dir (ext4) copy_entry s, d, true if secure remove_entry_secure s, force @@ -485,11 +548,6 @@ module Bundler::FileUtils alias move mv module_function :move - def rename_cannot_overwrite_file? #:nodoc: - /emx/ =~ RUBY_PLATFORM - end - private_module_function :rename_cannot_overwrite_file? - # # Remove file(s) specified in +list+. This method cannot remove directories. # All StandardErrors are ignored when the :force option is set. @@ -601,8 +659,8 @@ module Bundler::FileUtils # # For details of this security vulnerability, see Perl's case: # - # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 - # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 + # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 + # * https://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 # # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. # @@ -626,22 +684,38 @@ module Bundler::FileUtils unless parent_st.sticky? raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})" end + # freeze tree root euid = Process.euid - File.open(fullpath + '/.') {|f| - unless fu_stat_identical_entry?(st, f.stat) - # symlink (TOC-to-TOU attack?) - File.unlink fullpath - return - end - f.chown euid, -1 - f.chmod 0700 - unless fu_stat_identical_entry?(st, File.lstat(fullpath)) - # TOC-to-TOU attack? - File.unlink fullpath - return - end - } + dot_file = fullpath + "/." + begin + File.open(dot_file) {|f| + unless fu_stat_identical_entry?(st, f.stat) + # symlink (TOC-to-TOU attack?) + File.unlink fullpath + return + end + f.chown euid, -1 + f.chmod 0700 + } + rescue Errno::EISDIR # JRuby in non-native mode can't open files as dirs + File.lstat(dot_file).tap {|fstat| + unless fu_stat_identical_entry?(st, fstat) + # symlink (TOC-to-TOU attack?) + File.unlink fullpath + return + end + File.chown euid, -1, dot_file + File.chmod 0700, dot_file + } + end + + unless fu_stat_identical_entry?(st, File.lstat(fullpath)) + # TOC-to-TOU attack? + File.unlink fullpath + return + end + # ---- tree root is frozen ---- root = Entry_.new(path) root.preorder_traverse do |ent| @@ -742,8 +816,15 @@ module Bundler::FileUtils # def compare_stream(a, b) bsize = fu_stream_blksize(a, b) - sa = String.new(capacity: bsize) - sb = String.new(capacity: bsize) + + if RUBY_VERSION > "2.4" + sa = String.new(capacity: bsize) + sb = String.new(capacity: bsize) + else + sa = String.new + sb = String.new + end + begin a.read(bsize, sa) b.read(bsize, sb) @@ -1001,11 +1082,6 @@ module Bundler::FileUtils end module_function :chown_R - begin - require 'etc' - rescue LoadError # rescue LoadError for miniruby - end - def fu_get_uid(user) #:nodoc: return nil unless user case user @@ -1014,6 +1090,7 @@ module Bundler::FileUtils when /\A\d+\z/ user.to_i else + require 'etc' Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil end end @@ -1027,6 +1104,7 @@ module Bundler::FileUtils when /\A\d+\z/ group.to_i else + require 'etc' Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil end end @@ -1067,8 +1145,11 @@ module Bundler::FileUtils module StreamUtils_ private - def fu_windows? - /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM + case (defined?(::RbConfig) ? ::RbConfig::CONFIG['host_os'] : ::RUBY_PLATFORM) + when /mswin|mingw/ + def fu_windows?; true end + else + def fu_windows?; false end end def fu_copy_stream0(src, dest, blksize = nil) #:nodoc: @@ -1193,9 +1274,15 @@ module Bundler::FileUtils def entries opts = {} opts[:encoding] = ::Encoding::UTF_8 if fu_windows? - Dir.entries(path(), opts)\ - .reject {|n| n == '.' or n == '..' }\ - .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } + + files = if Dir.respond_to?(:children) + Dir.children(path, opts) + else + Dir.entries(path(), opts) + .reject {|n| n == '.' or n == '..' } + end + + files.map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } end def stat @@ -1250,6 +1337,22 @@ module Bundler::FileUtils end end + def link(dest) + case + when directory? + if !File.exist?(dest) and descendant_directory?(dest, path) + raise ArgumentError, "cannot link directory %s to itself %s" % [path, dest] + end + begin + Dir.mkdir dest + rescue + raise unless File.directory?(dest) + end + else + File.link path(), dest + end + end + def copy(dest) lstat case diff --git a/lib/bundler/vendor/fileutils/lib/fileutils/version.rb b/lib/bundler/vendor/fileutils/lib/fileutils/version.rb new file mode 100644 index 0000000000..6d8504ccd5 --- /dev/null +++ b/lib/bundler/vendor/fileutils/lib/fileutils/version.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module Bundler::FileUtils + VERSION = "1.2.0" +end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb index 7ecdc4b65a..41bc013143 100644 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +++ b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb @@ -50,14 +50,25 @@ module Bundler::Molinillo incoming_edges.map(&:origin) end - # @return [Array<Vertex>] the vertices of {#graph} where `self` is a + # @return [Set<Vertex>] the vertices of {#graph} where `self` is a # {#descendent?} def recursive_predecessors - vertices = predecessors - vertices += Compatibility.flat_map(vertices, &:recursive_predecessors) - vertices.uniq! + _recursive_predecessors + end + + # @param [Set<Vertex>] vertices the set to add the predecessors to + # @return [Set<Vertex>] the vertices of {#graph} where `self` is a + # {#descendent?} + def _recursive_predecessors(vertices = Set.new) + incoming_edges.each do |edge| + vertex = edge.origin + next unless vertices.add?(vertex) + vertex._recursive_predecessors(vertices) + end + vertices end + protected :_recursive_predecessors # @return [Array<Vertex>] the vertices of {#graph} that have an edge with # `self` as their {Edge#origin} @@ -65,14 +76,25 @@ module Bundler::Molinillo outgoing_edges.map(&:destination) end - # @return [Array<Vertex>] the vertices of {#graph} where `self` is an + # @return [Set<Vertex>] the vertices of {#graph} where `self` is an # {#ancestor?} def recursive_successors - vertices = successors - vertices += Compatibility.flat_map(vertices, &:recursive_successors) - vertices.uniq! + _recursive_successors + end + + # @param [Set<Vertex>] vertices the set to add the successors to + # @return [Set<Vertex>] the vertices of {#graph} where `self` is an + # {#ancestor?} + def _recursive_successors(vertices = Set.new) + outgoing_edges.each do |edge| + vertex = edge.destination + next unless vertices.add?(vertex) + vertex._recursive_successors(vertices) + end + vertices end + protected :_recursive_successors # @return [String] a string suitable for debugging def inspect diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb index e6698572a9..b06feac2a0 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions.rb @@ -113,8 +113,10 @@ class Bundler::Thor # the script started). # def relative_to_original_destination_root(path, remove_dot = true) - path = path.dup - if path.gsub!(@destination_stack[0], ".") + root = @destination_stack[0] + if path.start_with?(root) && [File::SEPARATOR, File::ALT_SEPARATOR, nil, ''].include?(path[root.size..root.size]) + path = path.dup + path[0...root.size] = '.' remove_dot ? (path[2..-1] || "") : path else path @@ -217,6 +219,7 @@ class Bundler::Thor shell.padding += 1 if verbose contents = if is_uri + require "open-uri" open(path, "Accept" => "application/x-thor-template", &:read) else open(path, &:read) @@ -252,9 +255,16 @@ class Bundler::Thor say_status :run, desc, config.fetch(:verbose, true) - unless options[:pretend] - config[:capture] ? `#{command}` : system(command.to_s) + return if options[:pretend] + + result = config[:capture] ? `#{command}` : system(command.to_s) + + if config[:abort_on_failure] + success = config[:capture] ? $?.success? : result + abort unless success end + + result end # Executes a ruby script (taking into account WIN32 platform quirks). diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb index 4c83bebc86..cc29db05a8 100644 --- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +++ b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb @@ -60,6 +60,9 @@ class Bundler::Thor # destination. If a block is given instead of destination, the content of # the url is yielded and used as location. # + # +get+ relies on open-uri, so passing application user input would provide + # a command injection attack vector. + # # ==== Parameters # source<String>:: the address of the given content. # destination<String>:: the relative path to the destination root. @@ -117,7 +120,13 @@ class Bundler::Thor context = config.delete(:context) || instance_eval("binding") create_file destination, nil, config do - content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").tap do |erb| + match = ERB.version.match(/(\d+\.\d+\.\d+)/) + capturable_erb = if match && match[1] >= "2.2.0" # Ruby 2.6+ + CapturableERB.new(::File.binread(source), :trim_mode => "-", :eoutvar => "@output_buffer") + else + CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer") + end + content = capturable_erb.tap do |erb| erb.filename = source end.result(context) content = yield(content) if block @@ -301,7 +310,7 @@ class Bundler::Thor def comment_lines(path, flag, *args) flag = flag.respond_to?(:source) ? flag.source : flag - gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args) + gsub_file(path, /^(\s*)([^#\n]*#{flag})/, '\1# \2', *args) end # Removes a file at the given location. diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb index 9bd1077170..e79d03d087 100644 --- a/lib/bundler/vendor/thor/lib/thor/base.rb +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -113,7 +113,7 @@ class Bundler::Thor end # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the - # class and the file on Bundler::Thor::Base. This is the method responsable for it. + # class and the file on Bundler::Thor::Base. This is the method responsible for it. # def register_klass_file(klass) #:nodoc: file = caller[1].match(/(.*):\d+/)[1] @@ -466,13 +466,13 @@ class Bundler::Thor dispatch(nil, given_args.dup, nil, config) rescue Bundler::Thor::Error => e config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) - exit(1) if exit_on_failure? + exit(false) if exit_on_failure? rescue Errno::EPIPE # This happens if a thor command is piped to something like `head`, # which closes the pipe when it's done reading. This will also # mean that if the pipe is closed, further unnecessary # computation will not occur. - exit(0) + exit(true) end # Allows to use private methods from parent in child classes as commands. @@ -493,8 +493,7 @@ class Bundler::Thor alias_method :public_task, :public_command def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: - raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace - raise UndefinedCommandError, "Could not find command #{command.inspect}." + raise UndefinedCommandError.new(command, all_commands.keys, (namespace if has_namespace)) end alias_method :handle_no_task_error, :handle_no_command_error diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb index 2f816081f3..16c68294e4 100644 --- a/lib/bundler/vendor/thor/lib/thor/error.rb +++ b/lib/bundler/vendor/thor/lib/thor/error.rb @@ -1,4 +1,23 @@ class Bundler::Thor + Correctable = + begin + require 'did_you_mean' + + # In order to support versions of Ruby that don't have keyword + # arguments, we need our own spell checker class that doesn't take key + # words. Even though this code wouldn't be hit because of the check + # above, it's still necessary because the interpreter would otherwise be + # unable to parse the file. + class NoKwargSpellChecker < DidYouMean::SpellChecker # :nodoc: + def initialize(dictionary) + @dictionary = dictionary + end + end + + DidYouMean::Correctable + rescue LoadError, NameError + end + # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those # errors have their backtrace suppressed and are nicely shown to the user. # @@ -10,6 +29,35 @@ class Bundler::Thor # Raised when a command was not found. class UndefinedCommandError < Error + class SpellChecker + attr_reader :error + + def initialize(error) + @error = error + end + + def corrections + @corrections ||= spell_checker.correct(error.command).map(&:inspect) + end + + def spell_checker + NoKwargSpellChecker.new(error.all_commands) + end + end + + attr_reader :command, :all_commands + + def initialize(command, all_commands, namespace) + @command = command + @all_commands = all_commands + + message = "Could not find command #{command.inspect}" + message = namespace ? "#{message} in #{namespace.inspect} namespace." : "#{message}." + + super(message) + end + + prepend Correctable if Correctable end UndefinedTaskError = UndefinedCommandError @@ -22,6 +70,33 @@ class Bundler::Thor end class UnknownArgumentError < Error + class SpellChecker + attr_reader :error + + def initialize(error) + @error = error + end + + def corrections + @corrections ||= + error.unknown.flat_map { |unknown| spell_checker.correct(unknown) }.uniq.map(&:inspect) + end + + def spell_checker + @spell_checker ||= NoKwargSpellChecker.new(error.switches) + end + end + + attr_reader :switches, :unknown + + def initialize(switches, unknown) + @switches = switches + @unknown = unknown + + super("Unknown switches #{unknown.map(&:inspect).join(', ')}") + end + + prepend Correctable if Correctable end class RequiredArgumentMissingError < InvocationError @@ -29,4 +104,11 @@ class Bundler::Thor class MalformattedArgumentError < InvocationError end + + if Correctable + DidYouMean::SPELL_CHECKERS.merge!( + 'Bundler::Thor::UndefinedCommandError' => UndefinedCommandError::SpellChecker, + 'Bundler::Thor::UnknownArgumentError' => UnknownArgumentError::SpellChecker + ) + end end diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb index 05ddc10cd3..30db46529e 100644 --- a/lib/bundler/vendor/thor/lib/thor/group.rb +++ b/lib/bundler/vendor/thor/lib/thor/group.rb @@ -61,7 +61,7 @@ class Bundler::Thor::Group invocations[name] = false invocation_blocks[name] = block if block_given? - class_eval <<-METHOD, __FILE__, __LINE__ + class_eval <<-METHOD, __FILE__, __LINE__ + 1 def _invoke_#{name.to_s.gsub(/\W/, '_')} klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) @@ -120,7 +120,7 @@ class Bundler::Thor::Group invocations[name] = true invocation_blocks[name] = block if block_given? - class_eval <<-METHOD, __FILE__, __LINE__ + class_eval <<-METHOD, __FILE__, __LINE__ + 1 def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} return unless options[#{name.inspect}] diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb index 70f6366842..179f4fa015 100644 --- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb +++ b/lib/bundler/vendor/thor/lib/thor/parser/options.rb @@ -44,6 +44,7 @@ class Bundler::Thor @shorts = {} @switches = {} @extra = [] + @stopped_parsing_after_extra_index = nil options.each do |option| @switches[option.switch_name] = option @@ -66,6 +67,7 @@ class Bundler::Thor if result == OPTS_END shift @parsing_options = false + @stopped_parsing_after_extra_index ||= @extra.size super else result @@ -99,6 +101,7 @@ class Bundler::Thor elsif @stop_on_unknown @parsing_options = false @extra << shifted + @stopped_parsing_after_extra_index ||= @extra.size @extra << shift while peek break elsif match @@ -120,9 +123,11 @@ class Bundler::Thor end def check_unknown! + to_check = @stopped_parsing_after_extra_index ? @extra[0...@stopped_parsing_after_extra_index] : @extra + # an unknown option starts with - or -- and has no more --'s afterward. - unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } - raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? + unknown = to_check.select { |str| str =~ /^--?(?:(?!--).)*$/ } + raise UnknownArgumentError.new(@switches.keys, unknown) unless unknown.empty? end protected diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb index b110b8d478..65ae422d7f 100644 --- a/lib/bundler/vendor/thor/lib/thor/runner.rb +++ b/lib/bundler/vendor/thor/lib/thor/runner.rb @@ -3,7 +3,7 @@ require "bundler/vendor/thor/lib/thor/group" require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" require "yaml" -require "digest" +require "digest/md5" require "pathname" class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength @@ -90,7 +90,7 @@ class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLeng end thor_yaml[as] = { - :filename => Digest(:MD5).hexdigest(name + as), + :filename => Digest::MD5.hexdigest(name + as), :location => location, :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) } diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb index e945549324..a68cdf8a98 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell.rb @@ -55,7 +55,7 @@ class Bundler::Thor # Common methods that are delegated to the shell. SHELL_DELEGATED_METHODS.each do |method| - module_eval <<-METHOD, __FILE__, __LINE__ + module_eval <<-METHOD, __FILE__, __LINE__ + 1 def #{method}(*args,&block) shell.#{method}(*args,&block) end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb index 5162390efd..52648fee8f 100644 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb @@ -1,6 +1,8 @@ class Bundler::Thor module Shell class Basic + DEFAULT_TERMINAL_WIDTH = 80 + attr_accessor :base attr_reader :padding @@ -45,6 +47,10 @@ class Bundler::Thor # Asks something to the user and receives a response. # + # If a default value is specified it will be presented to the user + # and allows them to select that value with an empty response. This + # option is ignored when limited answers are supplied. + # # If asked to limit the correct responses, you can pass in an # array of acceptable answers. If one of those is not supplied, # they will be shown a message stating that one of those answers @@ -61,6 +67,8 @@ class Bundler::Thor # ==== Example # ask("What is your name?") # + # ask("What is the planet furthest from the sun?", :default => "Pluto") + # # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) # # ask("What is your password?", :echo => false) @@ -222,8 +230,20 @@ class Bundler::Thor paras = message.split("\n\n") paras.map! do |unwrapped| - unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } - end + counter = 0 + unwrapped.split(" ").inject do |memo, word| + word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n") + counter = 0 if word.include? "\n" + if (counter + word.length + 1) < width + memo = "#{memo} #{word}" + counter += (word.length + 1) + else + memo = "#{memo}\n#{word}" + counter = word.length + end + memo + end + end.compact! paras.each do |para| para.split("\n").each do |line| @@ -239,11 +259,11 @@ class Bundler::Thor # # ==== Parameters # destination<String>:: the destination file to solve conflicts - # block<Proc>:: an optional block that returns the value to be used in diff + # block<Proc>:: an optional block that returns the value to be used in diff and merge # def file_collision(destination) return true if @always_force - options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" + options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]" loop do answer = ask( @@ -267,6 +287,13 @@ class Bundler::Thor when is?(:diff) show_diff(destination, yield) if block_given? say "Retrying..." + when is?(:merge) + if block_given? && !merge_tool.empty? + merge(destination, yield) + return nil + end + + say "Please specify merge tool to `THOR_MERGE` env." else say file_collision_help end @@ -279,11 +306,11 @@ class Bundler::Thor result = if ENV["THOR_COLUMNS"] ENV["THOR_COLUMNS"].to_i else - unix? ? dynamic_width : 80 + unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH end - result < 10 ? 80 : result + result < 10 ? DEFAULT_TERMINAL_WIDTH : result rescue - 80 + DEFAULT_TERMINAL_WIDTH end # Called if something goes wrong during the execution. This is used by Bundler::Thor @@ -344,6 +371,7 @@ class Bundler::Thor q - quit, abort d - diff, show the differences between the old and the new h - help, show this help + m - merge, run merge tool HELP end @@ -432,6 +460,23 @@ class Bundler::Thor end correct_answer end + + def merge(destination, content) #:nodoc: + require "tempfile" + Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp| + temp.write content + temp.rewind + system %(#{merge_tool} "#{temp.path}" "#{destination}") + end + end + + def merge_tool #:nodoc: + @merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool + end + + def git_merge_tool #:nodoc: + `git config merge.tool`.rstrip rescue "" + end end end end diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb index 5d03177a28..0fe7d4d859 100644 --- a/lib/bundler/vendor/thor/lib/thor/util.rb +++ b/lib/bundler/vendor/thor/lib/thor/util.rb @@ -27,7 +27,7 @@ class Bundler::Thor end # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor - # commands can be added to a sandbox, this method is also responsable for + # commands can be added to a sandbox, this method is also responsible for # removing the sandbox namespace. # # This method should not be used in general because it's used to deal with diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb index df8f18821a..98f2b79081 100644 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ b/lib/bundler/vendor/thor/lib/thor/version.rb @@ -1,3 +1,3 @@ class Bundler::Thor - VERSION = "0.20.0" + VERSION = "0.20.3" end diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 401f62447c..cfd6300950 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,13 +1,10 @@ # frozen_string_literal: false -# Ruby 1.9.3 and old RubyGems don't play nice with frozen version strings -# rubocop:disable MutableConstant - module Bundler # We're doing this because we might write tests that deal # with other versions of bundler and we are unsure how to # handle this better. - VERSION = "2.0.1" unless defined?(::Bundler::VERSION) + VERSION = "2.1.0.pre.1".freeze unless defined?(::Bundler::VERSION) def self.overwrite_loaded_gem_version begin diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb index ec25716cde..12a956d6a0 100644 --- a/lib/bundler/version_ranges.rb +++ b/lib/bundler/version_ranges.rb @@ -5,11 +5,42 @@ module Bundler NEq = Struct.new(:version) ReqR = Struct.new(:left, :right) class ReqR - Endpoint = Struct.new(:version, :inclusive) + Endpoint = Struct.new(:version, :inclusive) do + def <=>(other) + if version.equal?(INFINITY) + return 0 if other.version.equal?(INFINITY) + return 1 + elsif other.version.equal?(INFINITY) + return -1 + end + + comp = version <=> other.version + return comp unless comp.zero? + + if inclusive && !other.inclusive + 1 + elsif !inclusive && other.inclusive + -1 + else + 0 + end + end + end + def to_s "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}" end - INFINITY = Object.new.freeze + INFINITY = begin + inf = Object.new + def inf.to_s + "∞" + end + def inf.<=>(other) + return 0 if other.equal?(self) + 1 + end + inf.freeze + end ZERO = Gem::Version.new("0.a") def cover?(v) @@ -32,6 +63,15 @@ module Bundler left.version == right.version end + def <=>(other) + return -1 if other.equal?(INFINITY) + + comp = left <=> other.left + return comp unless comp.zero? + + right <=> other.right + end + UNIVERSAL = ReqR.new(ReqR::Endpoint.new(Gem::Version.new("0.a"), true), ReqR::Endpoint.new(ReqR::INFINITY, false)).freeze end @@ -57,7 +97,7 @@ module Bundler end.uniq ranges, neqs = ranges.partition {|r| !r.is_a?(NEq) } - [ranges.sort_by {|range| [range.left.version, range.left.inclusive ? 0 : 1] }, neqs.map(&:version)] + [ranges.sort, neqs.map(&:version)] end def self.empty?(ranges, neqs) @@ -66,8 +106,14 @@ module Bundler next false if curr_range.single? && neqs.include?(curr_range.left.version) next curr_range if last_range.right.version == ReqR::INFINITY case last_range.right.version <=> curr_range.left.version - when 1 then next curr_range - when 0 then next(last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) && curr_range) + # higher + when 1 then next ReqR.new(curr_range.left, last_range.right) + # equal + when 0 + if last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) + ReqR.new(curr_range.left, [curr_range.right, last_range.right].max) + end + # lower when -1 then next false end end diff --git a/lib/bundler/vlad.rb b/lib/bundler/vlad.rb index a6b13435c9..68181e7db8 100644 --- a/lib/bundler/vlad.rb +++ b/lib/bundler/vlad.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 3, +Bundler::SharedHelpers.major_deprecation 2, "The Bundler task for Vlad" # Vlad task for Bundler. diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb index e91cfa7805..3471654b43 100644 --- a/lib/bundler/worker.rb +++ b/lib/bundler/worker.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require "thread" - module Bundler class Worker POISON = Object.new @@ -62,7 +60,7 @@ module Bundler def apply_func(obj, i) @func.call(obj, i) - rescue Exception => e + rescue Exception => e # rubocop:disable Lint/RescueException WrappedException.new(e) end diff --git a/lib/bundler/yaml_serializer.rb b/lib/bundler/yaml_serializer.rb index 0fd81c40ef..374b3bb5e3 100644 --- a/lib/bundler/yaml_serializer.rb +++ b/lib/bundler/yaml_serializer.rb @@ -32,7 +32,7 @@ module Bundler (.*) # value \1 # matching closing quote $ - /xo + /xo.freeze HASH_REGEX = / ^ @@ -40,12 +40,11 @@ module Bundler (.+) # key (?::(?=(?:\s|$))) # : (without the lookahead the #key includes this when : is present in value) [ ]? - (?: !\s)? # optional exclamation mark found with ruby 1.9.3 (['"]?) # optional opening quote (.*) # value \3 # matching closing quote $ - /xo + /xo.freeze def load(str) res = {} |