diff options
156 files changed, 5231 insertions, 1512 deletions
@@ -1,8 +1,8 @@ # coding: utf-8 # frozen_string_literal: true -lib = File.expand_path("../lib/", __FILE__) -$:.unshift lib unless $:.include?(lib) -require "bundler/version" Gem::Specification.new do |s| s.name = "bundler" @@ -27,225 +27,315 @@ Gem::Specification.new do |s| } end - s.required_ruby_version = ">= 1.8.7" - s.required_rubygems_version = ">= 1.3.6" 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 "ronn", "~> 0.7.3" - s.add_development_dependency "rspec", "~> 3.5" - s.files = [ - "lib/bundler.gemspec", - "bin/bundle", - "bin/bundle_ruby", - "bin/bundler", - "lib/bundler.rb", - "lib/bundler/capistrano.rb", - "lib/bundler/cli.rb", - "lib/bundler/cli/add.rb", - "lib/bundler/cli/binstubs.rb", - "lib/bundler/cli/cache.rb", - "lib/bundler/cli/check.rb", - "lib/bundler/cli/clean.rb", - "lib/bundler/cli/common.rb", - "lib/bundler/cli/config.rb", - "lib/bundler/cli/console.rb", - "lib/bundler/cli/doctor.rb", - "lib/bundler/cli/exec.rb", - "lib/bundler/cli/gem.rb", - "lib/bundler/cli/info.rb", - "lib/bundler/cli/init.rb", - "lib/bundler/cli/inject.rb", - "lib/bundler/cli/install.rb", - "lib/bundler/cli/issue.rb", - "lib/bundler/cli/lock.rb", - "lib/bundler/cli/open.rb", - "lib/bundler/cli/outdated.rb", - "lib/bundler/cli/package.rb", - "lib/bundler/cli/platform.rb", - "lib/bundler/cli/plugin.rb", - "lib/bundler/cli/pristine.rb", - "lib/bundler/cli/show.rb", - "lib/bundler/cli/update.rb", - "lib/bundler/cli/viz.rb", - "lib/bundler/compact_index_client.rb", - "lib/bundler/compact_index_client/cache.rb", - "lib/bundler/compact_index_client/updater.rb", - "lib/bundler/constants.rb", - "lib/bundler/current_ruby.rb", - "lib/bundler/definition.rb", - "lib/bundler/dep_proxy.rb", - "lib/bundler/dependency.rb", - "lib/bundler/deployment.rb", - "lib/bundler/deprecate.rb", - "lib/bundler/dsl.rb", - "lib/bundler/endpoint_specification.rb", - "lib/bundler/env.rb", - "lib/bundler/environment_preserver.rb", - "lib/bundler/errors.rb", - "lib/bundler/feature_flag.rb", - "lib/bundler/fetcher.rb", - "lib/bundler/fetcher/base.rb", - "lib/bundler/fetcher/compact_index.rb", - "lib/bundler/fetcher/dependency.rb", - "lib/bundler/fetcher/downloader.rb", - "lib/bundler/fetcher/index.rb", - "lib/bundler/friendly_errors.rb", - "lib/bundler/gem_helper.rb", - "lib/bundler/gem_helpers.rb", - "lib/bundler/gem_remote_fetcher.rb", - "lib/bundler/gem_tasks.rb", - "lib/bundler/gem_version_promoter.rb", - "lib/bundler/gemdeps.rb", - "lib/bundler/graph.rb", - "lib/bundler/index.rb", - "lib/bundler/injector.rb", - "lib/bundler/inline.rb", - "lib/bundler/installer.rb", - "lib/bundler/installer/gem_installer.rb", - "lib/bundler/installer/parallel_installer.rb", - "lib/bundler/installer/standalone.rb", - "lib/bundler/lazy_specification.rb", - "lib/bundler/lockfile_parser.rb", - "lib/bundler/match_platform.rb", - "lib/bundler/mirror.rb", - "lib/bundler/plugin.rb", - "lib/bundler/plugin/api.rb", - "lib/bundler/plugin/api/source.rb", - "lib/bundler/plugin/dsl.rb", - "lib/bundler/plugin/index.rb", - "lib/bundler/plugin/installer.rb", - "lib/bundler/plugin/installer/git.rb", - "lib/bundler/plugin/installer/rubygems.rb", - "lib/bundler/plugin/source_list.rb", - "lib/bundler/psyched_yaml.rb", - "lib/bundler/remote_specification.rb", - "lib/bundler/resolver.rb", - "lib/bundler/retry.rb", - "lib/bundler/ruby_dsl.rb", - "lib/bundler/ruby_version.rb", - "lib/bundler/rubygems_ext.rb", - "lib/bundler/rubygems_gem_installer.rb", - "lib/bundler/rubygems_integration.rb", - "lib/bundler/runtime.rb", - "lib/bundler/settings.rb", - "lib/bundler/setup.rb", - "lib/bundler/shared_helpers.rb", - "lib/bundler/similarity_detector.rb", - "lib/bundler/source.rb", - "lib/bundler/source/gemspec.rb", - "lib/bundler/source/git.rb", - "lib/bundler/source/git/git_proxy.rb", - "lib/bundler/source/path.rb", - "lib/bundler/source/path/installer.rb", - "lib/bundler/source/rubygems.rb", - "lib/bundler/source/rubygems/remote.rb", - "lib/bundler/source_list.rb", - "lib/bundler/spec_set.rb", - "lib/bundler/ssl_certs/.document", - "lib/bundler/ssl_certs/certificate_manager.rb", - "lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem", - "lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem", - "lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem", - "lib/bundler/stub_specification.rb", - "lib/bundler/templates/Executable", - "lib/bundler/templates/Executable.standalone", - "lib/bundler/templates/Gemfile", - "lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt", - "lib/bundler/templates/newgem/Gemfile.tt", - "lib/bundler/templates/newgem/LICENSE.txt.tt", - "lib/bundler/templates/newgem/README.md.tt", - "lib/bundler/templates/newgem/Rakefile.tt", - "lib/bundler/templates/newgem/bin/console.tt", - "lib/bundler/templates/newgem/bin/setup.tt", - "lib/bundler/templates/newgem/exe/newgem.tt", - "lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt", - "lib/bundler/templates/newgem/ext/newgem/newgem.c.tt", - "lib/bundler/templates/newgem/ext/newgem/newgem.h.tt", - "lib/bundler/templates/newgem/gitignore.tt", - "lib/bundler/templates/newgem/lib/newgem.rb.tt", - "lib/bundler/templates/newgem/lib/newgem/version.rb.tt", - "lib/bundler/templates/newgem/newgem.gemspec.tt", - "lib/bundler/templates/newgem/rspec.tt", - "lib/bundler/templates/newgem/spec/newgem_spec.rb.tt", - "lib/bundler/templates/newgem/spec/spec_helper.rb.tt", - "lib/bundler/templates/newgem/test/newgem_test.rb.tt", - "lib/bundler/templates/newgem/test/test_helper.rb.tt", - "lib/bundler/templates/newgem/travis.yml.tt", - "lib/bundler/ui.rb", - "lib/bundler/ui/rg_proxy.rb", - "lib/bundler/ui/shell.rb", - "lib/bundler/ui/silent.rb", - "lib/bundler/uri_credentials_filter.rb", - "lib/bundler/vendor/molinillo/lib/molinillo.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/errors.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb", - "lib/bundler/vendor/molinillo/lib/molinillo/state.rb", - "lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb", - "lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb", - "lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb", - "lib/bundler/vendor/thor/lib/thor.rb", - "lib/bundler/vendor/thor/lib/thor/actions.rb", - "lib/bundler/vendor/thor/lib/thor/actions/create_file.rb", - "lib/bundler/vendor/thor/lib/thor/actions/create_link.rb", - "lib/bundler/vendor/thor/lib/thor/actions/directory.rb", - "lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb", - "lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb", - "lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb", - "lib/bundler/vendor/thor/lib/thor/base.rb", - "lib/bundler/vendor/thor/lib/thor/command.rb", - "lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb", - "lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb", - "lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb", - "lib/bundler/vendor/thor/lib/thor/error.rb", - "lib/bundler/vendor/thor/lib/thor/group.rb", - "lib/bundler/vendor/thor/lib/thor/invocation.rb", - "lib/bundler/vendor/thor/lib/thor/line_editor.rb", - "lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb", - "lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb", - "lib/bundler/vendor/thor/lib/thor/parser.rb", - "lib/bundler/vendor/thor/lib/thor/parser/argument.rb", - "lib/bundler/vendor/thor/lib/thor/parser/arguments.rb", - "lib/bundler/vendor/thor/lib/thor/parser/option.rb", - "lib/bundler/vendor/thor/lib/thor/parser/options.rb", - "lib/bundler/vendor/thor/lib/thor/rake_compat.rb", - "lib/bundler/vendor/thor/lib/thor/runner.rb", - "lib/bundler/vendor/thor/lib/thor/shell.rb", - "lib/bundler/vendor/thor/lib/thor/shell/basic.rb", - "lib/bundler/vendor/thor/lib/thor/shell/color.rb", - "lib/bundler/vendor/thor/lib/thor/shell/html.rb", - "lib/bundler/vendor/thor/lib/thor/util.rb", - "lib/bundler/vendor/thor/lib/thor/version.rb", - "lib/bundler/vendored_molinillo.rb", - "lib/bundler/vendored_persistent.rb", - "lib/bundler/vendored_thor.rb", - "lib/bundler/version.rb", - "lib/bundler/version_ranges.rb", - "lib/bundler/vlad.rb", - "lib/bundler/worker.rb", - "lib/bundler/yaml_serializer.rb" ] s.bindir = "exe" - s.executables = %w(bundle bundler) s.require_paths = ["lib"] end @@ -1,9 +1,11 @@ # frozen_string_literal: true -require "fileutils" require "pathname" require "rbconfig" require "thread" -require "tmpdir" require "bundler/errors" require "bundler/environment_preserver" @@ -13,9 +15,10 @@ require "bundler/rubygems_integration" require "bundler/version" require "bundler/constants" require "bundler/current_ruby" module Bundler - environment_preserver = EnvironmentPreserver.new(ENV, %w(PATH GEM_PATH)) ORIGINAL_ENV = environment_preserver.restore ENV.replace(environment_preserver.backup) SUDO_MUTEX = Mutex.new @@ -40,6 +43,7 @@ module Bundler autoload :LazySpecification, "bundler/lazy_specification" autoload :LockfileParser, "bundler/lockfile_parser" autoload :MatchPlatform, "bundler/match_platform" autoload :RemoteSpecification, "bundler/remote_specification" autoload :Resolver, "bundler/resolver" autoload :Retry, "bundler/retry" @@ -58,8 +62,6 @@ module Bundler autoload :VersionRanges, "bundler/version_ranges" class << self - attr_writer :bundle_path - def configure @configured ||= configure_gem_home_and_path end @@ -75,7 +77,11 @@ module Bundler # Returns absolute path of where gems are installed on the filesystem. def bundle_path - @bundle_path ||= Pathname.new(settings.path).expand_path(root) end # Returns absolute location of where binstubs are installed to. @@ -113,7 +119,7 @@ module Bundler end def environment - SharedHelpers.major_deprecation "Bundler.environment has been removed in favor of Bundler.load" load end @@ -130,6 +136,12 @@ module Bundler end end def locked_gems @locked_gems ||= if defined?(@definition) && @definition @@ -168,6 +180,7 @@ module Bundler def tmp_home_path(login, warning) login ||= "unknown" path = Pathname.new(Dir.tmpdir).join("bundler", "home") SharedHelpers.filesystem_access(path) do |tmp_home_path| unless tmp_home_path.exist? @@ -196,17 +209,13 @@ module Bundler bundle_path.join("specifications") end - def cache - bundle_path.join("cache/bundler") - end - def user_cache user_bundle_path.join("cache") end def root @root ||= begin - default_gemfile.dirname.expand_path rescue GemfileNotFound bundle_dir = default_bundle_dir raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir @@ -215,8 +224,8 @@ module Bundler end def app_config_path - if ENV["BUNDLE_APP_CONFIG"] - Pathname.new(ENV["BUNDLE_APP_CONFIG"]).expand_path(root) else root.join(".bundle") end @@ -224,10 +233,11 @@ module Bundler def app_cache(custom_path = nil) path = custom_path || root - path.join(settings.app_cache_path) end def tmp(name = Process.pid.to_s) Pathname.new(Dir.mktmpdir(["bundler", name])) end @@ -257,7 +267,7 @@ EOF # @deprecated Use `original_env` instead # @return [Hash] Environment with all bundler-related variables removed def clean_env - Bundler::SharedHelpers.major_deprecation("`Bundler.clean_env` has weird edge cases, use `.original_env` instead") env = original_env if env.key?("BUNDLER_ORIG_MANPATH") @@ -313,21 +323,25 @@ EOF end def system_bindir - # Gem.bindir doesn't always return the location that Rubygems will install - # system binaries. If you put '-n foo' in your .gemrc, Rubygems will - # install binstubs there instead. Unfortunately, Rubygems doesn't expose # that directory at all, so rather than parse .gemrc ourselves, we allow # the directory to be set as well, via `bundle config bindir foo`. Bundler.settings[:system_bindir] || Bundler.rubygems.gem_bindir end def requires_sudo? return @requires_sudo if defined?(@requires_sudo_ran) sudo_present = which "sudo" if settings.allow_sudo? if sudo_present - # the bundle path and subdirectories need to be writable for Rubygems # to be able to unpack and install gems without exploding path = bundle_path path = path.parent until path.exist? @@ -449,14 +463,17 @@ EOF end def reset_paths! - @root = nil - @settings = nil @definition = nil - @setup = nil @load = nil @locked_gems = nil - @bundle_path = nil - @bin_path = nil @user_home = nil end @@ -470,6 +487,8 @@ EOF private def eval_yaml_gemspec(path, contents) # If the YAML is invalid, Syck raises an ArgumentError, and Psych # raises a Psych::SyntaxError. See psyched_yaml.rb for more info. Gem::Specification.from_yaml(contents) @@ -478,7 +497,7 @@ EOF end def eval_gemspec(path, contents) - eval(contents, TOPLEVEL_BINDING, path.expand_path.to_s) rescue ScriptError, StandardError => e msg = "There was an error while loading `#{path.basename}`: #{e.message}" @@ -495,14 +514,14 @@ EOF bundle_path end - def configure_gem_path(env = ENV, settings = self.settings) blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty? - if settings[:disable_shared_gems] # this needs to be empty string to cause # PathSupport.split_gem_path to only load up the # Bundler --path setting as the GEM_PATH. env["GEM_PATH"] = "" - elsif blank_home || Bundler.rubygems.gem_dir != bundle_path.to_s possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path] paths = possibles.flatten.compact.uniq.reject(&:empty?) env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR) @@ -510,14 +529,7 @@ EOF end def configure_gem_home - # TODO: This mkdir_p is only needed for JRuby <= 1.5 and should go away (GH #602) - begin - FileUtils.mkdir_p bundle_path.to_s - rescue - nil - end - - ENV["GEM_HOME"] = File.expand_path(bundle_path, root) Bundler.rubygems.clear_paths end @@ -0,0 +1,36 @@ @@ -1,4 +1,9 @@ # frozen_string_literal: true # Capistrano task for Bundler. # # Add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and @@ -1,13 +1,18 @@ # frozen_string_literal: true require "bundler" require "bundler/vendored_thor" module Bundler class CLI < Thor - AUTO_INSTALL_CMDS = %w(show binstubs outdated exec open console licenses clean).freeze - PARSEABLE_COMMANDS = %w( check config help exec platform show version - ).freeze def self.start(*) super @@ -30,11 +35,11 @@ module Bundler custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile] if custom_gemfile && !custom_gemfile.empty? - ENV["BUNDLE_GEMFILE"] = File.expand_path(custom_gemfile) Bundler.reset_paths! end - Bundler.settings[:retry] = options[:retry] if options[:retry] current_cmd = args.last[:current_command].name auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) @@ -42,7 +47,6 @@ module Bundler raise InvalidOption, e.message ensure self.options ||= {} - Bundler.settings.cli_flags_given = !options.empty? unprinted_warnings = Bundler.ui.unprinted_warnings Bundler.ui = UI::Shell.new(options) Bundler.ui.level = "debug" if options["verbose"] @@ -57,10 +61,41 @@ module Bundler end end check_unknown_options!(:except => [:config, :exec]) stop_on_unknown_option! :exec - default_task :install class_option "no-color", :type => :boolean, :desc => "Disable colorization in output" class_option "retry", :type => :numeric, :aliases => "-r", :banner => "NUM", :desc => "Specify the number of times you wish to attempt network commands" @@ -107,7 +142,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 - 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 @@ -124,7 +159,7 @@ module Bundler method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile" 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" map "c" => "check" def check require "bundler/cli/check" @@ -142,13 +177,13 @@ module Bundler If the bundle has already been installed, bundler will tell you so and then exit. D - method_option "binstubs", :type => :string, :lazy_default => "bin", :banner => "Generate bin stubs for bundled gems to ./bin" - method_option "clean", :type => :boolean, :banner => "Run bundle clean automatically after install" - method_option "deployment", :type => :boolean, :banner => "Install using defaults tuned for deployment environments" - 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" @@ -158,28 +193,29 @@ 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" - method_option "no-cache", :type => :boolean, :banner => "Don't update the existing gem cache." - method_option "force", :type => :boolean, :banner => "Force downloading every gem." - method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache." - 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." - 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" - 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("|") - method_option "without", :type => :array, :banner => "Exclude gems that are part of the specified named group." - method_option "with", :type => :array, :banner => "Include gems that are part of the specified named group." map "i" => "install" def install @@ -189,7 +225,7 @@ module Bundler end end - desc "update [OPTIONS]", "update the current environment" long_desc <<-D Update will install the newest versions of the gems listed in the Gemfile. Use update when you have changed the Gemfile, or if you want to get the newest @@ -223,6 +259,8 @@ module Bundler "Do not allow any gem to be updated past latest -- | --minor | --major" method_option "conservative", :type => :boolean, :banner => "Use bundle install conservative update behavior and do not allow shared dependencies to be updated." def update(*gems) require "bundler/cli/update" Update.new(options, gems).run @@ -238,12 +276,24 @@ module Bundler method_option "outdated", :type => :boolean, :banner => "Show verbose output including whether gems are outdated." def show(gem_name = nil) - Bundler::SharedHelpers.major_deprecation("use `bundle show` instead of `bundle list`") if ARGV[0] == "list" require "bundler/cli/show" Show.new(options, gem_name).run end - # TODO: 2.0 remove `bundle list` - map %w(list) => "show" desc "info GEM [OPTIONS]", "Show information for the given gem" method_option "path", :type => :boolean, :banner => "Print full path to gem" @@ -262,6 +312,8 @@ module Bundler "Overwrite existing binstubs if they exist" method_option "path", :type => :string, :lazy_default => "bin", :banner => "Binstub destination directory (default bin)" method_option "standalone", :type => :boolean, :banner => "Make binstubs that can work without the Bundler runtime" def binstubs(*gems) @@ -282,7 +334,7 @@ module Bundler Add.new(options.dup, gem_name).run end - desc "outdated GEM [OPTIONS]", "list installed gems with newer versions available" long_desc <<-D Outdated lists the names and versions of gems that have a newer version available in the given source. Calling outdated with [GEM [GEM]] will only check for newer @@ -292,8 +344,8 @@ module Bundler For more information on level options (--major, --minor, --, --update-strict) see documentation on the same options on the update command. D - method_option "group", :aliases => "--group", :type => :string, :banner => "List gems from a specific group" - method_option "groups", :aliases => "--groups", :type => :boolean, :banner => "List gems organized by groups" method_option "local", :type => :boolean, :banner => "Do not attempt to fetch gems remotely and use the gem cache instead" method_option "pre", :type => :boolean, :banner => "Check for newer gems" @@ -315,17 +367,27 @@ module Bundler Outdated.new(options, gems).run end - desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true - method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)." - method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one" - method_option "no-prune", :type => :boolean, :banner => "Don't remove stale gems from the cache." - def cache - require "bundler/cli/cache" - Cache.new(options).run end - desc "package [OPTIONS]", "Locks and then caches all of the gems into vendor/cache" - method_option "all", :type => :boolean, :banner => "Include all sources (including path and git)." method_option "all-platforms", :type => :boolean, :banner => "Include gems for all platforms present in the lockfile, not only the current one" method_option "cache-path", :type => :string, :banner => "Specify a different cache path than the default (vendor/cache)." @@ -347,14 +409,14 @@ module Bundler require "bundler/cli/package" Package.new(options).run end - map %w(pack) => :package desc "exec [OPTIONS]", "Run the command in context of the bundle" method_option :keep_file_descriptors, :type => :boolean, :default => false long_desc <<-D Exec runs a command, providing it access to the gems in the bundle. While using bundle exec you can require and call the bundled gems as if they were installed - into the system wide Rubygems repository. D map "e" => "exec" def exec(*args) @@ -362,7 +424,7 @@ module Bundler Exec.new(options, args).run end - desc "config NAME [VALUE]", "retrieve or set a configuration value" long_desc <<-D Retrieves or sets a configuration value. If only one parameter is provided, retrieve the value. If two parameters are provided, replace the existing value with the newly provided one. @@ -386,18 +448,28 @@ module Bundler Open.new(options, name).run end - desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded" - def console(group = nil) - # TODO: Remove for 2.0 - require "bundler/cli/console" - Console.new(options, group).run end desc "version", "Prints the bundler's version information" def version - Bundler.ui.info "Bundler version #{Bundler::VERSION}" end - map %w(-v --version) => :version desc "licenses", "Prints the license of all gems in the bundle" def licenses @@ -413,7 +485,7 @@ module Bundler end end - desc "viz [OPTIONS]", "Generates a visual dependency graph" long_desc <<-D Viz generates a PNG file of the current Gemfile as a dependency graph. Viz requires the ruby-graphviz gem (and its dependencies). @@ -431,7 +503,7 @@ module Bundler old_gem = instance_method(:gem) - desc "gem GEM [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 :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR", @@ -470,7 +542,7 @@ module Bundler File.expand_path(File.join(File.dirname(__FILE__), "templates")) end - desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory" method_option "dry-run", :type => :boolean, :default => false, :banner => "Only print out changes, do not clean gems" method_option "force", :type => :boolean, :default => false, :banner => @@ -488,13 +560,13 @@ module Bundler Platform.new(options).run end - desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile" method_option "source", :type => :string, :banner => "Install gem from the given source" method_option "group", :type => :string, :banner => "Install gem into a bundler group" def inject(name, version) - SharedHelpers.major_deprecation "The `inject` command has been replaced by the `add` command" require "bundler/cli/inject" Inject.new(options.dup, name, version).run end @@ -531,7 +603,7 @@ module Bundler desc "env", "Print information about the environment Bundler is running under" def env - Env.new.write($stdout) end desc "doctor [OPTIONS]", "Checks the bundle for common problems" @@ -555,15 +627,20 @@ module Bundler Issue.new.run end - desc "pristine", "Restores installed gems to pristine condition from files located in the gem cache. Gem installed from a git repository will be issued `git checkout --force`." - def pristine require "bundler/cli/pristine" - Pristine.new.run end if Bundler.feature_flag.plugins? require "bundler/cli/plugin" - desc "plugin SUBCOMMAND ...ARGS", "manage the bundler plugins" subcommand "plugin", Plugin end @@ -571,14 +648,14 @@ module Bundler # into the corresponding `bundle help #{command}` call def self.reformatted_help_args(args) bundler_commands = all_commands.keys - help_flags = %w(--help -h) - exec_commands = %w(e ex exe exec) help_used = args.index {|a| help_flags.include? a } exec_used = args.index {|a| exec_commands.include? a } command = args.find {|a| bundler_commands.include? a } if exec_used && help_used if exec_used + help_used == 1 - %w(help exec) else args end @@ -613,16 +690,20 @@ module Bundler end end def print_command return unless Bundler.ui.debug? - _, _, config = @_initializer - current_command = config[:current_command] - command_name = current_command.name return if PARSEABLE_COMMANDS.include?(command_name) command = ["bundle", command_name] + args options_to_print = options.dup options_to_print.delete_if do |k, v| - next unless o = current_command.options[k] o.default == v end command << Thor::Options.to_switches(options_to_print.sort_by(&:first)).strip @@ -633,8 +714,6 @@ module Bundler def warn_on_outdated_bundler return if Bundler.settings[:disable_version_check] - _, _, config = @_initializer - current_command = config[:current_command] command_name = current_command.name return if PARSEABLE_COMMANDS.include?(command_name) @@ -649,8 +728,17 @@ module Bundler current = Gem::Version.new(VERSION) return if current >= latest - Bundler.ui.warn "The latest bundler is #{latest}, but you are currently running #{current}.\nTo update, run `gem install bundler#{" --pre" if latest.prerelease?}`" rescue nil end @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Add @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Binstubs @@ -11,8 +10,10 @@ module Bundler def run Bundler.definition.validate_runtime! - Bundler.settings[:bin] = options["path"] if options["path"] - Bundler.settings[:bin] = nil if options["path"] && options["path"].empty? installer = Installer.new(Bundler.root, Bundler.definition) if gems.empty? @@ -28,10 +29,11 @@ module Bundler ) end - if spec.name == "bundler" - Bundler.ui.warn "Sorry, Bundler can only be run via Rubygems." - elsif options[:standalone] - installer.generate_standalone_bundler_executable_stubs(spec) else installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true) end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Cache attr_reader :options @@ -10,9 +11,9 @@ module Bundler Bundler.definition.validate_runtime! Bundler.definition.resolve_with_cache! setup_cache_all - Bundler.settings[:cache_all_platforms] = options["all-platforms"] if options.key?("all-platforms") Bundler.load.cache - Bundler.settings[:no_prune] = true if options["no-prune"] Bundler.load.lock rescue GemNotFound => e Bundler.ui.error(e.message) @@ -23,9 +24,9 @@ module Bundler private def setup_cache_all - Bundler.settings[:cache_all] = options[:all] if options.key?("all") - if Bundler.definition.has_local_dependencies? && !Bundler.settings[: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." @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Check attr_reader :options @@ -8,10 +9,7 @@ module Bundler end def run - if options[:path] - Bundler.settings[:path] = File.expand_path(options[:path]) - Bundler.settings[:disable_shared_gems] = true - end begin definition = Bundler.definition @@ -28,7 +26,7 @@ module Bundler not_installed.each {|s| Bundler.ui.error " * #{s.name} (#{s.version})" } Bundler.ui.warn "Install missing gems with `bundle install`" exit 1 - elsif !Bundler.default_lockfile.file? && Bundler.settings[:frozen] Bundler.ui.error "This bundle has been frozen, but there is no #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} present" exit 1 else @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Clean attr_reader :options @@ -15,12 +16,10 @@ module Bundler protected def require_path_or_force - if !Bundler.settings[:path] && !options[:force] - Bundler.ui.error "Cleaning all the gems on your system is dangerous! " \ - "If you're sure you want to remove every system gem not in this " \ - "bundle, run `bundle clean --force`." - exit 1 - end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module CLI::Common def self.output_post_install_messages(messages) @@ -14,12 +15,12 @@ module Bundler end def self.output_without_groups_message - return unless Bundler.settings.without.any? Bundler.ui.confirm without_groups_message end def self.without_groups_message - 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" @@ -89,5 +90,13 @@ module Bundler def self._level_options(options) [:major, :minor, :].select {|v| options.keys.include?(v.to_s) } end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Config attr_reader :name, :options, :scope, :thor @@ -112,7 +113,7 @@ module Bundler end def valid_scope?(scope) - %w(delete local global).include?(scope) end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Console attr_reader :options, :group @@ -8,7 +9,7 @@ module Bundler end def run - Bundler::SharedHelpers.major_deprecation "bundle console will be replaced " \ "by `bin/console` generated by `bundle gem <name>`" group ? Bundler.require(:default, *(group.split.map!(&:to_sym))) : Bundler.require @@ -62,6 +62,7 @@ module Bundler def run Bundler.ui.level = "error" if options[:quiet] check! definition = Bundler.definition @@ -1,11 +1,12 @@ # frozen_string_literal: true require "bundler/current_ruby" module Bundler class CLI::Exec attr_reader :options, :args, :cmd - RESERVED_SIGNALS = %w(SEGV BUS ILL FPE VTALRM KILL STOP).freeze def initialize(options, args) @options = options @@ -72,7 +73,7 @@ module Bundler signals = Signal.list.keys - RESERVED_SIGNALS signals.each {|s| trap(s, "DEFAULT") } Kernel.load(file) - rescue SystemExit raise rescue Exception => e # rubocop:disable Lint/RescueException Bundler.ui = ui @@ -1,4 +1,5 @@ # frozen_string_literal: true require "pathname" module Bundler @@ -71,10 +72,10 @@ module Bundler "bin/setup.tt" => "bin/setup" } - executables = %w( bin/console bin/setup - ) templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present? @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Info @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Init attr_reader :options @@ -7,8 +8,8 @@ module Bundler end def run - if File.exist?("Gemfile") - Bundler.ui.error "Gemfile already exists at #{SharedHelpers.pwd}/Gemfile" exit 1 end @@ -21,14 +22,24 @@ module Bundler spec = Bundler.load_gemspec_uncached(gemspec) - puts "Writing new Gemfile to #{SharedHelpers.pwd}/Gemfile" - File.open("Gemfile", "wb") do |file| file << "# Generated from #{gemspec}\n" file << spec.to_gemfile end else - puts "Writing new Gemfile to #{SharedHelpers.pwd}/Gemfile" - FileUtils.cp(File.expand_path("../../templates/Gemfile", __FILE__), "Gemfile") end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Inject attr_reader :options, :name, :version, :group, :source, :gems @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Install @@ -13,17 +12,9 @@ module Bundler warn_if_root - [:with, :without].each do |option| - if options[option] - options[option] = options[option].join(":").tr(" ", ":").split(":") - end - end - - check_for_group_conflicts - normalize_groups - ENV["RB_USER_INSTALL"] = "1" if Bundler::FREEBSD # Disable color in deployment mode Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment] @@ -32,22 +23,28 @@ module Bundler check_trust_policy - if options[:deployment] || options[:frozen] unless Bundler.default_lockfile.exist? - flag = options[:deployment] ? "--deployment" : "--frozen" - raise ProductionError, "The #{flag} flag requires a #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}. Please make " \ "sure you have checked your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} into version control " \ "before deploying." end options[:local] = true if Bundler.app_cache.exist? - Bundler.settings[:frozen] = "1" end # When install is called with --no-deployment, disable deployment mode if options[:deployment] == false - Bundler.settings.delete(:frozen) options[:system] = true end @@ -56,7 +53,7 @@ module Bundler Bundler::Fetcher.disable_endpoint = options["full-index"] if options["binstubs"] - Bundler::SharedHelpers.major_deprecation \ "The --binstubs option will be removed in favor of `bundle binstubs`" end @@ -66,24 +63,24 @@ module Bundler definition.validate_runtime! installer = Installer.install(Bundler.root, definition, options) - Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.settings[:frozen] Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}." Bundler::CLI::Common.output_without_groups_message - if Bundler.settings[:path] - absolute_path = File.expand_path(Bundler.settings[:path]) - relative_path = absolute_path.sub(File.expand_path(".") + File::SEPARATOR, "." + File::SEPARATOR) - Bundler.ui.confirm "Bundled gems are installed into #{relative_path}." - else Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed." end Bundler::CLI::Common.output_post_install_messages installer.post_install_messages warn_ambiguous_gems - if Bundler.settings[:clean] && Bundler.settings[:path] require "bundler/cli/clean" Bundler::CLI::Clean.new(options).run end @@ -124,15 +121,11 @@ module Bundler "#{count} #{count == 1 ? "gem" : "gems"} now installed" end - def check_for_group_conflicts - if options[:without] && options[:with] - conflicting_groups = options[:without] & options[:with] - unless conflicting_groups.empty? - Bundler.ui.error "You can't list a group in both, --with and --without." \ - " The offending groups are: #{conflicting_groups.join(", ")}." - exit 1 - end - end end def check_for_options_conflicts @@ -145,28 +138,29 @@ module Bundler end def check_trust_policy - if options["trust-policy"] - unless Bundler.rubygems.security_policies.keys.include?(options["trust-policy"]) - Bundler.ui.error "Rubygems doesn't know about trust policy '#{options["trust-policy"]}'. " \ - "The known policies are: #{Bundler.rubygems.security_policies.keys.join(", ")}." - exit 1 - end - Bundler.settings["trust-policy"] = options["trust-policy"] - else - Bundler.settings["trust-policy"] = nil if Bundler.settings["trust-policy"] end end def normalize_groups - Bundler.settings.with = [] if options[:with] && options[:with].empty? - Bundler.settings.without = [] if options[:without] && options[:without].empty? - with = options.fetch("with", []) - with |= Bundler.settings.with.map(&:to_s) with -= options[:without] if options[:without] - without = options.fetch("without", []) - without |= Bundler.settings.without.map(&:to_s) without -= options[:with] if options[:with] options[:with] = with @@ -174,28 +168,34 @@ module Bundler end def normalize_settings - Bundler.settings[:path] = nil if options[:system] - Bundler.settings[:path] = "vendor/bundle" if options[:deployment] - Bundler.settings[:path] = options["path"] if options["path"] - Bundler.settings[:path] ||= "bundle" if options["standalone"] - Bundler.settings[:bin] = options["binstubs"] if options["binstubs"] - Bundler.settings[:bin] = nil if options["binstubs"] && options["binstubs"].empty? - Bundler.settings[:shebang] = options["shebang"] if options["shebang"] - Bundler.settings[:jobs] = options["jobs"] if options["jobs"] - Bundler.settings[:no_prune] = true if options["no-prune"] - Bundler.settings[:no_install] = true if options["no-install"] - Bundler.settings[:clean] = options["clean"] if options["clean"] - Bundler.settings.without = options[:without] - Bundler.settings.with = options[:with] - Bundler.settings[:disable_shared_gems] = Bundler.settings[:path] ? true : nil end def warn_ambiguous_gems @@ -26,7 +26,7 @@ module Bundler EOS - Bundler.ui.info Bundler::Env.new.report Bundler.ui.info "\n## Bundle Doctor" doctor @@ -0,0 +1,22 @@ @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Lock @@ -1,5 +1,5 @@ # frozen_string_literal: true -require "bundler/cli/common" require "shellwords" module Bundler @@ -17,7 +17,7 @@ module Bundler path = spec.full_gem_path Dir.chdir(path) do command = Shellwords.split(editor) + [path] - Bundler.with_clean_env do system(*command) end || Bundler.ui.info("Could not run '#{command.join(" ")}'") end @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Outdated @@ -46,7 +45,7 @@ module Bundler Bundler::CLI::Common._level_options(options).any? filter_options_ = options.keys & - %w(filter-major filter-minor filter-) definition_resolution = proc do options[:local] ? definition.resolve_with_cache! : definition.resolve_remotely! @@ -214,13 +213,19 @@ module Bundler end def check_for_deployment_mode - if Bundler.settings[:frozen] - raise ProductionError, "You are trying to check outdated gems in " \ - "deployment mode. Run `bundle outdated` elsewhere.\n" \ - "\nIf this is a development machine, remove the " \ - "#{Bundler.default_gemfile} freeze" \ - "\nby running `bundle install --no-deployment`." end end def update_present_via_semver_portions(current_spec, active_spec, options) @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Package attr_reader :options @@ -9,15 +10,15 @@ module Bundler def run Bundler.ui.level = "error" if options[:quiet] - Bundler.settings[:path] = File.expand_path(options[:path]) if options[:path] - Bundler.settings[:cache_all_platforms] = options["all-platforms"] if options.key?("all-platforms") - Bundler.settings[:cache_path] = options["cache-path"] if options.key?("cache-path") setup_cache_all install # TODO: move cache contents here now that all bundles are locked - custom_path = Pathname.new(options[:path]) if options[:path] Bundler.load.cache(custom_path) end @@ -34,9 +35,11 @@ module Bundler end def setup_cache_all - Bundler.settings[:cache_all] = options[:all] if options.key?("all") - if Bundler.definition.has_local_dependencies? && !Bundler.settings[: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." @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Platform attr_reader :options @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/vendored_thor" module Bundler class CLI::Plugin < Thor @@ -1,15 +1,20 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Pristine def run definition = Bundler.definition definition.validate_runtime! installer = Bundler::Installer.new(Bundler.root, definition) Bundler.load.specs.each do |spec| next if spec.name == "bundler" # Source::Rubygems doesn't install bundler gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})" gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY @@ -21,13 +26,15 @@ module Bundler Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.") next end when Source::Git source.remote! else Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") next end - FileUtils.rm_rf spec.full_gem_path Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec end @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Show @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "bundler/cli/common" module Bundler class CLI::Update @@ -17,7 +16,18 @@ module Bundler sources = Array(options[:source]) groups = Array(options[:group]).map(&:to_sym) - if gems.empty? && sources.empty? && groups.empty? && !options[:ruby] && !options[:bundler] # We're doing a full update Bundler.definition(true) else @@ -33,7 +43,8 @@ module Bundler end Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby], - :lock_shared_dependencies => options[:conservative]) end Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) @@ -44,17 +55,32 @@ module Bundler opts["update"] = true opts["local"] = options[:local] - Bundler.settings[:jobs] = opts["jobs"] if opts["jobs"] Bundler.definition.validate_runtime! installer = Installer.install Bundler.root, Bundler.definition, opts Bundler.load.cache if Bundler.app_cache.exist? - if Bundler.settings[:clean] && Bundler.settings[:path] require "bundler/cli/clean" Bundler::CLI::Clean.new(options).run end Bundler.ui.confirm "Bundle updated!" Bundler::CLI::Common.output_without_groups_message Bundler::CLI::Common.output_post_install_messages installer.post_install_messages @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class CLI::Viz attr_reader :options, :gem_name @@ -1,4 +1,5 @@ # frozen_string_literal: true require "pathname" require "set" @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "digest/md5" module Bundler class CompactIndexClient @@ -68,7 +67,7 @@ module Bundler def info_path(name) name = name.to_s if name =~ /[^a-z0-9_-]/ - name += "-#{Digest::MD5.hexdigest(name).downcase}" info_roots.last.join(name) else info_roots.first.join(name) @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "fileutils" require "stringio" -require "tmpdir" require "zlib" module Bundler @@ -22,6 +22,7 @@ module Bundler def initialize(fetcher) @fetcher = fetcher end def update(local_path, remote_path, retrying = nil) @@ -98,7 +99,7 @@ module Bundler # because we need to preserve \n line endings on windows when calculating # the checksum SharedHelpers.filesystem_access(path, :read) do - Digest::MD5.hexdigest(IO.read(path)) end end end @@ -0,0 +1,14 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/ FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/ @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler # Returns current version of Ruby # @@ -8,7 +9,7 @@ module Bundler end class CurrentRuby - KNOWN_MINOR_VERSIONS = %w( 1.8 1.9 2.0 @@ -17,11 +18,11 @@ module Bundler 2.3 2.4 2.5 - ).freeze KNOWN_MAJOR_VERSIONS = KNOWN_MINOR_VERSIONS.map {|v| v.split(".", 2).first }.uniq.freeze - KNOWN_PLATFORMS = %w( jruby maglev mingw @@ -31,7 +32,7 @@ module Bundler rbx ruby x64_mingw - ).freeze def ruby? !mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev") @@ -1,6 +1,6 @@ # frozen_string_literal: true require "bundler/lockfile_parser" -require "digest/sha1" require "set" module Bundler @@ -14,7 +14,9 @@ module Bundler :locked_gems, :platforms, :requires, - :ruby_version ) # Given a gemfile and lockfile creates a Bundler definition @@ -51,8 +53,16 @@ module Bundler # to be updated or true if all gems should be updated # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version # @param optional_groups [Array(String)] A list of optional groups - def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = []) - @unlocking = unlock == true || !unlock.empty? @dependencies = dependencies @sources = sources @@ -61,6 +71,7 @@ module Bundler @remote = false @specs = nil @ruby_version = ruby_version @lockfile = lockfile @lockfile_contents = String.new @@ -102,7 +113,7 @@ module Bundler end @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) - add_current_platform unless Bundler.settings[:frozen] converge_path_sources_to_gemspec_sources @path_changes = converge_paths @@ -167,9 +178,8 @@ module Bundler "to a different version of #{locked_gem} that hasn't been removed in order to install." end unless specs["bundler"].any? - local = Bundler.settings[:frozen] ? rubygems_index : index - bundler = local.search(Gem::Dependency.new("bundler", VERSION)).last - specs["bundler"] = bundler if bundler end specs @@ -194,10 +204,19 @@ module Bundler missing end - def missing_dependencies - missing = [] - resolve.materialize(current_dependencies, missing) - missing end def requested_specs @@ -226,7 +245,10 @@ module Bundler def resolve @resolve ||= begin last_resolve = converge_locked_specs - if Bundler.settings[:frozen] || (!unlocking? && nothing_changed?) Bundler.ui.debug("Found no changes, using resolution from the lockfile") last_resolve else @@ -242,25 +264,44 @@ module Bundler dependency_names = @dependencies.map(&:name) sources.all_sources.each do |source| - source.dependency_names = dependency_names.dup idx.add_source source.specs - dependency_names -= pinned_spec_names(source.specs) dependency_names.concat(source.unmet_deps).uniq! end - idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_level) - idx << Gem::Specification.new("rubygems\0", Gem::VERSION) - end - end - # used when frozen is enabled so we can find the bundler - # spec, even if (say) a git gem is not checked out. - def rubygems_index - @rubygems_index ||= Index.build do |idx| - sources.rubygems_sources.each do |rubygems| - idx.add_source rubygems.specs end end end def has_rubygems_remotes? sources.rubygems_sources.any? {|s| s.remotes.any? } @@ -295,10 +336,10 @@ module Bundler end end - preserve_unknown_sections ||= !updating_major && (Bundler.settings[:frozen] || !unlocking?) return if lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections) - if Bundler.settings[:frozen] Bundler.ui.error "Cannot write a changed lockfile while frozen." return end @@ -338,51 +379,8 @@ module Bundler end def to_lock - out = String.new - - sources.lock_sources.each do |source| - # Add the source header - out << source.to_lock - # Find all specs for this source - resolve. - select {|s| source.can_lock?(s) }. - # This needs to be sorted by full name so that - # gems with the same name, but different platform - # are ordered consistently - sort_by(&:full_name). - each do |spec| - next if spec.name == "bundler" - out << spec.to_lock - end - out << "\n" - end - - out << "PLATFORMS\n" - - platforms.map(&:to_s).sort.each do |p| - out << " #{p}\n" - end - - out << "\n" - out << "DEPENDENCIES\n" - - handled = [] - dependencies.sort_by(&:to_s).each do |dep| - next if handled.include?(dep.name) - out << dep.to_lock - handled << dep.name - end - - if locked_ruby_version - out << "\nRUBY VERSION\n" - out << " #{locked_ruby_version}\n" - end - - # Record the version of Bundler that was used to create the lockfile - out << "\nBUNDLED WITH\n" - out << " #{locked_bundler_version}\n" - - out end def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) @@ -392,8 +390,13 @@ module Bundler "updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control." unless explicit_flag - - suggested_command = Bundler.settings.locations("frozen")[:global] == "1" ? "bundle config --delete frozen" : "bundle install --no-deployment" msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \ "freeze \nby running `#{suggested_command}`." end @@ -417,8 +420,8 @@ module Bundler # Check if it is possible that the source is only changed thing if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?) - new_sources.reject! {|source| source.is_a_path? && source.path.exist? } - deleted_sources.reject! {|source| source.is_a_path? && source.path.exist? } end if @locked_sources != gemfile_sources @@ -511,7 +514,7 @@ module Bundler def add_current_platform current_platform = Bundler.local_platform - add_platform(current_platform) if Bundler.settings[:specific_platform] add_platform(generic(current_platform)) end @@ -558,10 +561,7 @@ module Bundler end def pretty_dep(dep, source = false) - msg = String.new(dep.name) - msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default - msg << " from the `#{dep.source}` source" if source && dep.source - msg end # Check if the specs of the given source changed @@ -585,6 +585,9 @@ module Bundler # order here matters, since Index#== is checking source.specs.include?(locked_index) locked_index != source.specs end # Get all locals and override their matching sources. @@ -632,22 +635,32 @@ module Bundler end end - def converge_sources changes = false - # Get the Rubygems sources from the Gemfile.lock locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } - # Get the Rubygems remotes from the Gemfile actual_remotes = sources.rubygems_remotes - # If there is a Rubygems source in both if !locked_gem_sources.empty? && !actual_remotes.empty? locked_gem_sources.each do |locked_gem| # Merge the remotes from the Gemfile into the Gemfile.lock - changes |= locked_gem.replace_remotes(actual_remotes) end end # Replace the sources from the Gemfile with the sources from the Gemfile.lock, # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent # source in the Gemfile.lock, use the one from the Gemfile. @@ -669,7 +682,7 @@ module Bundler end def converge_dependencies - frozen = Bundler.settings[:frozen] (@dependencies + @locked_deps.values).each do |dep| locked_source = @locked_deps[dep.name] # This is to make sure that if bundler is installing in deployment mode and @@ -739,6 +752,8 @@ module Bundler end end converged = [] @locked_specs.each do |s| # Replace the locked dependency's source with the equivalent source from the Gemfile @@ -746,21 +761,33 @@ module Bundler s.source = (dep && dep.source) || sources.get(s.source) # Don't add a spec to the list if its source is expired. For example, - # if you change a Git gem to Rubygems. next if s.source.nil? next if @unlock[:sources].include?(s.source.name) # XXX This is a backwards-compatibility fix to preserve the ability to # unlock a single gem by passing its name via `--source`. See issue #3759 # TODO: delete in Bundler 2 - next if @unlock[:sources].include?(s.name) # If the spec is from a path source and it doesn't exist anymore # then we unlock it. # Path sources have special logic if s.source.instance_of?(Source::Path) || s.source.instance_of?(Source::Gemspec) - other = s.source.specs[s].first # If the spec is no longer in the path source, unlock it. This # commonly happens if the version changed in the gemspec @@ -807,17 +834,21 @@ module Bundler # the metadata dependencies here def expanded_dependencies @expanded_dependencies ||= begin ruby_versions = concat_ruby_version_requirements(@ruby_version) if ruby_versions.empty? || !@ruby_version.exact? concat_ruby_version_requirements(RubyVersion.system) concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby] end - - metadata_dependencies = [ Dependency.new("ruby\0", ruby_versions), Dependency.new("rubygems\0", Gem::VERSION), ] - expand_dependencies(dependencies + metadata_dependencies, @remote) end end @@ -838,11 +869,12 @@ module Bundler end def expand_dependencies(dependencies, remote = false) deps = [] dependencies.each do |dep| dep = Dependency.new(dep, ">= 0") unless dep.respond_to?(:name) next if !remote && !dep.current_platform? - platforms = dep.gem_platforms(@platforms) if platforms.empty? mapped_platforms = dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] } Bundler.ui.warn \ @@ -872,30 +904,33 @@ module Bundler # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) - source_requirements = {} dependencies.each do |dep| - next unless dep.source - source_requirements[dep.name] = dep.source.specs end source_requirements end - def pinned_spec_names(specs) - names = [] - specs.each do |s| - # TODO: when two sources without blocks is an error, we can change - # this check to !s.source.is_a?(Source::LocalRubygems). For now, - # we need to ask every Rubygems for every gem name. - if s.source.is_a?(Source::Git) || s.source.is_a?(Source::Path) - names << s.name - end end - names.uniq! - names end def requested_groups - groups - Bundler.settings.without - @optional_groups + Bundler.settings.with end def lockfiles_equal?(current, proposed, preserve_unknown_sections) @@ -930,11 +965,20 @@ module Bundler def additional_base_requirements_for_resolve return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions? @locked_gems.specs.reduce({}) do |requirements, locked_spec| - dep = Gem::Dependency.new(locked_spec.name, ">= #{locked_spec.version}") - requirements[locked_spec.name] = DepProxy.new(dep, locked_spec.platform) requirements end.values end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class DepProxy attr_reader :__platform, :dep @@ -13,6 +14,7 @@ module Bundler end def ==(other) dep == other.dep && __platform == other.__platform end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "rubygems/dependency" require "bundler/shared_helpers" require "bundler/rubygems_ext" @@ -90,16 +91,14 @@ module Bundler @autorequire = Array(options["require"] || []) if options.key?("require") end def gem_platforms(valid_platforms) return valid_platforms if @platforms.empty? - platforms = [] - @platforms.each do |p| - platform = PLATFORM_MAP[p] - next unless valid_platforms.include?(platform) - platforms |= [platform] - end - platforms end def should_include? @@ -1,7 +1,7 @@ # frozen_string_literal: true require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation "Bundler no longer integrates with " \ "Capistrano, but Capistrano provides its own integration with " \ "Bundler via the capistrano-bundler gem. Use it instead." @@ -1,11 +1,22 @@ # frozen_string_literal: true module Bundler - if defined? ::Deprecate Deprecate = ::Deprecate elsif defined? Gem::Deprecate Deprecate = Gem::Deprecate else - class Deprecate; end end unless Deprecate.respond_to?(:skip_during) @@ -20,7 +31,7 @@ module Bundler unless Deprecate.respond_to?(:skip) def Deprecate.skip - @skip end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/dependency" require "bundler/ruby_dsl" @@ -14,6 +15,9 @@ module Bundler VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze attr_reader :gemspecs attr_accessor :dependencies @@ -30,14 +34,16 @@ module Bundler @ruby_version = nil @gemspecs = [] @gemfile = nil add_git_sources end def eval_gemfile(gemfile, contents = nil) - expanded_gemfile_path = Pathname.new(gemfile).expand_path original_gemfile = @gemfile @gemfile = expanded_gemfile_path - contents ||= Bundler.read_file(gemfile.to_s) instance_eval(contents.dup.untaint, gemfile.to_s, 1) rescue Exception => e message = "There was an error " \ @@ -95,10 +101,10 @@ module Bundler # if there's already a dependency with this name we try to prefer one if current = @dependencies.find {|d| d.name == dep.name } if current.requirement != dep.requirement - if current.type == :development - @dependencies.delete current - else return if dep.type == :development raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" @@ -111,9 +117,7 @@ module Bundler end if current.source != dep.source - if current.type == :development - @dependencies.delete current - else return if dep.type == :development raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \ "You specified that #{dep.name} (#{dep.requirement}) should come from " \ @@ -128,10 +132,12 @@ module Bundler def source(source, *args, &blk) options = args.last.is_a?(Hash) ? args.pop.dup : {} options = normalize_hash(options) if options.key?("type") options["type"] = options["type"].to_s unless Plugin.source?(options["type"]) - raise "No sources available for #{options["type"]}" end unless block_given? @@ -141,12 +147,10 @@ module Bundler source_opts = options.merge("uri" => source) with_source(@sources.add_plugin_source(options["type"], source_opts), &blk) elsif block_given? - source = normalize_source(source) with_source(@sources.add_rubygems_source("remotes" => source), &blk) else - source = normalize_source(source) check_primary_source_safety(@sources) - @sources.add_rubygems_remote(source) end end @@ -164,6 +168,19 @@ module Bundler end def path(path, options = {}, &blk) source_options = normalize_hash(options).merge( "path" => Pathname.new(path), "root_path" => gemfile_root, @@ -190,6 +207,7 @@ module Bundler def (repo, options = {}) raise ArgumentError, " sources require a block" unless block_given? _uri = @git_sources[""].call(repo) git_options = normalize_hash(options).merge("uri" => _uri) git_source = @sources.add_git_source(git_options) @@ -197,16 +215,16 @@ module Bundler end def to_definition(lockfile, unlock) - Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups) end def group(*args, &blk) - opts = Hash === args.last ? args.pop.dup : {} - normalize_group_options(opts, args) @groups.concat args - if opts["optional"] optional_groups = args - @optional_groups @optional_groups.concat optional_groups end @@ -216,9 +234,9 @@ module Bundler args.each { @groups.pop } end - def install_if(*args, &blk) @install_conditionals.concat args - blk.call ensure args.each { @install_conditionals.pop } end @@ -250,7 +268,12 @@ module Bundler private def add_git_sources git_source(:) do |repo_name| # 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 @@ -267,23 +290,29 @@ module Bundler repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") # TODO: 2.0 upgrade this setting to the default if Bundler.settings[".https"] "https://.com/#{repo_name}.git" else - warn__source_change(repo_name) "git://.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..com/#{repo_name}.git') "https://gist..com/#{repo_name}.git" end # TODO: 2.0 remove this deprecated git source git_source(:bitbucket) do |repo_name| - user_name, repo_name = repo_name.split "/" - warn_deprecated_git_source(:bitbucket, 'https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git') repo_name ||= user_name "https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git" end @@ -308,7 +337,7 @@ module Bundler end def valid_keys - @valid_keys ||= %w(group groups git path glob name branch ref tag require submodules platform platforms type source install_if) end def normalize_options(name, version, opts) @@ -318,6 +347,9 @@ module Bundler if name =~ /\s/ raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace) end normalize_hash(opts) @@ -355,7 +387,7 @@ module Bundler opts["git"] = @git_sources[git_name].call(opts[git_name]) end - %w(git path).each do |type| next unless param = opts[type] if version.first && version.first =~ /^\s*=?\s*(\d[^\s]*)\s*$/ options = opts.merge("name" => name, "version" => $1) @@ -366,8 +398,8 @@ module Bundler opts["source"] = source end - opts["source"] ||= @source - opts["env"] ||= @env opts["platforms"] = platforms.dup opts["group"] = groups opts["should_include"] = install_if @@ -377,7 +409,7 @@ module Bundler normalize_hash(opts) groups = groups.map {|group| ":#{group}" }.join(", ") - validate_keys("group #{groups}", opts, %w(optional)) opts["optional"] ||= false end @@ -390,25 +422,25 @@ module Bundler raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch) end - if invalid_keys.any? - 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 - - message << " Valid options are: #{valid_keys.join(", ")}." - message << " You may be able to resolve this by upgrading Bundler to the newest version." - raise InvalidOption, message - end end def normalize_source(source) case source when :gemcutter, :rubygems, :rubyforge - Bundler::SharedHelpers.major_deprecation "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" @@ -419,17 +451,20 @@ module Bundler end end - def check_primary_source_safety(source) - return unless source.rubygems_primary_remotes.any? - # TODO: 2.0 upgrade from setting to default - if Bundler.settings[:disable_multisource] - raise GemfileError, "Warning: this Gemfile contains multiple primary sources. " \ "Each source after the first must include a block to indicate which gems " \ - "should come from that source. To downgrade this error to a warning, run " \ - "`bundle config --delete disable_multisource`" else - Bundler::SharedHelpers.major_deprecation "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. " \ @@ -438,20 +473,20 @@ module Bundler end end - def warn__source_change(repo_name) # TODO: 2.0 remove deprecation - Bundler::SharedHelpers.major_deprecation "The : option uses the git: protocol, which is not secure. " \ - "Bundler 2.0 will use the https: protocol, which is secure. Enable this change now by " \ - "running `bundle config .https true`." - end - def warn_deprecated_git_source(name, repo_string) - # TODO: 2.0 remove deprecation - Bundler::SharedHelpers.major_deprecation <<-EOS -The :#{name} git source is deprecated, and will be removed in Bundler 2.0. Add this code to your Gemfile to ensure it continues to work: - git_source(:#{name}) do |repo_name| - "#{repo_string}" - end EOS end @@ -530,7 +565,7 @@ The :#{name} git source is deprecated, and will be removed in Bundler 2.0. Add t lines = contents.lines.to_a indent = " # " indicator = indent.tr("#", ">") - first_line = (line_numer.zero?) last_line = (line_numer == (lines.count - 1)) m << "\n" @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler # used for Creating Specifications from the Gemcutter Endpoint class EndpointSpecification < Gem::Specification @@ -9,11 +10,15 @@ module Bundler attr_accessor :source, :remote, :dependencies def initialize(name, version, platform, dependencies, metadata = nil) @name = name @version = Gem::Version.create version @platform = platform @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } parse_metadata(metadata) end @@ -71,6 +76,8 @@ module Bundler @remote_specification.post_install_message elsif _local_specification _local_specification.post_install_message end end @@ -80,6 +87,8 @@ module Bundler @remote_specification.extensions elsif _local_specification _local_specification.extensions end end @@ -1,33 +1,21 @@ # frozen_string_literal: true require "bundler/rubygems_integration" require "bundler/source/git/git_proxy" module Bundler class Env - def write(io) io.write report end - def report(options = {}) print_gemfile = options.delete(:print_gemfile) { true } print_gemspecs = options.delete(:print_gemspecs) { true } - out = String.new("## Environment\n\n```\n") - out << "Bundler #{Bundler::VERSION}\n" - out << "Rubygems #{Gem::VERSION}\n" - out << "Ruby #{ruby_version}" - out << "GEM_HOME #{ENV["GEM_HOME"]}\n" unless ENV["GEM_HOME"].nil? || ENV["GEM_HOME"].empty? - out << "GEM_PATH #{ENV["GEM_PATH"]}\n" unless ENV["GEM_PATH"] == ENV["GEM_HOME"] - out << "RVM #{ENV["rvm_version"]}\n" if ENV["rvm_version"] - out << "Git #{git_version}\n" - out << "Platform #{Gem::Platform.local}\n" - out << "OpenSSL #{OpenSSL::OPENSSL_VERSION}\n" if defined?(OpenSSL::OPENSSL_VERSION) - %w(rubygems-bundler open_gem).each do |name| - specs = Bundler.rubygems.find_name(name) - out << "#{name} (#{specs.map(&:version).join(",")})\n" unless specs.empty? - end - - out << "```\n" unless Bundler.settings.all.empty? out << "\n## Bundler settings\n\n```\n" @@ -43,9 +31,18 @@ module Bundler return out unless SharedHelpers.in_bundle? if print_gemfile out << "\n## Gemfile\n" - out << "\n### #{Bundler.default_gemfile.relative_path_from(SharedHelpers.pwd)}\n\n" - out << "```ruby\n" << read_file(Bundler.default_gemfile).chomp << "\n```\n" out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n" out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n" @@ -63,9 +60,7 @@ module Bundler out end - private - - def read_file(filename) File.read(filename.to_s).strip rescue Errno::ENOENT "<No #{filename} found>" @@ -73,22 +68,86 @@ module Bundler "#{e.class}: #{e.message}" end - def ruby_version str = String.new("#{RUBY_VERSION}") if RUBY_VERSION < "1.9" str << " (#{RUBY_RELEASE_DATE}" str << " level #{RUBY_LEVEL}" if defined? RUBY_LEVEL - str << ") [#{RUBY_PLATFORM}]\n" else str << "p#{RUBY_LEVEL}" if defined? RUBY_LEVEL - str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{RUBY_PLATFORM}]\n" end end - def git_version Bundler::Source::Git::GitProxy.new(nil, nil, nil).full_version rescue Bundler::Source::Git::GitNotInstalledError "not installed" end end end @@ -1,12 +1,29 @@ # frozen_string_literal: true module Bundler class EnvironmentPreserver # @param env [ENV] # @param keys [Array<String>] def initialize(env, keys) @original = env.to_hash @keys = keys - @prefix = "BUNDLER_ORIG_" end # @return [Hash] @@ -14,9 +31,10 @@ module Bundler env = @original.clone @keys.each do |key| value = env[key] - original_value = env[@prefix + key] - if !value.nil? && !value.empty? && original_value.nil? - env[@prefix + key] = value end end env @@ -27,10 +45,13 @@ module Bundler env = @original.clone @keys.each do |key| value_original = env[@prefix + key] - unless value_original.nil? || value_original.empty? env[key] = value_original - env.delete(@prefix + key) end end env end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class BundlerError < StandardError def self.status_code(code) @@ -1,22 +1,59 @@ # frozen_string_literal: true module Bundler class FeatureFlag def self.settings_flag(flag, &default) unless Bundler::Settings::BOOL_KEYS.include?(flag.to_s) raise "Cannot use `#{flag}` as a settings feature flag since it isn't a bool key" end - define_method("#{flag}?") do - value = Bundler.settings[flag] value = instance_eval(&default) if value.nil? && !default.nil? value end end (1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } } settings_flag(:allow_offline_install) { bundler_2_mode? } settings_flag(:only_update_to_newer_versions) { bundler_2_mode? } settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") } def initialize(bundler_version) @bundler_version = Gem::Version.create(bundler_version) @@ -26,7 +63,5 @@ module Bundler @bundler_version.segments.first end private :major_version - - class << self; private :settings_flag; end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/vendored_persistent" require "cgi" require "securerandom" @@ -237,7 +238,7 @@ module Bundler Bundler.settings[:ssl_client_cert] raise SSLError if needs_ssl && !defined?(OpenSSL::SSL) - con = Bundler::Persistent::Net::HTTP::Persistent.new "bundler", :ENV if gem_proxy = Bundler.rubygems.configuration[:http_proxy] con.proxy = URI.parse(gem_proxy) if gem_proxy != :no_proxy end @@ -248,8 +249,11 @@ module Bundler con.cert_store = bundler_cert_store end - if Bundler.settings[:ssl_client_cert] - pem = File.read(Bundler.settings[:ssl_client_cert]) con.cert = OpenSSL::X509::Certificate.new(pem) con.key = OpenSSL::PKey::RSA.new(pem) end @@ -273,16 +277,19 @@ module Bundler Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH, Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, - Bundler::Persistent::Net::HTTP::Persistent::Error, Zlib::BufError, Errno::EHOSTUNREACH ].freeze def bundler_cert_store store = OpenSSL::X509::Store.new - if Bundler.settings[:ssl_ca_cert] - if File.directory? Bundler.settings[:ssl_ca_cert] - store.add_path Bundler.settings[:ssl_ca_cert] else - store.add_file Bundler.settings[:ssl_ca_cert] end else store.set_default_paths @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Fetcher class Base @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/fetcher/base" require "bundler/worker" @@ -61,7 +62,7 @@ module Bundler compact_index_request :fetch_spec def available? - return nil unless md5_available? user_home = Bundler.user_home return nil unless user_home.directory? && user_home.writable? # Read info file checksums out of /versions, so we can know if gems are up to date @@ -120,16 +121,6 @@ module Bundler Net::HTTPNotModified.new(nil, nil, nil) end end - - def md5_available? - require "openssl" - OpenSSL::Digest::MD5.digest("") - true - rescue LoadError - true - rescue OpenSSL::Digest::DigestError - false - end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/fetcher/base" require "cgi" @@ -6,7 +7,7 @@ module Bundler class Fetcher class Dependency < Base def available? - fetch_uri.scheme != "file" && downloader.fetch(dependency_api_uri) rescue NetworkDownError => e raise HTTPError, e.message rescue AuthenticationRequiredError @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Fetcher class Downloader @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/fetcher/base" require "rubygems/remote_fetcher" @@ -1,5 +1,6 @@ # encoding: utf-8 # frozen_string_literal: true require "cgi" require "bundler/vendored_thor" @@ -92,7 +93,7 @@ module Bundler #{e.backtrace && e.backtrace.join("\n ").chomp} ``` - #{Bundler::Env.new.report} --- TEMPLATE END ---------------------------------------------------------------- EOS @@ -119,6 +120,8 @@ module Bundler def self.with_friendly_errors yield rescue Exception => e FriendlyErrors.log_error(e) exit FriendlyErrors.exit_status(e) @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/vendored_thor" unless defined?(Thor) require "bundler" @@ -50,8 +51,8 @@ module Bundler install_gem(built_gem_path, :local) end - desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to Rubygems\n" \ - "To prevent publishing in Rubygems use `gem_push=no rake release`" task "release", [:remote] => ["build", "release:guard_clean", "release:source_control_push", "release:rubygem_push"] do end @@ -92,18 +93,14 @@ module Bundler protected def rubygem_push(path) - allowed_push_host = nil gem_command = "gem push '#{path}'" gem_command += " --key #{gem_key}" if gem_key - if @gemspec.respond_to?(:metadata) - allowed_push_host = @gemspec.metadata["allowed_push_host"] - gem_command += " --host #{allowed_push_host}" if allowed_push_host - end 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 sh(gem_command) - Bundler.ui.confirm "Pushed #{name} #{version} to #{allowed_push_host ? allowed_push_host : "rubygems.org."}" end def built_gem_path @@ -116,6 +113,18 @@ module Bundler Bundler.ui.confirm "Pushed git commits and tags." end def perform_git_push(options = "") cmd = "git push #{options}" out, code = sh_with_code(cmd) @@ -187,7 +196,7 @@ module Bundler end def gem_push? - !%w(n no nil false off 0).include?(ENV["gem_push"].to_s.downcase) end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module GemHelpers GENERIC_CACHE = {} # rubocop:disable MutableConstant @@ -1,4 +1,5 @@ # frozen_string_literal: true require "rubygems/remote_fetcher" module Bundler @@ -1,4 +1,5 @@ # frozen_string_literal: true require "rake/clean" CLOBBER.include "pkg" @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler # This class contains all of the logic for determining the next version of a # Gem to update to based on the requested level (, minor, major). @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Gemdeps def initialize(runtime) @@ -1,4 +1,5 @@ # frozen_string_literal: true require "set" module Bundler class Graph @@ -1,4 +1,5 @@ # frozen_string_literal: true require "set" module Bundler @@ -111,6 +112,13 @@ module Bundler spec_sets.values.each(&blk) end sources.each {|s| s.each(&blk) } end # returns a list of the dependencies @@ -191,14 +199,6 @@ module Bundler end end - wants_prerelease = dependency.requirement.prerelease? - wants_prerelease ||= base && base.any? {|base_spec| base_spec.version.prerelease? } - only_prerelease = specs.all? {|spec| spec.version.prerelease? } - - unless wants_prerelease || only_prerelease - found.reject! {|spec| spec.version.prerelease? } - end - found end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Injector def self.inject(new_deps, options = {}) @@ -12,38 +13,40 @@ module Bundler end def inject(gemfile_path, lockfile_path) - if Bundler.settings[:frozen] # ensure the lock and Gemfile are synced Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) - # temporarily remove frozen while we inject - frozen = Bundler.settings.delete(:frozen) end - # evaluate the Gemfile we have now - builder = Dsl.new - builder.eval_gemfile(gemfile_path) - # don't inject any gems that are already in the Gemfile - @new_deps -= builder.dependencies - # add new deps to the end of the in-memory Gemfile - # Set conservative versioining to false because we want to let the resolver resolve the version first - builder.eval_gemfile("injected gems", build_gem_lines(false)) if @new_deps.any? - # resolve to see if the new deps broke anything - @definition = builder.to_definition(lockfile_path, {}) - @definition.resolve_remotely! - # since nothing broke, we can add those gems to the gemfile - append_to(gemfile_path, build_gem_lines(@options[:conservative_versioning])) if @new_deps.any? - # since we resolved successfully, write out the lockfile - @definition.lock(Bundler.default_lockfile) - # return an array of the deps that we added - return @new_deps - ensure - Bundler.settings[:frozen] = "1" if frozen end private @@ -1,4 +1,7 @@ # frozen_string_literal: true # Allows for declaring a Gemfile inline in a ruby script, optionally installing # any gems that aren't already installed on the user's system. # @@ -39,7 +42,7 @@ def gemfile(install = false, options = {}, &gemfile) def Bundler.root Bundler::SharedHelpers.pwd.expand_path end - ENV["BUNDLE_GEMFILE"] = "Gemfile" Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins? builder = Bundler::Dsl.new @@ -50,12 +53,7 @@ def gemfile(install = false, options = {}, &gemfile) definition.validate_runtime! missing_specs = proc do - begin - !definition.missing_specs.empty? - rescue Bundler::GemNotFound, Bundler::GitError - definition.instance_variable_set(:@index, nil) - true - end end Bundler.ui = ui if install @@ -1,4 +1,5 @@ # frozen_string_literal: true require "erb" require "rubygems/dependency_installer" require "bundler/worker" @@ -33,25 +34,26 @@ module Bundler # Runs the install procedures for a specific Gemfile. # - # Firstly, this method will check to see if Bundler.bundle_path exists - # and if not then will create it. This is usually the location of gems - # on the system, be it RVM or at a system path. # - # Secondly, it checks if Bundler has been configured to be "frozen" # Frozen ensures that the Gemfile and the Gemfile.lock file are matching. # This stops a situation where a developer may update the Gemfile but may not run # `bundle install`, which leads to the Gemfile.lock file not being correctly updated. # If this file is not correctly updated then any other developer running # `bundle install` will potentially not install the correct gems. # - # Thirdly, Bundler checks if there are any dependencies specified in the Gemfile using - # Bundler::Environment#dependencies. If there are no dependencies specified then - # Bundler returns a warning message stating so and this method returns. # - # Fourthly, Bundler checks if the default lockfile (Gemfile.lock) exists, and if so - # then proceeds to set up a definition based on the default gemfile (Gemfile) and the - # default lock file (Gemfile.lock). However, this is not the case if the platform is different - # to that which is specified in Gemfile.lock, or if there are any missing specs for the gems. # # Fifthly, Bundler resolves the dependencies either through a cache of gems or by remote. # This then leads into the gems being installed, along with stubs for their executables, @@ -61,26 +63,36 @@ module Bundler # Sixthly, a new Gemfile.lock is created from the installed gems to ensure that the next time # that a user runs `bundle install` they will receive any updates from this process. # - # Finally: TODO add documentation for how the standalone process works. def run(options) create_bundle_path - if Bundler.settings[:frozen] - @definition.ensure_equivalent_gemfile_and_lockfile(options[:deployment]) - end - if @definition.dependencies.empty? - Bundler.ui.warn "The Gemfile specifies no dependencies" - lock - return - end - resolve_if_need(options) - ensure_specs_are_compatible! - install(options) - lock unless Bundler.settings[:frozen] - Standalone.new(options[:standalone], @definition).generate if options[:standalone] end def generate_bundler_executable_stubs(spec, options = {}) @@ -101,15 +113,21 @@ module Bundler end # double-assignment to avoid warnings about variables that will be used by ERB - bin_path = bin_path = Bundler.bin_path - template = template = File.read(File.expand_path("../templates/Executable", __FILE__)) - relative_gemfile_path = relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path) - ruby_command = ruby_command = Thor::Util.ruby_command exists = [] spec.executables.each do |executable| - next if executable == "bundle" - binstub_path = "#{bin_path}/#{executable}" if File.exist?(binstub_path) && !options[:force] exists << executable @@ -139,13 +157,19 @@ module Bundler def generate_standalone_bundler_executable_stubs(spec) # double-assignment to avoid warnings about variables that will be used by ERB bin_path = Bundler.bin_path - standalone_path = standalone_path = Bundler.root.join(Bundler.settings[:path]).relative_path_from(bin_path) template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__)) - ruby_command = ruby_command = Thor::Util.ruby_command spec.executables.each do |executable| next if executable == "bundle" - executable_path = executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path) File.open "#{bin_path}/#{executable}", "w", 0o755 do |f| f.puts ERB.new(template, nil, "-").result(binding) end @@ -159,13 +183,32 @@ module Bundler # that said, it's a rare situation (other than rake), and parallel # installation is SO MUCH FASTER. so we let people opt in. def install(options) - Bundler.rubygems.load_plugins force = options["force"] - jobs = 1 - jobs = [Bundler.settings[:jobs].to_i - 1, 1].max if can_install_in_parallel? install_in_parallel jobs, options[:standalone], force end def ensure_specs_are_compatible! system_ruby = Bundler::RubyVersion.system rubygems_version = Gem::Version.create(Gem::VERSION) @@ -184,12 +227,28 @@ module Bundler end 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 @@ -207,23 +266,18 @@ module Bundler Bundler.mkdir_p(p) end unless Bundler.bundle_path.exist? rescue Errno::EEXIST - raise PathError, "Could not install to path `#{Bundler.settings[:path]}` " \ "because a file already exists at that path. Either remove or rename the file so the directory can be created." end - def resolve_if_need(options) - if !options["update"] && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file? - local = Bundler.ui.silence do - begin - tmpdef = Definition.build(Bundler.default_gemfile, Bundler.default_lockfile, nil) - true unless tmpdef.new_platform? || tmpdef.missing_dependencies.any? - rescue BundlerError - end - end end - return if local options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely! end def lock(opts = {}) @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class GemInstaller attr_reader :spec, :standalone, :worker, :force, :installer @@ -65,6 +66,7 @@ module Bundler end def generate_executable_stubs return if Bundler.settings[:inline] if Bundler.settings[:bin] && standalone installer.generate_standalone_bundler_executable_stubs(spec) @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/worker" require "bundler/installer/gem_installer" @@ -77,11 +78,6 @@ module Bundler new(*args).call end - # Returns max number of threads machine can handle with a min of 1 - def self.max_threads - [Bundler.settings[:jobs].to_i - 1, 1].max - end - attr_reader :size def initialize(installer, all_specs, size, standalone, force) @@ -99,49 +95,19 @@ module Bundler require "bundler/gem_remote_fetcher" if RUBY_VERSION < "1.9" check_for_corrupt_lockfile - enqueue_specs - process_specs until @specs.all?(&:installed?) || @specs.any?(&:failed?) handle_error if @specs.any?(&:failed?) @specs ensure worker_pool && worker_pool.stop end - def worker_pool - @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num| - gem_installer = Bundler::GemInstaller.new( - spec_install.spec, @installer, @standalone, worker_num, @force - ) - success, message = gem_installer.install_from_spec - if success && !message.nil? - spec_install.post_install_message = message - elsif !success - spec_install.state = :failed - spec_install.error = "#{message}\n\n#{require_tree_for_spec(spec_install.spec)}" - end - spec_install - } - end - - # Dequeue a spec and save its post-install message and then enqueue the - # remaining specs. - # Some specs might've had to wait til this spec was installed to be - # processed so the call to `enqueue_specs` is important after every - # dequeue. - def process_specs - spec = worker_pool.deq - spec.state = :installed unless spec.failed? - enqueue_specs - end - - def handle_error - errors = @specs.select(&:failed?).map(&:error) - if exception = errors.find {|e| e.is_a?(Bundler::BundlerError) } - raise exception - end - raise Bundler::InstallError, errors.map(&:to_s).join("\n\n") - end - def check_for_corrupt_lockfile missing_dependencies = @specs.map do |s| [ @@ -167,6 +133,71 @@ module Bundler Bundler.ui.warn(warning.join("\n")) end def require_tree_for_spec(spec) tree = @spec_set.what_required(spec) t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n") @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Standalone def initialize(groups, definition) @@ -1,4 +1,5 @@ # frozen_string_literal: true require "uri" require "bundler/match_platform" @@ -68,7 +69,7 @@ module Bundler end def __materialize__ - search_object = Bundler.settings[:specific_platform] || Bundler.settings[:force_ruby_platform] ? self : Dependency.new(name, version) @specification = if source.is_a?(Source::Gemspec) && source.gemspec.name == name source.gemspec.tap {|s| s.source = source } else @@ -0,0 +1,95 @@ @@ -90,7 +90,7 @@ module Bundler send("parse_#{@state}", line) end end - @sources << @rubygems_aggregate @specs = @specs.values.sort_by(&:identifier) warn_for_outdated_bundler_version rescue ArgumentError => e @@ -141,10 +141,16 @@ module Bundler @sources << @current_source end when GEM - Array(@opts["remote"]).each do |url| - @rubygems_aggregate.add_remote(url) end - @current_source = @rubygems_aggregate when PLUGIN @current_source = Plugin.source_from_lock(@opts) @sources << @current_source @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/gem_helpers" module Bundler @@ -1,4 +1,5 @@ # frozen_string_literal: true require "socket" module Bundler @@ -37,7 +38,7 @@ module Bundler mirror = if config.all? @all else - (@mirrors[config.uri] = @mirrors[config.uri] || Mirror.new) end config.update_mirror(mirror) end @@ -45,7 +46,9 @@ module Bundler private def fetch_valid_mirror_for(uri) - mirror = (@mirrors[URI(uri.to_s.downcase)] || @mirrors[URI(uri.to_s).host] || Mirror.new(uri)).validate!(@prober) mirror = Mirror.new(uri) unless mirror.valid? mirror end @@ -117,7 +120,7 @@ module Bundler def initialize(config_line, value) uri, fallback = - config_line.match(%r{^mirror\.(all|.+?)(\.fallback_timeout)?\/?$}).captures @fallback = !fallback.nil? @all = false if uri == "all" @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/plugin/api" module Bundler @@ -1,6 +1,6 @@ # frozen_string_literal: true require "uri" -require "digest/sha1" module Bundler module Plugin @@ -271,7 +271,7 @@ module Bundler end def uri_hash - Digest::SHA1.hexdigest(uri) end # Note: Do not override if you don't know what you are doing. @@ -293,6 +293,13 @@ module Bundler def bundler_plugin_api_source? true end end end end @@ -13,12 +13,13 @@ module Bundler def install(names, options) version = options[:version] || [">= 0"] - - if options[:git] - install_git(names, version, options) - else - sources = options[:source] || Bundler.rubygems.sources - install_rubygems(names, version, sources) end end @@ -5,13 +5,6 @@ module Bundler # approptiate options to be used with Source classes for plugin installation module Plugin class SourceList < Bundler::SourceList - def initialize - @path_sources = [] - @git_sources = [] - @rubygems_aggregate = Plugin::Installer::Rubygems.new - @rubygems_sources = [] - end - def add_git_source(options = {}) add_source_to_list Plugin::Installer::Git.new(options), git_sources end @@ -21,7 +14,13 @@ module Bundler end def all_sources - path_sources + git_sources + rubygems_sources end end end @@ -0,0 +1,24 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true # Psych could be a gem, so try to ask for it begin gem "psych" @@ -25,3 +26,12 @@ module Bundler YamlLibrarySyntaxError = ::ArgumentError end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "uri" module Bundler @@ -1,178 +1,9 @@ # frozen_string_literal: true module Bundler class Resolver require "bundler/vendored_molinillo" - - class Molinillo::VersionConflict - def printable_dep(dep) - if dep.is_a?(Bundler::Dependency) - DepProxy.new(dep, dep.platforms.join(", ")).to_s.strip - else - dep.to_s - end - end - - def message - conflicts.sort.reduce(String.new) do |o, (name, conflict)| - o << %(\nBundler could not find compatible versions for gem "#{name}":\n) - if conflict.locked_requirement - o << %( In snapshot (#{Bundler.default_lockfile.basename}):\n) - o << %( #{printable_dep(conflict.locked_requirement)}\n) - o << %(\n) - end - o << %( In Gemfile:\n) - trees = conflict.requirement_trees - - maximal = 1.upto(trees.size).map do |size| - trees.map(&:last).flatten(1).combination(size).to_a - 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 - - o << trees.sort_by {|t| t.reverse.map(&:name) }.map do |tree| - t = String.new - depth = 2 - tree.each do |req| - t << " " * depth << req.to_s - unless tree.last == req - if spec = conflict.activated_by_name[req.name] - t << %( was resolved to #{spec.version}, which) - end - t << %( depends on) - end - t << %(\n) - depth += 1 - end - t - end.join("\n") - - 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) - end - - if name == "bundler" && other_bundler_required - o << "\n" - o << "This Gemfile requires a different version of Bundler.\n" - o << "Perhaps you need to update Bundler by running `gem install bundler`?\n" - end - if conflict.locked_requirement - o << "\n" - o << %(Running `bundle update` will rebuild your snapshot from scratch, using only\n) - o << %(the gems in your Gemfile, which may resolve the conflict.\n) - elsif !conflict.existing - o << "\n" - if conflict.requirement_trees.first.size > 1 - o << "Could not find gem '#{conflict.requirement}', which is required by " - o << "gem '#{conflict.requirement_trees.first[-2]}', in any of the sources." - else - o << "Could not find gem '#{conflict.requirement}' in any of the sources\n" - end - end - o - end.strip - end - end - - class SpecGroup < Array - include GemHelpers - - attr_reader :activated - - def initialize(a) - super - @required_by = [] - @activated_platforms = [] - @dependencies = nil - @specs = Hash.new do |specs, platform| - specs[platform] = select_best_platform_match(self, platform) - end - end - - def initialize_copy(o) - super - @activated_platforms = o.activated.dup - end - - def to_specs - @activated_platforms.map do |p| - next unless s = @specs[p] - lazy_spec = LazySpecification.new(name, version, s.platform, source) - lazy_spec.dependencies.replace s.dependencies - lazy_spec - end.compact - end - - def activate_platform!(platform) - return unless for?(platform) - return if @activated_platforms.include?(platform) - @activated_platforms << platform - end - - def name - @name ||= first.name - end - - def version - @version ||= first.version - end - - def source - @source ||= first.source - end - - def for?(platform) - spec = @specs[platform] - !spec.nil? - end - - def to_s - "#{name} (#{version})" - end - - def dependencies_for_activated_platforms - dependencies = @activated_platforms.map {|p| __dependencies[p] } - metadata_dependencies = @activated_platforms.map do |platform| - metadata_dependencies(@specs[platform], platform) - end - dependencies.concat(metadata_dependencies).flatten - end - - def platforms_for_dependency_named(dependency) - __dependencies.select {|_, deps| deps.map(&:name).include? dependency }.keys - end - - private - - def __dependencies - @dependencies = Hash.new do |dependencies, platform| - dependencies[platform] = [] - if spec = @specs[platform] - spec.dependencies.each do |dep| - next if dep.type == :development - dependencies[platform] << DepProxy.new(dep, platform) - end - end - dependencies[platform] - end - end - - def metadata_dependencies(spec, platform) - return [] unless spec - # Only allow endpoint specifications since they won't hit the network to - # fetch the full gemspec when calling required_ruby_version - 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) - 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) - end - dependencies - end - end # Figures out the best possible configuration of gems that satisfies # the list of passed dependencies and any child dependencies without @@ -206,16 +37,22 @@ module Bundler additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) } @platforms = platforms @gem_version_promoter = gem_version_promoter end def start(requirements) verify_gemfile_dependencies_are_found!(requirements) dg = @resolver.resolve(requirements, @base_dg) dg.map(&:payload). reject {|sg| sg.name.end_with?("\0") }. map(&:to_specs).flatten rescue Molinillo::VersionConflict => e - raise VersionConflict.new(e.conflicts.keys.uniq, e.message) rescue Molinillo::CircularDependencyError => e names = e.dependencies.sort_by(&:name).map {|d| "gem '#{d.name}'" } raise CyclicDependencyError, "Your bundle requires gems that depend" \ @@ -266,6 +103,14 @@ module Bundler search = @search_for[dependency] ||= begin index = index_for(dependency) results = index.search(dependency, @base[dependency.name]) if vertex = @base_dg.vertex_named(dependency.name) locked_requirement = vertex.payload.requirement end @@ -281,7 +126,9 @@ module Bundler end nested.reduce([]) do |groups, (version, specs)| next groups if locked_requirement && !locked_requirement.satisfied_by?(version) - groups << SpecGroup.new(specs) end else [] @@ -298,7 +145,20 @@ module Bundler end def index_for(dependency) - @source_requirements[dependency.name] || @index end def name_for(dependency) @@ -319,23 +179,53 @@ module Bundler def requirement_satisfied_by?(requirement, activated, spec) return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform) true end def sort_dependencies(dependencies, activated, conflicts) dependencies.sort_by do |dependency| name = name_for(dependency) [ @base_dg.vertex_named(name) ? 0 : 1, - activated.vertex_named(name).payload ? 0 : 1, amount_constrained(dependency), conflicts[name] ? 0 : 1, - activated.vertex_named(name).payload ? 0 : search_for(dependency).count, ] end end private # returns an integer \in (-\infty, 0] @@ -364,32 +254,34 @@ module Bundler def verify_gemfile_dependencies_are_found!(requirements) requirements.each do |requirement| - next if requirement.name == "bundler" next unless search_for(requirement).empty? - if (base = @base[requirement.name]) && !base.empty? version = base.first.version message = "You have requested:\n" \ - " #{requirement.name} #{requirement.requirement}\n\n" \ - "The bundle currently has #{requirement.name} locked at #{version}.\n" \ - "Try running `bundle update #{requirement.name}`\n\n" \ "If you are updating multiple gems in your Gemfile at once,\n" \ "try passing them all to `bundle update`" - elsif requirement.source - name = requirement.name - specs = @source_requirements[name][name] versions_with_platforms = specs.map {|s| [s.version, s.platform] } - message = String.new("Could not find gem '#{requirement}' in #{requirement.source}.\n") message << if versions_with_platforms.any? - "Source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}" else - "Source does not contain any versions of '#{requirement}'" end else - cache_message = begin - " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist? - rescue GemfileNotFound - nil - end message = "Could not find gem '#{requirement}' in any of the gem sources " \ "listed in your Gemfile#{cache_message}." end @@ -402,9 +294,76 @@ module Bundler version = vwp.first platform = vwp.last version_platform_str = String.new(version.to_s) - version_platform_str << " #{platform}" unless platform.nil? end version_platform_strs.join(", ") end end end @@ -0,0 +1,111 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler # General purpose class for retrying code that may fail class Retry @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module RubyDsl def ruby(*ruby_version) @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class RubyVersion attr_reader :versions, @@ -1,4 +1,5 @@ # frozen_string_literal: true require "pathname" if defined?(Gem::QuickLoader) @@ -14,7 +15,7 @@ begin # shouldn't be deferred. require "rubygems/source" rescue LoadError - # Not available before Rubygems 2.0.0, ignore nil end @@ -135,7 +136,7 @@ module Gem end class Dependency - attr_accessor :source, :groups alias_method :eql?, :== @@ -146,7 +147,7 @@ module Gem end def to_yaml_properties - instance_variables.reject {|p| ["@source", "@groups"].include?(p.to_s) } end def to_lock @@ -158,7 +159,7 @@ module Gem out end - # Backport of performance enhancement added to Rubygems 1.4 def matches_spec?(spec) # name can be a Regexp, so use === return false unless name === spec.name @@ -1,4 +1,5 @@ # frozen_string_literal: true require "rubygems/installer" module Bundler @@ -17,6 +18,28 @@ module Bundler super && validate_bundler_checksum(options[:bundler_expected_checksum]) end private def validate_bundler_checksum(checksum) @@ -25,7 +48,7 @@ module Bundler return true unless source = @package.instance_variable_get(:@gem) return true unless source.respond_to?(:with_read_io) digest = source.with_read_io do |io| - digest = Digest::SHA256.new digest << io.read(16_384) until io.eof? io.rewind send(checksum_type(checksum), digest) @@ -1,4 +1,5 @@ # frozen_string_literal: true require "monitor" require "rubygems" require "rubygems/config_file" @@ -84,6 +85,19 @@ module Bundler spec.respond_to?(:default_gem?) && spec.default_gem? end def stub_set_spec(stub, spec) stub.instance_variable_set(:@spec, spec) end @@ -158,6 +172,10 @@ module Bundler Gem.post_reset_hooks end def gem_cache gem_path.map {|p| File.expand_path("cache", p) } end @@ -165,7 +183,7 @@ module Bundler def spec_cache_dirs @spec_cache_dirs ||= begin dirs = gem_path.map {|dir| File.join(dir, "specifications") } - dirs << Gem.spec_cache_dir if Gem.respond_to?(:spec_cache_dir) # Not in Rubygems 2.0.3 or earlier dirs.uniq.select {|dir| File.directory? dir } end end @@ -179,7 +197,7 @@ module Bundler end def repository_subdirectories - %w(cache doc gems specifications) end def clear_paths @@ -190,8 +208,12 @@ module Bundler Gem.bin_path(gem, bin, ver) end def preserve_paths - # this is a no-op outside of Rubygems 1.8 yield end @@ -212,6 +234,10 @@ module Bundler Gem.load_plugins if Gem.respond_to?(:load_plugins) end def ui=(obj) Gem::DefaultUserInteraction.ui = obj end @@ -233,9 +259,9 @@ module Bundler {} # if we can't download them, there aren't any end - # TODO: This is for older versions of Rubygems... should we support the # X-Gemfile-Source header on these old versions? - # Maybe the newer implementation will work on older Rubygems? # It seems difficult to keep this implementation and still send the header. def fetch_all_remote_specs(remote) old_sources = Bundler.rubygems.sources @@ -273,6 +299,7 @@ module Bundler def spec_from_gem(path, policy = nil) require "rubygems/security" gem_from_path(path, security_policies[policy]).spec rescue Gem::Package::FormatError raise GemspecError, "Could not read gem at #{path}. It may be corrupted." @@ -306,7 +333,7 @@ module Bundler end def security_policy_keys - %w(High Medium Low AlmostNo No).map {|level| "#{level}Security" } end def security_policies @@ -377,9 +404,8 @@ module Bundler raise e end - # TODO: delete this in 2.0, it's a backwards compatibility shim - # see https://.com/bundler/bundler/issues/5102 - kernel_class.send(:public, :gem) end end @@ -434,9 +460,9 @@ module Bundler raise Gem::Exception, "no default executable for #{spec.full_name}" unless exec_name ||= spec.default_executable - unless spec.name == name - Bundler::SharedHelpers.major_deprecation \ - "Bundler is using a binstub that was created for a different gem.\n" \ "You should run `bundle binstub #{gem_name}` " \ "to work around a system/bundle conflict." end @@ -476,7 +502,7 @@ module Bundler redefine_method(gem_class, :refresh) {} end - # Replace or hook into Rubygems to provide a bundlerized view # of the world. def replace_entrypoints(specs) specs_by_name = specs.reduce({}) do |h, s| @@ -492,8 +518,8 @@ module Bundler Gem.clear_paths end - # This backports the correct segment generation code from Rubygems 1.4+ - # by monkeying it into the method in Rubygems 1.3.6 and 1.3.7. def backport_segment_generation redefine_method(Gem::Version, :segments) do @segments ||= @version.scan(/[0-9]+|[a-z]+/i).map do |s| @@ -512,7 +538,7 @@ module Bundler end # This backports base_dir which replaces installation path - # Rubygems 1.8+ def backport_base_dir redefine_method(Gem::Specification, :base_dir) do return Gem.dir unless loaded_from @@ -581,7 +607,7 @@ module Bundler end end - # Rubygems 1.4 through 1.6 class Legacy < RubygemsIntegration def initialize super @@ -592,7 +618,7 @@ module Bundler end def stub_rubygems(specs) - # Rubygems versions lower than 1.7 use SourceIndex#from_gems_in source_index_class = (class << Gem::SourceIndex; self; end) redefine_method(source_index_class, :from_gems_in) do |*args| Gem::SourceIndex.new.tap do |source_index| @@ -624,7 +650,7 @@ module Bundler end end - # Rubygems versions 1.3.6 and 1.3.7 class Ancient < Legacy def initialize super @@ -632,7 +658,7 @@ module Bundler end end - # Rubygems 1.7 class Transitional < Legacy def stub_rubygems(specs) stub_source_index(specs) @@ -646,7 +672,7 @@ module Bundler end end - # Rubygems 1.8.5-1.8.19 class Modern < RubygemsIntegration def stub_rubygems(specs) Gem::Specification.all = specs @@ -667,9 +693,9 @@ module Bundler end end - # Rubygems 1.8.0 to 1.8.4 class AlmostModern < Modern - # Rubygems [>= 1.8.0, < 1.8.5] has a bug that changes Gem.dir whenever # you call Gem::Installer#install with an :install_dir set. We have to # change it back for our sudo mode to work. def preserve_paths @@ -680,9 +706,9 @@ module Bundler end end - # Rubygems 1.8.20+ class MoreModern < Modern - # Rubygems 1.8.20 and adds the skip_validation parameter, so that's # when we start passing it through. def build(spec, skip_validation = false) require "rubygems/builder" @@ -690,7 +716,7 @@ module Bundler end end - # Rubygems 2.0 class Future < RubygemsIntegration def stub_rubygems(specs) Gem::Specification.all = specs @@ -767,6 +793,10 @@ module Bundler def install_with_build_args(args) yield end end # RubyGems 2.1.0 @@ -855,7 +885,7 @@ module Bundler RubygemsIntegration::Transitional.new elsif RubygemsIntegration.provides?(">= 1.4.0") RubygemsIntegration::Legacy.new - else # Rubygems 1.3.6 and 1.3.7 RubygemsIntegration::Ancient.new end end @@ -1,5 +1,4 @@ # frozen_string_literal: true -require "digest/sha1" module Bundler class Runtime @@ -11,7 +10,7 @@ module Bundler end def setup(*groups) - @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.settings[:frozen] groups.map!(&:to_sym) @@ -262,9 +261,6 @@ module Bundler end def setup_manpath - # Store original MANPATH for restoration later in with_clean_env() - ENV["BUNDLER_ORIG_MANPATH"] = ENV["MANPATH"] - # Add man/ subdirectories from activated bundles to MANPATH for man(1) manuals = $LOAD_PATH.map do |path| man_subdir = path.sub(/lib$/, "man") @@ -272,7 +268,7 @@ module Bundler end.compact return if manuals.empty? - ENV["MANPATH"] = manuals.concat( ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR) ).uniq.join(File::PATH_SEPARATOR) end @@ -1,60 +1,89 @@ # frozen_string_literal: true require "uri" module Bundler class Settings autoload :Mirror, "bundler/mirror" autoload :Mirrors, "bundler/mirror" - BOOL_KEYS = %w( allow_offline_install auto_install cache_all cache_all_platforms disable_checksum_validation disable_exec_load disable_local_branch_check disable_shared_gems disable_version_check force_ruby_platform frozen gem.coc gem.mit ignore_messages major_deprecations no_install no_prune only_update_to_newer_versions plugins silence_root_warning - ).freeze - - NUMBER_KEYS = %w( redirect retry ssl_verify_mode timeout - ).freeze DEFAULT_CONFIG = { :redirect => 5, :retry => 3, :timeout => 10, }.freeze - attr_accessor :cli_flags_given - def initialize(root = nil) @root = root @local_config = load_config(local_config_file) @global_config = load_config(global_config_file) - @cli_flags_given = false @temporary = {} end def [](name) key = key_for(name) - value = @temporary.fetch(name) do @local_config.fetch(key) do ENV.fetch(key) do @global_config.fetch(key) do @@ -65,48 +94,59 @@ module Bundler converted_value(value, name) end - def []=(key, value) - if cli_flags_given command = if value.nil? "bundle config --delete #{key}" else "bundle config #{key} #{Array(value).join(":")}" end - Bundler::SharedHelpers.major_deprecation \ "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}`" end local_config_file || raise(GemfileNotFound, "Could not locate Gemfile") set_key(key, value, @local_config, local_config_file) end - alias_method :set_local, :[]= def temporary(update) - existing = Hash[update.map {|k, _| [k, @temporary[k]] }] - @temporary.update(update) return unless block_given? begin yield ensure - existing.each {|k, v| v.nil? ? @temporary.delete(k) : @temporary[k] = v } end end - def delete(key) - @local_config.delete(key_for(key)) - end - def set_global(key, value) set_key(key, value, @global_config, global_config_file) end def all - env_keys = ENV.keys.select {|k| k =~ /BUNDLE_.*/ } - keys = @global_config.keys | @local_config.keys | env_keys keys.map do |key| key.sub(/^BUNDLE_/, "").gsub(/__/, ".").downcase @@ -132,7 +172,7 @@ module Bundler def gem_mirrors all.inject(Mirrors.new) do |mirrors, k| - mirrors.parse(k, self[k]) if k =~ /^mirror\./ mirrors end end @@ -140,6 +180,7 @@ module Bundler def locations(key) key = key_for(key) locations = {} locations[:local] = @local_config[key] if @local_config.key?(key) locations[:env] = ENV[key] if ENV[key] locations[:global] = @global_config[key] if @global_config.key?(key) @@ -151,6 +192,11 @@ module Bundler key = key_for(exposed_key) locations = [] if @local_config.key?(key) locations << "Set for your local app (#{local_config_file}): #{converted_value(@local_config[key], exposed_key).inspect}" end @@ -167,37 +213,56 @@ module Bundler locations end - def without=(array) - set_array(:without, array) - end - def with=(array) - set_array(:with, array) end - def without - get_array(:without) - end - def with - get_array(:with) - end - # @local_config["BUNDLE_PATH"] should be prioritized over ENV["BUNDLE_PATH"] - def path - key = key_for(:path) - path = ENV[key] || @global_config[key] - return path if path && !@local_config.key?(key) - if path = self[:path] - "#{path}/#{Bundler.ruby_scope}" - else - Bundler.rubygems.gem_dir end end def allow_sudo? - !@local_config.key?(key_for(:path)) end def ignore_config? @@ -205,14 +270,17 @@ module Bundler end def app_cache_path - @app_cache_path ||= begin - path = self[:cache_path] || "vendor/cache" - raise InvalidOption, "Cache path must be relative to the bundle path" if path.start_with?("/") - path - end end - private def key_for(key) key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key @@ -220,15 +288,17 @@ module Bundler "BUNDLE_#{key}" end def parent_setting_for(name) - split_specfic_setting_for(name)[0] end - def specfic_gem_for(name) - split_specfic_setting_for(name)[1] end - def split_specfic_setting_for(name) name.split(".") end @@ -245,43 +315,57 @@ module Bundler end end - def is_num(value) - NUMBER_KEYS.include?(value.to_s) end - def get_array(key) - self[key] ? self[key].split(":").map(&:to_sym) : [] end - def set_array(key, array) - self[key] = (array.empty? ? nil : array.join(":")) if array end - def set_key(key, value, hash, file) - key = key_for(key) - unless hash[key] == value - hash[key] = value - hash.delete(key) if value.nil? - SharedHelpers.filesystem_access(file) do |p| - FileUtils.mkdir_p(p.dirname) - require "bundler/yaml_serializer" - p.open("w") {|f| f.write(YAMLSerializer.dump(hash)) } - end - end - value end def converted_value(value, key) - if value.nil? nil elsif is_bool(key) || value == "false" to_bool(value) elsif is_num(key) value.to_i else - value end end @@ -325,16 +409,34 @@ module Bundler end end # TODO: duplicates Rubygems#normalize_uri # TODO: is this the correct place to validate mirror URIs? def self.normalize_uri(uri) uri = uri.to_s - uri = "#{uri}/" unless uri =~ %r{/\Z} uri = URI(uri) unless uri.absolute? raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri) end - uri end end end @@ -0,0 +1,79 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/shared_helpers" if Bundler::SharedHelpers.in_bundle? @@ -1,7 +1,11 @@ # frozen_string_literal: true require "pathname" require "rubygems" require "bundler/constants" require "bundler/rubygems_integration" require "bundler/current_ruby" @@ -19,10 +23,16 @@ end module Bundler module SharedHelpers - def default_gemfile gemfile = find_gemfile raise GemfileNotFound, "Could not locate Gemfile" unless gemfile - Pathname.new(gemfile).untaint end def default_lockfile @@ -63,7 +73,7 @@ module Bundler end def with_clean_git_env(&block) - keys = %w(GIT_DIR GIT_WORK_TREE) old_env = keys.inject({}) do |h, k| h.update(k => ENV[k]) end @@ -129,20 +139,34 @@ module Bundler namespace.const_get(constant_name) end - def major_deprecation(message) return unless 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 #{Bundler::VERSION.split(".").first.to_i + 1}.0] #{message}") end def print_major_deprecations! - deprecate_gemfile(find_gemfile) if find_gemfile == find_file("Gemfile") if RUBY_VERSION < "2" - major_deprecation("Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}") end return if Bundler.rubygems.provides?(">= 2") - major_deprecation("Bundler will only support rubygems >= 2.0, you are running #{Bundler.rubygems.version}") end def trap(signal, override = false, &block) @@ -170,23 +194,59 @@ module Bundler "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem." end private def validate_bundle_path - return unless Bundler.bundle_path.to_s.include?(File::PATH_SEPARATOR) - message = "Your bundle path contains a '#{File::PATH_SEPARATOR}', " \ "which is the path separator for your system. Bundler cannot " \ "function correctly when the Bundle path contains the " \ "system's PATH separator. Please change your " \ - "bundle path to not include '#{File::PATH_SEPARATOR}'." \ "\nYour current bundle path is '#{Bundler.bundle_path}'." raise Bundler::PathError, message end - def find_gemfile given = ENV["BUNDLE_GEMFILE"] return given if given && !given.empty? - find_file("Gemfile", "gems.rb") end def find_file(*names) @@ -226,40 +286,51 @@ module Bundler end end def set_bundle_variables begin - ENV["BUNDLE_BIN_PATH"] = Bundler.rubygems.bin_path("bundler", "bundle", VERSION) rescue Gem::GemNotFoundException if File.exist?(File.expand_path("../../../exe/bundle", __FILE__)) - ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../exe/bundle", __FILE__) else - ENV["BUNDLE_BIN_PATH"] = File.expand_path("../../../../bin/bundle", __FILE__) end end # Set BUNDLE_GEMFILE - ENV["BUNDLE_GEMFILE"] = find_gemfile.to_s - ENV["BUNDLER_VERSION"] = Bundler::VERSION end def set_path validate_bundle_path paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR) paths.unshift "#{Bundler.bundle_path}/bin" - ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR) end def set_rubyopt rubyopt = [ENV["RUBYOPT"]].compact return if !rubyopt.empty? && rubyopt.first =~ %r{-rbundler/setup} rubyopt.unshift %(-rbundler/setup) - ENV["RUBYOPT"] = rubyopt.join(" ") end def set_rubylib rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR) rubylib.unshift bundler_ruby_lib - ENV["RUBYLIB"] = rubylib.uniq.join(File::PATH_SEPARATOR) end def bundler_ruby_lib @@ -290,12 +361,6 @@ module Bundler true end - def deprecate_gemfile(gemfile) - return unless gemfile && File.basename(gemfile) == "Gemfile" - Bundler::SharedHelpers.major_deprecation \ - "gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock." - end - extend self end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class SimilarityDetector SimilarityScore = Struct.new(:string, :distance) @@ -1,8 +1,10 @@ # frozen_string_literal: true module Bundler class Source autoload :Gemspec, "bundler/source/gemspec" autoload :Git, "bundler/source/git" autoload :Path, "bundler/source/path" autoload :Rubygems, "bundler/source/rubygems" @@ -31,6 +33,15 @@ module Bundler spec.source == self end def include?(other) other == self end @@ -39,6 +50,10 @@ module Bundler "#<#{self.class}:0x#{object_id} #{self}>" end private def version_color(spec_version, locked_spec_version) @@ -54,5 +69,26 @@ module Bundler def earlier_version?(spec_version, locked_spec_version) Gem::Version.new(spec_version) < Gem::Version.new(locked_spec_version) end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Source class Gemspec < Path @@ -1,7 +1,7 @@ # frozen_string_literal: true -require "fileutils" require "uri" -require "digest/sha1" module Bundler class Source @@ -18,7 +18,7 @@ module Bundler @allow_remote = false # Stringify options that could be set as symbols - %w(ref branch tag revision).each {|k| options[k] = options[k].to_s if options[k] } @uri = options["uri"] || "" @branch = options["branch"] @@ -39,7 +39,7 @@ module Bundler out = String.new("GIT\n") out << " remote: #{@uri}\n" out << " revision: #{revision}\n" - %w(ref branch tag submodules).each do |opt| out << " #{opt}: #{options[opt]}\n" if options[opt] end out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB @@ -169,15 +169,13 @@ module Bundler def install(spec, options = {}) force = options[:force] - Bundler.ui.info "Using #{version_message(spec)} from #{self}" - if requires_checkout? && !@copied && !force Bundler.ui.debug " * Checking out revision: #{ref}" git_proxy.copy_to(install_path, submodules) serialize_gemspecs_in(install_path) @copied = true - elsif force - git_proxy.copy_to(install_path, submodules) end generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] } @@ -188,7 +186,7 @@ module Bundler def cache(spec, custom_path = nil) app_cache_path = app_cache_path(custom_path) - return unless Bundler.settings[:cache_all] return if path == app_cache_path cached! FileUtils.rm_rf(app_cache_path) @@ -210,13 +208,11 @@ module Bundler # When using local git repos, this is set to the local repo. def cache_path @cache_path ||= begin - git_scope = "#{base_name}-#{uri_hash}" - - if Bundler.requires_sudo? - Bundler.user_bundle_path.join("cache/git", git_scope) else - Bundler.cache.join("git", git_scope) - end end end @@ -287,7 +283,7 @@ module Bundler # If there is no URI scheme, assume it is an ssh/git URI input = uri end - Digest::SHA1.hexdigest(input) end def cached_revision @@ -304,9 +300,9 @@ module Bundler def fetch git_proxy.checkout - rescue GitError raise unless Bundler.feature_flag.allow_offline_install? - Bundler.ui.warn "Using cached git data because of network errors" end # no-op, since we validate when re-serializing the gemspec @@ -319,6 +315,14 @@ module Bundler StubSpecification.from_stub(stub) end end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "shellwords" require "tempfile" module Bundler @@ -62,7 +63,7 @@ module Bundler begin @revision ||= find_local_revision rescue GitCommandError - raise MissingGitRevisionError.new(ref, uri) end @revision @@ -90,18 +91,21 @@ module Bundler end def checkout - if path.exist? - return if has_revision_cached? - Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" - in_path do - git_retry %(fetch --force --quiet --tags #{uri_escaped_with_configured_credentials} "refs/heads/*:refs/heads/*") - end - else - Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" SharedHelpers.filesystem_access(path.dirname) do |p| FileUtils.mkdir_p(p) end git_retry %(clone #{uri_escaped_with_configured_credentials} "#{path}" --bare --no-hardlinks --quiet) end end @@ -149,7 +153,7 @@ module Bundler end def git_retry(command) - Bundler::Retry.new("`git #{command}`", GitNotAllowedError).attempts do git(command) end end @@ -217,6 +221,7 @@ module Bundler def in_path(&blk) checkout unless path.exist? SharedHelpers.chdir(path, &blk) end @@ -0,0 +1,63 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Source class Path < Source @@ -35,10 +36,12 @@ module Bundler end def remote! @allow_remote = true end def cached! @allow_cached = true end @@ -74,14 +77,14 @@ module Bundler end def install(spec, options = {}) - Bundler.ui.info "Using #{version_message(spec)} from #{self}" generate_bin(spec, :disable_extensions => true) nil # no post-install message end def cache(spec, custom_path = nil) app_cache_path = app_cache_path(custom_path) - return unless Bundler.settings[:cache_all] return if expand(@original_path).to_s.index(root_path.to_s + "/") == 0 unless @original_path.exist? @@ -113,10 +116,6 @@ module Bundler Bundler.root end - def is_a_path? - instance_of?(Path) - end - def expanded_original_path @expanded_original_path ||= expand(original_path) end @@ -228,7 +227,8 @@ module Bundler spec, :env_shebang => false, :disable_extensions => options[:disable_extensions], - :build_args => options[:build_args] ) installer.post_install rescue Gem::InvalidSpecificationException => e @@ -242,7 +242,7 @@ module Bundler "to modify their .gemspec so it can work with `gem build`." end - Bundler.ui.warn "The validation message from Rubygems was:\n #{e.message}" end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Source class Path @@ -6,6 +7,7 @@ module Bundler attr_reader :spec def initialize(spec, options = {}) @spec = spec @gem_dir = Bundler.rubygems.path(spec.full_gem_path) @wrappers = true @@ -1,4 +1,5 @@ # frozen_string_literal: true require "uri" require "rubygems/user_interaction" @@ -31,6 +32,7 @@ module Bundler end def cached! @allow_cached = true end @@ -49,6 +51,7 @@ module Bundler end def can_lock?(spec) spec.source.is_a?(Rubygems) end @@ -69,8 +72,12 @@ module Bundler end def to_s - remote_names = remotes.map(&:to_s).join(", ") - "rubygems repository #{remote_names}" end alias_method :name, :to_s @@ -99,8 +106,8 @@ module Bundler end end - if installed?(spec) && (!force || spec.name.eql?("bundler")) - Bundler.ui.info "Using #{version_message(spec)}" return nil # no post-install message end @@ -141,7 +148,8 @@ module Bundler :wrappers => true, :env_shebang => true, :build_args => opts[:build_args], - :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum ).install end spec.full_gem_path = installed_spec.full_gem_path @@ -212,13 +220,21 @@ module Bundler @remotes.unshift(uri) unless @remotes.include?(uri) end - def replace_remotes(other_remotes) return false if other_remotes == @remotes @remotes = [] other_remotes.reverse_each do |r| add_remote r.to_s end end def unmet_deps @@ -236,6 +252,43 @@ module Bundler end end protected def credless_remotes @@ -276,7 +329,7 @@ module Bundler end def suppress_configured_credentials(remote) - remote_nouser = remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser] remote_nouser else @@ -284,15 +337,14 @@ module Bundler end end def installed_specs - @installed_specs ||= begin - idx = Index.new - have_bundler = false Bundler.rubygems.all_specs.reverse_each do |spec| - if spec.name == "bundler" - next unless spec.version.to_s == VERSION - have_bundler = true - end spec.source = self if Bundler.rubygems.spec_missing_extensions?(spec, false) Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions" @@ -300,23 +352,6 @@ module Bundler end idx << spec end - - # Always have bundler locally - unless have_bundler - # We're running bundler directly from the source - # so, let's create a fake gemspec for it (it's a path) - # gemspec - bundler = Gem::Specification.new do |s| - s.name = "bundler" - s.version = VERSION - s.platform = Gem::Platform::RUBY - s.source = self - s.authors = ["bundler team"] - s.loaded_from = File.expand_path("..", __FILE__) - end - idx << bundler - end - idx end end @@ -334,9 +369,9 @@ module Bundler end idx << s end - end - idx end def api_fetchers @@ -348,71 +383,36 @@ module Bundler index_fetchers = fetchers - api_fetchers # gather lists from non-api sites - index_fetchers.each do |f| - Bundler.ui.info "Fetching source index from #{f.uri}" - idx.use f.specs_with_retry(nil, self) - end # because ensuring we have all the gems we need involves downloading # the gemspecs of those gems, if the non-api sites contain more than - # about 100 gems, we treat all sites as non-api for speed. allow_api = idx.size < API_REQUEST_LIMIT && dependency_names.size < API_REQUEST_LIMIT Bundler.ui.debug "Need to query more than #{API_REQUEST_LIMIT} gems." \ " Downloading full index instead..." unless allow_api - if allow_api - api_fetchers.each do |f| - Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug? - idx.use f.specs_with_retry(dependency_names, self) - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - end - - # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both - # sources A and B. At this point, the API request will have found all the versions of Bar in source A, - # but will not have found any versions of Bar from source B, which is a problem if the requested version - # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for - # each spec we found, we add all possible versions from all sources to the index. - loop do - idxcount = idx.size - api_fetchers.each do |f| - Bundler.ui.info "Fetching version metadata from #{f.uri}", Bundler.ui.debug? - idx.use f.specs_with_retry(idx.dependency_names, self), true - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - end - break if idxcount == idx.size - end - - if api_fetchers.any? - # it's possible that gems from one source depend on gems from some - # other source, so now we download gemspecs and iterate over those - # dependencies, looking for gems we don't have info on yet. - unmet = idx.unmet_dependency_names - - # if there are any cross-site gems we missed, get them now - api_fetchers.each do |f| - Bundler.ui.info "Fetching dependency metadata from #{f.uri}", Bundler.ui.debug? - idx.use f.specs_with_retry(unmet, self) - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - end if unmet.any? - else - allow_api = false - end - end - unless allow_api - api_fetchers.each do |f| - Bundler.ui.info "Fetching source index from #{f.uri}" - idx.use f.specs_with_retry(nil, self) - end end end end def fetch_gem(spec) return false unless spec.remote - uri = spec.remote.uri spec.fetch_platform - Bundler.ui.confirm("Fetching #{version_message(spec)}") download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem" @@ -420,7 +420,7 @@ module Bundler SharedHelpers.filesystem_access("#{download_path}/cache") do |p| FileUtils.mkdir_p(p) end - Bundler.rubygems.download_gem(spec, uri, download_path) if requires_sudo? SharedHelpers.filesystem_access("#{rubygems_dir}/cache") do |p| @@ -457,6 +457,76 @@ module Bundler def cache_path Bundler.app_cache end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler class Source class Rubygems @@ -20,10 +21,12 @@ module Bundler # def cache_slug @cache_slug ||= begin cache_uri = original_uri || uri uri_parts = [cache_uri.host, cache_uri.user, cache_uri.port, cache_uri.path] - uri_digest = Digest::MD5.hexdigest(uri_parts.compact.join(".")) uri_parts[-1] = uri_digest uri_parts.compact.join(".") @@ -1,16 +1,21 @@ # frozen_string_literal: true module Bundler class SourceList attr_reader :path_sources, :git_sources, - :plugin_sources def initialize - @path_sources = [] - @git_sources = [] - @plugin_sources = [] - @rubygems_aggregate = Source::Rubygems.new - @rubygems_sources = [] end def add_path_source(options = {}) @@ -35,13 +40,28 @@ module Bundler add_source_to_list Plugin.source(source).new(options), @plugin_sources end def add_rubygems_remote(uri) @rubygems_aggregate.add_remote(uri) @rubygems_aggregate end def rubygems_sources - @rubygems_sources + [@rubygems_aggregate] end def rubygems_remotes @@ -49,18 +69,25 @@ module Bundler end def all_sources - path_sources + git_sources + plugin_sources + rubygems_sources end def get(source) - source_list_for(source).find {|s| source == s } end def lock_sources - lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s) - lock_sources << combine_rubygems_sources end def replace_sources!(replacement_sources) return true if replacement_sources.empty? @@ -70,13 +97,14 @@ module Bundler end end - replacement_rubygems = replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } @rubygems_aggregate = replacement_rubygems if replacement_rubygems - # Return true if there were changes - lock_sources.to_set != replacement_sources.to_set || - rubygems_remotes.to_set != replacement_rubygems.remotes.to_set end def cached! @@ -93,6 +121,10 @@ module Bundler private def add_source_to_list(source, list) list.unshift(source).uniq! source @@ -122,5 +154,33 @@ module Bundler "protocol to keep your data secure." end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true require "tsort" require "forwardable" require "set" @@ -76,7 +77,7 @@ module Bundler end def materialize(deps, missing_specs = nil) - materialized = self.for(deps, [], false, true, missing_specs).to_a deps = materialized.map(&:name).uniq materialized.map! do |s| next s unless s.is_a?(LazySpecification) @@ -109,9 +110,10 @@ module Bundler def merge(set) arr = sorted.dup - set.each do |s| - next if arr.any? {|s2| s2.name == s.name && s2.version == s.version && s2.platform == s.platform } - arr << s end SpecSet.new(arr) end @@ -1,5 +1,6 @@ # frozen_string_literal: true -require "fileutils" require "net/https" require "openssl" @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/remote_specification" module Bundler @@ -1,5 +1,6 @@ #!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %> # frozen_string_literal: true # # This file was generated by Bundler. # @@ -7,6 +8,9 @@ # this file is here to facilitate running it. # require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../<%= relative_gemfile_path %>", Pathname.new(__FILE__).realpath) @@ -0,0 +1,105 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true source "https://rubygems.org" git_source(:) {|repo_name| "https://.com/#{repo_name}" } @@ -0,0 +1,8 @@ @@ -37,7 +37,7 @@ Bug reports and pull requests are welcome on at https://.com/<%= co ## License -The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). <% end -%> <% if config[:coc] -%> @@ -1,6 +1,5 @@ /.bundle/ /.yardoc -/Gemfile.lock /_yardoc/ /coverage/ /doc/ @@ -1,4 +1,7 @@ # coding: utf-8 lib = File.expand_path("../lib", __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require "<%= config[:namespaced_path] %>/version" @@ -9,7 +12,7 @@ Gem::Specification.new do |spec| spec.authors = [<%= config[:author].inspect %>] spec.email = [<%= config[:email].inspect %>] - spec.summary = %q{TODO: Write a short summary, because Rubygems requires one.} spec.description = %q{TODO: Write a longer description or delete this line.} spec.homepage = "TODO: Put your gem's website or public repo URL here." <%- if config[:mit] -%> @@ -1,2 +1,3 @@ --format documentation --color @@ -1,5 +1,3 @@ -require "spec_helper" - RSpec.describe <%= config[:constant_name] %> do it "has a version number" do expect(<%= config[:constant_name] %>::VERSION).not_to be nil @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module UI autoload :RGProxy, "bundler/ui/rg_proxy" @@ -1,4 +1,5 @@ # frozen_string_literal: true require "bundler/ui" require "rubygems/user_interaction" @@ -1,15 +1,16 @@ # frozen_string_literal: true require "bundler/vendored_thor" module Bundler module UI class Shell - LEVELS = %w(silent error warn confirm info debug).freeze attr_writer :shell def initialize(options = {}) - if options["no-color"] || !STDOUT.tty? Thor::Base.shell = Thor::Shell::Basic end @shell = Thor::Base.shell.new @@ -30,13 +31,18 @@ module Bundler end def warn(msg, newline = nil) return if @warning_history.include? msg @warning_history << msg - tell_me(msg, :yellow, newline) if level("warn") end def error(msg, newline = nil) - tell_me(msg, :red, newline) if level("error") end def debug(msg, newline = nil) @@ -103,6 +109,11 @@ module Bundler end def tell_err(message, color = nil, newline = nil) buffer = @shell.send(:prepare_message, message, *color) buffer << "\n" if newline && !message.to_s.end_with?("\n") @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module UI class Silent @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module URICredentialsFilter module_function @@ -0,0 +1,1638 @@ @@ -1,4 +1,6 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/gem_metadata' require 'bundler/vendor/molinillo/lib/molinillo/errors' require 'bundler/vendor/molinillo/lib/molinillo/resolver' @@ -0,0 +1,26 @@ @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo # @!visibility private module Delegates @@ -45,6 +46,12 @@ module Bundler::Molinillo current_state = state || Bundler::Molinillo::ResolutionState.empty current_state.conflicts end end end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo module Delegates # Delegates all {Bundler::Molinillo::SpecificationProvider} methods to a @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'set' require 'tsort' @@ -147,8 +148,8 @@ module Bundler::Molinillo vertex = add_vertex(name, payload, root) vertex.explicit_requirements << requirement if root parent_names.each do |parent_name| - parent_node = vertex_named(parent_name) - add_edge(parent_node, vertex, requirement) end vertex end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo class DependencyGraph # An action that modifies a {DependencyGraph} that is reversible. @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular' require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex' require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge' @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' module Bundler::Molinillo class DependencyGraph @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo class DependencyGraph # A vertex in a {DependencyGraph} that encapsulates a {#name} and a @@ -32,7 +33,7 @@ module Bundler::Molinillo # @return [Array<Object>] all of the requirements that required # this vertex def requirements - incoming_edges.map(&:requirement) + explicit_requirements end # @return [Array<Edge>] the edges of {#graph} that have `self` as their @@ -53,7 +54,7 @@ module Bundler::Molinillo # {#descendent?} def recursive_predecessors vertices = predecessors - vertices += vertices.map(&:recursive_predecessors).flatten(1) vertices.uniq! vertices end @@ -68,7 +69,7 @@ module Bundler::Molinillo # {#ancestor?} def recursive_successors vertices = successors - vertices += vertices.map(&:recursive_successors).flatten(1) vertices.uniq! vertices end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo # An error that occurred during the resolution process class ResolverError < StandardError; end @@ -41,11 +42,11 @@ module Bundler::Molinillo attr_reader :dependencies # Initializes a new error with the given circular vertices. - # @param [Array<DependencyGraph::Vertex>] nodes the nodes in the dependency # that caused the error - def initialize(nodes) - super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}" - @dependencies = nodes.map(&:payload).to_set end end @@ -55,11 +56,16 @@ module Bundler::Molinillo # resolution to fail attr_reader :conflicts # Initializes a new error with the given version conflicts. # @param [{String => Resolution::Conflict}] conflicts see {#conflicts} - def initialize(conflicts) pairs = [] - conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting| conflicting.each do |source, conflict_requirements| conflict_requirements.each do |c| pairs << [c, source] @@ -69,7 +75,64 @@ module Bundler::Molinillo super "Unable to satisfy the following requirements:\n\n" \ "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}" @conflicts = conflicts end end end @@ -1,5 +1,6 @@ # frozen_string_literal: true module Bundler::Molinillo # The version of Bundler::Molinillo. - VERSION = '0.5.7'.freeze end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo # Provides information about specifcations and dependencies to the resolver, # allowing the {Resolver} class to remain generic while still providing power @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo # Conveys information about the resolution process to a user. module UI @@ -48,7 +49,8 @@ module Bundler::Molinillo if debug? debug_info = yield debug_info = debug_info.inspect unless debug_info.is_a?(String) - output.puts debug_info.split("\n").map { |s| ' ' * depth + s } end end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo class Resolver # A specific resolution from a given {Resolver} @@ -8,22 +9,125 @@ module Bundler::Molinillo # @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict # @attr [Object, nil] existing the existing spec that was in conflict with # the {#possibility} - # @attr [Object] possibility the spec that was unable to be activated due - # to a conflict # @attr [Object] locked_requirement the relevant locking requirement. # @attr [Array<Array<Object>>] requirement_trees the different requirement # trees that led to every requirement for the conflicting name. # @attr [{String=>Object}] activated_by_name the already-activated specs. Conflict = Struct.new( :requirement, :requirements, :existing, - :possibility, :locked_requirement, :requirement_trees, - :activated_by_name ) # @return [SpecificationProvider] the provider that knows about # dependencies, requirements, specifications, versions, etc. attr_reader :specification_provider @@ -64,7 +168,7 @@ module Bundler::Molinillo start_resolution while state - break unless state.requirements.any? || state.requirement indicate_progress if state.respond_to?(:pop_possibility_state) # DependencyState debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } @@ -78,7 +182,7 @@ module Bundler::Molinillo process_topmost_state end - activated.freeze ensure end_resolution end @@ -109,6 +213,19 @@ module Bundler::Molinillo resolver_ui.before_resolution end # Ends the resolution process # @return [void] def end_resolution @@ -136,9 +253,12 @@ module Bundler::Molinillo if possibility attempt_to_activate else - create_conflict if state.is_a? PossibilityState - unwind_for_conflict until possibility && state.is_a?(DependencyState) end end # @return [Object] the current possibility that the resolution is trying @@ -158,7 +278,10 @@ module Bundler::Molinillo # @return [DependencyState] the initial state for the resolution def initial_state graph = DependencyGraph.new.tap do |dg| - original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } } dg.tag(:initial_state) end @@ -169,45 +292,280 @@ module Bundler::Molinillo requirements, graph, initial_requirement, - initial_requirement && search_for(initial_requirement), 0, - {} ) end # Unwinds the states stack because a conflict has been encountered # @return [void] def unwind_for_conflict - debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" } conflicts.tap do |c| - sliced_states = states.slice!((state_index_for_unwind + 1)..-1) - raise VersionConflict.new(c) unless state activated.rewind_to(sliced_states.first || :initial_state) if sliced_states state.conflicts = c index = states.size - 1 @parents_of.each { |_, a| a.reject! { |i| i >= index } } end end - # @return [Integer] The index to which the resolution should unwind in the - # case of conflict. - def state_index_for_unwind - current_requirement = requirement - existing_requirement = requirement_for_existing_name(name) - index = -1 - [current_requirement, existing_requirement].each do |r| - until r.nil? - current_state = find_state_for(r) - if state_any?(current_state) - current_index = states.index(current_state) - index = current_index if current_index > index - break end - r = parent_of(r) end end - index end # @return [Object] the requirement that led to `requirement` being added @@ -222,7 +580,8 @@ module Bundler::Molinillo # @return [Object] the requirement that led to a version of a possibility # with the given name being activated. def requirement_for_existing_name(name) - return nil unless activated.vertex_named(name).payload states.find { |s| s.name == name }.requirement end @@ -230,18 +589,12 @@ module Bundler::Molinillo # `requirement`. def find_state_for(requirement) return nil unless requirement - states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) } - end - - # @return [Boolean] whether or not the given state has any possibilities - # left. - def state_any?(state) - state && state.possibilities.any? end # @return [Conflict] a {Conflict} that reflects the failure to activate # the {#possibility} in conjunction with the current {#state} - def create_conflict vertex = activated.vertex_named(name) locked_requirement = locked_requirement_named(name) @@ -250,18 +603,21 @@ module Bundler::Molinillo requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements end requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement - vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) } activated_by_name = {} - activated.each { |v| activated_by_name[v.name] = v.payload if v.payload } conflicts[name] = Conflict.new( requirement, requirements, - vertex.payload, possibility, locked_requirement, requirement_trees, - activated_by_name ) end @@ -311,116 +667,48 @@ module Bundler::Molinillo # @return [void] def attempt_to_activate debug(depth) { 'Attempting to activate ' + possibility.to_s } - existing_node = activated.vertex_named(name) - if existing_node.payload - debug(depth) { "Found existing spec (#{existing_node.payload})" } - attempt_to_activate_existing_spec(existing_node) - else - attempt_to_activate_new_spec - end - end - - # Attempts to activate the current {#possibility} (given that it has - # already been activated) - # @return [void] - def attempt_to_activate_existing_spec(existing_node) - existing_spec = existing_node.payload - if requirement_satisfied_by?(requirement, activated, existing_spec) - new_requirements = requirements.dup - push_state_for_requirements(new_requirements, false) else - return if attempt_to_swap_possibility - create_conflict - debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" } - unwind_for_conflict - end - end - - # Attempts to swp the current {#possibility} with the already-activated - # spec with the given name - # @return [Boolean] Whether the possibility was swapped into {#activated} - def attempt_to_swap_possibility - activated.tag(:swap) - vertex = activated.vertex_named(name) - activated.set_payload(name, possibility) - if !vertex.requirements. - all? { |r| requirement_satisfied_by?(r, activated, possibility) } || - !new_spec_satisfied? - activated.rewind_to(:swap) - return - end - fixup_swapped_children(vertex) - activate_spec - end - - # Ensures there are no orphaned successors to the given {vertex}. - # @param [DependencyGraph::Vertex] vertex the vertex to fix up. - # @return [void] - def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity - payload = vertex.payload - deps = dependencies_for(payload).group_by(&method(:name_for)) - vertex.outgoing_edges.each do |outgoing_edge| - requirement = outgoing_edge.requirement - parent_index = @parents_of[requirement].last - succ = outgoing_edge.destination - matching_deps = Array(deps[succ.name]) - dep_matched = matching_deps.include?(requirement) - - # only push the current index when it was originally required by the - # same named spec - if parent_index && states[parent_index].name == name - @parents_of[requirement].push(states.size - 1) end - - if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex] - debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" } - succ.requirements.each { |r| @parents_of.delete(r) } - - removed_names = activated.detach_vertex_named(succ.name).map(&:name) - requirements.delete_if do |r| - # the only removed vertices are those with no other requirements, - # so it's safe to delete only based upon name here - removed_names.include?(name_for(r)) - end - elsif !dep_matched - debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" } - # also reset if we're removing the edge, but only if its parent has - # already been fixed up - @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty? - - activated.delete_edge(outgoing_edge) - requirements.delete(requirement) end end end - # Attempts to activate the current {#possibility} (given that it hasn't - # already been activated) # @return [void] - def attempt_to_activate_new_spec - if new_spec_satisfied? - activate_spec else create_conflict unwind_for_conflict end end - # @return [Boolean] whether the current spec is satisfied as a new - # possibility. - def new_spec_satisfied? - unless requirement_satisfied_by?(requirement, activated, possibility) - debug(depth) { 'Unsatisfied by requested spec' } - return false - end - - locked_requirement = locked_requirement_named(name) - - locked_spec_satisfied = !locked_requirement || - requirement_satisfied_by?(locked_requirement, activated, possibility) - debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied - - locked_spec_satisfied end # @param [String] requirement_name the spec name to search for @@ -434,7 +722,7 @@ module Bundler::Molinillo # Add the current {#possibility} to the dependency graph of the current # {#state} # @return [void] - def activate_spec conflicts.delete(name) debug(depth) { "Activated #{name} at #{possibility}" } activated.set_payload(name, possibility) @@ -442,14 +730,14 @@ module Bundler::Molinillo end # Requires the dependencies that the recently activated spec has - # @param [Object] activated_spec the specification that has just been # activated # @return [void] - def require_nested_dependencies_for(activated_spec) - nested_dependencies = dependencies_for(activated_spec) debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" } nested_dependencies.each do |d| - activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d) parent_index = states.size - 1 parents = @parents_of[d] parents << parent_index if parents.empty? @@ -464,20 +752,75 @@ module Bundler::Molinillo # @return [void] def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated) new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort - new_requirement = new_requirements.shift new_name = new_requirement ? name_for(new_requirement) : ''.freeze - possibilities = new_requirement ? search_for(new_requirement) : [] handle_missing_or_push_dependency_state DependencyState.new( new_name, new_requirements, new_activated, - new_requirement, possibilities, depth, conflicts.dup ) end # Pushes a new {DependencyState}. # If the {#specification_provider} says to # {SpecificationProvider#allow_missing?} that particular requirement, and # there are no possibilities for that requirement, then `state` is not - # pushed, and the node in {#activated} is removed, and we continue # resolving the remaining requirements. # @param [DependencyState] state # @return [void] @@ -1,4 +1,5 @@ # frozen_string_literal: true require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph' module Bundler::Molinillo @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler::Molinillo # A state that a {Resolution} can be in # @attr [String] name the name of the current requirement @@ -7,7 +8,8 @@ module Bundler::Molinillo # @attr [Object] requirement the current requirement # @attr [Object] possibilities the possibilities to satisfy the current requirement # @attr [Integer] depth the depth of the resolution - # @attr [Set<Object>] conflicts unresolved conflicts ResolutionState = Struct.new( :name, :requirements, @@ -15,14 +17,15 @@ module Bundler::Molinillo :requirement, :possibilities, :depth, - :conflicts ) class ResolutionState # Returns an empty resolution state # @return [ResolutionState] an empty state def self.empty - new(nil, [], DependencyGraph.new, nil, nil, 0, Set.new) end end @@ -40,7 +43,8 @@ module Bundler::Molinillo requirement, [possibilities.pop], depth + 1, - conflicts.dup ).tap do |state| state.activated.tag(state) end @@ -814,7 +814,7 @@ class Bundler::Persistent::Net::HTTP::Persistent ## # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a - # block is given. Returns all responses recieved. # # See # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html] @@ -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/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), :location => location, :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) } @@ -0,0 +1,9 @@ @@ -1,3 +1,4 @@ # frozen_string_literal: true module Bundler; end require "bundler/vendor/molinillo/lib/molinillo" @@ -1,4 +1,5 @@ # frozen_string_literal: true # We forcibly require OpenSSL, because net/http/persistent will only autoload # it. On some Rubies, autoload fails but explicit require succeeds. begin @@ -15,3 +16,37 @@ module Bundler end end require "bundler/vendor/net-http-persistent/lib/net/http/persistent" @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler def self.require_thor_actions Kernel.send(:require, "bundler/vendor/thor/lib/thor/actions") @@ -1,4 +1,4 @@ -# frozen_string_literal: true # Ruby 1.9.3 and old RubyGems don't play nice with frozen version strings # rubocop:disable MutableConstant @@ -7,7 +7,7 @@ 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 = "1.15.4" unless defined?(::Bundler::VERSION) def self.overwrite_loaded_gem_version begin @@ -21,4 +21,8 @@ module Bundler end private_class_method :overwrite_loaded_gem_version overwrite_loaded_gem_version end @@ -1,4 +1,5 @@ # frozen_string_literal: true module Bundler module VersionRanges NEq = Struct.new(:version) @@ -1,4 +1,9 @@ # frozen_string_literal: true # Vlad task for Bundler. # # Add "require 'bundler/vlad'" in your Vlad deploy.rb, and @@ -1,4 +1,5 @@ # frozen_string_literal: true require "thread" module Bundler @@ -37,7 +37,7 @@ module Bundler HASH_REGEX = / ^ ([ ]*) # indentations - (.*) # key (?::(?=(?:\s|$))) # : (without the lookahead the #key includes this when : is present in value) [ ]? (?: !\s)? # optional exclamation mark found with ruby 1.9.3 @@ -54,10 +54,10 @@ module Bundler last_empty_key = nil str.split(/\r?\n/).each do |line| if match = HASH_REGEX.match(line) - indent, key, _, val = match.captures key = convert_to_backward_compatible_key(key) depth = indent.scan(/ /).length - if val.empty? new_hash = {} stack[depth][key] = new_hash stack[depth + 1] = new_hash |