diff options
author | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-12-22 23:08:05 +0000 |
---|---|---|
committer | hsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2017-12-22 23:08:05 +0000 |
commit | 7825e8363d4b2ccad8e2d3f5eeba9e26f6656911 (patch) | |
tree | 83cbcf419e0feeb2ab0fd063ed85e0776eb0081b /lib | |
parent | 73bed0312895322e0fd18310e840356c8e6af812 (diff) |
Postponing the Bundler merge.
I faced a big issue about Bundler with ruby core.
I have no time to resolve it issue before 2.5 final release.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61416 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
218 files changed, 10 insertions, 28636 deletions
diff --git a/lib/bundler.gemspec b/lib/bundler.gemspec deleted file mode 100644 index 227191b090..0000000000 --- a/lib/bundler.gemspec +++ /dev/null @@ -1,347 +0,0 @@ -# coding: utf-8 -# frozen_string_literal: true - -begin - require File.expand_path("../lib/bundler/version", __FILE__) -rescue LoadError # for Ruby core repository - require File.expand_path("../bundler/version", __FILE__) -end -require "shellwords" - -Gem::Specification.new do |s| - s.name = "bundler" - s.version = Bundler::VERSION - s.license = "MIT" - s.authors = [ - "André Arko", "Samuel Giddins", "Chris Morris", "James Wen", "Tim Moore", - "André Medeiros", "Jessica Lynn Suttles", "Terence Lee", "Carl Lerche", - "Yehuda Katz" - ] - s.email = ["team@bundler.io"] - s.homepage = "http://bundler.io" - s.summary = "The best way to manage your application's dependencies" - s.description = "Bundler manages an application's dependencies through its entire life, across many machines, systematically and repeatably" - - if s.respond_to?(:metadata=) - s.metadata = { - "bug_tracker_uri" => "http://github.com/bundler/bundler/issues", - "changelog_uri" => "https://github.com/bundler/bundler/blob/master/CHANGELOG.md", - "homepage_uri" => "https://bundler.io/", - "source_code_uri" => "http://github.com/bundler/bundler/", - } - end - - if s.version >= Gem::Version.new("2.a".dup) - s.required_ruby_version = ">= 2.3.0" - s.required_rubygems_version = ">= 2.5.0" - else - s.required_ruby_version = ">= 1.8.7" - s.required_rubygems_version = ">= 1.3.6" - end - - s.add_development_dependency "automatiek", "~> 0.1.0" - s.add_development_dependency "mustache", "0.99.6" - s.add_development_dependency "rake", "~> 10.0" - s.add_development_dependency "rdiscount", "~> 2.2" - s.add_development_dependency "ronn", "~> 0.7.3" - s.add_development_dependency "rspec", "~> 3.6" - - s.files = %w[ - exe/bundle - exe/bundle_ruby - exe/bundler - lib/bundler.rb - lib/bundler/build_metadata.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/list.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/compatibility_guard.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_generator.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/process_lock.rb - lib/bundler/psyched_yaml.rb - lib/bundler/remote_specification.rb - lib/bundler/resolver.rb - lib/bundler/resolver/spec_group.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/settings/validator.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/metadata.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/.document - lib/bundler/templates/Executable - lib/bundler/templates/Executable.bundler - lib/bundler/templates/Executable.standalone - lib/bundler/templates/Gemfile - lib/bundler/templates/gems.rb - 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/fileutils/lib/fileutils.rb - lib/bundler/vendor/molinillo/lib/molinillo.rb - lib/bundler/vendor/molinillo/lib/molinillo/compatibility.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_fileutils.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 - man/bundle-platform.1 - man/bundle-update.1 - man/bundle-init.1.txt - man/bundle-info.ronn - man/bundle-gem.ronn - man/bundle-add.1.txt - man/bundle-list.ronn - man/bundle-info.1 - man/bundle-init.1 - man/bundle-outdated.ronn - man/bundle-init.ronn - man/bundle.1 - man/bundle-show.1.txt - man/bundle-exec.1 - man/bundle-install.1.txt - man/bundle-binstubs.1.txt - man/bundle-open.1.txt - man/index.txt - man/bundle-pristine.ronn - man/bundle-install.1 - man/bundle-inject.ronn - man/bundle-list.1 - man/bundle-outdated.1.txt - man/bundle-list.1.txt - man/bundle-update.ronn - man/bundle-clean.1.txt - man/bundle-show.ronn - man/bundle-pristine.1.txt - man/bundle-outdated.1 - man/bundle-check.1 - man/bundle-show.1 - man/gemfile.5 - man/bundle-gem.1 - man/bundle-install.ronn - man/bundle-gem.1.txt - man/bundle-open.1 - man/bundle-add.ronn - man/bundle-lock.1.txt - man/bundle-open.ronn - man/bundle-lock.1 - man/bundle-exec.ronn - man/bundle-check.ronn - man/bundle-info.1.txt - man/bundle-lock.ronn - man/bundle-pristine.1 - man/bundle-viz.1.txt - man/bundle.ronn - man/bundle-platform.ronn - man/bundle-binstubs.ronn - man/bundle-exec.1.txt - man/bundle.1.txt - man/bundle-config.1.txt - man/bundle-package.1.txt - man/bundle-platform.1.txt - man/bundle-binstubs.1 - man/bundle-viz.1 - man/bundle-clean.ronn - man/bundle-package.1 - man/bundle-add.1 - man/bundle-config.1 - man/bundle-package.ronn - man/bundle-viz.ronn - man/bundle-check.1.txt - man/bundle-clean.1 - man/gemfile.5.txt - man/bundle-inject.1 - man/gemfile.5.ronn - man/bundle-config.ronn - man/bundle-inject.1.txt - man/bundle-update.1.txt - CHANGELOG.md - LICENSE.md - README.md - bundler.gemspec - ] - - s.bindir = "exe" - s.executables = %w[bundle bundler] - s.require_paths = ["lib"] -end diff --git a/lib/bundler.rb b/lib/bundler.rb deleted file mode 100644 index bef2caabcf..0000000000 --- a/lib/bundler.rb +++ /dev/null @@ -1,545 +0,0 @@ -# frozen_string_literal: true - -require "bundler/compatibility_guard" - -require "bundler/vendored_fileutils" -require "pathname" -require "rbconfig" -require "thread" - -require "bundler/errors" -require "bundler/environment_preserver" -require "bundler/plugin" -require "bundler/rubygems_ext" -require "bundler/rubygems_integration" -require "bundler/version" -require "bundler/constants" -require "bundler/current_ruby" -require "bundler/build_metadata" - -module Bundler - environment_preserver = EnvironmentPreserver.new(ENV, EnvironmentPreserver::BUNDLER_KEYS) - ORIGINAL_ENV = environment_preserver.restore - ENV.replace(environment_preserver.backup) - SUDO_MUTEX = Mutex.new - - autoload :Definition, "bundler/definition" - autoload :Dependency, "bundler/dependency" - autoload :DepProxy, "bundler/dep_proxy" - autoload :Deprecate, "bundler/deprecate" - autoload :Dsl, "bundler/dsl" - autoload :EndpointSpecification, "bundler/endpoint_specification" - autoload :Env, "bundler/env" - autoload :Fetcher, "bundler/fetcher" - autoload :FeatureFlag, "bundler/feature_flag" - autoload :GemHelper, "bundler/gem_helper" - autoload :GemHelpers, "bundler/gem_helpers" - autoload :GemRemoteFetcher, "bundler/gem_remote_fetcher" - autoload :GemVersionPromoter, "bundler/gem_version_promoter" - autoload :Graph, "bundler/graph" - autoload :Index, "bundler/index" - autoload :Injector, "bundler/injector" - autoload :Installer, "bundler/installer" - autoload :LazySpecification, "bundler/lazy_specification" - autoload :LockfileParser, "bundler/lockfile_parser" - autoload :MatchPlatform, "bundler/match_platform" - autoload :ProcessLock, "bundler/process_lock" - autoload :RemoteSpecification, "bundler/remote_specification" - autoload :Resolver, "bundler/resolver" - autoload :Retry, "bundler/retry" - autoload :RubyDsl, "bundler/ruby_dsl" - autoload :RubyGemsGemInstaller, "bundler/rubygems_gem_installer" - autoload :RubyVersion, "bundler/ruby_version" - autoload :Runtime, "bundler/runtime" - autoload :Settings, "bundler/settings" - autoload :SharedHelpers, "bundler/shared_helpers" - autoload :Source, "bundler/source" - autoload :SourceList, "bundler/source_list" - autoload :SpecSet, "bundler/spec_set" - autoload :StubSpecification, "bundler/stub_specification" - autoload :UI, "bundler/ui" - autoload :URICredentialsFilter, "bundler/uri_credentials_filter" - autoload :VersionRanges, "bundler/version_ranges" - - class << self - def configure - @configured ||= configure_gem_home_and_path - end - - def ui - (defined?(@ui) && @ui) || (self.ui = UI::Silent.new) - end - - def ui=(ui) - Bundler.rubygems.ui = ui ? UI::RGProxy.new(ui) : nil - @ui = ui - end - - # Returns absolute path of where gems are installed on the filesystem. - def bundle_path - @bundle_path ||= Pathname.new(configured_bundle_path.path).expand_path(root) - end - - def configured_bundle_path - @configured_bundle_path ||= settings.path.tap(&:validate!) - end - - # Returns absolute location of where binstubs are installed to. - def bin_path - @bin_path ||= begin - path = settings[:bin] || "bin" - path = Pathname.new(path).expand_path(root).expand_path - SharedHelpers.filesystem_access(path) {|p| FileUtils.mkdir_p(p) } - path - end - end - - def setup(*groups) - # Return if all groups are already loaded - return @setup if defined?(@setup) && @setup - - definition.validate_runtime! - - SharedHelpers.print_major_deprecations! - - if groups.empty? - # Load all groups, but only once - @setup = load.setup - else - load.setup(*groups) - end - end - - def require(*groups) - setup(*groups).require(*groups) - end - - def load - @load ||= Runtime.new(root, definition) - end - - def environment - SharedHelpers.major_deprecation 2, "Bundler.environment has been removed in favor of Bundler.load" - load - end - - # Returns an instance of Bundler::Definition for given Gemfile and lockfile - # - # @param unlock [Hash, Boolean, nil] Gems that have been requested - # to be updated or true if all gems should be updated - # @return [Bundler::Definition] - def definition(unlock = nil) - @definition = nil if unlock - @definition ||= begin - configure - Definition.build(default_gemfile, default_lockfile, unlock) - end - end - - def frozen? - frozen = settings[:deployment] - frozen ||= settings[:frozen] unless feature_flag.deployment_means_frozen? - frozen - end - - def locked_gems - @locked_gems ||= - if defined?(@definition) && @definition - definition.locked_gems - elsif Bundler.default_lockfile.file? - lock = Bundler.read_file(Bundler.default_lockfile) - LockfileParser.new(lock) - end - end - - def ruby_scope - "#{Bundler.rubygems.ruby_engine}/#{Bundler.rubygems.config_map[:ruby_version]}" - end - - def user_home - @user_home ||= begin - home = Bundler.rubygems.user_home - - warning = if home.nil? - "Your home directory is not set." - elsif !File.directory?(home) - "`#{home}` is not a directory." - elsif !File.writable?(home) - "`#{home}` is not writable." - end - - if warning - user_home = tmp_home_path(Etc.getlogin, warning) - Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n" - user_home - else - Pathname.new(home) - end - end - end - - def tmp_home_path(login, warning) - login ||= "unknown" - Kernel.send(:require, "tmpdir") - path = Pathname.new(Dir.tmpdir).join("bundler", "home") - SharedHelpers.filesystem_access(path) do |tmp_home_path| - unless tmp_home_path.exist? - tmp_home_path.mkpath - tmp_home_path.chmod(0o777) - end - tmp_home_path.join(login).tap(&:mkpath) - end - rescue => e - raise e.exception("#{warning}\nBundler also failed to create a temporary home directory at `#{path}':\n#{e}") - end - - def user_bundle_path - Pathname.new(user_home).join(".bundle") - end - - def home - bundle_path.join("bundler") - end - - def install_path - home.join("gems") - end - - def specs_path - bundle_path.join("specifications") - end - - def user_cache - user_bundle_path.join("cache") - end - - def root - @root ||= begin - SharedHelpers.root - rescue GemfileNotFound - bundle_dir = default_bundle_dir - raise GemfileNotFound, "Could not locate Gemfile or .bundle/ directory" unless bundle_dir - Pathname.new(File.expand_path("..", bundle_dir)) - end - end - - def app_config_path - if app_config = ENV["BUNDLE_APP_CONFIG"] - Pathname.new(app_config).expand_path(root) - else - root.join(".bundle") - end - end - - def app_cache(custom_path = nil) - path = custom_path || root - Pathname.new(path).join(settings.app_cache_path) - end - - def tmp(name = Process.pid.to_s) - Kernel.send(:require, "tmpdir") - Pathname.new(Dir.mktmpdir(["bundler", name])) - end - - def rm_rf(path) - FileUtils.remove_entry_secure(path) if path && File.exist?(path) - rescue ArgumentError - message = <<EOF -It is a security vulnerability to allow your home directory to be world-writable, and bundler can not continue. -You should probably consider fixing this issue by running `chmod o-w ~` on *nix. -Please refer to http://ruby-doc.org/stdlib-2.1.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-remove_entry_secure for details. -EOF - File.world_writable?(path) ? Bundler.ui.warn(message) : raise - raise PathError, "Please fix the world-writable issue with your #{path} directory" - end - - def settings - @settings ||= Settings.new(app_config_path) - rescue GemfileNotFound - @settings = Settings.new(Pathname.new(".bundle").expand_path) - end - - # @return [Hash] Environment present before Bundler was activated - def original_env - ORIGINAL_ENV.clone - end - - # @deprecated Use `original_env` instead - # @return [Hash] Environment with all bundler-related variables removed - def clean_env - Bundler::SharedHelpers.major_deprecation(2, "`Bundler.clean_env` has weird edge cases, use `.original_env` instead") - env = original_env - - if env.key?("BUNDLER_ORIG_MANPATH") - env["MANPATH"] = env["BUNDLER_ORIG_MANPATH"] - end - - env.delete_if {|k, _| k[0, 7] == "BUNDLE_" } - - if env.key?("RUBYOPT") - env["RUBYOPT"] = env["RUBYOPT"].sub "-rbundler/setup", "" - end - - if env.key?("RUBYLIB") - rubylib = env["RUBYLIB"].split(File::PATH_SEPARATOR) - rubylib.delete(File.expand_path("..", __FILE__)) - env["RUBYLIB"] = rubylib.join(File::PATH_SEPARATOR) - end - - env - end - - def with_original_env - with_env(original_env) { yield } - end - - def with_clean_env - with_env(clean_env) { yield } - end - - def clean_system(*args) - with_clean_env { Kernel.system(*args) } - end - - def clean_exec(*args) - with_clean_env { Kernel.exec(*args) } - end - - def local_platform - return Gem::Platform::RUBY if settings[:force_ruby_platform] - Gem::Platform.local - end - - def default_gemfile - SharedHelpers.default_gemfile - end - - def default_lockfile - SharedHelpers.default_lockfile - end - - def default_bundle_dir - SharedHelpers.default_bundle_dir - 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 use_system_gems? - configured_bundle_path.use_system_gems? - 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? - - # bins are written to a different location on OS X - bin_dir = Pathname.new(Bundler.system_bindir) - bin_dir = bin_dir.parent until bin_dir.exist? - - # if any directory is not writable, we need sudo - files = [path, bin_dir] | Dir[path.join("build_info/*").to_s] | Dir[path.join("*").to_s] - sudo_needed = files.any? {|f| !File.writable?(f) } - end - - @requires_sudo_ran = true - @requires_sudo = settings.allow_sudo? && sudo_present && sudo_needed - end - - def mkdir_p(path) - if requires_sudo? - sudo "mkdir -p '#{path}'" unless File.exist?(path) - else - SharedHelpers.filesystem_access(path, :write) do |p| - FileUtils.mkdir_p(p) - end - end - end - - def which(executable) - if File.file?(executable) && File.executable?(executable) - executable - elsif paths = ENV["PATH"] - quote = '"'.freeze - paths.split(File::PATH_SEPARATOR).find do |path| - path = path[1..-2] if path.start_with?(quote) && path.end_with?(quote) - executable_path = File.expand_path(executable, path) - return executable_path if File.file?(executable_path) && File.executable?(executable_path) - end - end - end - - def sudo(str) - SUDO_MUTEX.synchronize do - prompt = "\n\n" + <<-PROMPT.gsub(/^ {6}/, "").strip + " " - Your user account isn't allowed to install to the system RubyGems. - You can cancel this installation and run: - - bundle install --path vendor/bundle - - to install the gems into ./vendor/bundle/, or you can enter your password - and install the bundled gems to RubyGems using sudo. - - Password: - PROMPT - - unless @prompted_for_sudo ||= system(%(sudo -k -p "#{prompt}" true)) - raise SudoNotPermittedError, - "Bundler requires sudo access to install at the moment. " \ - "Try installing again, granting Bundler sudo access when prompted, or installing into a different path." - end - - `sudo -p "#{prompt}" #{str}` - end - end - - def read_file(file) - File.open(file, "rb", &:read) - end - - def load_marshal(data) - Marshal.load(data) - rescue => e - raise MarshalError, "#{e.class}: #{e.message}" - end - - def load_gemspec(file, validate = false) - @gemspec_cache ||= {} - key = File.expand_path(file) - @gemspec_cache[key] ||= load_gemspec_uncached(file, validate) - # Protect against caching side-effected gemspecs by returning a - # new instance each time. - @gemspec_cache[key].dup if @gemspec_cache[key] - end - - def load_gemspec_uncached(file, validate = false) - path = Pathname.new(file) - contents = path.read - spec = if contents.start_with?("---") # YAML header - eval_yaml_gemspec(path, contents) - else - # Eval the gemspec from its parent directory, because some gemspecs - # depend on "./" relative paths. - SharedHelpers.chdir(path.dirname.to_s) do - eval_gemspec(path, contents) - end - end - return unless spec - spec.loaded_from = path.expand_path.to_s - Bundler.rubygems.validate(spec) if validate - spec - end - - def clear_gemspec_cache - @gemspec_cache = {} - end - - def git_present? - return @git_present if defined?(@git_present) - @git_present = Bundler.which("git") || Bundler.which("git.exe") - end - - def feature_flag - @feature_flag ||= FeatureFlag.new(VERSION) - end - - def reset! - reset_paths! - Plugin.reset! - reset_rubygems! - end - - def reset_paths! - @bin_path = nil - @bundler_major_version = nil - @bundle_path = nil - @configured = nil - @configured_bundle_path = nil - @definition = nil - @load = nil - @locked_gems = nil - @root = nil - @settings = nil - @setup = nil - @user_home = nil - end - - def reset_rubygems! - return unless defined?(@rubygems) && @rubygems - rubygems.undo_replacements - rubygems.reset - @rubygems = nil - end - - private - - def eval_yaml_gemspec(path, contents) - Kernel.send(:require, "bundler/psyched_yaml") - - # 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) - rescue YamlLibrarySyntaxError, ArgumentError, Gem::EndOfYAMLException, Gem::Exception - eval_gemspec(path, contents) - end - - def eval_gemspec(path, contents) - eval(contents, TOPLEVEL_BINDING.dup, path.expand_path.to_s) - rescue ScriptError, StandardError => e - msg = "There was an error while loading `#{path.basename}`: #{e.message}" - - if e.is_a?(LoadError) && RUBY_VERSION >= "1.9" - msg += "\nDoes it try to require a relative path? That's been removed in Ruby 1.9" - end - - raise GemspecError, Dsl::DSLError.new(msg, path, e.backtrace, contents) - end - - def configure_gem_home_and_path - configure_gem_path - configure_gem_home - bundle_path - end - - def configure_gem_path(env = ENV) - blank_home = env["GEM_HOME"].nil? || env["GEM_HOME"].empty? - if !use_system_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 - possibles = [Bundler.rubygems.gem_dir, Bundler.rubygems.gem_path] - paths = possibles.flatten.compact.uniq.reject(&:empty?) - env["GEM_PATH"] = paths.join(File::PATH_SEPARATOR) - end - end - - def configure_gem_home - Bundler::SharedHelpers.set_env "GEM_HOME", File.expand_path(bundle_path, root) - Bundler.rubygems.clear_paths - end - - # @param env [Hash] - def with_env(env) - backup = ENV.to_hash - ENV.replace(env) - yield - ensure - ENV.replace(backup) - end - end -end diff --git a/lib/bundler/build_metadata.rb b/lib/bundler/build_metadata.rb deleted file mode 100644 index 54436f982d..0000000000 --- a/lib/bundler/build_metadata.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # Represents metadata from when the Bundler gem was built. - module BuildMetadata - # begin ivars - @release = false - # end ivars - - # A hash representation of the build metadata. - def self.to_h - { - "Built At" => built_at, - "Git SHA" => git_commit_sha, - "Released Version" => release?, - } - end - - # A string representing the date the bundler gem was built. - def self.built_at - @built_at ||= Time.now.utc.strftime("%Y-%m-%d").freeze - end - - # The SHA for the git commit the bundler gem was built from. - def self.git_commit_sha - @git_commit_sha ||= Dir.chdir(File.expand_path("..", __FILE__)) do - `git rev-parse --short HEAD`.strip.freeze - end - end - - # Whether this is an official release build of Bundler. - def self.release? - @release - end - end -end diff --git a/lib/bundler/capistrano.rb b/lib/bundler/capistrano.rb deleted file mode 100644 index 1b7145b72b..0000000000 --- a/lib/bundler/capistrano.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 2, - "The Bundler task for Capistrano. Please use http://github.com/capistrano/bundler" - -# Capistrano task for Bundler. -# -# Add "require 'bundler/capistrano'" in your Capistrano deploy.rb, and -# Bundler will be activated after each new deployment. -require "bundler/deployment" -require "capistrano/version" - -if defined?(Capistrano::Version) && Gem::Version.new(Capistrano::Version).release >= Gem::Version.new("3.0") - raise "For Capistrano 3.x integration, please use http://github.com/capistrano/bundler" -end - -Capistrano::Configuration.instance(:must_exist).load do - before "deploy:finalize_update", "bundle:install" - Bundler::Deployment.define_task(self, :task, :except => { :no_release => true }) - set :rake, lambda { "#{fetch(:bundle_cmd, "bundle")} exec rake" } -end diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb deleted file mode 100644 index 05e1851c18..0000000000 --- a/lib/bundler/cli.rb +++ /dev/null @@ -1,746 +0,0 @@ -# frozen_string_literal: true - -require "bundler" -require "bundler/vendored_thor" - -module Bundler - class CLI < Thor - require "bundler/cli/common" - - package_name "Bundler" - - 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 - rescue Exception => e - Bundler.ui = UI::Shell.new - raise e - ensure - Bundler::SharedHelpers.print_major_deprecations! - end - - def self.dispatch(*) - super do |i| - i.send(:print_command) - i.send(:warn_on_outdated_bundler) - end - end - - def initialize(*args) - super - - custom_gemfile = options[:gemfile] || Bundler.settings[:gemfile] - if custom_gemfile && !custom_gemfile.empty? - Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", File.expand_path(custom_gemfile) - Bundler.reset_paths! - end - - Bundler.settings.set_command_option_if_given :retry, options[:retry] - - current_cmd = args.last[:current_command].name - auto_install if AUTO_INSTALL_CMDS.include?(current_cmd) - rescue UnknownArgumentError => e - raise InvalidOption, e.message - ensure - self.options ||= {} - unprinted_warnings = Bundler.ui.unprinted_warnings - Bundler.ui = UI::Shell.new(options) - Bundler.ui.level = "debug" if options["verbose"] - unprinted_warnings.each {|w| Bundler.ui.warn(w) } - - if ENV["RUBYGEMS_GEMDEPS"] && !ENV["RUBYGEMS_GEMDEPS"].empty? - Bundler.ui.warn( - "The RUBYGEMS_GEMDEPS environment variable is set. This enables RubyGems' " \ - "experimental Gemfile mode, which may conflict with Bundler and cause unexpected errors. " \ - "To remove this warning, unset RUBYGEMS_GEMDEPS.", :wrap => true - ) - end - end - - def self.deprecated_option(*args, &blk) - return if Bundler.feature_flag.forget_cli_options? - method_option(*args, &blk) - end - - check_unknown_options!(:except => [:config, :exec]) - stop_on_unknown_option! :exec - - desc "cli_help", "Prints a summary of bundler commands", :hide => true - def cli_help - version - Bundler.ui.info "\n" - - primary_commands = ["install", "update", - Bundler.feature_flag.cache_command_is_package? ? "cache" : "package", - "exec", "config", "help"] - - list = self.class.printable_commands(true) - by_name = list.group_by {|name, _message| name.match(/^bundle (\w+)/)[1] } - utilities = by_name.keys.sort - primary_commands - primary_commands.map! {|name| (by_name[name] || raise("no primary command #{name}")).first } - utilities.map! {|name| by_name[name].first } - - shell.say "Bundler commands:\n\n" - - shell.say " Primary commands:\n" - shell.print_table(primary_commands, :indent => 4, :truncate => true) - shell.say - shell.say " Utilities:\n" - shell.print_table(utilities, :indent => 4, :truncate => true) - shell.say - self.class.send(:class_options_help, shell) - end - default_task(Bundler.feature_flag.default_cli_command) - - 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" - class_option "verbose", :type => :boolean, :desc => "Enable verbose output mode", :aliases => "-V" - - def help(cli = nil) - case cli - when "gemfile" then command = "gemfile" - when nil then command = "bundle" - else command = "bundle-#{cli}" - end - - man_path = File.expand_path("../../../man", __FILE__) - man_pages = Hash[Dir.glob(File.join(man_path, "*")).grep(/.*\.\d*\Z/).collect do |f| - [File.basename(f, ".*"), f] - end] - - if man_pages.include?(command) - if Bundler.which("man") && man_path !~ %r{^file:/.+!/META-INF/jruby.home/.+} - Kernel.exec "man #{man_pages[command]}" - else - puts File.read("#{man_path}/#{File.basename(man_pages[command])}.txt") - end - elsif command_path = Bundler.which("bundler-#{cli}") - Kernel.exec(command_path, "--help") - else - super - end - end - - def self.handle_no_command_error(command, has_namespace = $thor_runner) - if Bundler.feature_flag.plugins? && Bundler::Plugin.command?(command) - return Bundler::Plugin.exec_command(command, ARGV[1..-1]) - end - - return super unless command_path = Bundler.which("bundler-#{command}") - - Kernel.exec(command_path, *ARGV[1..-1]) - end - - desc "init [OPTIONS]", "Generates a Gemfile into the current working directory" - long_desc <<-D - Init generates a default Gemfile in the current working directory. When adding a - Gemfile to a gem with a gemspec, the --gemspec option will automatically add each - dependency listed in the gemspec file to the newly created Gemfile. - D - deprecated_option "gemspec", :type => :string, :banner => "Use the specified .gemspec to create the Gemfile" - def init - require "bundler/cli/init" - Init.new(options.dup).run - end - - desc "check [OPTIONS]", "Checks if the dependencies listed in Gemfile are satisfied by currently installed gems" - long_desc <<-D - Check searches the local machine for each of the gems requested in the Gemfile. If - all gems are found, Bundler prints a success message and exits with a status of 0. - If not, the first missing gem is listed and Bundler exits status 1. - D - method_option "dry-run", :type => :boolean, :default => false, :banner => - "Lock the Gemfile" - 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" unless Bundler.feature_flag.forget_cli_options?}" - map "c" => "check" - def check - require "bundler/cli/check" - Check.new(options).run - end - - desc "install [OPTIONS]", "Install the current environment to the system" - long_desc <<-D - Install will install all of the gems in the current bundle, making them available - for use. In a freshly checked out repository, this command will give you the same - gem versions as the last person who updated the Gemfile and ran `bundle update`. - - Passing [DIR] to install (e.g. vendor) will cause the unpacked gems to be installed - into the [DIR] directory rather than into system gems. - - If the bundle has already been installed, bundler will tell you so and then exit. - D - deprecated_option "binstubs", :type => :string, :lazy_default => "bin", :banner => - "Generate bin stubs for bundled gems to ./bin" - deprecated_option "clean", :type => :boolean, :banner => - "Run bundle clean automatically after install" - deprecated_option "deployment", :type => :boolean, :banner => - "Install using defaults tuned for deployment environments" - deprecated_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" - method_option "gemfile", :type => :string, :banner => - "Use the specified gemfile instead of Gemfile" - method_option "jobs", :aliases => "-j", :type => :numeric, :banner => - "Specify the number of jobs to run in parallel" - method_option "local", :type => :boolean, :banner => - "Do not attempt to fetch gems remotely and use the gem cache instead" - deprecated_option "no-cache", :type => :boolean, :banner => - "Don't update the existing gem cache." - method_option "redownload", :type => :boolean, :aliases => - [Bundler.feature_flag.forget_cli_options? ? nil : "--force"].compact, :banner => - "Force downloading every gem." - deprecated_option "no-prune", :type => :boolean, :banner => - "Don't remove stale gems from the cache." - deprecated_option "path", :type => :string, :banner => - "Specify a different path than the system default ($BUNDLE_PATH or $GEM_HOME). Bundler will remember this value for future installs on this machine" - method_option "quiet", :type => :boolean, :banner => - "Only output warnings and errors." - deprecated_option "shebang", :type => :string, :banner => - "Specify a different shebang executable name than the default (usually 'ruby')" - method_option "standalone", :type => :array, :lazy_default => [], :banner => - "Make a bundle that can work without the Bundler runtime" - deprecated_option "system", :type => :boolean, :banner => - "Install to the system location ($BUNDLE_PATH or $GEM_HOME) even if the bundle was previously installed somewhere else for this application" - method_option "trust-policy", :alias => "P", :type => :string, :banner => - "Gem trust policy (like gem install -P). Must be one of " + - Bundler.rubygems.security_policy_keys.join("|") - deprecated_option "without", :type => :array, :banner => - "Exclude gems that are part of the specified named group." - deprecated_option "with", :type => :array, :banner => - "Include gems that are part of the specified named group." - map "i" => "install" - def install - require "bundler/cli/install" - Bundler.settings.temporary(:no_install => false) do - Install.new(options.dup).run - 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 - possible versions of the gems in the bundle. - D - method_option "full-index", :type => :boolean, :banner => - "Fall back to using the single-file index of all gems" - method_option "group", :aliases => "-g", :type => :array, :banner => - "Update a specific group" - method_option "jobs", :aliases => "-j", :type => :numeric, :banner => - "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 "quiet", :type => :boolean, :banner => - "Only output warnings and errors." - method_option "source", :type => :array, :banner => - "Update a specific source (and all gems associated with it)" - method_option "force", :type => :boolean, :banner => - "Force downloading every gem." - method_option "ruby", :type => :boolean, :banner => - "Update ruby specified in Gemfile.lock" - method_option "bundler", :type => :string, :lazy_default => "> 0.a", :banner => - "Update the locked version of bundler" - method_option "patch", :type => :boolean, :banner => - "Prefer updating only to next patch version" - method_option "minor", :type => :boolean, :banner => - "Prefer updating only to next minor version" - method_option "major", :type => :boolean, :banner => - "Prefer updating to next major version (default)" - method_option "strict", :type => :boolean, :banner => - "Do not allow any gem to be updated past latest --patch | --minor | --major" - method_option "conservative", :type => :boolean, :banner => - "Use bundle install conservative update behavior and do not allow shared dependencies to be updated." - method_option "all", :type => :boolean, :banner => - "Update everything." - def update(*gems) - require "bundler/cli/update" - Update.new(options, gems).run - end - - desc "show GEM [OPTIONS]", "Shows all gems that are part of the bundle, or the path to a given gem" - long_desc <<-D - Show lists the names and versions of all gems that are required by your Gemfile. - Calling show with [GEM] will list the exact location of that gem on your machine. - D - method_option "paths", :type => :boolean, - :banner => "List the paths of all gems that are required by your Gemfile." - method_option "outdated", :type => :boolean, - :banner => "Show verbose output including whether gems are outdated." - def show(gem_name = nil) - Bundler::SharedHelpers.major_deprecation(2, "use `bundle list` instead of `bundle show`") if ARGV[0] == "show" - require "bundler/cli/show" - Show.new(options, gem_name).run - end - # TODO: 2.0 remove `bundle show` - - if Bundler.feature_flag.list_command? - desc "list", "List all gems in the bundle" - method_option "name-only", :type => :boolean, :banner => "print only the gem names" - def list - require "bundler/cli/list" - List.new(options).run - end - - map %w[ls] => "list" - else - map %w[list] => "show" - end - - desc "info GEM [OPTIONS]", "Show information for the given gem" - method_option "path", :type => :boolean, :banner => "Print full path to gem" - def info(gem_name) - require "bundler/cli/info" - Info.new(options, gem_name).run - end - - desc "binstubs GEM [OPTIONS]", "Install the binstubs of the listed gem" - long_desc <<-D - Generate binstubs for executables in [GEM]. Binstubs are put into bin, - or the --binstubs directory if one has been set. Calling binstubs with [GEM [GEM]] - will create binstubs for all given gems. - D - method_option "force", :type => :boolean, :default => false, :banner => - "Overwrite existing binstubs if they exist" - method_option "path", :type => :string, :lazy_default => "bin", :banner => - "Binstub destination directory (default bin)" - method_option "shebang", :type => :string, :banner => - "Specify a different shebang executable name than the default (usually 'ruby')" - method_option "standalone", :type => :boolean, :banner => - "Make binstubs that can work without the Bundler runtime" - def binstubs(*gems) - require "bundler/cli/binstubs" - Binstubs.new(options, gems).run - end - - desc "add GEM VERSION", "Add gem to Gemfile and run bundle install" - long_desc <<-D - Adds the specified gem to Gemfile (if valid) and run 'bundle install' in one step. - D - method_option "version", :aliases => "-v", :type => :string - method_option "group", :aliases => "-g", :type => :string - method_option "source", :aliases => "-s", :type => :string - - def add(gem_name) - require "bundler/cli/add" - 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 - versions of the given gems. Prerelease gems are ignored by default. If your gems - are up to date, Bundler will exit with a status of 0. Otherwise, it will exit 1. - - For more information on patch level options (--major, --minor, --patch, - --update-strict) see documentation on the same options on the update command. - D - method_option "group", :type => :string, :banner => "List gems from a specific group" - method_option "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 pre-release gems" - method_option "source", :type => :array, :banner => "Check against a specific source" - method_option "strict", :type => :boolean, :banner => - "Only list newer versions allowed by your Gemfile requirements" - method_option "update-strict", :type => :boolean, :banner => - "Strict conservative resolution, do not allow any gem to be updated past latest --patch | --minor | --major" - method_option "minor", :type => :boolean, :banner => "Prefer updating only to next minor version" - method_option "major", :type => :boolean, :banner => "Prefer updating to next major version (default)" - method_option "patch", :type => :boolean, :banner => "Prefer updating only to next patch version" - method_option "filter-major", :type => :boolean, :banner => "Only list major newer versions" - method_option "filter-minor", :type => :boolean, :banner => "Only list minor newer versions" - method_option "filter-patch", :type => :boolean, :banner => "Only list patch newer versions" - method_option "parseable", :aliases => "--porcelain", :type => :boolean, :banner => - "Use minimal formatting for more parseable output" - def outdated(*gems) - require "bundler/cli/outdated" - Outdated.new(options, gems).run - end - - if Bundler.feature_flag.cache_command_is_package? - map %w[cache] => :package - else - desc "cache [OPTIONS]", "Cache all the gems to vendor/cache", :hide => true - unless Bundler.feature_flag.cache_command_is_package? - method_option "all", :type => :boolean, - :banner => "Include all sources (including path and git)." - end - 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 - end - - desc "#{Bundler.feature_flag.cache_command_is_package? ? :cache : :package} [OPTIONS]", "Locks and then caches all of the gems into vendor/cache" - unless Bundler.feature_flag.cache_command_is_package? - method_option "all", :type => :boolean, - :banner => "Include all sources (including path and git)." - end - 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)." - method_option "gemfile", :type => :string, :banner => "Use the specified gemfile instead of Gemfile" - method_option "no-install", :type => :boolean, :banner => "Don't install the gems, only the package." - 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 "frozen", :type => :boolean, :banner => - "Do not allow the Gemfile.lock to be updated after this package operation's install" - long_desc <<-D - The package command will copy the .gem files for every gem in the bundle into the - directory ./vendor/cache. If you then check that directory into your source - control repository, others who check out your source will be able to install the - bundle without having to download any additional gems. - D - def package - 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) - require "bundler/cli/exec" - 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. - - By default, setting a configuration value sets it for all projects - on the machine. - - If a global setting is superceded by local configuration, this command - will show the current value, as well as any superceded values and - where they were specified. - D - method_option "parseable", :type => :boolean, :banner => "Use minimal formatting for more parseable output" - def config(*args) - require "bundler/cli/config" - Config.new(options, args, self).run - end - - desc "open GEM", "Opens the source directory of the given bundled gem" - def open(name) - require "bundler/cli/open" - Open.new(options, name).run - end - - if Bundler.feature_flag.console_command? - desc "console [GROUP]", "Opens an IRB session with the bundle pre-loaded" - def console(group = nil) - require "bundler/cli/console" - Console.new(options, group).run - end - end - - desc "version", "Prints the bundler's version information" - def version - cli_help = current_command.name == "cli_help" - if cli_help || ARGV.include?("version") - build_info = " (#{BuildMetadata.built_at} commit #{BuildMetadata.git_commit_sha})" - end - - if !cli_help && Bundler.feature_flag.print_only_version_number? - Bundler.ui.info "#{Bundler::VERSION}#{build_info}" - else - Bundler.ui.info "Bundler version #{Bundler::VERSION}#{build_info}" - end - end - map %w[-v --version] => :version - - desc "licenses", "Prints the license of all gems in the bundle" - def licenses - Bundler.load.specs.sort_by {|s| s.license.to_s }.reverse_each do |s| - gem_name = s.name - license = s.license || s.licenses - - if license.empty? - Bundler.ui.warn "#{gem_name}: Unknown" - else - Bundler.ui.info "#{gem_name}: #{license}" - end - end - end - - desc "viz [OPTIONS]", "Generates a visual dependency graph", :hide => true - long_desc <<-D - Viz generates a PNG file of the current Gemfile as a dependency graph. - Viz requires the ruby-graphviz gem (and its dependencies). - The associated gems must also be installed via 'bundle install'. - D - method_option :file, :type => :string, :default => "gem_graph", :aliases => "-f", :desc => "The name to use for the generated file. see format option" - method_option :format, :type => :string, :default => "png", :aliases => "-F", :desc => "This is output format option. Supported format is png, jpg, svg, dot ..." - method_option :requirements, :type => :boolean, :default => false, :aliases => "-R", :desc => "Set to show the version of each required dependency." - method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version." - method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group." - def viz - require "bundler/cli/viz" - Viz.new(options.dup).run - end - - old_gem = instance_method(:gem) - - desc "gem NAME [OPTIONS]", "Creates a skeleton for creating a rubygem" - method_option :exe, :type => :boolean, :default => false, :aliases => ["--bin", "-b"], :desc => "Generate a binary executable for your library." - method_option :coc, :type => :boolean, :desc => "Generate a code of conduct file. Set a default with `bundle config gem.coc true`." - method_option :edit, :type => :string, :aliases => "-e", :required => false, :banner => "EDITOR", - :lazy_default => [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? }, - :desc => "Open generated gemspec in the specified editor (defaults to $EDITOR or $BUNDLER_EDITOR)" - method_option :ext, :type => :boolean, :default => false, :desc => "Generate the boilerplate for C extension code" - method_option :mit, :type => :boolean, :desc => "Generate an MIT license file. Set a default with `bundle config gem.mit true`." - method_option :test, :type => :string, :lazy_default => "rspec", :aliases => "-t", :banner => "rspec", - :desc => "Generate a test directory for your library, either rspec or minitest. Set a default with `bundle config gem.test rspec`." - def gem(name) - end - - commands["gem"].tap do |gem_command| - def gem_command.run(instance, args = []) - arity = 1 # name - - require "bundler/cli/gem" - cmd_args = args + [instance] - cmd_args.unshift(instance.options) - - cmd = begin - Gem.new(*cmd_args) - rescue ArgumentError => e - instance.class.handle_argument_error(self, e, args, arity) - end - - cmd.run - end - end - - undef_method(:gem) - define_method(:gem, old_gem) - private :gem - - def self.source_root - File.expand_path(File.join(File.dirname(__FILE__), "templates")) - end - - desc "clean [OPTIONS]", "Cleans up unused gems in your bundler directory", :hide => true - 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 => - "Forces clean even if --path is not set" - def clean - require "bundler/cli/clean" - Clean.new(options.dup).run - end - - desc "platform [OPTIONS]", "Displays platform compatibility information" - method_option "ruby", :type => :boolean, :default => false, :banner => - "only display ruby related platform information" - def platform - require "bundler/cli/platform" - Platform.new(options).run - end - - desc "inject GEM VERSION", "Add the named gem, with version requirements, to the resolved Gemfile", :hide => true - 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 2, "The `inject` command has been replaced by the `add` command" - require "bundler/cli/inject" - Inject.new(options.dup, name, version).run - end - - desc "lock", "Creates a lockfile without installing" - method_option "update", :type => :array, :lazy_default => true, :banner => - "ignore the existing lockfile, update all gems by default, or update list of given gems" - method_option "local", :type => :boolean, :default => false, :banner => - "do not attempt to fetch remote gemspecs and use the local gem cache only" - method_option "print", :type => :boolean, :default => false, :banner => - "print the lockfile to STDOUT instead of writing to the file system" - method_option "lockfile", :type => :string, :default => nil, :banner => - "the path the lockfile should be written to" - method_option "full-index", :type => :boolean, :default => false, :banner => - "Fall back to using the single-file index of all gems" - method_option "add-platform", :type => :array, :default => [], :banner => - "Add a new platform to the lockfile" - method_option "remove-platform", :type => :array, :default => [], :banner => - "Remove a platform from the lockfile" - method_option "patch", :type => :boolean, :banner => - "If updating, prefer updating only to next patch version" - method_option "minor", :type => :boolean, :banner => - "If updating, prefer updating only to next minor version" - method_option "major", :type => :boolean, :banner => - "If updating, prefer updating to next major version (default)" - method_option "strict", :type => :boolean, :banner => - "If updating, do not allow any gem to be updated past latest --patch | --minor | --major" - method_option "conservative", :type => :boolean, :banner => - "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated" - def lock - require "bundler/cli/lock" - Lock.new(options).run - end - - desc "env", "Print information about the environment Bundler is running under" - def env - Env.write($stdout) - end - - desc "doctor [OPTIONS]", "Checks the bundle for common problems" - long_desc <<-D - Doctor scans the OS dependencies of each of the gems requested in the Gemfile. If - missing dependencies are detected, Bundler prints them and exits status 1. - Otherwise, Bundler prints a success message and exits with a status of 0. - D - method_option "gemfile", :type => :string, :banner => - "Use the specified gemfile instead of Gemfile" - method_option "quiet", :type => :boolean, :banner => - "Only output warnings and errors." - def doctor - require "bundler/cli/doctor" - Doctor.new(options).run - end - - desc "issue", "Learn how to report an issue in Bundler" - def issue - require "bundler/cli/issue" - Issue.new.run - end - - desc "pristine [GEMS...]", "Restores installed gems to pristine condition" - long_desc <<-D - Restores installed gems to pristine condition from files located in the - gem cache. Gems installed from a git repository will be issued `git - checkout --force`. - D - def pristine(*gems) - require "bundler/cli/pristine" - Pristine.new(gems).run - end - - if Bundler.feature_flag.plugins? - require "bundler/cli/plugin" - desc "plugin", "Manage the bundler plugins" - subcommand "plugin", Plugin - end - - # Reformat the arguments passed to bundle that include a --help flag - # 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 - elsif help_used - args = args.dup - args.delete_at(help_used) - ["help", command || args].flatten.compact - else - args - end - end - - private - - # Automatically invoke `bundle install` and resume if - # Bundler.settings[:auto_install] exists. This is set through config cmd - # `bundle config auto_install 1`. - # - # Note that this method `nil`s out the global Definition object, so it - # should be called first, before you instantiate anything like an - # `Installer` that'll keep a reference to the old one instead. - def auto_install - return unless Bundler.settings[:auto_install] - - begin - Bundler.definition.specs - rescue GemNotFound - Bundler.ui.info "Automatically installing missing gems." - Bundler.reset! - invoke :install, [] - Bundler.reset! - end - end - - def current_command - _, _, config = @_initializer - config[:current_command] - end - - def print_command - return unless Bundler.ui.debug? - cmd = current_command - command_name = cmd.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 = cmd.options[k] - o.default == v - end - command << Thor::Options.to_switches(options_to_print.sort_by(&:first)).strip - command.reject!(&:empty?) - Bundler.ui.info "Running `#{command * " "}` with bundler #{Bundler::VERSION}" - end - - def warn_on_outdated_bundler - return if Bundler.settings[:disable_version_check] - - command_name = current_command.name - return if PARSEABLE_COMMANDS.include?(command_name) - - latest = Fetcher::CompactIndex. - new(nil, Source::Rubygems::Remote.new(URI("https://rubygems.org")), nil). - send(:compact_index_client). - instance_variable_get(:@cache). - dependencies("bundler"). - map {|d| Gem::Version.new(d.first) }. - max - return unless latest - - current = Gem::Version.new(VERSION) - return if current >= latest - latest_installed = Bundler.rubygems.find_name("bundler").map(&:version).max - - installation = "To install the latest version, run `gem install bundler#{" --pre" if latest.prerelease?}`" - if latest_installed && latest_installed > current - suggestion = "To update to the most recent installed version (#{latest_installed}), run `bundle update --bundler`" - suggestion = "#{installation}\n#{suggestion}" if latest_installed < latest - else - suggestion = installation - end - - Bundler.ui.warn "The latest bundler is #{latest}, but you are currently running #{current}.\n#{suggestion}" - rescue - nil - end - end -end diff --git a/lib/bundler/cli/add.rb b/lib/bundler/cli/add.rb deleted file mode 100644 index 1fcbd22f28..0000000000 --- a/lib/bundler/cli/add.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Add - def initialize(options, gem_name) - @gem_name = gem_name - @options = options - @options[:group] = @options[:group].split(",").map(&:strip) if !@options[:group].nil? && !@options[:group].empty? - end - - def run - version = @options[:version].nil? ? nil : @options[:version].split(",").map(&:strip) - - unless version.nil? - version.each do |v| - raise InvalidOption, "Invalid gem requirement pattern '#{v}'" unless Gem::Requirement::PATTERN =~ v.to_s - end - end - dependency = Bundler::Dependency.new(@gem_name, version, @options) - - Injector.inject([dependency], :conservative_versioning => @options[:version].nil?) # Perform conservative versioning only when version is not specified - Installer.install(Bundler.root, Bundler.definition) - end - end -end diff --git a/lib/bundler/cli/binstubs.rb b/lib/bundler/cli/binstubs.rb deleted file mode 100644 index 449204d821..0000000000 --- a/lib/bundler/cli/binstubs.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Binstubs - attr_reader :options, :gems - def initialize(options, gems) - @options = options - @gems = gems - end - - def run - Bundler.definition.validate_runtime! - path_option = options["path"] - path_option = nil if path_option && path_option.empty? - Bundler.settings.set_command_option :bin, path_option if options["path"] - Bundler.settings.set_command_option_if_given :shebang, options["shebang"] - installer = Installer.new(Bundler.root, Bundler.definition) - - if gems.empty? - Bundler.ui.error "`bundle binstubs` needs at least one gem to run." - exit 1 - end - - gems.each do |gem_name| - spec = Bundler.definition.specs.find {|s| s.name == gem_name } - unless spec - raise GemNotFound, Bundler::CLI::Common.gem_not_found_message( - gem_name, Bundler.definition.specs - ) - end - - if options[:standalone] - next Bundler.ui.warn("Sorry, Bundler can only be run via RubyGems.") if gem_name == "bundler" - Bundler.settings.temporary(:path => (Bundler.settings[:path] || Bundler.root)) do - installer.generate_standalone_bundler_executable_stubs(spec) - end - else - installer.generate_bundler_executable_stubs(spec, :force => options[:force], :binstubs_cmd => true) - end - end - end - end -end diff --git a/lib/bundler/cli/cache.rb b/lib/bundler/cli/cache.rb deleted file mode 100644 index 9d2ba87d34..0000000000 --- a/lib/bundler/cli/cache.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Cache - attr_reader :options - def initialize(options) - @options = options - end - - def run - Bundler.definition.validate_runtime! - Bundler.definition.resolve_with_cache! - setup_cache_all - Bundler.settings.set_command_option_if_given :cache_all_platforms, options["all-platforms"] - Bundler.load.cache - Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"] - Bundler.load.lock - rescue GemNotFound => e - Bundler.ui.error(e.message) - Bundler.ui.warn "Run `bundle install` to install missing gems." - exit 1 - end - - private - - def setup_cache_all - Bundler.settings.set_command_option_if_given :cache_all, options[:all] - - if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all? - Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \ - "to package them as well, please pass the --all flag. This will be the default " \ - "on Bundler 2.0." - end - end - end -end diff --git a/lib/bundler/cli/check.rb b/lib/bundler/cli/check.rb deleted file mode 100644 index e572787dc4..0000000000 --- a/lib/bundler/cli/check.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Check - attr_reader :options - - def initialize(options) - @options = options - end - - def run - Bundler.settings.set_command_option_if_given :path, options[:path] - - begin - definition = Bundler.definition - definition.validate_runtime! - not_installed = definition.missing_specs - rescue GemNotFound, VersionConflict - Bundler.ui.error "Bundler can't satisfy your Gemfile's dependencies." - Bundler.ui.warn "Install missing gems with `bundle install`." - exit 1 - end - - if not_installed.any? - Bundler.ui.error "The following gems are missing" - 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.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 - Bundler.load.lock(:preserve_unknown_sections => true) unless options[:"dry-run"] - Bundler.ui.info "The Gemfile's dependencies are satisfied" - end - end - end -end diff --git a/lib/bundler/cli/clean.rb b/lib/bundler/cli/clean.rb deleted file mode 100644 index 4a407fbae7..0000000000 --- a/lib/bundler/cli/clean.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Clean - attr_reader :options - - def initialize(options) - @options = options - end - - def run - require_path_or_force unless options[:"dry-run"] - Bundler.load.clean(options[:"dry-run"]) - end - - protected - - def require_path_or_force - return unless Bundler.use_system_gems? && !options[:force] - raise InvalidOption, "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`." - end - end -end diff --git a/lib/bundler/cli/common.rb b/lib/bundler/cli/common.rb deleted file mode 100644 index 9d40ee9dfd..0000000000 --- a/lib/bundler/cli/common.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module CLI::Common - def self.output_post_install_messages(messages) - return if Bundler.settings["ignore_messages"] - messages.to_a.each do |name, msg| - print_post_install_message(name, msg) unless Bundler.settings["ignore_messages.#{name}"] - end - end - - def self.print_post_install_message(name, msg) - Bundler.ui.confirm "Post-install message from #{name}:" - Bundler.ui.info msg - end - - def self.output_without_groups_message - return if Bundler.settings[:without].empty? - 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" - "Gems in the #{group_str} #{group_list} were not installed." - end - - def self.select_spec(name, regex_match = nil) - specs = [] - regexp = Regexp.new(name) if regex_match - - Bundler.definition.specs.each do |spec| - return spec if spec.name == name - specs << spec if regexp && spec.name =~ regexp - end - - case specs.count - when 0 - raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies) - when 1 - specs.first - else - ask_for_spec_from(specs) - end - rescue RegexpError - raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies) - end - - def self.ask_for_spec_from(specs) - if !$stdout.tty? && ENV["BUNDLE_SPEC_RUN"].nil? - raise GemNotFound, gem_not_found_message(name, Bundler.definition.dependencies) - end - - specs.each_with_index do |spec, index| - Bundler.ui.info "#{index.succ} : #{spec.name}", true - end - Bundler.ui.info "0 : - exit -", true - - num = Bundler.ui.ask("> ").to_i - num > 0 ? specs[num - 1] : nil - end - - def self.gem_not_found_message(missing_gem_name, alternatives) - require "bundler/similarity_detector" - message = "Could not find gem '#{missing_gem_name}'." - alternate_names = alternatives.map {|a| a.respond_to?(:name) ? a.name : a } - suggestions = SimilarityDetector.new(alternate_names).similar_word_list(missing_gem_name) - message += "\nDid you mean #{suggestions}?" if suggestions - message - end - - def self.ensure_all_gems_in_lockfile!(names, locked_gems = Bundler.locked_gems) - locked_names = locked_gems.specs.map(&:name) - names.-(locked_names).each do |g| - raise GemNotFound, gem_not_found_message(g, locked_names) - end - end - - def self.configure_gem_version_promoter(definition, options) - patch_level = patch_level_options(options) - raise InvalidOption, "Provide only one of the following options: #{patch_level.join(", ")}" unless patch_level.length <= 1 - definition.gem_version_promoter.tap do |gvp| - gvp.level = patch_level.first || :major - gvp.strict = options[:strict] || options["update-strict"] - end - end - - def self.patch_level_options(options) - [:major, :minor, :patch].select {|v| options.keys.include?(v.to_s) } - end - - def self.clean_after_install? - clean = Bundler.settings[:clean] - return clean unless clean.nil? - clean ||= Bundler.feature_flag.auto_clean_without_path? && Bundler.settings[:path].nil? - clean &&= !Bundler.use_system_gems? - clean - end - end -end diff --git a/lib/bundler/cli/config.rb b/lib/bundler/cli/config.rb deleted file mode 100644 index 12f71ea8fe..0000000000 --- a/lib/bundler/cli/config.rb +++ /dev/null @@ -1,119 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Config - attr_reader :name, :options, :scope, :thor - attr_accessor :args - - def initialize(options, args, thor) - @options = options - @args = args - @thor = thor - @name = peek = args.shift - @scope = "global" - return unless peek && peek.start_with?("--") - @name = args.shift - @scope = peek[2..-1] - end - - def run - unless name - confirm_all - return - end - - unless valid_scope?(scope) - Bundler.ui.error "Invalid scope --#{scope} given. Please use --local or --global." - exit 1 - end - - if scope == "delete" - Bundler.settings.set_local(name, nil) - Bundler.settings.set_global(name, nil) - return - end - - if args.empty? - if options[:parseable] - if value = Bundler.settings[name] - Bundler.ui.info("#{name}=#{value}") - end - return - end - - confirm(name) - return - end - - Bundler.ui.info(message) if message - Bundler.settings.send("set_#{scope}", name, new_value) - end - - private - - def confirm_all - if @options[:parseable] - thor.with_padding do - Bundler.settings.all.each do |setting| - val = Bundler.settings[setting] - Bundler.ui.info "#{setting}=#{val}" - end - end - else - Bundler.ui.confirm "Settings are listed in order of priority. The top value will be used.\n" - Bundler.settings.all.each do |setting| - Bundler.ui.confirm "#{setting}" - show_pretty_values_for(setting) - Bundler.ui.confirm "" - end - end - end - - def confirm(name) - Bundler.ui.confirm "Settings for `#{name}` in order of priority. The top value will be used" - show_pretty_values_for(name) - end - - def new_value - pathname = Pathname.new(args.join(" ")) - if name.start_with?("local.") && pathname.directory? - pathname.expand_path.to_s - else - args.join(" ") - end - end - - def message - locations = Bundler.settings.locations(name) - if @options[:parseable] - "#{name}=#{new_value}" if new_value - elsif scope == "global" - if locations[:local] - "Your application has set #{name} to #{locations[:local].inspect}. " \ - "This will override the global value you are currently setting" - elsif locations[:env] - "You have a bundler environment variable for #{name} set to " \ - "#{locations[:env].inspect}. This will take precedence over the global value you are setting" - elsif locations[:global] && locations[:global] != args.join(" ") - "You are replacing the current global value of #{name}, which is currently " \ - "#{locations[:global].inspect}" - end - elsif scope == "local" && locations[:local] != args.join(" ") - "You are replacing the current local value of #{name}, which is currently " \ - "#{locations[:local].inspect}" - end - end - - def show_pretty_values_for(setting) - thor.with_padding do - Bundler.settings.pretty_values_for(setting).each do |line| - Bundler.ui.info line - end - end - end - - def valid_scope?(scope) - %w[delete local global].include?(scope) - end - end -end diff --git a/lib/bundler/cli/console.rb b/lib/bundler/cli/console.rb deleted file mode 100644 index 853eca8358..0000000000 --- a/lib/bundler/cli/console.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Console - attr_reader :options, :group - def initialize(options, group) - @options = options - @group = group - end - - def run - Bundler::SharedHelpers.major_deprecation 2, "bundle console will be replaced " \ - "by `bin/console` generated by `bundle gem <name>`" - - group ? Bundler.require(:default, *(group.split.map!(&:to_sym))) : Bundler.require - ARGV.clear - - console = get_console(Bundler.settings[:console] || "irb") - console.start - end - - def get_console(name) - require name - get_constant(name) - rescue LoadError - Bundler.ui.error "Couldn't load console #{name}, falling back to irb" - require "irb" - get_constant("irb") - end - - def get_constant(name) - const_name = { - "pry" => :Pry, - "ripl" => :Ripl, - "irb" => :IRB, - }[name] - Object.const_get(const_name) - rescue NameError - Bundler.ui.error "Could not find constant #{const_name}" - exit 1 - end - end -end diff --git a/lib/bundler/cli/doctor.rb b/lib/bundler/cli/doctor.rb deleted file mode 100644 index 7f28a5eb13..0000000000 --- a/lib/bundler/cli/doctor.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -require "rbconfig" - -module Bundler - class CLI::Doctor - DARWIN_REGEX = /\s+(.+) \(compatibility / - LDD_REGEX = /\t\S+ => (\S+) \(\S+\)/ - - attr_reader :options - - def initialize(options) - @options = options - end - - def otool_available? - Bundler.which("otool") - end - - def ldd_available? - Bundler.which("ldd") - end - - def dylibs_darwin(path) - output = `/usr/bin/otool -L "#{path}"`.chomp - dylibs = output.split("\n")[1..-1].map {|l| l.match(DARWIN_REGEX).captures[0] }.uniq - # ignore @rpath and friends - dylibs.reject {|dylib| dylib.start_with? "@" } - end - - def dylibs_ldd(path) - output = `/usr/bin/ldd "#{path}"`.chomp - output.split("\n").map do |l| - match = l.match(LDD_REGEX) - next if match.nil? - match.captures[0] - end.compact - end - - def dylibs(path) - case RbConfig::CONFIG["host_os"] - when /darwin/ - return [] unless otool_available? - dylibs_darwin(path) - when /(linux|solaris|bsd)/ - return [] unless ldd_available? - dylibs_ldd(path) - else # Windows, etc. - Bundler.ui.warn("Dynamic library check not supported on this platform.") - [] - end - end - - def bundles_for_gem(spec) - Dir.glob("#{spec.full_gem_path}/**/*.bundle") - end - - def check! - require "bundler/cli/check" - Bundler::CLI::Check.new({}).run - end - - def run - Bundler.ui.level = "error" if options[:quiet] - Bundler.settings.validate! - check! - - definition = Bundler.definition - broken_links = {} - - definition.specs.each do |spec| - bundles_for_gem(spec).each do |bundle| - bad_paths = dylibs(bundle).select {|f| !File.exist?(f) } - if bad_paths.any? - broken_links[spec] ||= [] - broken_links[spec].concat(bad_paths) - end - end - end - - if broken_links.any? - message = "The following gems are missing OS dependencies:" - broken_links.map do |spec, paths| - paths.uniq.map do |path| - "\n * #{spec.name}: #{path}" - end - end.flatten.sort.each {|m| message += m } - raise ProductionError, message - else - Bundler.ui.info "No issues found with the installed bundle" - end - end - end -end diff --git a/lib/bundler/cli/exec.rb b/lib/bundler/cli/exec.rb deleted file mode 100644 index 2fdc614fbb..0000000000 --- a/lib/bundler/cli/exec.rb +++ /dev/null @@ -1,105 +0,0 @@ -# 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 - @cmd = args.shift - @args = args - - if Bundler.current_ruby.ruby_2? && !Bundler.current_ruby.jruby? - @args << { :close_others => !options.keep_file_descriptors? } - elsif options.keep_file_descriptors? - Bundler.ui.warn "Ruby version #{RUBY_VERSION} defaults to keeping non-standard file descriptors on Kernel#exec." - end - end - - def run - validate_cmd! - SharedHelpers.set_bundle_environment - if bin_path = Bundler.which(cmd) - if !Bundler.settings[:disable_exec_load] && ruby_shebang?(bin_path) - return kernel_load(bin_path, *args) - end - # First, try to exec directly to something in PATH - if Bundler.current_ruby.jruby_18? - kernel_exec(bin_path, *args) - else - kernel_exec([bin_path, cmd], *args) - end - else - # exec using the given command - kernel_exec(cmd, *args) - end - end - - private - - def validate_cmd! - return unless cmd.nil? - Bundler.ui.error "bundler: exec needs a command to run" - exit 128 - end - - def kernel_exec(*args) - ui = Bundler.ui - Bundler.ui = nil - Kernel.exec(*args) - rescue Errno::EACCES, Errno::ENOEXEC - Bundler.ui = ui - Bundler.ui.error "bundler: not executable: #{cmd}" - exit 126 - rescue Errno::ENOENT - Bundler.ui = ui - Bundler.ui.error "bundler: command not found: #{cmd}" - Bundler.ui.warn "Install missing gem executables with `bundle install`" - exit 127 - end - - def kernel_load(file, *args) - args.pop if args.last.is_a?(Hash) - ARGV.replace(args) - $0 = file - Process.setproctitle(process_title(file, args)) if Process.respond_to?(:setproctitle) - ui = Bundler.ui - Bundler.ui = nil - require "bundler/setup" - signals = Signal.list.keys - RESERVED_SIGNALS - signals.each {|s| trap(s, "DEFAULT") } - Kernel.load(file) - rescue SystemExit, SignalException - raise - rescue Exception => e # rubocop:disable Lint/RescueException - Bundler.ui = ui - Bundler.ui.error "bundler: failed to load command: #{cmd} (#{file})" - backtrace = e.backtrace.take_while {|bt| !bt.start_with?(__FILE__) } - abort "#{e.class}: #{e.message}\n #{backtrace.join("\n ")}" - end - - def process_title(file, args) - "#{file} #{args.join(" ")}".strip - end - - def ruby_shebang?(file) - possibilities = [ - "#!/usr/bin/env ruby\n", - "#!/usr/bin/env jruby\n", - "#!#{Gem.ruby}\n", - ] - - if File.zero?(file) - Bundler.ui.warn "#{file} is empty" - return false - end - - first_line = File.open(file, "rb") {|f| f.read(possibilities.map(&:size).max) } - possibilities.any? {|shebang| first_line.start_with?(shebang) } - end - end -end diff --git a/lib/bundler/cli/gem.rb b/lib/bundler/cli/gem.rb deleted file mode 100644 index 885578e819..0000000000 --- a/lib/bundler/cli/gem.rb +++ /dev/null @@ -1,249 +0,0 @@ -# frozen_string_literal: true - -require "pathname" - -module Bundler - class CLI - Bundler.require_thor_actions - include Thor::Actions - end - - class CLI::Gem - TEST_FRAMEWORK_VERSIONS = { - "rspec" => "3.0", - "minitest" => "5.0" - }.freeze - - attr_reader :options, :gem_name, :thor, :name, :target - - def initialize(options, gem_name, thor) - @options = options - @gem_name = resolve_name(gem_name) - - @thor = thor - thor.behavior = :invoke - thor.destination_root = nil - - @name = @gem_name - @target = SharedHelpers.pwd.join(gem_name) - - validate_ext_name if options[:ext] - end - - def run - Bundler.ui.confirm "Creating gem '#{name}'..." - - underscored_name = name.tr("-", "_") - namespaced_path = name.tr("-", "/") - constant_name = name.gsub(/-[_-]*(?![_-]|$)/) { "::" }.gsub(/([_-]+|(::)|^)(.|$)/) { $2.to_s + $3.upcase } - constant_array = constant_name.split("::") - - git_installed = Bundler.git_present? - - git_author_name = git_installed ? `git config user.name`.chomp : "" - github_username = git_installed ? `git config github.user`.chomp : "" - git_user_email = git_installed ? `git config user.email`.chomp : "" - - config = { - :name => name, - :underscored_name => underscored_name, - :namespaced_path => namespaced_path, - :makefile_path => "#{underscored_name}/#{underscored_name}", - :constant_name => constant_name, - :constant_array => constant_array, - :author => git_author_name.empty? ? "TODO: Write your name" : git_author_name, - :email => git_user_email.empty? ? "TODO: Write your email address" : git_user_email, - :test => options[:test], - :ext => options[:ext], - :exe => options[:exe], - :bundler_version => bundler_dependency_version, - :github_username => github_username.empty? ? "[USERNAME]" : github_username - } - ensure_safe_gem_name(name, constant_array) - - templates = { - "Gemfile.tt" => "Gemfile", - "lib/newgem.rb.tt" => "lib/#{namespaced_path}.rb", - "lib/newgem/version.rb.tt" => "lib/#{namespaced_path}/version.rb", - "newgem.gemspec.tt" => "#{name}.gemspec", - "Rakefile.tt" => "Rakefile", - "README.md.tt" => "README.md", - "bin/console.tt" => "bin/console", - "bin/setup.tt" => "bin/setup" - } - - executables = %w[ - bin/console - bin/setup - ] - - templates.merge!("gitignore.tt" => ".gitignore") if Bundler.git_present? - - if test_framework = ask_and_set_test_framework - config[:test] = test_framework - config[:test_framework_version] = TEST_FRAMEWORK_VERSIONS[test_framework] - - templates.merge!("travis.yml.tt" => ".travis.yml") - - case test_framework - when "rspec" - templates.merge!( - "rspec.tt" => ".rspec", - "spec/spec_helper.rb.tt" => "spec/spec_helper.rb", - "spec/newgem_spec.rb.tt" => "spec/#{namespaced_path}_spec.rb" - ) - when "minitest" - templates.merge!( - "test/test_helper.rb.tt" => "test/test_helper.rb", - "test/newgem_test.rb.tt" => "test/#{namespaced_path}_test.rb" - ) - end - end - - config[:test_task] = config[:test] == "minitest" ? "test" : "spec" - - if ask_and_set(:mit, "Do you want to license your code permissively under the MIT license?", - "This means that any other developer or company will be legally allowed to use your code " \ - "for free as long as they admit you created it. You can read more about the MIT license " \ - "at http://choosealicense.com/licenses/mit.") - config[:mit] = true - Bundler.ui.info "MIT License enabled in config" - templates.merge!("LICENSE.txt.tt" => "LICENSE.txt") - end - - if ask_and_set(:coc, "Do you want to include a code of conduct in gems you generate?", - "Codes of conduct can increase contributions to your project by contributors who " \ - "prefer collaborative, safe spaces. You can read more about the code of conduct at " \ - "contributor-covenant.org. Having a code of conduct means agreeing to the responsibility " \ - "of enforcing it, so be sure that you are prepared to do that. Be sure that your email " \ - "address is specified as a contact in the generated code of conduct so that people know " \ - "who to contact in case of a violation. For suggestions about " \ - "how to enforce codes of conduct, see http://bit.ly/coc-enforcement.") - config[:coc] = true - Bundler.ui.info "Code of conduct enabled in config" - templates.merge!("CODE_OF_CONDUCT.md.tt" => "CODE_OF_CONDUCT.md") - end - - templates.merge!("exe/newgem.tt" => "exe/#{name}") if config[:exe] - - if options[:ext] - templates.merge!( - "ext/newgem/extconf.rb.tt" => "ext/#{name}/extconf.rb", - "ext/newgem/newgem.h.tt" => "ext/#{name}/#{underscored_name}.h", - "ext/newgem/newgem.c.tt" => "ext/#{name}/#{underscored_name}.c" - ) - end - - templates.each do |src, dst| - destination = target.join(dst) - SharedHelpers.filesystem_access(destination) do - thor.template("newgem/#{src}", destination, config) - end - end - - executables.each do |file| - SharedHelpers.filesystem_access(target.join(file)) do |path| - executable = (path.stat.mode | 0o111) - path.chmod(executable) - end - end - - if Bundler.git_present? - Bundler.ui.info "Initializing git repo in #{target}" - Dir.chdir(target) do - `git init` - `git add .` - end - end - - # Open gemspec in editor - open_editor(options["edit"], target.join("#{name}.gemspec")) if options[:edit] - rescue Errno::EEXIST => e - raise GenericSystemCallError.new(e, "There was a conflict while creating the new gem.") - end - - private - - def resolve_name(name) - SharedHelpers.pwd.join(name).basename.to_s - end - - def ask_and_set(key, header, message) - choice = options[key] - choice = Bundler.settings["gem.#{key}"] if choice.nil? - - if choice.nil? - Bundler.ui.confirm header - choice = Bundler.ui.yes? "#{message} y/(n):" - Bundler.settings.set_global("gem.#{key}", choice) - end - - choice - end - - def validate_ext_name - return unless gem_name.index("-") - - Bundler.ui.error "You have specified a gem name which does not conform to the \n" \ - "naming guidelines for C extensions. For more information, \n" \ - "see the 'Extension Naming' section at the following URL:\n" \ - "http://guides.rubygems.org/gems-with-extensions/\n" - exit 1 - end - - def ask_and_set_test_framework - test_framework = options[:test] || Bundler.settings["gem.test"] - - if test_framework.nil? - Bundler.ui.confirm "Do you want to generate tests with your gem?" - result = Bundler.ui.ask "Type 'rspec' or 'minitest' to generate those test files now and " \ - "in the future. rspec/minitest/(none):" - if result =~ /rspec|minitest/ - test_framework = result - else - test_framework = false - end - end - - if Bundler.settings["gem.test"].nil? - Bundler.settings.set_global("gem.test", test_framework) - end - - test_framework - end - - def bundler_dependency_version - v = Gem::Version.new(Bundler::VERSION) - req = v.segments[0..1] - req << "a" if v.prerelease? - req.join(".") - end - - def ensure_safe_gem_name(name, constant_array) - if name =~ /^\d/ - Bundler.ui.error "Invalid gem name #{name} Please give a name which does not start with numbers." - exit 1 - end - - constant_name = constant_array.join("::") - - existing_constant = constant_array.inject(Object) do |c, s| - defined = begin - c.const_defined?(s) - rescue NameError - Bundler.ui.error "Invalid gem name #{name} -- `#{constant_name}` is an invalid constant name" - exit 1 - end - (defined && c.const_get(s)) || break - end - - return unless existing_constant - Bundler.ui.error "Invalid gem name #{name} constant #{constant_name} is already in use. Please choose another gem name." - exit 1 - end - - def open_editor(editor, file) - thor.run(%(#{editor} "#{file}")) - end - end -end diff --git a/lib/bundler/cli/info.rb b/lib/bundler/cli/info.rb deleted file mode 100644 index 958b525067..0000000000 --- a/lib/bundler/cli/info.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Info - attr_reader :gem_name, :options - def initialize(options, gem_name) - @options = options - @gem_name = gem_name - end - - def run - spec = spec_for_gem(gem_name) - - spec_not_found(gem_name) unless spec - return print_gem_path(spec) if @options[:path] - print_gem_info(spec) - end - - private - - def spec_for_gem(gem_name) - spec = Bundler.definition.specs.find {|s| s.name == gem_name } - spec || default_gem_spec(gem_name) - end - - def default_gem_spec(gem_name) - return unless Gem::Specification.respond_to?(:find_all_by_name) - gem_spec = Gem::Specification.find_all_by_name(gem_name).last - return gem_spec if gem_spec && gem_spec.respond_to?(:default_gem?) && gem_spec.default_gem? - end - - def spec_not_found(gem_name) - raise GemNotFound, Bundler::CLI::Common.gem_not_found_message(gem_name, Bundler.definition.dependencies) - end - - def print_gem_path(spec) - Bundler.ui.info spec.full_gem_path - end - - def print_gem_info(spec) - gem_info = String.new - gem_info << " * #{spec.name} (#{spec.version}#{spec.git_version})\n" - gem_info << "\tSummary: #{spec.summary}\n" if spec.summary - gem_info << "\tHomepage: #{spec.homepage}\n" if spec.homepage - gem_info << "\tPath: #{spec.full_gem_path}\n" - gem_info << "\tDefault Gem: yes" if spec.respond_to?(:default_gem?) && spec.default_gem? - Bundler.ui.info gem_info - end - end -end diff --git a/lib/bundler/cli/init.rb b/lib/bundler/cli/init.rb deleted file mode 100644 index fa53e7c74b..0000000000 --- a/lib/bundler/cli/init.rb +++ /dev/null @@ -1,42 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Init - attr_reader :options - def initialize(options) - @options = options - end - - def run - if File.exist?(gemfile) - Bundler.ui.error "#{gemfile} already exists at #{File.expand_path(gemfile)}" - exit 1 - end - - if options[:gemspec] - gemspec = File.expand_path(options[:gemspec]) - unless File.exist?(gemspec) - Bundler.ui.error "Gem specification #{gemspec} doesn't exist" - exit 1 - end - - spec = Bundler.load_gemspec_uncached(gemspec) - - File.open(gemfile, "wb") do |file| - file << "# Generated from #{gemspec}\n" - file << spec.to_gemfile - end - else - FileUtils.cp(File.expand_path("../../templates/#{gemfile}", __FILE__), gemfile) - end - - puts "Writing new #{gemfile} to #{SharedHelpers.pwd}/#{gemfile}" - end - - private - - def gemfile - @gemfile ||= Bundler.feature_flag.init_gems_rb? ? "gems.rb" : "Gemfile" - end - end -end diff --git a/lib/bundler/cli/inject.rb b/lib/bundler/cli/inject.rb deleted file mode 100644 index b00675d348..0000000000 --- a/lib/bundler/cli/inject.rb +++ /dev/null @@ -1,60 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Inject - attr_reader :options, :name, :version, :group, :source, :gems - def initialize(options, name, version) - @options = options - @name = name - @version = version || last_version_number - @group = options[:group].split(",") unless options[:group].nil? - @source = options[:source] - @gems = [] - end - - def run - # The required arguments allow Thor to give useful feedback when the arguments - # are incorrect. This adds those first two arguments onto the list as a whole. - gems.unshift(source).unshift(group).unshift(version).unshift(name) - - # Build an array of Dependency objects out of the arguments - deps = [] - # when `inject` support addition of more than one gem, then this loop will - # help. Currently this loop is running once. - gems.each_slice(4) do |gem_name, gem_version, gem_group, gem_source| - ops = Gem::Requirement::OPS.map {|key, _val| key } - has_op = ops.any? {|op| gem_version.start_with? op } - gem_version = "~> #{gem_version}" unless has_op - deps << Bundler::Dependency.new(gem_name, gem_version, "group" => gem_group, "source" => gem_source) - end - - added = Injector.inject(deps, options) - - if added.any? - Bundler.ui.confirm "Added to Gemfile:" - Bundler.ui.confirm(added.map do |d| - name = "'#{d.name}'" - requirement = ", '#{d.requirement}'" - group = ", :group => #{d.groups.inspect}" if d.groups != Array(:default) - source = ", :source => '#{d.source}'" unless d.source.nil? - %(gem #{name}#{requirement}#{group}#{source}) - end.join("\n")) - else - Bundler.ui.confirm "All gems were already present in the Gemfile" - end - end - - private - - def last_version_number - definition = Bundler.definition(true) - definition.resolve_remotely! - specs = definition.index[name].sort_by(&:version) - unless options[:pre] - specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? } - end - spec = specs.last - spec.version.to_s - end - end -end diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb deleted file mode 100644 index f0b821ed84..0000000000 --- a/lib/bundler/cli/install.rb +++ /dev/null @@ -1,214 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Install - attr_reader :options - def initialize(options) - @options = options - end - - def run - Bundler.ui.level = "error" if options[:quiet] - - warn_if_root - - normalize_groups - - Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD - - # Disable color in deployment mode - Bundler.ui.shell = Thor::Shell::Basic.new if options[:deployment] - - check_for_options_conflicts - - check_trust_policy - - if options[:deployment] || options[:frozen] || Bundler.frozen? - unless Bundler.default_lockfile.exist? - flag = "--deployment flag" if options[:deployment] - flag ||= "--frozen flag" if options[:frozen] - flag ||= "deployment setting" - raise ProductionError, "The #{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? - - if Bundler.feature_flag.deployment_means_frozen? - Bundler.settings.set_command_option :deployment, true - else - Bundler.settings.set_command_option :frozen, true - end - end - - # When install is called with --no-deployment, disable deployment mode - if options[:deployment] == false - Bundler.settings.set_command_option :frozen, nil - options[:system] = true - end - - normalize_settings - - Bundler::Fetcher.disable_endpoint = options["full-index"] - - if options["binstubs"] - Bundler::SharedHelpers.major_deprecation 2, - "The --binstubs option will be removed in favor of `bundle binstubs`" - end - - Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins? - - definition = Bundler.definition - definition.validate_runtime! - - installer = Installer.install(Bundler.root, definition, options) - Bundler.load.cache if Bundler.app_cache.exist? && !options["no-cache"] && !Bundler.frozen? - - Bundler.ui.confirm "Bundle complete! #{dependencies_count_for(definition)}, #{gems_installed_for(definition)}." - Bundler::CLI::Common.output_without_groups_message - - if Bundler.use_system_gems? - Bundler.ui.confirm "Use `bundle info [gemname]` to see where a bundled gem is installed." - else - absolute_path = File.expand_path(Bundler.configured_bundle_path.base_path) - relative_path = absolute_path.sub(File.expand_path(".") + File::SEPARATOR, "." + File::SEPARATOR) - Bundler.ui.confirm "Bundled gems are installed into `#{relative_path}`" - end - - Bundler::CLI::Common.output_post_install_messages installer.post_install_messages - - warn_ambiguous_gems - - if CLI::Common.clean_after_install? - require "bundler/cli/clean" - Bundler::CLI::Clean.new(options).run - end - rescue GemNotFound, VersionConflict => e - if options[:local] && Bundler.app_cache.exist? - Bundler.ui.warn "Some gems seem to be missing from your #{Bundler.settings.app_cache_path} directory." - end - - unless Bundler.definition.has_rubygems_remotes? - Bundler.ui.warn <<-WARN, :wrap => true - Your Gemfile has no gem server sources. If you need gems that are \ - not already on your machine, add a line like this to your Gemfile: - source 'https://rubygems.org' - WARN - end - raise e - rescue Gem::InvalidSpecificationException => e - Bundler.ui.warn "You have one or more invalid gemspecs that need to be fixed." - raise e - end - - private - - def warn_if_root - return if Bundler.settings[:silence_root_warning] || Bundler::WINDOWS || !Process.uid.zero? - Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \ - "if it is needed, and installing your bundle as root will break this " \ - "application for all non-root users on this machine.", :wrap => true - end - - def dependencies_count_for(definition) - count = definition.dependencies.count - "#{count} Gemfile #{count == 1 ? "dependency" : "dependencies"}" - end - - def gems_installed_for(definition) - count = definition.specs.count - "#{count} #{count == 1 ? "gem" : "gems"} now installed" - end - - def check_for_group_conflicts_in_cli_options - conflicting_groups = Array(options[:without]) & Array(options[:with]) - return if conflicting_groups.empty? - raise InvalidOption, "You can't list a group in both with and without." \ - " The offending groups are: #{conflicting_groups.join(", ")}." - end - - def check_for_options_conflicts - if (options[:path] || options[:deployment]) && options[:system] - error_message = String.new - error_message << "You have specified both --path as well as --system. Please choose only one option.\n" if options[:path] - error_message << "You have specified both --deployment as well as --system. Please choose only one option.\n" if options[:deployment] - raise InvalidOption.new(error_message) - end - end - - def check_trust_policy - trust_policy = options["trust-policy"] - unless Bundler.rubygems.security_policies.keys.unshift(nil).include?(trust_policy) - raise InvalidOption, "RubyGems doesn't know about trust policy '#{trust_policy}'. " \ - "The known policies are: #{Bundler.rubygems.security_policies.keys.join(", ")}." - end - Bundler.settings.set_command_option_if_given :"trust-policy", trust_policy - end - - def normalize_groups - options[:with] &&= options[:with].join(":").tr(" ", ":").split(":") - options[:without] &&= options[:without].join(":").tr(" ", ":").split(":") - - check_for_group_conflicts_in_cli_options - - Bundler.settings.set_command_option :with, nil if options[:with] == [] - Bundler.settings.set_command_option :without, nil if options[:without] == [] - - 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 - options[:without] = without - end - - def normalize_settings - Bundler.settings.set_command_option :path, nil if options[:system] - Bundler.settings.set_command_option :path, "vendor/bundle" if options[:deployment] - Bundler.settings.set_command_option_if_given :path, options["path"] - Bundler.settings.set_command_option :path, "bundle" if options["standalone"] && Bundler.settings[:path].nil? - - bin_option = options["binstubs"] - bin_option = nil if bin_option && bin_option.empty? - Bundler.settings.set_command_option :bin, bin_option if options["binstubs"] - - Bundler.settings.set_command_option_if_given :shebang, options["shebang"] - - Bundler.settings.set_command_option_if_given :jobs, options["jobs"] - - Bundler.settings.set_command_option_if_given :no_prune, options["no-prune"] - - Bundler.settings.set_command_option_if_given :no_install, options["no-install"] - - Bundler.settings.set_command_option_if_given :clean, options["clean"] - - unless Bundler.settings[:without] == options[:without] && Bundler.settings[:with] == options[:with] - # need to nil them out first to get around validation for backwards compatibility - Bundler.settings.set_command_option :without, nil - Bundler.settings.set_command_option :with, nil - Bundler.settings.set_command_option :without, options[:without] - options[:with] - Bundler.settings.set_command_option :with, options[:with] - end - - options[:force] = options[:redownload] - end - - def warn_ambiguous_gems - Installer.ambiguous_gems.to_a.each do |name, installed_from_uri, *also_found_in_uris| - Bundler.ui.error "Warning: the gem '#{name}' was found in multiple sources." - Bundler.ui.error "Installed from: #{installed_from_uri}" - Bundler.ui.error "Also found in:" - also_found_in_uris.each {|uri| Bundler.ui.error " * #{uri}" } - Bundler.ui.error "You should add a source requirement to restrict this gem to your preferred source." - Bundler.ui.error "For example:" - Bundler.ui.error " gem '#{name}', :source => '#{installed_from_uri}'" - Bundler.ui.error "Then uninstall the gem '#{name}' (or delete all bundled gems) and then install again." - end - end - end -end diff --git a/lib/bundler/cli/issue.rb b/lib/bundler/cli/issue.rb deleted file mode 100644 index 91f827ea99..0000000000 --- a/lib/bundler/cli/issue.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -require "rbconfig" - -module Bundler - class CLI::Issue - def run - Bundler.ui.info <<-EOS.gsub(/^ {8}/, "") - Did you find an issue with Bundler? Before filing a new issue, - be sure to check out these resources: - - 1. Check out our troubleshooting guide for quick fixes to common issues: - https://github.com/bundler/bundler/blob/master/doc/TROUBLESHOOTING.md - - 2. Instructions for common Bundler uses can be found on the documentation - site: http://bundler.io/ - - 3. Information about each Bundler command can be found in the Bundler - man pages: http://bundler.io/man/bundle.1.html - - Hopefully the troubleshooting steps above resolved your problem! If things - still aren't working the way you expect them to, please let us know so - that we can diagnose and help fix the problem you're having. Please - view the Filing Issues guide for more information: - https://github.com/bundler/bundler/blob/master/doc/contributing/ISSUES.md - - EOS - - Bundler.ui.info Bundler::Env.report - - Bundler.ui.info "\n## Bundle Doctor" - doctor - end - - def doctor - require "bundler/cli/doctor" - Bundler::CLI::Doctor.new({}).run - end - end -end diff --git a/lib/bundler/cli/list.rb b/lib/bundler/cli/list.rb deleted file mode 100644 index b5e7c1e650..0000000000 --- a/lib/bundler/cli/list.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::List - def initialize(options) - @options = options - end - - def run - specs = Bundler.load.specs.reject {|s| s.name == "bundler" }.sort_by(&:name) - return specs.each {|s| Bundler.ui.info s.name } if @options["name-only"] - - return Bundler.ui.info "No gems in the Gemfile" if specs.empty? - Bundler.ui.info "Gems included by the bundle:" - specs.each do |s| - Bundler.ui.info " * #{s.name} (#{s.version}#{s.git_version})" - end - - Bundler.ui.info "Use `bundle info` to print more detailed information about a gem" - end - end -end diff --git a/lib/bundler/cli/lock.rb b/lib/bundler/cli/lock.rb deleted file mode 100644 index 7dd078b1ef..0000000000 --- a/lib/bundler/cli/lock.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Lock - attr_reader :options - - def initialize(options) - @options = options - end - - def run - unless Bundler.default_gemfile - Bundler.ui.error "Unable to find a Gemfile to lock" - exit 1 - end - - print = options[:print] - ui = Bundler.ui - Bundler.ui = UI::Silent.new if print - - Bundler::Fetcher.disable_endpoint = options["full-index"] - - update = options[:update] - if update.is_a?(Array) # unlocking specific gems - Bundler::CLI::Common.ensure_all_gems_in_lockfile!(update) - update = { :gems => update, :lock_shared_dependencies => options[:conservative] } - end - definition = Bundler.definition(update) - - Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) if options[:update] - - options["remove-platform"].each do |platform| - definition.remove_platform(platform) - end - - options["add-platform"].each do |platform_string| - platform = Gem::Platform.new(platform_string) - if platform.to_s == "unknown" - Bundler.ui.warn "The platform `#{platform_string}` is unknown to RubyGems " \ - "and adding it will likely lead to resolution errors" - end - definition.add_platform(platform) - end - - if definition.platforms.empty? - raise InvalidOption, "Removing all platforms from the bundle is not allowed" - end - - definition.resolve_remotely! unless options[:local] - - if print - puts definition.to_lock - else - file = options[:lockfile] - file = file ? File.expand_path(file) : Bundler.default_lockfile - puts "Writing lockfile to #{file}" - definition.lock(file) - end - - Bundler.ui = ui - end - end -end diff --git a/lib/bundler/cli/open.rb b/lib/bundler/cli/open.rb deleted file mode 100644 index 552fe6f128..0000000000 --- a/lib/bundler/cli/open.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -require "shellwords" - -module Bundler - class CLI::Open - attr_reader :options, :name - def initialize(options, name) - @options = options - @name = name - end - - def run - editor = [ENV["BUNDLER_EDITOR"], ENV["VISUAL"], ENV["EDITOR"]].find {|e| !e.nil? && !e.empty? } - return Bundler.ui.info("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") unless editor - return unless spec = Bundler::CLI::Common.select_spec(name, :regex_match) - path = spec.full_gem_path - Dir.chdir(path) do - command = Shellwords.split(editor) + [path] - Bundler.with_original_env do - system(*command) - end || Bundler.ui.info("Could not run '#{command.join(" ")}'") - end - end - end -end diff --git a/lib/bundler/cli/outdated.rb b/lib/bundler/cli/outdated.rb deleted file mode 100644 index 5125cc710b..0000000000 --- a/lib/bundler/cli/outdated.rb +++ /dev/null @@ -1,260 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Outdated - attr_reader :options, :gems - - def initialize(options, gems) - @options = options - @gems = gems - end - - def run - check_for_deployment_mode - - sources = Array(options[:source]) - - gems.each do |gem_name| - Bundler::CLI::Common.select_spec(gem_name) - end - - Bundler.definition.validate_runtime! - current_specs = Bundler.ui.silence { Bundler.definition.resolve } - current_dependencies = {} - Bundler.ui.silence do - Bundler.load.dependencies.each do |dep| - current_dependencies[dep.name] = dep - end - end - - definition = if gems.empty? && sources.empty? - # We're doing a full update - Bundler.definition(true) - else - Bundler.definition(:gems => gems, :sources => sources) - end - - Bundler::CLI::Common.configure_gem_version_promoter( - Bundler.definition, - options - ) - - # the patch level options imply strict is also true. It wouldn't make - # sense otherwise. - strict = options[:strict] || - Bundler::CLI::Common.patch_level_options(options).any? - - filter_options_patch = options.keys & - %w[filter-major filter-minor filter-patch] - - definition_resolution = proc do - options[:local] ? definition.resolve_with_cache! : definition.resolve_remotely! - end - - if options[:parseable] - Bundler.ui.silence(&definition_resolution) - else - definition_resolution.call - end - - Bundler.ui.info "" - outdated_gems_by_groups = {} - outdated_gems_list = [] - - # Loop through the current specs - gemfile_specs, dependency_specs = current_specs.partition do |spec| - current_dependencies.key? spec.name - end - - (gemfile_specs + dependency_specs).sort_by(&:name).each do |current_spec| - next if !gems.empty? && !gems.include?(current_spec.name) - - dependency = current_dependencies[current_spec.name] - active_spec = retrieve_active_spec(strict, definition, current_spec) - - next if active_spec.nil? - if filter_options_patch.any? - update_present = update_present_via_semver_portions(current_spec, active_spec, options) - next unless update_present - end - - gem_outdated = Gem::Version.new(active_spec.version) > Gem::Version.new(current_spec.version) - next unless gem_outdated || (current_spec.git_version != active_spec.git_version) - groups = nil - if dependency && !options[:parseable] - groups = dependency.groups.join(", ") - end - - outdated_gems_list << { :active_spec => active_spec, - :current_spec => current_spec, - :dependency => dependency, - :groups => groups } - - outdated_gems_by_groups[groups] ||= [] - outdated_gems_by_groups[groups] << { :active_spec => active_spec, - :current_spec => current_spec, - :dependency => dependency, - :groups => groups } - end - - if outdated_gems_list.empty? - display_nothing_outdated_message(filter_options_patch) - else - unless options[:parseable] - if options[:pre] - Bundler.ui.info "Outdated gems included in the bundle (including " \ - "pre-releases):" - else - Bundler.ui.info "Outdated gems included in the bundle:" - end - end - - options_include_groups = [:group, :groups].select do |v| - options.keys.include?(v.to_s) - end - - if options_include_groups.any? - ordered_groups = outdated_gems_by_groups.keys.compact.sort - [nil, ordered_groups].flatten.each do |groups| - gems = outdated_gems_by_groups[groups] - contains_group = if groups - groups.split(",").include?(options[:group]) - else - options[:group] == "group" - end - - next if (!options[:groups] && !contains_group) || gems.nil? - - unless options[:parseable] - if groups - Bundler.ui.info "===== Group #{groups} =====" - else - Bundler.ui.info "===== Without group =====" - end - end - - gems.each do |gem| - print_gem( - gem[:current_spec], - gem[:active_spec], - gem[:dependency], - groups, - options_include_groups.any? - ) - end - end - else - outdated_gems_list.each do |gem| - print_gem( - gem[:current_spec], - gem[:active_spec], - gem[:dependency], - gem[:groups], - options_include_groups.any? - ) - end - end - - exit 1 - end - end - - private - - def retrieve_active_spec(strict, definition, current_spec) - if strict - active_spec = definition.find_resolved_spec(current_spec) - else - active_specs = definition.find_indexed_specs(current_spec) - if !current_spec.version.prerelease? && !options[:pre] && active_specs.size > 1 - active_specs.delete_if {|b| b.respond_to?(:version) && b.version.prerelease? } - end - active_spec = active_specs.last - end - - active_spec - end - - def display_nothing_outdated_message(filter_options_patch) - unless options[:parseable] - if filter_options_patch.any? - display = filter_options_patch.map do |o| - o.sub("filter-", "") - end.join(" or ") - - Bundler.ui.info "No #{display} updates to display.\n" - else - Bundler.ui.info "Bundle up to date!\n" - end - end - end - - def print_gem(current_spec, active_spec, dependency, groups, options_include_groups) - spec_version = "#{active_spec.version}#{active_spec.git_version}" - spec_version += " (from #{active_spec.loaded_from})" if Bundler.ui.debug? && active_spec.loaded_from - current_version = "#{current_spec.version}#{current_spec.git_version}" - - if dependency && dependency.specific? - dependency_version = %(, requested #{dependency.requirement}) - end - - spec_outdated_info = "#{active_spec.name} (newest #{spec_version}, " \ - "installed #{current_version}#{dependency_version})" - - output_message = if options[:parseable] - spec_outdated_info.to_s - elsif options_include_groups || !groups - " * #{spec_outdated_info}" - else - " * #{spec_outdated_info} in groups \"#{groups}\"" - end - - Bundler.ui.info output_message.rstrip - end - - def check_for_deployment_mode - return unless Bundler.frozen? - suggested_command = if Bundler.settings.locations("frozen")[:global] - "bundle config --delete frozen" - elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any? - "bundle config --delete deployment" - else - "bundle install --no-deployment" - end - 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 `#{suggested_command}`." - end - - def update_present_via_semver_portions(current_spec, active_spec, options) - current_major = current_spec.version.segments.first - active_major = active_spec.version.segments.first - - update_present = false - update_present = active_major > current_major if options["filter-major"] - - if !update_present && (options["filter-minor"] || options["filter-patch"]) && current_major == active_major - current_minor = get_version_semver_portion_value(current_spec, 1) - active_minor = get_version_semver_portion_value(active_spec, 1) - - update_present = active_minor > current_minor if options["filter-minor"] - - if !update_present && options["filter-patch"] && current_minor == active_minor - current_patch = get_version_semver_portion_value(current_spec, 2) - active_patch = get_version_semver_portion_value(active_spec, 2) - - update_present = active_patch > current_patch - end - end - - update_present - end - - def get_version_semver_portion_value(spec, version_portion_index) - version_section = spec.version.segments[version_portion_index, 1] - version_section.nil? ? 0 : (version_section.first || 0) - end - end -end diff --git a/lib/bundler/cli/package.rb b/lib/bundler/cli/package.rb deleted file mode 100644 index 2dcd0e1e29..0000000000 --- a/lib/bundler/cli/package.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Package - attr_reader :options - - def initialize(options) - @options = options - end - - def run - Bundler.ui.level = "error" if options[:quiet] - Bundler.settings.set_command_option_if_given :path, options[:path] - Bundler.settings.set_command_option_if_given :cache_all_platforms, options["all-platforms"] - Bundler.settings.set_command_option_if_given :cache_path, options["cache-path"] - - setup_cache_all - install - - # TODO: move cache contents here now that all bundles are locked - custom_path = Bundler.settings[:path] if options[:path] - Bundler.load.cache(custom_path) - end - - private - - def install - require "bundler/cli/install" - options = self.options.dup - if Bundler.settings[:cache_all_platforms] - options["local"] = false - options["update"] = true - end - Bundler::CLI::Install.new(options).run - end - - def setup_cache_all - all = options.fetch(:all, Bundler.feature_flag.cache_command_is_package? || nil) - - Bundler.settings.set_command_option_if_given :cache_all, all - - if Bundler.definition.has_local_dependencies? && !Bundler.feature_flag.cache_all? - Bundler.ui.warn "Your Gemfile contains path and git dependencies. If you want " \ - "to package them as well, please pass the --all flag. This will be the default " \ - "on Bundler 2.0." - end - end - end -end diff --git a/lib/bundler/cli/platform.rb b/lib/bundler/cli/platform.rb deleted file mode 100644 index e97cad49a4..0000000000 --- a/lib/bundler/cli/platform.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Platform - attr_reader :options - def initialize(options) - @options = options - end - - def run - platforms, ruby_version = Bundler.ui.silence do - locked_ruby_version = Bundler.locked_gems && Bundler.locked_gems.ruby_version - gemfile_ruby_version = Bundler.definition.ruby_version && Bundler.definition.ruby_version.single_version_string - [Bundler.definition.platforms.map {|p| "* #{p}" }, - locked_ruby_version || gemfile_ruby_version] - end - output = [] - - if options[:ruby] - if ruby_version - output << ruby_version - else - output << "No ruby version specified" - end - else - output << "Your platform is: #{RUBY_PLATFORM}" - output << "Your app has gems that work on these platforms:\n#{platforms.join("\n")}" - - if ruby_version - output << "Your Gemfile specifies a Ruby version requirement:\n* #{ruby_version}" - - begin - Bundler.definition.validate_runtime! - output << "Your current platform satisfies the Ruby version requirement." - rescue RubyVersionMismatch => e - output << e.message - end - else - output << "Your Gemfile does not specify a Ruby version requirement." - end - end - - Bundler.ui.info output.join("\n\n") - end - end -end diff --git a/lib/bundler/cli/plugin.rb b/lib/bundler/cli/plugin.rb deleted file mode 100644 index 5488a9f28d..0000000000 --- a/lib/bundler/cli/plugin.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_thor" -module Bundler - class CLI::Plugin < Thor - desc "install PLUGINS", "Install the plugin from the source" - long_desc <<-D - Install plugins either from the rubygems source provided (with --source option) or from a git source provided with (--git option). If no sources are provided, it uses Gem.sources - D - method_option "source", :type => :string, :default => nil, :banner => - "URL of the RubyGems source to fetch the plugin from" - method_option "version", :type => :string, :default => nil, :banner => - "The version of the plugin to fetch" - method_option "git", :type => :string, :default => nil, :banner => - "URL of the git repo to fetch from" - method_option "branch", :type => :string, :default => nil, :banner => - "The git branch to checkout" - method_option "ref", :type => :string, :default => nil, :banner => - "The git revision to check out" - def install(*plugins) - Bundler::Plugin.install(plugins, options) - end - end -end diff --git a/lib/bundler/cli/pristine.rb b/lib/bundler/cli/pristine.rb deleted file mode 100644 index 9b9cdaa9b3..0000000000 --- a/lib/bundler/cli/pristine.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Pristine - def initialize(gems) - @gems = gems - end - - def run - CLI::Common.ensure_all_gems_in_lockfile!(@gems) - 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 - next if !@gems.empty? && !@gems.include?(spec.name) - - gem_name = "#{spec.name} (#{spec.version}#{spec.git_version})" - gem_name += " (#{spec.platform})" if !spec.platform.nil? && spec.platform != Gem::Platform::RUBY - - case source = spec.source - when Source::Rubygems - cached_gem = spec.cache_file - unless File.exist?(cached_gem) - Bundler.ui.error("Failed to pristine #{gem_name}. Cached gem #{cached_gem} does not exist.") - next - end - - FileUtils.rm_rf spec.full_gem_path - when Source::Git - source.remote! - FileUtils.rm_rf spec.full_gem_path - else - Bundler.ui.warn("Cannot pristine #{gem_name}. Gem is sourced from local path.") - next - end - - Bundler::GemInstaller.new(spec, installer, false, 0, true).install_from_spec - end - end - end -end diff --git a/lib/bundler/cli/show.rb b/lib/bundler/cli/show.rb deleted file mode 100644 index 61756801b2..0000000000 --- a/lib/bundler/cli/show.rb +++ /dev/null @@ -1,75 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Show - attr_reader :options, :gem_name, :latest_specs - def initialize(options, gem_name) - @options = options - @gem_name = gem_name - @verbose = options[:verbose] || options[:outdated] - @latest_specs = fetch_latest_specs if @verbose - end - - def run - Bundler.ui.silence do - Bundler.definition.validate_runtime! - Bundler.load.lock - end - - if gem_name - if gem_name == "bundler" - path = File.expand_path("../../../..", __FILE__) - else - spec = Bundler::CLI::Common.select_spec(gem_name, :regex_match) - return unless spec - path = spec.full_gem_path - unless File.directory?(path) - Bundler.ui.warn "The gem #{gem_name} has been deleted. It was installed at:" - end - end - return Bundler.ui.info(path) - end - - if options[:paths] - Bundler.load.specs.sort_by(&:name).map do |s| - Bundler.ui.info s.full_gem_path - end - else - Bundler.ui.info "Gems included by the bundle:" - Bundler.load.specs.sort_by(&:name).each do |s| - desc = " * #{s.name} (#{s.version}#{s.git_version})" - if @verbose - latest = latest_specs.find {|l| l.name == s.name } - Bundler.ui.info <<-END.gsub(/^ +/, "") - #{desc} - \tSummary: #{s.summary || "No description available."} - \tHomepage: #{s.homepage || "No website available."} - \tStatus: #{outdated?(s, latest) ? "Outdated - #{s.version} < #{latest.version}" : "Up to date"} - END - else - Bundler.ui.info desc - end - end - end - end - - private - - def fetch_latest_specs - definition = Bundler.definition(true) - if options[:outdated] - Bundler.ui.info "Fetching remote specs for outdated check...\n\n" - Bundler.ui.silence { definition.resolve_remotely! } - else - definition.resolve_with_cache! - end - Bundler.reset! - definition.specs - end - - def outdated?(current, latest) - return false unless latest - Gem::Version.new(current.version) < Gem::Version.new(latest.version) - end - end -end diff --git a/lib/bundler/cli/update.rb b/lib/bundler/cli/update.rb deleted file mode 100644 index 3890ea307a..0000000000 --- a/lib/bundler/cli/update.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Update - attr_reader :options, :gems - def initialize(options, gems) - @options = options - @gems = gems - end - - def run - Bundler.ui.level = "error" if options[:quiet] - - Plugin.gemfile_install(Bundler.default_gemfile) if Bundler.feature_flag.plugins? - - sources = Array(options[:source]) - groups = Array(options[:group]).map(&:to_sym) - - full_update = gems.empty? && sources.empty? && groups.empty? && !options[:ruby] && !options[:bundler] - - if full_update && !options[:all] - if Bundler.feature_flag.update_requires_all_flag? - raise InvalidOption, "To update everything, pass the `--all` flag." - end - SharedHelpers.major_deprecation 2, "Pass --all to `bundle update` to update everything" - elsif !full_update && options[:all] - raise InvalidOption, "Cannot specify --all along with specific options." - end - - if full_update - # We're doing a full update - Bundler.definition(true) - else - unless Bundler.default_lockfile.exist? - raise GemfileLockNotFound, "This Bundle hasn't been installed yet. " \ - "Run `bundle install` to update and install the bundled gems." - end - Bundler::CLI::Common.ensure_all_gems_in_lockfile!(gems) - - if groups.any? - specs = Bundler.definition.specs_for groups - gems.concat(specs.map(&:name)) - end - - Bundler.definition(:gems => gems, :sources => sources, :ruby => options[:ruby], - :lock_shared_dependencies => options[:conservative], - :bundler => options[:bundler]) - end - - Bundler::CLI::Common.configure_gem_version_promoter(Bundler.definition, options) - - Bundler::Fetcher.disable_endpoint = options["full-index"] - - opts = options.dup - opts["update"] = true - opts["local"] = options[:local] - - Bundler.settings.set_command_option_if_given :jobs, opts["jobs"] - - Bundler.definition.validate_runtime! - installer = Installer.install Bundler.root, Bundler.definition, opts - Bundler.load.cache if Bundler.app_cache.exist? - - if CLI::Common.clean_after_install? - require "bundler/cli/clean" - Bundler::CLI::Clean.new(options).run - end - - if locked_gems = Bundler.definition.locked_gems - gems.each do |name| - locked_version = locked_gems.specs.find {|s| s.name == name } - locked_version &&= locked_version.version - next unless locked_version - new_version = Bundler.definition.specs[name].first - new_version &&= new_version.version - if !new_version - Bundler.ui.warn "Bundler attempted to update #{name} but it was removed from the bundle" - elsif new_version < locked_version - Bundler.ui.warn "Bundler attempted to update #{name} but its version regressed from #{locked_version} to #{new_version}" - elsif new_version == locked_version - Bundler.ui.warn "Bundler attempted to update #{name} but its version stayed the same" - end - end - end - - Bundler.ui.confirm "Bundle updated!" - Bundler::CLI::Common.output_without_groups_message - Bundler::CLI::Common.output_post_install_messages installer.post_install_messages - end - end -end diff --git a/lib/bundler/cli/viz.rb b/lib/bundler/cli/viz.rb deleted file mode 100644 index 644f9b25cf..0000000000 --- a/lib/bundler/cli/viz.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CLI::Viz - attr_reader :options, :gem_name - def initialize(options) - @options = options - end - - def run - # make sure we get the right `graphviz`. There is also a `graphviz` - # gem we're not built to support - gem "ruby-graphviz" - require "graphviz" - - options[:without] = options[:without].join(":").tr(" ", ":").split(":") - output_file = File.expand_path(options[:file]) - - graph = Graph.new(Bundler.load, output_file, options[:version], options[:requirements], options[:format], options[:without]) - graph.viz - rescue LoadError => e - Bundler.ui.error e.inspect - Bundler.ui.warn "Make sure you have the graphviz ruby gem. You can install it with:" - Bundler.ui.warn "`gem install ruby-graphviz`" - rescue StandardError => e - raise unless e.message =~ /GraphViz not installed or dot not in PATH/ - Bundler.ui.error e.message - Bundler.ui.warn "Please install GraphViz. On a Mac with Homebrew, you can run `brew install graphviz`." - end - end -end diff --git a/lib/bundler/compact_index_client.rb b/lib/bundler/compact_index_client.rb deleted file mode 100644 index 6c241ca07a..0000000000 --- a/lib/bundler/compact_index_client.rb +++ /dev/null @@ -1,109 +0,0 @@ -# frozen_string_literal: true - -require "pathname" -require "set" - -module Bundler - class CompactIndexClient - DEBUG_MUTEX = Mutex.new - def self.debug - return unless ENV["DEBUG_COMPACT_INDEX"] - DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") } - end - - class Error < StandardError; end - - require "bundler/compact_index_client/cache" - require "bundler/compact_index_client/updater" - - attr_reader :directory - - # @return [Lambda] A lambda that takes an array of inputs and a block, and - # maps the inputs with the block in parallel. - # - attr_accessor :in_parallel - - def initialize(directory, fetcher) - @directory = Pathname.new(directory) - @updater = Updater.new(fetcher) - @cache = Cache.new(@directory) - @endpoints = Set.new - @info_checksums_by_name = {} - @parsed_checksums = false - @mutex = Mutex.new - @in_parallel = lambda do |inputs, &blk| - inputs.map(&blk) - end - end - - def names - Bundler::CompactIndexClient.debug { "/names" } - update(@cache.names_path, "names") - @cache.names - end - - def versions - Bundler::CompactIndexClient.debug { "/versions" } - update(@cache.versions_path, "versions") - versions, @info_checksums_by_name = @cache.versions - versions - end - - def dependencies(names) - Bundler::CompactIndexClient.debug { "dependencies(#{names})" } - in_parallel.call(names) do |name| - update_info(name) - @cache.dependencies(name).map {|d| d.unshift(name) } - end.flatten(1) - end - - def spec(name, version, platform = nil) - Bundler::CompactIndexClient.debug { "spec(name = #{name}, version = #{version}, platform = #{platform})" } - update_info(name) - @cache.specific_dependency(name, version, platform) - end - - def update_and_parse_checksums! - Bundler::CompactIndexClient.debug { "update_and_parse_checksums!" } - return @info_checksums_by_name if @parsed_checksums - update(@cache.versions_path, "versions") - @info_checksums_by_name = @cache.checksums - @parsed_checksums = true - end - - private - - def update(local_path, remote_path) - Bundler::CompactIndexClient.debug { "update(#{local_path}, #{remote_path})" } - unless synchronize { @endpoints.add?(remote_path) } - Bundler::CompactIndexClient.debug { "already fetched #{remote_path}" } - return - end - @updater.update(local_path, url(remote_path)) - end - - def update_info(name) - Bundler::CompactIndexClient.debug { "update_info(#{name})" } - path = @cache.info_path(name) - checksum = @updater.checksum_for_file(path) - unless existing = @info_checksums_by_name[name] - Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since it is missing from versions" } - return - end - if checksum == existing - Bundler::CompactIndexClient.debug { "skipping updating info for #{name} since the versions checksum matches the local checksum" } - return - end - Bundler::CompactIndexClient.debug { "updating info for #{name} since the versions checksum #{existing} != the local checksum #{checksum}" } - update(path, "info/#{name}") - end - - def url(path) - path - end - - def synchronize - @mutex.synchronize { yield } - end - end -end diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb deleted file mode 100644 index f6105d3bb3..0000000000 --- a/lib/bundler/compact_index_client/cache.rb +++ /dev/null @@ -1,118 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class CompactIndexClient - class Cache - attr_reader :directory - - def initialize(directory) - @directory = Pathname.new(directory).expand_path - info_roots.each do |dir| - SharedHelpers.filesystem_access(dir) do - FileUtils.mkdir_p(dir) - end - end - end - - def names - lines(names_path) - end - - def names_path - directory.join("names") - end - - def versions - versions_by_name = Hash.new {|hash, key| hash[key] = [] } - info_checksums_by_name = {} - - lines(versions_path).each do |line| - name, versions_string, info_checksum = line.split(" ", 3) - info_checksums_by_name[name] = info_checksum || "" - versions_string.split(",").each do |version| - if version.start_with?("-") - version = version[1..-1].split("-", 2).unshift(name) - versions_by_name[name].delete(version) - else - version = version.split("-", 2).unshift(name) - versions_by_name[name] << version - end - end - end - - [versions_by_name, info_checksums_by_name] - end - - def versions_path - directory.join("versions") - end - - def checksums - checksums = {} - - lines(versions_path).each do |line| - name, _, checksum = line.split(" ", 3) - checksums[name] = checksum - end - - checksums - end - - def dependencies(name) - lines(info_path(name)).map do |line| - parse_gem(line) - end - end - - def info_path(name) - name = name.to_s - if name =~ /[^a-z0-9_-]/ - name += "-#{SharedHelpers.digest(:MD5).hexdigest(name).downcase}" - info_roots.last.join(name) - else - info_roots.first.join(name) - end - end - - def specific_dependency(name, version, platform) - pattern = [version, platform].compact.join("-") - return nil if pattern.empty? - - gem_lines = info_path(name).read - gem_line = gem_lines[/^#{Regexp.escape(pattern)}\b.*/, 0] - gem_line ? parse_gem(gem_line) : nil - end - - private - - def lines(path) - return [] unless path.file? - lines = SharedHelpers.filesystem_access(path, :read, &:read).split("\n") - header = lines.index("---") - header ? lines[header + 1..-1] : lines - end - - def parse_gem(string) - version_and_platform, rest = string.split(" ", 2) - version, platform = version_and_platform.split("-", 2) - dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest - dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] - requirements = requirements ? requirements.map {|r| parse_dependency(r) } : [] - [version, platform, dependencies, requirements] - end - - def parse_dependency(string) - dependency = string.split(":") - dependency[-1] = dependency[-1].split("&") if dependency.size > 1 - dependency - end - - def info_roots - [ - directory.join("info"), - directory.join("info-special-characters"), - ] - end - end - end -end diff --git a/lib/bundler/compact_index_client/updater.rb b/lib/bundler/compact_index_client/updater.rb deleted file mode 100644 index 3a4e4441ca..0000000000 --- a/lib/bundler/compact_index_client/updater.rb +++ /dev/null @@ -1,107 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_fileutils" -require "stringio" -require "zlib" - -module Bundler - class CompactIndexClient - class Updater - class MisMatchedChecksumError < Error - def initialize(path, server_checksum, local_checksum) - @path = path - @server_checksum = server_checksum - @local_checksum = local_checksum - end - - def message - "The checksum of /#{@path} does not match the checksum provided by the server! Something is wrong " \ - "(local checksum is #{@local_checksum.inspect}, was expecting #{@server_checksum.inspect})." - end - end - - def initialize(fetcher) - @fetcher = fetcher - require "tmpdir" - end - - def update(local_path, remote_path, retrying = nil) - headers = {} - - Dir.mktmpdir("bundler-compact-index-") do |local_temp_dir| - local_temp_path = Pathname.new(local_temp_dir).join(local_path.basename) - - # first try to fetch any new bytes on the existing file - if retrying.nil? && local_path.file? - FileUtils.cp local_path, local_temp_path - headers["If-None-Match"] = etag_for(local_temp_path) - headers["Range"] = - if local_temp_path.size.nonzero? - # Subtract a byte to ensure the range won't be empty. - # Avoids 416 (Range Not Satisfiable) responses. - "bytes=#{local_temp_path.size - 1}-" - else - "bytes=#{local_temp_path.size}-" - end - else - # Fastly ignores Range when Accept-Encoding: gzip is set - headers["Accept-Encoding"] = "gzip" - end - - response = @fetcher.call(remote_path, headers) - return nil if response.is_a?(Net::HTTPNotModified) - - content = response.body - if response["Content-Encoding"] == "gzip" - content = Zlib::GzipReader.new(StringIO.new(content)).read - end - - SharedHelpers.filesystem_access(local_temp_path) do - if response.is_a?(Net::HTTPPartialContent) && local_temp_path.size.nonzero? - local_temp_path.open("a") {|f| f << slice_body(content, 1..-1) } - else - local_temp_path.open("w") {|f| f << content } - end - end - - response_etag = (response["ETag"] || "").gsub(%r{\AW/}, "") - if etag_for(local_temp_path) == response_etag - SharedHelpers.filesystem_access(local_path) do - FileUtils.mv(local_temp_path, local_path) - end - return nil - end - - if retrying - raise MisMatchedChecksumError.new(remote_path, response_etag, etag_for(local_temp_path)) - end - - update(local_path, remote_path, :retrying) - end - end - - def etag_for(path) - sum = checksum_for_file(path) - sum ? %("#{sum}") : nil - end - - def slice_body(body, range) - if body.respond_to?(:byteslice) - body.byteslice(range) - else # pre-1.9.3 - body.unpack("@#{range.first}a#{range.end + 1}").first - end - end - - def checksum_for_file(path) - return nil unless path.file? - # This must use IO.read instead of Digest.file().hexdigest - # because we need to preserve \n line endings on windows when calculating - # the checksum - SharedHelpers.filesystem_access(path, :read) do - SharedHelpers.digest(:MD5).hexdigest(IO.read(path)) - end - end - end - end -end diff --git a/lib/bundler/compatibility_guard.rb b/lib/bundler/compatibility_guard.rb deleted file mode 100644 index 750a1db04f..0000000000 --- a/lib/bundler/compatibility_guard.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: false - -require "rubygems" -require "bundler/version" - -if Bundler::VERSION.split(".").first.to_i >= 2 - if Gem::Version.new(Object::RUBY_VERSION.dup) < Gem::Version.new("2.3") - abort "Bundler 2 requires Ruby 2.3 or later. Either install bundler 1 or update to a supported Ruby version." - end - - if Gem::Version.new(Gem::VERSION.dup) < Gem::Version.new("2.5") - abort "Bundler 2 requires RubyGems 2.5 or later. Either install bundler 1 or update to a supported RubyGems version." - end -end diff --git a/lib/bundler/constants.rb b/lib/bundler/constants.rb deleted file mode 100644 index 2e4ebb37ee..0000000000 --- a/lib/bundler/constants.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -module Bundler - WINDOWS = RbConfig::CONFIG["host_os"] =~ /(msdos|mswin|djgpp|mingw)/ - FREEBSD = RbConfig::CONFIG["host_os"] =~ /bsd/ - NULL = WINDOWS ? "NUL" : "/dev/null" -end diff --git a/lib/bundler/current_ruby.rb b/lib/bundler/current_ruby.rb deleted file mode 100644 index 31532d108d..0000000000 --- a/lib/bundler/current_ruby.rb +++ /dev/null @@ -1,86 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # Returns current version of Ruby - # - # @return [CurrentRuby] Current version of Ruby - def self.current_ruby - @current_ruby ||= CurrentRuby.new - end - - class CurrentRuby - KNOWN_MINOR_VERSIONS = %w[ - 1.8 - 1.9 - 2.0 - 2.1 - 2.2 - 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 - mri - mswin - mswin64 - rbx - ruby - x64_mingw - ].freeze - - def ruby? - !mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev") - end - - def mri? - !mswin? && (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") - end - - def rbx? - ruby? && defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx" - end - - def jruby? - defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby" - end - - def maglev? - defined?(RUBY_ENGINE) && RUBY_ENGINE == "maglev" - end - - def mswin? - Bundler::WINDOWS - end - - def mswin64? - Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64" - end - - def mingw? - Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64" - end - - def x64_mingw? - Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64" - end - - (KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version| - trimmed_version = version.tr(".", "") - define_method(:"on_#{trimmed_version}?") do - RUBY_VERSION.start_with?("#{version}.") - end - - KNOWN_PLATFORMS.each do |platform| - define_method(:"#{platform}_#{trimmed_version}?") do - send(:"#{platform}?") && send(:"on_#{trimmed_version}?") - end - end - end - end -end diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb deleted file mode 100644 index ad16389dae..0000000000 --- a/lib/bundler/definition.rb +++ /dev/null @@ -1,984 +0,0 @@ -# frozen_string_literal: true - -require "bundler/lockfile_parser" -require "set" - -module Bundler - class Definition - include GemHelpers - - attr_reader( - :dependencies, - :gem_version_promoter, - :locked_deps, - :locked_gems, - :platforms, - :requires, - :ruby_version, - :lockfile, - :gemfiles - ) - - # Given a gemfile and lockfile creates a Bundler definition - # - # @param gemfile [Pathname] Path to Gemfile - # @param lockfile [Pathname,nil] Path to Gemfile.lock - # @param unlock [Hash, Boolean, nil] Gems that have been requested - # to be updated or true if all gems should be updated - # @return [Bundler::Definition] - def self.build(gemfile, lockfile, unlock) - unlock ||= {} - gemfile = Pathname.new(gemfile).expand_path - - raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file? - - Dsl.evaluate(gemfile, lockfile, unlock) - end - - # - # How does the new system work? - # - # * Load information from Gemfile and Lockfile - # * Invalidate stale locked specs - # * All specs from stale source are stale - # * All specs that are reachable only through a stale - # dependency are stale. - # * If all fresh dependencies are satisfied by the locked - # specs, then we can try to resolve locally. - # - # @param lockfile [Pathname] Path to Gemfile.lock - # @param dependencies [Array(Bundler::Dependency)] array of dependencies from Gemfile - # @param sources [Bundler::SourceList] - # @param unlock [Hash, Boolean, nil] Gems that have been requested - # 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 = [], gemfiles = []) - if [true, false].include?(unlock) - @unlocking_bundler = false - @unlocking = unlock - else - unlock = unlock.dup - @unlocking_bundler = unlock.delete(:bundler) - unlock.delete_if {|_k, v| Array(v).empty? } - @unlocking = !unlock.empty? - end - - @dependencies = dependencies - @sources = sources - @unlock = unlock - @optional_groups = optional_groups - @remote = false - @specs = nil - @ruby_version = ruby_version - @gemfiles = gemfiles - - @lockfile = lockfile - @lockfile_contents = String.new - @locked_bundler_version = nil - @locked_ruby_version = nil - - if lockfile && File.exist?(lockfile) - @lockfile_contents = Bundler.read_file(lockfile) - @locked_gems = LockfileParser.new(@lockfile_contents) - @locked_platforms = @locked_gems.platforms - @platforms = @locked_platforms.dup - @locked_bundler_version = @locked_gems.bundler_version - @locked_ruby_version = @locked_gems.ruby_version - - if unlock != true - @locked_deps = @locked_gems.dependencies - @locked_specs = SpecSet.new(@locked_gems.specs) - @locked_sources = @locked_gems.sources - else - @unlock = {} - @locked_deps = {} - @locked_specs = SpecSet.new([]) - @locked_sources = [] - end - else - @unlock = {} - @platforms = [] - @locked_gems = nil - @locked_deps = {} - @locked_specs = SpecSet.new([]) - @locked_sources = [] - @locked_platforms = [] - end - - @unlock[:gems] ||= [] - @unlock[:sources] ||= [] - @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object - @ruby_version.diff(locked_ruby_version_object) - end - @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version) - - add_current_platform unless Bundler.frozen? - - converge_path_sources_to_gemspec_sources - @path_changes = converge_paths - @source_changes = converge_sources - - unless @unlock[:lock_shared_dependencies] - eager_unlock = expand_dependencies(@unlock[:gems]) - @unlock[:gems] = @locked_specs.for(eager_unlock).map(&:name) - end - - @gem_version_promoter = create_gem_version_promoter - - @dependency_changes = converge_dependencies - @local_changes = converge_locals - - @requires = compute_requires - end - - def create_gem_version_promoter - locked_specs = - if unlocking? && @locked_specs.empty? && !@lockfile_contents.empty? - # Definition uses an empty set of locked_specs to indicate all gems - # are unlocked, but GemVersionPromoter needs the locked_specs - # for conservative comparison. - Bundler::SpecSet.new(@locked_gems.specs) - else - @locked_specs - end - GemVersionPromoter.new(locked_specs, @unlock[:gems]) - end - - def resolve_with_cache! - raise "Specs already loaded" if @specs - sources.cached! - specs - end - - def resolve_remotely! - raise "Specs already loaded" if @specs - @remote = true - sources.remote! - specs - end - - # For given dependency list returns a SpecSet with Gemspec of all the required - # dependencies. - # 1. The method first resolves the dependencies specified in Gemfile - # 2. After that it tries and fetches gemspec of resolved dependencies - # - # @return [Bundler::SpecSet] - def specs - @specs ||= begin - begin - specs = resolve.materialize(Bundler.settings[:cache_all_platforms] ? dependencies : requested_dependencies) - rescue GemNotFound => e # Handle yanked gem - gem_name, gem_version = extract_gem_info(e) - locked_gem = @locked_specs[gem_name].last - raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote - raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \ - "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \ - "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \ - "to a different version of #{locked_gem} that hasn't been removed in order to install." - end - unless specs["bundler"].any? - bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last - specs["bundler"] = bundler - end - - specs - end - end - - def new_specs - specs - @locked_specs - end - - def removed_specs - @locked_specs - specs - end - - def new_platform? - @new_platform - end - - def missing_specs - missing = [] - resolve.materialize(requested_dependencies, missing) - missing - end - - def missing_specs? - missing = missing_specs - return false if missing.empty? - Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}" - true - rescue BundlerError => e - @index = nil - @resolve = nil - @specs = nil - @gem_version_promoter = create_gem_version_promoter - - Bundler.ui.debug "The definition is missing dependencies, failed to resolve & materialize locally (#{e})" - true - end - - def requested_specs - @requested_specs ||= begin - groups = requested_groups - groups.map!(&:to_sym) - specs_for(groups) - end - end - - def current_dependencies - dependencies.select(&:should_include?) - end - - def specs_for(groups) - deps = dependencies.select {|d| (d.groups & groups).any? } - deps.delete_if {|d| !d.should_include? } - specs.for(expand_dependencies(deps)) - end - - # Resolve all the dependencies specified in Gemfile. It ensures that - # dependencies that have been already resolved via locked file and are fresh - # are reused when resolving dependencies - # - # @return [SpecSet] resolved dependencies - def resolve - @resolve ||= begin - last_resolve = converge_locked_specs - if Bundler.frozen? - Bundler.ui.debug "Frozen, using resolution from the lockfile" - last_resolve - elsif !unlocking? && nothing_changed? - Bundler.ui.debug("Found no changes, using resolution from the lockfile") - last_resolve - else - # Run a resolve against the locally available gems - Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}") - last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms) - end - end - end - - def index - @index ||= Index.build do |idx| - dependency_names = @dependencies.map(&:name) - - sources.all_sources.each do |source| - source.dependency_names = dependency_names - pinned_spec_names(source) - idx.add_source source.specs - dependency_names.concat(source.unmet_deps).uniq! - end - - double_check_for_index(idx, dependency_names) - end - 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. - def double_check_for_index(idx, dependency_names) - pinned_names = pinned_spec_names - loop do - idxcount = idx.size - - names = :names # do this so we only have to traverse to get dependency_names from the index once - unmet_dependency_names = lambda do - return names unless names == :names - new_names = sources.all_sources.map(&:dependency_names_to_double_check) - return names = nil if new_names.compact! - names = new_names.flatten(1).concat(dependency_names) - names.uniq! - names -= pinned_names - names - end - - sources.all_sources.each do |source| - source.double_check_for(unmet_dependency_names) - end - - break if idxcount == idx.size - end - end - private :double_check_for_index - - def has_rubygems_remotes? - sources.rubygems_sources.any? {|s| s.remotes.any? } - end - - def has_local_dependencies? - !sources.path_sources.empty? || !sources.git_sources.empty? - end - - def spec_git_paths - sources.git_sources.map {|s| s.path.to_s } - end - - def groups - dependencies.map(&:groups).flatten.uniq - end - - def lock(file, preserve_unknown_sections = false) - contents = to_lock - - # Convert to \r\n if the existing lock has them - # i.e., Windows with `git config core.autocrlf=true` - contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match("\r\n") - - if @locked_bundler_version - locked_major = @locked_bundler_version.segments.first - current_major = Gem::Version.create(Bundler::VERSION).segments.first - - if updating_major = locked_major < current_major - Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{current_major}, " \ - "after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}." - end - end - - preserve_unknown_sections ||= !updating_major && (Bundler.frozen? || !(unlocking? || @unlocking_bundler)) - return if lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections) - - if Bundler.frozen? - Bundler.ui.error "Cannot write a changed lockfile while frozen." - return - end - - SharedHelpers.filesystem_access(file) do |p| - File.open(p, "wb") {|f| f.puts(contents) } - end - end - - def locked_bundler_version - if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION) - new_version = Bundler::VERSION - end - - new_version || @locked_bundler_version || Bundler::VERSION - end - - def locked_ruby_version - return unless ruby_version - if @unlock[:ruby] || !@locked_ruby_version - Bundler::RubyVersion.system - else - @locked_ruby_version - end - end - - def locked_ruby_version_object - return unless @locked_ruby_version - @locked_ruby_version_object ||= begin - unless version = RubyVersion.from_string(@locked_ruby_version) - raise LockfileError, "The Ruby version #{@locked_ruby_version} from " \ - "#{@lockfile} could not be parsed. " \ - "Try running bundle update --ruby to resolve this." - end - version - end - end - - def to_lock - require "bundler/lockfile_generator" - LockfileGenerator.generate(self) - end - - def ensure_equivalent_gemfile_and_lockfile(explicit_flag = false) - msg = String.new - msg << "You are trying to install in deployment mode after changing\n" \ - "your Gemfile. Run `bundle install` elsewhere and add the\n" \ - "updated #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} to version control." - - unless explicit_flag - suggested_command = if Bundler.settings.locations("frozen")[:global] - "bundle config --delete frozen" - elsif Bundler.settings.locations("deployment").keys.&([:global, :local]).any? - "bundle config --delete deployment" - else - "bundle install --no-deployment" - end - msg << "\n\nIf this is a development machine, remove the #{Bundler.default_gemfile} " \ - "freeze \nby running `#{suggested_command}`." - end - - added = [] - deleted = [] - changed = [] - - new_platforms = @platforms - @locked_platforms - deleted_platforms = @locked_platforms - @platforms - added.concat new_platforms.map {|p| "* platform: #{p}" } - deleted.concat deleted_platforms.map {|p| "* platform: #{p}" } - - gemfile_sources = sources.lock_sources - - new_sources = gemfile_sources - @locked_sources - deleted_sources = @locked_sources - gemfile_sources - - new_deps = @dependencies - @locked_deps.values - deleted_deps = @locked_deps.values - @dependencies - - # 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.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) } - deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) } - end - - if @locked_sources != gemfile_sources - if new_sources.any? - added.concat new_sources.map {|source| "* source: #{source}" } - end - - if deleted_sources.any? - deleted.concat deleted_sources.map {|source| "* source: #{source}" } - end - end - - added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any? - if deleted_deps.any? - deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } - end - - both_sources = Hash.new {|h, k| h[k] = [] } - @dependencies.each {|d| both_sources[d.name][0] = d } - @locked_deps.each {|name, d| both_sources[name][1] = d.source } - - both_sources.each do |name, (dep, lock_source)| - next unless (dep.nil? && !lock_source.nil?) || (!dep.nil? && !lock_source.nil? && !lock_source.can_lock?(dep)) - gemfile_source_name = (dep && dep.source) || "no specified source" - lockfile_source_name = lock_source || "no specified source" - changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`" - end - - reason = change_reason - msg << "\n\n#{reason.split(", ").map(&:capitalize).join("\n")}" unless reason.strip.empty? - msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any? - msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any? - msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any? - msg << "\n" - - raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed? - end - - def validate_runtime! - validate_ruby! - validate_platforms! - end - - def validate_ruby! - return unless ruby_version - - if diff = ruby_version.diff(Bundler::RubyVersion.system) - problem, expected, actual = diff - - msg = case problem - when :engine - "Your Ruby engine is #{actual}, but your Gemfile specified #{expected}" - when :version - "Your Ruby version is #{actual}, but your Gemfile specified #{expected}" - when :engine_version - "Your #{Bundler::RubyVersion.system.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}" - when :patchlevel - if !expected.is_a?(String) - "The Ruby patchlevel in your Gemfile must be a string" - else - "Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}" - end - end - - raise RubyVersionMismatch, msg - end - end - - def validate_platforms! - return if @platforms.any? do |bundle_platform| - Bundler.rubygems.platforms.any? do |local_platform| - MatchPlatform.platforms_match?(bundle_platform, local_platform) - end - end - - raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \ - "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \ - "there's no compatible match between those two lists." - end - - def add_platform(platform) - @new_platform ||= !@platforms.include?(platform) - @platforms |= [platform] - end - - def remove_platform(platform) - return if @platforms.delete(Gem::Platform.new(platform)) - raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" - end - - def add_current_platform - current_platform = Bundler.local_platform - add_platform(current_platform) if Bundler.feature_flag.specific_platform? - add_platform(generic(current_platform)) - end - - def find_resolved_spec(current_spec) - specs.find_by_name_and_platform(current_spec.name, current_spec.platform) - end - - def find_indexed_specs(current_spec) - index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version) - end - - attr_reader :sources - private :sources - - def nothing_changed? - !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes - end - - def unlocking? - @unlocking - end - - private - - def change_reason - if unlocking? - unlock_reason = @unlock.reject {|_k, v| Array(v).empty? }.map do |k, v| - if v == true - k.to_s - else - v = Array(v) - "#{k}: (#{v.join(", ")})" - end - end.join(", ") - return "bundler is unlocking #{unlock_reason}" - end - [ - [@source_changes, "the list of sources changed"], - [@dependency_changes, "the dependencies in your gemfile changed"], - [@new_platform, "you added a new platform to your gemfile"], - [@path_changes, "the gemspecs for path gems changed"], - [@local_changes, "the gemspecs for git local gems changed"], - ].select(&:first).map(&:last).join(", ") - end - - def pretty_dep(dep, source = false) - SharedHelpers.pretty_dependency(dep, source) - end - - # Check if the specs of the given source changed - # according to the locked source. - def specs_changed?(source) - locked = @locked_sources.find {|s| s == source } - - !locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source) - end - - def dependencies_for_source_changed?(source, locked_source = source) - deps_for_source = @dependencies.select {|s| s.source == source } - locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source } - - Set.new(deps_for_source) != Set.new(locked_deps_for_source) - end - - def specs_for_source_changed?(source) - locked_index = Index.new - locked_index.use(@locked_specs.select {|s| source.can_lock?(s) }) - - # order here matters, since Index#== is checking source.specs.include?(locked_index) - locked_index != source.specs - rescue PathError, GitError => e - Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})" - false - end - - # Get all locals and override their matching sources. - # Return true if any of the locals changed (for example, - # they point to a new revision) or depend on new specs. - def converge_locals - locals = [] - - Bundler.settings.local_overrides.map do |k, v| - spec = @dependencies.find {|s| s.name == k } - source = spec && spec.source - if source && source.respond_to?(:local_override!) - source.unlock! if @unlock[:gems].include?(spec.name) - locals << [source, source.local_override!(v)] - end - end - - sources_with_changes = locals.select do |source, changed| - changed || specs_changed?(source) - end.map(&:first) - !sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty? - end - - def converge_paths - sources.path_sources.any? do |source| - specs_changed?(source) - end - end - - def converge_path_source_to_gemspec_source(source) - return source unless source.instance_of?(Source::Path) - gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source } - gemspec_source || source - end - - def converge_path_sources_to_gemspec_sources - @locked_sources.map! do |source| - converge_path_source_to_gemspec_source(source) - end - @locked_specs.each do |spec| - spec.source &&= converge_path_source_to_gemspec_source(spec.source) - end - @locked_deps.each do |_, dep| - dep.source &&= converge_path_source_to_gemspec_source(dep.source) - end - end - - def converge_rubygems_sources - return false if Bundler.feature_flag.lockfile_uses_separate_rubygems_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, Bundler.settings[:allow_deployment_source_credential_changes]) - end - end - - changes - end - - def converge_sources - changes = false - - changes |= converge_rubygems_sources - - # 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. - changes |= sources.replace_sources!(@locked_sources) - - sources.all_sources.each do |source| - # If the source is unlockable and the current command allows an unlock of - # the source (for example, you are doing a `bundle update <foo>` of a git-pinned - # gem), unlock it. For git sources, this means to unlock the revision, which - # will cause the `ref` used to be the most recent for the branch (or master) if - # an explicit `ref` is not used. - if source.respond_to?(:unlock!) && @unlock[:sources].include?(source.name) - source.unlock! - changes = true - end - end - - changes - end - - def converge_dependencies - frozen = Bundler.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 - # after locked_source and sources don't match, we still use locked_source. - if frozen && !locked_source.nil? && - locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist? - dep.source = locked_source.source - elsif dep.source - dep.source = sources.get(dep.source) - end - if dep.source.is_a?(Source::Gemspec) - dep.platforms.concat(@platforms.map {|p| Dependency::REVERSE_PLATFORM_MAP[p] }.flatten(1)).uniq! - end - end - - changes = false - # We want to know if all match, but don't want to check all entries - # This means we need to return false if any dependency doesn't match - # the lock or doesn't exist in the lock. - @dependencies.each do |dependency| - unless locked_dep = @locked_deps[dependency.name] - changes = true - next - end - - # Gem::Dependency#== matches Gem::Dependency#type. As the lockfile - # doesn't carry a notion of the dependency type, if you use - # add_development_dependency in a gemspec that's loaded with the gemspec - # directive, the lockfile dependencies and resolved dependencies end up - # with a mismatch on #type. Work around that by setting the type on the - # dep from the lockfile. - locked_dep.instance_variable_set(:@type, dependency.type) - - # We already know the name matches from the hash lookup - # so we only need to check the requirement now - changes ||= dependency.requirement != locked_dep.requirement - end - - changes - end - - # Remove elements from the locked specs that are expired. This will most - # commonly happen if the Gemfile has changed since the lockfile was last - # generated - def converge_locked_specs - deps = [] - - # Build a list of dependencies that are the same in the Gemfile - # and Gemfile.lock. If the Gemfile modified a dependency, but - # the gem in the Gemfile.lock still satisfies it, this is fine - # too. - @dependencies.each do |dep| - locked_dep = @locked_deps[dep.name] - - # If the locked_dep doesn't match the dependency we're looking for then we ignore the locked_dep - locked_dep = nil unless locked_dep == dep - - if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep) - deps << dep - elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source) - @locked_specs.each do |s| - @unlock[:gems] << s.name if s.source == dep.source - end - - dep.source.unlock! if dep.source.respond_to?(:unlock!) - dep.source.specs.each {|s| @unlock[:gems] << s.name } - end - end - - unlock_source_unlocks_spec = Bundler.feature_flag.unlock_source_unlocks_spec? - - converged = [] - @locked_specs.each do |s| - # Replace the locked dependency's source with the equivalent source from the Gemfile - dep = @dependencies.find {|d| s.satisfies?(d) } - 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_source_unlocks_spec && @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_sources_specs = begin - s.source.specs - rescue PathError, GitError - # if we won't need the source (according to the lockfile), - # don't error if the path/git source isn't available - next if @locked_specs. - for(requested_dependencies, [], false, true, false). - none? {|locked_spec| locked_spec.source == s.source } - - raise - end - - other = other_sources_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 - next unless other - - deps2 = other.dependencies.select {|d| d.type != :development } - runtime_dependencies = s.dependencies.select {|d| d.type != :development } - # If the dependencies of the path source have changed, unlock it - next unless runtime_dependencies.sort == deps2.sort - end - - converged << s - end - - resolve = SpecSet.new(converged) - resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems], false, false, false) - diff = nil - - # Now, we unlock any sources that do not have anymore gems pinned to it - sources.all_sources.each do |source| - next unless source.respond_to?(:unlock!) - - unless resolve.any? {|s| s.source == source } - diff ||= @locked_specs.to_a - resolve.to_a - source.unlock! if diff.any? {|s| s.source == source } - end - end - - resolve - end - - def in_locked_deps?(dep, locked_dep) - # Because the lockfile can't link a dep to a specific remote, we need to - # treat sources as equivalent anytime the locked dep has all the remotes - # that the Gemfile dep does. - locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source) - end - - def satisfies_locked_spec?(dep) - @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) } - end - - # This list of dependencies is only used in #resolve, so it's OK to add - # the metadata dependencies here - def expanded_dependencies - @expanded_dependencies ||= begin - expand_dependencies(dependencies + metadata_dependencies, @remote) - end - end - - def metadata_dependencies - @metadata_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 - [ - Dependency.new("ruby\0", ruby_versions), - Dependency.new("rubygems\0", Gem::VERSION), - ] - end - end - - def concat_ruby_version_requirements(ruby_version, ruby_versions = []) - return ruby_versions unless ruby_version - if ruby_version.patchlevel - ruby_versions << ruby_version.to_gem_version_with_patchlevel - else - ruby_versions.concat(ruby_version.versions.map do |version| - requirement = Gem::Requirement.new(version) - if requirement.exact? - "~> #{version}.0" - else - requirement - end - end) - end - end - - def expand_dependencies(dependencies, remote = false) - sorted_platforms = Resolver.sort_platforms(@platforms) - 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(sorted_platforms) - if platforms.empty? - mapped_platforms = dep.platforms.map {|p| Dependency::PLATFORM_MAP[p] } - Bundler.ui.warn \ - "The dependency #{dep} will be unused by any of the platforms Bundler is installing for. " \ - "Bundler is installing for #{@platforms.join ", "} but the dependency " \ - "is only for #{mapped_platforms.join ", "}. " \ - "To add those platforms to the bundle, " \ - "run `bundle lock --add-platform #{mapped_platforms.join " "}`." - end - platforms.each do |p| - deps << DepProxy.new(dep, p) if remote || p == generic_local_platform - end - end - deps - end - - def requested_dependencies - groups = requested_groups - groups.map!(&:to_sym) - dependencies.reject {|d| !d.should_include? || (d.groups & groups).empty? } - end - - def source_requirements - # Load all specs from remote sources - index - - # 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) - default = sources.default_source - source_requirements = { :default => default } - default = nil unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - dependencies.each do |dep| - next unless source = dep.source || default - source_requirements[dep.name] = source - end - metadata_dependencies.each do |dep| - source_requirements[dep.name] = sources.metadata_source - end - source_requirements["bundler"] = sources.metadata_source # needs to come last to override - source_requirements - end - - def pinned_spec_names(skip = nil) - pinned_names = [] - default = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? && sources.default_source - @dependencies.each do |dep| - next unless dep_source = dep.source || default - next if dep_source == skip - pinned_names << dep.name - end - pinned_names - end - - def requested_groups - groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with] - end - - def lockfiles_equal?(current, proposed, preserve_unknown_sections) - if preserve_unknown_sections - sections_to_ignore = LockfileParser.sections_to_ignore(@locked_bundler_version) - sections_to_ignore += LockfileParser.unknown_sections_in_lockfile(current) - sections_to_ignore += LockfileParser::ENVIRONMENT_VERSION_SECTIONS - pattern = /#{Regexp.union(sections_to_ignore)}\n(\s{2,}.*\n)+/ - whitespace_cleanup = /\n{2,}/ - current = current.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip - proposed = proposed.gsub(pattern, "\n").gsub(whitespace_cleanup, "\n\n").strip - end - current == proposed - end - - def extract_gem_info(error) - # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources" - # to an array. The first element will be the gem name (e.g. foo), the second will be the version number. - error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten - end - - def compute_requires - dependencies.reduce({}) do |requires, dep| - next requires unless dep.should_include? - requires[dep.name] = Array(dep.autorequire || dep.name).map do |file| - # Allow `require: true` as an alias for `require: <name>` - file == true ? dep.name : file - end - requires - end - end - - def additional_base_requirements_for_resolve - return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions? - dependencies_by_name = dependencies.group_by(&:name) - @locked_gems.specs.reduce({}) do |requirements, locked_spec| - name = locked_spec.name - next requirements if @locked_deps[name] != dependencies_by_name[name] - dep = Gem::Dependency.new(name, ">= #{locked_spec.version}") - requirements[name] = DepProxy.new(dep, locked_spec.platform) - requirements - end.values - end - - def equivalent_rubygems_remotes?(source) - return false unless source.is_a?(Source::Rubygems) - - Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes) - end - end -end diff --git a/lib/bundler/dep_proxy.rb b/lib/bundler/dep_proxy.rb deleted file mode 100644 index 7a9423b14a..0000000000 --- a/lib/bundler/dep_proxy.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class DepProxy - attr_reader :__platform, :dep - - def initialize(dep, platform) - @dep = dep - @__platform = platform - end - - def hash - @hash ||= dep.hash - end - - def ==(other) - return if other.nil? - dep == other.dep && __platform == other.__platform - end - - alias_method :eql?, :== - - def type - @dep.type - end - - def name - @dep.name - end - - def requirement - @dep.requirement - end - - def to_s - s = name.dup - s << " (#{requirement})" unless requirement == Gem::Requirement.default - s << " #{__platform}" unless __platform == Gem::Platform::RUBY - s - end - - private - - def method_missing(*args, &blk) - @dep.send(*args, &blk) - end - end -end diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb deleted file mode 100644 index 24257bc113..0000000000 --- a/lib/bundler/dependency.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true - -require "rubygems/dependency" -require "bundler/shared_helpers" -require "bundler/rubygems_ext" - -module Bundler - class Dependency < Gem::Dependency - attr_reader :autorequire - attr_reader :groups - attr_reader :platforms - - PLATFORM_MAP = { - :ruby => Gem::Platform::RUBY, - :ruby_18 => Gem::Platform::RUBY, - :ruby_19 => Gem::Platform::RUBY, - :ruby_20 => Gem::Platform::RUBY, - :ruby_21 => Gem::Platform::RUBY, - :ruby_22 => Gem::Platform::RUBY, - :ruby_23 => Gem::Platform::RUBY, - :ruby_24 => Gem::Platform::RUBY, - :ruby_25 => Gem::Platform::RUBY, - :mri => Gem::Platform::RUBY, - :mri_18 => Gem::Platform::RUBY, - :mri_19 => Gem::Platform::RUBY, - :mri_20 => Gem::Platform::RUBY, - :mri_21 => Gem::Platform::RUBY, - :mri_22 => Gem::Platform::RUBY, - :mri_23 => Gem::Platform::RUBY, - :mri_24 => Gem::Platform::RUBY, - :mri_25 => Gem::Platform::RUBY, - :rbx => Gem::Platform::RUBY, - :jruby => Gem::Platform::JAVA, - :jruby_18 => Gem::Platform::JAVA, - :jruby_19 => Gem::Platform::JAVA, - :mswin => Gem::Platform::MSWIN, - :mswin_18 => Gem::Platform::MSWIN, - :mswin_19 => Gem::Platform::MSWIN, - :mswin_20 => Gem::Platform::MSWIN, - :mswin_21 => Gem::Platform::MSWIN, - :mswin_22 => Gem::Platform::MSWIN, - :mswin_23 => Gem::Platform::MSWIN, - :mswin_24 => Gem::Platform::MSWIN, - :mswin_25 => Gem::Platform::MSWIN, - :mswin64 => Gem::Platform::MSWIN64, - :mswin64_19 => Gem::Platform::MSWIN64, - :mswin64_20 => Gem::Platform::MSWIN64, - :mswin64_21 => Gem::Platform::MSWIN64, - :mswin64_22 => Gem::Platform::MSWIN64, - :mswin64_23 => Gem::Platform::MSWIN64, - :mswin64_24 => Gem::Platform::MSWIN64, - :mswin64_25 => Gem::Platform::MSWIN64, - :mingw => Gem::Platform::MINGW, - :mingw_18 => Gem::Platform::MINGW, - :mingw_19 => Gem::Platform::MINGW, - :mingw_20 => Gem::Platform::MINGW, - :mingw_21 => Gem::Platform::MINGW, - :mingw_22 => Gem::Platform::MINGW, - :mingw_23 => Gem::Platform::MINGW, - :mingw_24 => Gem::Platform::MINGW, - :mingw_25 => Gem::Platform::MINGW, - :x64_mingw => Gem::Platform::X64_MINGW, - :x64_mingw_20 => Gem::Platform::X64_MINGW, - :x64_mingw_21 => Gem::Platform::X64_MINGW, - :x64_mingw_22 => Gem::Platform::X64_MINGW, - :x64_mingw_23 => Gem::Platform::X64_MINGW, - :x64_mingw_24 => Gem::Platform::X64_MINGW, - :x64_mingw_25 => Gem::Platform::X64_MINGW, - }.freeze - - REVERSE_PLATFORM_MAP = {}.tap do |reverse_platform_map| - PLATFORM_MAP.each do |key, value| - reverse_platform_map[value] ||= [] - reverse_platform_map[value] << key - end - - reverse_platform_map.each {|_, platforms| platforms.freeze } - end.freeze - - def initialize(name, version, options = {}, &blk) - type = options["type"] || :runtime - super(name, version, type) - - @autorequire = nil - @groups = Array(options["group"] || :default).map(&:to_sym) - @source = options["source"] - @platforms = Array(options["platforms"]) - @env = options["env"] - @should_include = options.fetch("should_include", true) - - @autorequire = Array(options["require"] || []) if options.key?("require") - end - - # Returns the platforms this dependency is valid for, in the same order as - # passed in the `valid_platforms` parameter - def gem_platforms(valid_platforms) - return valid_platforms if @platforms.empty? - - @gem_platforms ||= @platforms.map {|pl| PLATFORM_MAP[pl] }.compact.uniq - - valid_platforms & @gem_platforms - end - - def should_include? - @should_include && current_env? && current_platform? - end - - def current_env? - return true unless @env - if @env.is_a?(Hash) - @env.all? do |key, val| - ENV[key.to_s] && (val.is_a?(String) ? ENV[key.to_s] == val : ENV[key.to_s] =~ val) - end - else - ENV[@env.to_s] - end - end - - def current_platform? - return true if @platforms.empty? - @platforms.any? do |p| - Bundler.current_ruby.send("#{p}?") - end - end - - def to_lock - out = super - out << "!" if source - out << "\n" - end - - def specific? - super - rescue NoMethodError - requirement != ">= 0" - end - end -end diff --git a/lib/bundler/deployment.rb b/lib/bundler/deployment.rb deleted file mode 100644 index 291e158ca0..0000000000 --- a/lib/bundler/deployment.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 2, "Bundler no longer integrates with " \ - "Capistrano, but Capistrano provides its own integration with " \ - "Bundler via the capistrano-bundler gem. Use it instead." - -module Bundler - class Deployment - def self.define_task(context, task_method = :task, opts = {}) - if defined?(Capistrano) && context.is_a?(Capistrano::Configuration) - context_name = "capistrano" - role_default = "{:except => {:no_release => true}}" - error_type = ::Capistrano::CommandError - else - context_name = "vlad" - role_default = "[:app]" - error_type = ::Rake::CommandFailedError - end - - roles = context.fetch(:bundle_roles, false) - opts[:roles] = roles if roles - - context.send :namespace, :bundle do - send :desc, <<-DESC - Install the current Bundler environment. By default, gems will be \ - installed to the shared/bundle path. Gems in the development and \ - test group will not be installed. The install command is executed \ - with the --deployment and --quiet flags. If the bundle cmd cannot \ - be found then you can override the bundle_cmd variable to specify \ - which one it should use. The base path to the app is fetched from \ - the :latest_release variable. Set it for custom deploy layouts. - - You can override any of these defaults by setting the variables shown below. - - N.B. bundle_roles must be defined before you require 'bundler/#{context_name}' \ - in your deploy.rb file. - - set :bundle_gemfile, "Gemfile" - set :bundle_dir, File.join(fetch(:shared_path), 'bundle') - set :bundle_flags, "--deployment --quiet" - set :bundle_without, [:development, :test] - set :bundle_with, [:mysql] - set :bundle_cmd, "bundle" # e.g. "/opt/ruby/bin/bundle" - set :bundle_roles, #{role_default} # e.g. [:app, :batch] - DESC - send task_method, :install, opts do - bundle_cmd = context.fetch(:bundle_cmd, "bundle") - bundle_flags = context.fetch(:bundle_flags, "--deployment --quiet") - bundle_dir = context.fetch(:bundle_dir, File.join(context.fetch(:shared_path), "bundle")) - bundle_gemfile = context.fetch(:bundle_gemfile, "Gemfile") - bundle_without = [*context.fetch(:bundle_without, [:development, :test])].compact - bundle_with = [*context.fetch(:bundle_with, [])].compact - app_path = context.fetch(:latest_release) - if app_path.to_s.empty? - raise error_type.new("Cannot detect current release path - make sure you have deployed at least once.") - end - args = ["--gemfile #{File.join(app_path, bundle_gemfile)}"] - args << "--path #{bundle_dir}" unless bundle_dir.to_s.empty? - args << bundle_flags.to_s - args << "--without #{bundle_without.join(" ")}" unless bundle_without.empty? - args << "--with #{bundle_with.join(" ")}" unless bundle_with.empty? - - run "cd #{app_path} && #{bundle_cmd} install #{args.join(" ")}" - end - end - end - end -end diff --git a/lib/bundler/deprecate.rb b/lib/bundler/deprecate.rb deleted file mode 100644 index 387f632a39..0000000000 --- a/lib/bundler/deprecate.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -begin - require "rubygems/deprecate" -rescue LoadError - # it's fine if it doesn't exist on the current RubyGems... - nil -end - -module Bundler - if defined? Bundler::Deprecate - # nothing to do! - elsif defined? ::Deprecate - Deprecate = ::Deprecate - elsif defined? Gem::Deprecate - Deprecate = Gem::Deprecate - else - class Deprecate - end - end - - unless Deprecate.respond_to?(:skip_during) - def Deprecate.skip_during - original = skip - self.skip = true - yield - ensure - self.skip = original - end - end - - unless Deprecate.respond_to?(:skip) - def Deprecate.skip - @skip ||= false - end - end - - unless Deprecate.respond_to?(:skip=) - def Deprecate.skip=(skip) - @skip = skip - end - end -end diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb deleted file mode 100644 index 8681163277..0000000000 --- a/lib/bundler/dsl.rb +++ /dev/null @@ -1,599 +0,0 @@ -# frozen_string_literal: true - -require "bundler/dependency" -require "bundler/ruby_dsl" - -module Bundler - class Dsl - include RubyDsl - - def self.evaluate(gemfile, lockfile, unlock) - builder = new - builder.eval_gemfile(gemfile) - builder.to_definition(lockfile, unlock) - end - - VALID_PLATFORMS = Bundler::Dependency::PLATFORM_MAP.keys.freeze - - VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules - platform platforms type source install_if].freeze - - attr_reader :gemspecs - attr_accessor :dependencies - - def initialize - @source = nil - @sources = SourceList.new - @git_sources = {} - @dependencies = [] - @groups = [] - @install_conditionals = [] - @optional_groups = [] - @platforms = [] - @env = nil - @ruby_version = nil - @gemspecs = [] - @gemfile = nil - @gemfiles = [] - add_git_sources - end - - def eval_gemfile(gemfile, contents = nil) - expanded_gemfile_path = Pathname.new(gemfile).expand_path(@gemfile && @gemfile.parent) - original_gemfile = @gemfile - @gemfile = expanded_gemfile_path - @gemfiles << 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 " \ - "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \ - "`#{File.basename gemfile.to_s}`: #{e.message}" - - raise DSLError.new(message, gemfile, e.backtrace, contents) - ensure - @gemfile = original_gemfile - end - - def gemspec(opts = nil) - opts ||= {} - path = opts[:path] || "." - glob = opts[:glob] - name = opts[:name] - development_group = opts[:development_group] || :development - expanded_path = gemfile_root.join(path) - - gemspecs = Dir[File.join(expanded_path, "{,*}.gemspec")].map {|g| Bundler.load_gemspec(g) }.compact - gemspecs.reject! {|s| s.name != name } if name - Index.sort_specs(gemspecs) - specs_by_name_and_version = gemspecs.group_by {|s| [s.name, s.version] } - - case specs_by_name_and_version.size - when 1 - specs = specs_by_name_and_version.values.first - spec = specs.find {|s| s.match_platform(Bundler.local_platform) } || specs.first - - @gemspecs << spec - - gem_platforms = Bundler::Dependency::REVERSE_PLATFORM_MAP[Bundler::GemHelpers.generic_local_platform] - gem spec.name, :name => spec.name, :path => path, :glob => glob, :platforms => gem_platforms - - group(development_group) do - spec.development_dependencies.each do |dep| - gem dep.name, *(dep.requirement.as_list + [:type => :development]) - end - end - when 0 - raise InvalidOption, "There are no gemspecs at #{expanded_path}" - else - raise InvalidOption, "There are multiple gemspecs at #{expanded_path}. " \ - "Please use the :name option to specify which one should be used" - end - end - - def gem(name, *args) - options = args.last.is_a?(Hash) ? args.pop.dup : {} - version = args || [">= 0"] - - normalize_options(name, version, options) - - dep = Dependency.new(name, version, options) - - # if there's already a dependency with this name we try to prefer one - if current = @dependencies.find {|d| d.name == dep.name } - deleted_dep = @dependencies.delete(current) if current.type == :development - - if current.requirement != dep.requirement - unless deleted_dep - 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})" - end - - else - Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \ - "You should probably keep only one of them.\n" \ - "While it's not a problem now, it could cause errors if you change the version of one of them later." - end - - if current.source != dep.source - unless deleted_dep - 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 " \ - "#{current.source || "an unspecified source"} and #{dep.source}\n" - end - end - end - - @dependencies << dep - end - - def source(source, *args, &blk) - options = args.last.is_a?(Hash) ? args.pop.dup : {} - options = normalize_hash(options) - source = normalize_source(source) - - if options.key?("type") - options["type"] = options["type"].to_s - unless Plugin.source?(options["type"]) - raise InvalidOption, "No plugin sources available for #{options["type"]}" - end - - unless block_given? - raise InvalidOption, "You need to pass a block to #source with :type option" - end - - source_opts = options.merge("uri" => source) - with_source(@sources.add_plugin_source(options["type"], source_opts), &blk) - elsif block_given? - with_source(@sources.add_rubygems_source("remotes" => source), &blk) - else - check_primary_source_safety(@sources) - @sources.global_rubygems_source = source - end - end - - def git_source(name, &block) - unless block_given? - raise InvalidOption, "You need to pass a block to #git_source" - end - - if valid_keys.include?(name.to_s) - raise InvalidOption, "You cannot use #{name} as a git source. It " \ - "is a reserved key. Reserved keys are: #{valid_keys.join(", ")}" - end - - @git_sources[name.to_s] = block - end - - def path(path, options = {}, &blk) - unless block_given? - msg = "You can no longer specify a path source by itself. Instead, \n" \ - "either use the :path option on a gem, or specify the gems that \n" \ - "bundler should find in the path source by passing a block to \n" \ - "the path method, like: \n\n" \ - " path 'dir/containing/rails' do\n" \ - " gem 'rails'\n" \ - " end\n\n" - - raise DeprecatedError, msg if Bundler.feature_flag.disable_multisource? - SharedHelpers.major_deprecation(2, msg.strip) - end - - source_options = normalize_hash(options).merge( - "path" => Pathname.new(path), - "root_path" => gemfile_root, - "gemspec" => gemspecs.find {|g| g.name == options["name"] } - ) - source = @sources.add_path_source(source_options) - with_source(source, &blk) - end - - def git(uri, options = {}, &blk) - unless block_given? - msg = "You can no longer specify a git source by itself. Instead, \n" \ - "either use the :git option on a gem, or specify the gems that \n" \ - "bundler should find in the git source by passing a block to \n" \ - "the git method, like: \n\n" \ - " git 'git://github.com/rails/rails.git' do\n" \ - " gem 'rails'\n" \ - " end" - raise DeprecatedError, msg - end - - with_source(@sources.add_git_source(normalize_hash(options).merge("uri" => uri)), &blk) - end - - def github(repo, options = {}) - raise ArgumentError, "GitHub sources require a block" unless block_given? - raise DeprecatedError, "The #github method has been removed" if Bundler.feature_flag.skip_default_git_sources? - github_uri = @git_sources["github"].call(repo) - git_options = normalize_hash(options).merge("uri" => github_uri) - git_source = @sources.add_git_source(git_options) - with_source(git_source) { yield } - end - - def to_definition(lockfile, unlock) - Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles) - end - - def group(*args, &blk) - options = args.last.is_a?(Hash) ? args.pop.dup : {} - normalize_group_options(options, args) - - @groups.concat args - - if options["optional"] - optional_groups = args - @optional_groups - @optional_groups.concat optional_groups - end - - yield - ensure - args.each { @groups.pop } - end - - def install_if(*args) - @install_conditionals.concat args - yield - ensure - args.each { @install_conditionals.pop } - end - - def platforms(*platforms) - @platforms.concat platforms - yield - ensure - platforms.each { @platforms.pop } - end - alias_method :platform, :platforms - - def env(name) - old = @env - @env = name - yield - ensure - @env = old - end - - def plugin(*args) - # Pass on - end - - def method_missing(name, *args) - raise GemfileError, "Undefined local variable or method `#{name}' for Gemfile" - end - - private - - def add_git_sources - return if Bundler.feature_flag.skip_default_git_sources? - - git_source(:github) do |repo_name| - warn_deprecated_git_source(:github, <<-'RUBY'.strip, 'Change any "reponame" :github sources to "username/reponame".') -"https://github.com/#{repo_name}.git" - RUBY - # It would be better to use https instead of the git protocol, but this - # can break deployment of existing locked bundles when switching between - # different versions of Bundler. The change will be made in 2.0, which - # does not guarantee compatibility with the 1.x series. - # - # See https://github.com/bundler/bundler/pull/2569 for discussion - # - # This can be overridden by adding this code to your Gemfiles: - # - # git_source(:github) do |repo_name| - # repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - # "https://github.com/#{repo_name}.git" - # end - repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") - # TODO: 2.0 upgrade this setting to the default - if Bundler.settings["github.https"] - Bundler::SharedHelpers.major_deprecation 2, "The `github.https` setting will be removed" - "https://github.com/#{repo_name}.git" - else - "git://github.com/#{repo_name}.git" - end - end - - # TODO: 2.0 remove this deprecated git source - git_source(:gist) do |repo_name| - warn_deprecated_git_source(:gist, '"https://gist.github.com/#{repo_name}.git"') - - "https://gist.github.com/#{repo_name}.git" - end - - # TODO: 2.0 remove this deprecated git source - git_source(:bitbucket) do |repo_name| - warn_deprecated_git_source(:bitbucket, <<-'RUBY'.strip) -user_name, repo_name = repo_name.split("/") -repo_name ||= user_name -"https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git" - RUBY - - user_name, repo_name = repo_name.split("/") - repo_name ||= user_name - "https://#{user_name}@bitbucket.org/#{user_name}/#{repo_name}.git" - end - end - - def with_source(source) - old_source = @source - if block_given? - @source = source - yield - end - source - ensure - @source = old_source - end - - def normalize_hash(opts) - opts.keys.each do |k| - opts[k.to_s] = opts.delete(k) unless k.is_a?(String) - end - opts - end - - def valid_keys - @valid_keys ||= VALID_KEYS - end - - def normalize_options(name, version, opts) - if name.is_a?(Symbol) - raise GemfileError, %(You need to specify gem names as Strings. Use 'gem "#{name}"' instead) - end - if name =~ /\s/ - raise GemfileError, %('#{name}' is not a valid gem name because it contains whitespace) - end - if name.empty? - raise GemfileError, %(an empty gem name is not valid) - end - - normalize_hash(opts) - - git_names = @git_sources.keys.map(&:to_s) - validate_keys("gem '#{name}'", opts, valid_keys + git_names) - - groups = @groups.dup - opts["group"] = opts.delete("groups") || opts["group"] - groups.concat Array(opts.delete("group")) - groups = [:default] if groups.empty? - - install_if = @install_conditionals.dup - install_if.concat Array(opts.delete("install_if")) - install_if = install_if.reduce(true) do |memo, val| - memo && (val.respond_to?(:call) ? val.call : val) - end - - platforms = @platforms.dup - opts["platforms"] = opts["platform"] || opts["platforms"] - platforms.concat Array(opts.delete("platforms")) - platforms.map!(&:to_sym) - platforms.each do |p| - next if VALID_PLATFORMS.include?(p) - raise GemfileError, "`#{p}` is not a valid platform. The available options are: #{VALID_PLATFORMS.inspect}" - end - - # Save sources passed in a key - if opts.key?("source") - source = normalize_source(opts["source"]) - opts["source"] = @sources.add_rubygems_source("remotes" => source) - end - - git_name = (git_names & opts.keys).last - if @git_sources[git_name] - 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) - else - options = opts.dup - end - source = send(type, param, options) {} - opts["source"] = source - end - - opts["source"] ||= @source - opts["env"] ||= @env - opts["platforms"] = platforms.dup - opts["group"] = groups - opts["should_include"] = install_if - end - - def normalize_group_options(opts, groups) - normalize_hash(opts) - - groups = groups.map {|group| ":#{group}" }.join(", ") - validate_keys("group #{groups}", opts, %w[optional]) - - opts["optional"] ||= false - end - - def validate_keys(command, opts, valid_keys) - invalid_keys = opts.keys - valid_keys - - git_source = opts.keys & @git_sources.keys.map(&:to_s) - if opts["branch"] && !(opts["git"] || opts["github"] || git_source.any?) - raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch) - end - - return true unless 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 - - def normalize_source(source) - case source - when :gemcutter, :rubygems, :rubyforge - Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \ - "requests are insecure.\nPlease change your source to 'https://" \ - "rubygems.org' if possible, or 'http://rubygems.org' if not." - "http://rubygems.org" - when String - source - else - raise GemfileError, "Unknown source '#{source}'" - end - end - - def check_primary_source_safety(source_list) - return if source_list.rubygems_primary_remotes.empty? && source_list.global_rubygems_source.nil? - - if Bundler.feature_flag.disable_multisource? - msg = "This Gemfile contains multiple primary sources. " \ - "Each source after the first must include a block to indicate which gems " \ - "should come from that source" - unless Bundler.feature_flag.bundler_2_mode? - msg += ". To downgrade this error to a warning, run " \ - "`bundle config --delete disable_multisource`" - end - raise GemfileEvalError, msg - else - Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple primary sources. " \ - "Using `source` more than once without a block is a security risk, and " \ - "may result in installing unexpected gems. To resolve this warning, use " \ - "a block to indicate which gems should come from the secondary source. " \ - "To upgrade this warning to an error, run `bundle config " \ - "disable_multisource true`." - end - end - - def warn_deprecated_git_source(name, replacement, additional_message = nil) - # TODO: 2.0 remove deprecation - additional_message &&= " #{additional_message}" - replacement = if replacement.count("\n").zero? - "{|repo_name| #{replacement} }" - else - "do |repo_name|\n#{replacement.to_s.gsub(/^/, " ")}\n end" - end - - Bundler::SharedHelpers.major_deprecation 2, <<-EOS -The :#{name} git source is deprecated, and will be removed in Bundler 2.0.#{additional_message} Add this code to the top of your Gemfile to ensure it continues to work: - - git_source(:#{name}) #{replacement} - - EOS - end - - class DSLError < GemfileError - # @return [String] the description that should be presented to the user. - # - attr_reader :description - - # @return [String] the path of the dsl file that raised the exception. - # - attr_reader :dsl_path - - # @return [Exception] the backtrace of the exception raised by the - # evaluation of the dsl file. - # - attr_reader :backtrace - - # @param [Exception] backtrace @see backtrace - # @param [String] dsl_path @see dsl_path - # - def initialize(description, dsl_path, backtrace, contents = nil) - @status_code = $!.respond_to?(:status_code) && $!.status_code - - @description = description - @dsl_path = dsl_path - @backtrace = backtrace - @contents = contents - end - - def status_code - @status_code || super - end - - # @return [String] the contents of the DSL that cause the exception to - # be raised. - # - def contents - @contents ||= begin - dsl_path && File.exist?(dsl_path) && File.read(dsl_path) - end - end - - # The message of the exception reports the content of podspec for the - # line that generated the original exception. - # - # @example Output - # - # Invalid podspec at `RestKit.podspec` - undefined method - # `exclude_header_search_paths=' for #<Pod::Specification for - # `RestKit/Network (0.9.3)`> - # - # from spec-repos/master/RestKit/0.9.3/RestKit.podspec:36 - # ------------------------------------------- - # # because it would break: #import <CoreData/CoreData.h> - # > ns.exclude_header_search_paths = 'Code/RestKit.h' - # end - # ------------------------------------------- - # - # @return [String] the message of the exception. - # - def to_s - @to_s ||= begin - trace_line, description = parse_line_number_from_description - - m = String.new("\n[!] ") - m << description - m << ". Bundler cannot continue.\n" - - return m unless backtrace && dsl_path && contents - - trace_line = backtrace.find {|l| l.include?(dsl_path.to_s) } || trace_line - return m unless trace_line - line_numer = trace_line.split(":")[1].to_i - 1 - return m unless line_numer - - lines = contents.lines.to_a - indent = " # " - indicator = indent.tr("#", ">") - first_line = line_numer.zero? - last_line = (line_numer == (lines.count - 1)) - - m << "\n" - m << "#{indent}from #{trace_line.gsub(/:in.*$/, "")}\n" - m << "#{indent}-------------------------------------------\n" - m << "#{indent}#{lines[line_numer - 1]}" unless first_line - m << "#{indicator}#{lines[line_numer]}" - m << "#{indent}#{lines[line_numer + 1]}" unless last_line - m << "\n" unless m.end_with?("\n") - m << "#{indent}-------------------------------------------\n" - end - end - - private - - def parse_line_number_from_description - description = self.description - if dsl_path && description =~ /((#{Regexp.quote File.expand_path(dsl_path)}|#{Regexp.quote dsl_path.to_s}):\d+)/ - trace_line = Regexp.last_match[1] - description = description.sub(/#{Regexp.quote trace_line}:\s*/, "").sub("\n", " - ") - end - [trace_line, description] - end - end - - def gemfile_root - @gemfile ||= Bundler.default_gemfile - @gemfile.dirname - end - end -end diff --git a/lib/bundler/endpoint_specification.rb b/lib/bundler/endpoint_specification.rb deleted file mode 100644 index 8668c4ea7f..0000000000 --- a/lib/bundler/endpoint_specification.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # used for Creating Specifications from the Gemcutter Endpoint - class EndpointSpecification < Gem::Specification - ILLFORMED_MESSAGE = 'Ill-formed requirement ["#<YAML::Syck::DefaultKey'.freeze - include MatchPlatform - - attr_reader :name, :version, :platform, :required_rubygems_version, :required_ruby_version, :checksum - attr_accessor :source, :remote, :dependencies - - def initialize(name, version, platform, dependencies, metadata = nil) - super() - @name = name - @version = Gem::Version.create version - @platform = platform - @dependencies = dependencies.map {|dep, reqs| build_dependency(dep, reqs) } - - @loaded_from = nil - @remote_specification = nil - - parse_metadata(metadata) - end - - def fetch_platform - @platform - end - - # needed for standalone, load required_paths from local gemspec - # after the gem is installed - def require_paths - if @remote_specification - @remote_specification.require_paths - elsif _local_specification - _local_specification.require_paths - else - super - end - end - - # needed for inline - def load_paths - # remote specs aren't installed, and can't have load_paths - if _local_specification - _local_specification.load_paths - else - super - end - end - - # needed for binstubs - def executables - if @remote_specification - @remote_specification.executables - elsif _local_specification - _local_specification.executables - else - super - end - end - - # needed for bundle clean - def bindir - if @remote_specification - @remote_specification.bindir - elsif _local_specification - _local_specification.bindir - else - super - end - end - - # needed for post_install_messages during install - def post_install_message - if @remote_specification - @remote_specification.post_install_message - elsif _local_specification - _local_specification.post_install_message - else - super - end - end - - # needed for "with native extensions" during install - def extensions - if @remote_specification - @remote_specification.extensions - elsif _local_specification - _local_specification.extensions - else - super - end - end - - def _local_specification - return unless @loaded_from && File.exist?(local_specification_path) - eval(File.read(local_specification_path)).tap do |spec| - spec.loaded_from = @loaded_from - end - end - - def __swap__(spec) - SharedHelpers.ensure_same_dependencies(self, dependencies, spec.dependencies) - @remote_specification = spec - end - - private - - def local_specification_path - "#{base_dir}/specifications/#{full_name}.gemspec" - end - - def parse_metadata(data) - return unless data - data.each do |k, v| - next unless v - case k.to_s - when "checksum" - @checksum = v.last - when "rubygems" - @required_rubygems_version = Gem::Requirement.new(v) - when "ruby" - @required_ruby_version = Gem::Requirement.new(v) - end - end - rescue => e - raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}" - end - - def build_dependency(name, requirements) - Gem::Dependency.new(name, requirements) - rescue ArgumentError => e - raise unless e.message.include?(ILLFORMED_MESSAGE) - puts # we shouldn't print the error message on the "fetching info" status line - raise GemspecError, - "Unfortunately, the gem #{name} (#{version}) has an invalid " \ - "gemspec.\nPlease ask the gem author to yank the bad version to fix " \ - "this issue. For more information, see http://bit.ly/syck-defaultkey." - end - end -end diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb deleted file mode 100644 index 58fe20dbe7..0000000000 --- a/lib/bundler/env.rb +++ /dev/null @@ -1,153 +0,0 @@ -# frozen_string_literal: true - -require "bundler/rubygems_integration" -require "bundler/source/git/git_proxy" - -module Bundler - class Env - def self.write(io) - io.write report - end - - def self.report(options = {}) - print_gemfile = options.delete(:print_gemfile) { true } - print_gemspecs = options.delete(:print_gemspecs) { true } - - out = String.new - append_formatted_table("Environment", environment, out) - append_formatted_table("Bundler Build Metadata", BuildMetadata.to_h, out) - - unless Bundler.settings.all.empty? - out << "\n## Bundler settings\n\n```\n" - Bundler.settings.all.each do |setting| - out << setting << "\n" - Bundler.settings.pretty_values_for(setting).each do |line| - out << " " << line << "\n" - end - end - out << "```\n" - end - - return out unless SharedHelpers.in_bundle? - - if print_gemfile - gemfiles = [Bundler.default_gemfile] - begin - gemfiles = Bundler.definition.gemfiles - rescue GemfileNotFound - nil - end - - out << "\n## Gemfile\n" - gemfiles.each do |gemfile| - out << "\n### #{Pathname.new(gemfile).relative_path_from(SharedHelpers.pwd)}\n\n" - out << "```ruby\n" << read_file(gemfile).chomp << "\n```\n" - end - - out << "\n### #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}\n\n" - out << "```\n" << read_file(Bundler.default_lockfile).chomp << "\n```\n" - end - - if print_gemspecs - dsl = Dsl.new.tap {|d| d.eval_gemfile(Bundler.default_gemfile) } - out << "\n## Gemspecs\n" unless dsl.gemspecs.empty? - dsl.gemspecs.each do |gs| - out << "\n### #{File.basename(gs.loaded_from)}" - out << "\n\n```ruby\n" << read_file(gs.loaded_from).chomp << "\n```\n" - end - end - - out - end - - def self.read_file(filename) - File.read(filename.to_s).strip - rescue Errno::ENOENT - "<No #{filename} found>" - rescue => e - "#{e.class}: #{e.message}" - end - - def self.ruby_version - str = String.new("#{RUBY_VERSION}") - if RUBY_VERSION < "1.9" - str << " (#{RUBY_RELEASE_DATE}" - str << " patchlevel #{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL - str << ") [#{RUBY_PLATFORM}]" - else - str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL - str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{RUBY_PLATFORM}]" - end - end - - def self.git_version - Bundler::Source::Git::GitProxy.new(nil, nil, nil).full_version - rescue Bundler::Source::Git::GitNotInstalledError - "not installed" - end - - def self.version_of(script) - return "not installed" unless Bundler.which(script) - `#{script} --version` - end - - def self.chruby_version - return "not installed" unless Bundler.which("chruby-exec") - `chruby-exec -- chruby --version`. - sub(/.*^chruby: (#{Gem::Version::VERSION_PATTERN}).*/m, '\1') - end - - def self.environment - out = [] - - out << ["Bundler", Bundler::VERSION] - out << [" Platforms", Gem.platforms.join(", ")] - out << ["Ruby", ruby_version] - out << [" Full Path", Gem.ruby] - out << [" Config Dir", Pathname.new(Gem::ConfigFile::SYSTEM_WIDE_CONFIG_FILE).dirname] - out << ["RubyGems", Gem::VERSION] - out << [" Gem Home", ENV.fetch("GEM_HOME") { Gem.dir }] - out << [" Gem Path", ENV.fetch("GEM_PATH") { Gem.path.join(File::PATH_SEPARATOR) }] - out << [" User Path", Gem.user_dir] - out << [" Bin Dir", Gem.bindir] - out << ["OpenSSL"] if defined?(OpenSSL) - out << [" Compiled", OpenSSL::OPENSSL_VERSION] if defined?(OpenSSL::OPENSSL_VERSION) - out << [" Loaded", OpenSSL::OPENSSL_LIBRARY_VERSION] if defined?(OpenSSL::OPENSSL_LIBRARY_VERSION) - out << [" Cert File", OpenSSL::X509::DEFAULT_CERT_FILE] if defined?(OpenSSL::X509::DEFAULT_CERT_FILE) - out << [" Cert Dir", OpenSSL::X509::DEFAULT_CERT_DIR] if defined?(OpenSSL::X509::DEFAULT_CERT_DIR) - out << ["Tools"] - out << [" Git", git_version] - out << [" RVM", ENV.fetch("rvm_version") { version_of("rvm") }] - out << [" rbenv", version_of("rbenv")] - out << [" chruby", chruby_version] - - %w[rubygems-bundler open_gem].each do |name| - specs = Bundler.rubygems.find_name(name) - out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty? - end - if (exe = caller.last.split(":").first) && exe =~ %r{(exe|bin)/bundler?\z} - shebang = File.read(exe).lines.first - shebang.sub!(/^#!\s*/, "") - unless shebang.start_with?(Gem.ruby, "/usr/bin/env ruby") - out << ["Gem.ruby", Gem.ruby] - out << ["bundle #!", shebang] - end - end - - out - end - - def self.append_formatted_table(title, pairs, out) - return if pairs.empty? - out << "\n" unless out.empty? - out << "## #{title}\n\n```\n" - ljust = pairs.map {|k, _v| k.to_s.length }.max - pairs.each do |k, v| - out << "#{k.to_s.ljust(ljust)} #{v}\n" - end - out << "```\n" - end - - private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table, :version_of, :chruby_version - end -end diff --git a/lib/bundler/environment_preserver.rb b/lib/bundler/environment_preserver.rb deleted file mode 100644 index af7c1ef0a4..0000000000 --- a/lib/bundler/environment_preserver.rb +++ /dev/null @@ -1,59 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class EnvironmentPreserver - INTENTIONALLY_NIL = "BUNDLER_ENVIRONMENT_PRESERVER_INTENTIONALLY_NIL".freeze - BUNDLER_KEYS = %w[ - BUNDLE_BIN_PATH - BUNDLE_GEMFILE - BUNDLER_ORIG_MANPATH - BUNDLER_VERSION - GEM_HOME - GEM_PATH - MANPATH - PATH - RB_USER_INSTALL - RUBYLIB - RUBYOPT - ].map(&:freeze).freeze - BUNDLER_PREFIX = "BUNDLER_ORIG_".freeze - - # @param env [ENV] - # @param keys [Array<String>] - def initialize(env, keys) - @original = env.to_hash - @keys = keys - @prefix = BUNDLER_PREFIX - end - - # @return [Hash] - def backup - env = @original.clone - @keys.each do |key| - value = env[key] - if !value.nil? && !value.empty? - env[@prefix + key] ||= value - elsif value.nil? - env[@prefix + key] ||= INTENTIONALLY_NIL - end - end - env - end - - # @return [Hash] - def restore - env = @original.clone - @keys.each do |key| - value_original = env[@prefix + key] - next if value_original.nil? || value_original.empty? - if value_original == INTENTIONALLY_NIL - env.delete(key) - else - env[key] = value_original - end - env.delete(@prefix + key) - end - env - end - end -end diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb deleted file mode 100644 index e471bce0b6..0000000000 --- a/lib/bundler/errors.rb +++ /dev/null @@ -1,158 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class BundlerError < StandardError - def self.status_code(code) - define_method(:status_code) { code } - if match = BundlerError.all_errors.find {|_k, v| v == code } - error, _ = match - raise ArgumentError, - "Trying to register #{self} for status code #{code} but #{error} is already registered" - end - BundlerError.all_errors[self] = code - end - - def self.all_errors - @all_errors ||= {} - end - end - - class GemfileError < BundlerError; status_code(4); end - class InstallError < BundlerError; status_code(5); end - - # Internal error, should be rescued - class VersionConflict < BundlerError - attr_reader :conflicts - - def initialize(conflicts, msg = nil) - super(msg) - @conflicts = conflicts - end - - status_code(6) - end - - class GemNotFound < BundlerError; status_code(7); end - class InstallHookError < BundlerError; status_code(8); end - class GemfileNotFound < BundlerError; status_code(10); end - class GitError < BundlerError; status_code(11); end - class DeprecatedError < BundlerError; status_code(12); end - class PathError < BundlerError; status_code(13); end - class GemspecError < BundlerError; status_code(14); end - class InvalidOption < BundlerError; status_code(15); end - class ProductionError < BundlerError; status_code(16); end - class HTTPError < BundlerError - status_code(17) - def filter_uri(uri) - URICredentialsFilter.credential_filtered_uri(uri) - end - end - class RubyVersionMismatch < BundlerError; status_code(18); end - class SecurityError < BundlerError; status_code(19); end - class LockfileError < BundlerError; status_code(20); end - class CyclicDependencyError < BundlerError; status_code(21); end - class GemfileLockNotFound < BundlerError; status_code(22); end - class PluginError < BundlerError; status_code(29); end - class SudoNotPermittedError < BundlerError; status_code(30); end - class ThreadCreationError < BundlerError; status_code(33); end - class APIResponseMismatchError < BundlerError; status_code(34); end - class GemfileEvalError < GemfileError; end - class MarshalError < StandardError; end - - class PermissionError < BundlerError - def initialize(path, permission_type = :write) - @path = path - @permission_type = permission_type - end - - def action - case @permission_type - when :read then "read from" - when :write then "write to" - when :executable, :exec then "execute" - else @permission_type.to_s - end - end - - def message - "There was an error while trying to #{action} `#{@path}`. " \ - "It is likely that you need to grant #{@permission_type} permissions " \ - "for that path." - end - - status_code(23) - end - - class GemRequireError < BundlerError - attr_reader :orig_exception - - def initialize(orig_exception, msg) - full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\ - "Backtrace for gem load error is:\n"\ - "#{orig_exception.backtrace.join("\n")}\n"\ - "Bundler Error Backtrace:\n" - super(full_message) - @orig_exception = orig_exception - end - - status_code(24) - end - - class YamlSyntaxError < BundlerError - attr_reader :orig_exception - - def initialize(orig_exception, msg) - super(msg) - @orig_exception = orig_exception - end - - status_code(25) - end - - class TemporaryResourceError < PermissionError - def message - "There was an error while trying to #{action} `#{@path}`. " \ - "Some resource was temporarily unavailable. It's suggested that you try" \ - "the operation again." - end - - status_code(26) - end - - class VirtualProtocolError < BundlerError - def message - "There was an error relating to virtualization and file access." \ - "It is likely that you need to grant access to or mount some file system correctly." - end - - status_code(27) - end - - class OperationNotSupportedError < PermissionError - def message - "Attempting to #{action} `#{@path}` is unsupported by your OS." - end - - status_code(28) - end - - class NoSpaceOnDeviceError < PermissionError - def message - "There was an error while trying to #{action} `#{@path}`. " \ - "There was insufficient space remaining on the device." - end - - status_code(31) - end - - class GenericSystemCallError < BundlerError - attr_reader :underlying_error - - def initialize(underlying_error, message) - @underlying_error = underlying_error - super("#{message}\nThe underlying system error is #{@underlying_error.class}: #{@underlying_error}") - end - - status_code(32) - end -end diff --git a/lib/bundler/feature_flag.rb b/lib/bundler/feature_flag.rb deleted file mode 100644 index 6a1809cd40..0000000000 --- a/lib/bundler/feature_flag.rb +++ /dev/null @@ -1,67 +0,0 @@ -# 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 - - settings_method("#{flag}?", flag, &default) - end - private_class_method :settings_flag - - def self.settings_option(key, &default) - settings_method(key, key, &default) - end - private_class_method :settings_option - - def self.settings_method(name, key, &default) - define_method(name) do - value = Bundler.settings[key] - value = instance_eval(&default) if value.nil? && !default.nil? - value - end - end - private_class_method :settings_method - - (1..10).each {|v| define_method("bundler_#{v}_mode?") { major_version >= v } } - - settings_flag(:allow_bundler_dependency_conflicts) { bundler_2_mode? } - settings_flag(:allow_offline_install) { bundler_2_mode? } - settings_flag(:auto_clean_without_path) { bundler_2_mode? } - settings_flag(:cache_all) { bundler_2_mode? } - settings_flag(:cache_command_is_package) { bundler_2_mode? } - settings_flag(:console_command) { !bundler_2_mode? } - settings_flag(:default_install_uses_path) { bundler_2_mode? } - settings_flag(:deployment_means_frozen) { bundler_2_mode? } - settings_flag(:disable_multisource) { bundler_2_mode? } - settings_flag(:error_on_stderr) { bundler_2_mode? } - settings_flag(:forget_cli_options) { bundler_2_mode? } - settings_flag(:global_gem_cache) { bundler_2_mode? } - settings_flag(:init_gems_rb) { bundler_2_mode? } - settings_flag(:list_command) { bundler_2_mode? } - settings_flag(:lockfile_uses_separate_rubygems_sources) { bundler_2_mode? } - settings_flag(:only_update_to_newer_versions) { bundler_2_mode? } - settings_flag(:plugins) { @bundler_version >= Gem::Version.new("1.14") } - settings_flag(:prefer_gems_rb) { bundler_2_mode? } - settings_flag(:print_only_version_number) { bundler_2_mode? } - settings_flag(:setup_makes_kernel_gem_public) { !bundler_2_mode? } - settings_flag(:skip_default_git_sources) { bundler_2_mode? } - settings_flag(:specific_platform) { bundler_2_mode? } - settings_flag(:suppress_install_using_messages) { bundler_2_mode? } - settings_flag(:unlock_source_unlocks_spec) { !bundler_2_mode? } - settings_flag(:update_requires_all_flag) { bundler_2_mode? } - - settings_option(:default_cli_command) { bundler_2_mode? ? :cli_help : :install } - - def initialize(bundler_version) - @bundler_version = Gem::Version.create(bundler_version) - end - - def major_version - @bundler_version.segments.first - end - private :major_version - end -end diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb deleted file mode 100644 index 03ff528826..0000000000 --- a/lib/bundler/fetcher.rb +++ /dev/null @@ -1,312 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_persistent" -require "cgi" -require "securerandom" -require "zlib" - -module Bundler - # Handles all the fetching with the rubygems server - class Fetcher - autoload :CompactIndex, "bundler/fetcher/compact_index" - autoload :Downloader, "bundler/fetcher/downloader" - autoload :Dependency, "bundler/fetcher/dependency" - autoload :Index, "bundler/fetcher/index" - - # This error is raised when it looks like the network is down - class NetworkDownError < HTTPError; end - # This error is raised if the API returns a 413 (only printed in verbose) - class FallbackError < HTTPError; end - # This is the error raised if OpenSSL fails the cert verification - class CertificateFailureError < HTTPError - def initialize(remote_uri) - remote_uri = filter_uri(remote_uri) - super "Could not verify the SSL certificate for #{remote_uri}.\nThere" \ - " is a chance you are experiencing a man-in-the-middle attack, but" \ - " most likely your system doesn't have the CA certificates needed" \ - " for verification. For information about OpenSSL certificates, see" \ - " http://bit.ly/ruby-ssl. To connect without using SSL, edit your Gemfile" \ - " sources and change 'https' to 'http'." - end - end - # This is the error raised when a source is HTTPS and OpenSSL didn't load - class SSLError < HTTPError - def initialize(msg = nil) - super msg || "Could not load OpenSSL.\n" \ - "You must recompile Ruby with OpenSSL support or change the sources in your " \ - "Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL " \ - "using RVM are available at rvm.io/packages/openssl." - end - end - # This error is raised if HTTP authentication is required, but not provided. - class AuthenticationRequiredError < HTTPError - def initialize(remote_uri) - remote_uri = filter_uri(remote_uri) - super "Authentication is required for #{remote_uri}.\n" \ - "Please supply credentials for this source. You can do this by running:\n" \ - " bundle config #{remote_uri} username:password" - end - end - # This error is raised if HTTP authentication is provided, but incorrect. - class BadAuthenticationError < HTTPError - def initialize(remote_uri) - remote_uri = filter_uri(remote_uri) - super "Bad username or password for #{remote_uri}.\n" \ - "Please double-check your credentials and correct them." - end - end - - # Exceptions classes that should bypass retry attempts. If your password didn't work the - # first time, it's not going to the third time. - NET_ERRORS = [:HTTPBadGateway, :HTTPBadRequest, :HTTPFailedDependency, - :HTTPForbidden, :HTTPInsufficientStorage, :HTTPMethodNotAllowed, - :HTTPMovedPermanently, :HTTPNoContent, :HTTPNotFound, - :HTTPNotImplemented, :HTTPPreconditionFailed, :HTTPRequestEntityTooLarge, - :HTTPRequestURITooLong, :HTTPUnauthorized, :HTTPUnprocessableEntity, - :HTTPUnsupportedMediaType, :HTTPVersionNotSupported].freeze - FAIL_ERRORS = begin - fail_errors = [AuthenticationRequiredError, BadAuthenticationError, FallbackError] - fail_errors << Gem::Requirement::BadRequirementError if defined?(Gem::Requirement::BadRequirementError) - fail_errors.concat(NET_ERRORS.map {|e| SharedHelpers.const_get_safely(e, Net) }.compact) - end.freeze - - class << self - attr_accessor :disable_endpoint, :api_timeout, :redirect_limit, :max_retries - end - - self.redirect_limit = Bundler.settings[:redirect] # How many redirects to allow in one request - self.api_timeout = Bundler.settings[:timeout] # How long to wait for each API call - self.max_retries = Bundler.settings[:retry] # How many retries for the API call - - def initialize(remote) - @remote = remote - - Socket.do_not_reverse_lookup = true - connection # create persistent connection - end - - def uri - @remote.anonymized_uri - end - - # fetch a gem specification - def fetch_spec(spec) - spec -= [nil, "ruby", ""] - spec_file_name = "#{spec.join "-"}.gemspec" - - uri = URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz") - if uri.scheme == "file" - Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path)) - elsif cached_spec_path = gemspec_cached_path(spec_file_name) - Bundler.load_gemspec(cached_spec_path) - else - Bundler.load_marshal Gem.inflate(downloader.fetch(uri).body) - end - rescue MarshalError - raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \ - "Your network or your gem server is probably having issues right now." - end - - # return the specs in the bundler format as an index with retries - def specs_with_retry(gem_names, source) - Bundler::Retry.new("fetcher", FAIL_ERRORS).attempts do - specs(gem_names, source) - end - end - - # return the specs in the bundler format as an index - def specs(gem_names, source) - old = Bundler.rubygems.sources - index = Bundler::Index.new - - if Bundler::Fetcher.disable_endpoint - @use_api = false - specs = fetchers.last.specs(gem_names) - else - specs = [] - fetchers.shift until fetchers.first.available? || fetchers.empty? - fetchers.dup.each do |f| - break unless f.api_fetcher? && !gem_names || !specs = f.specs(gem_names) - fetchers.delete(f) - end - @use_api = false if fetchers.none?(&:api_fetcher?) - end - - specs.each do |name, version, platform, dependencies, metadata| - next if name == "bundler" - spec = if dependencies - EndpointSpecification.new(name, version, platform, dependencies, metadata) - else - RemoteSpecification.new(name, version, platform, self) - end - spec.source = source - spec.remote = @remote - index << spec - end - - index - rescue CertificateFailureError - Bundler.ui.info "" if gem_names && use_api # newline after dots - raise - ensure - Bundler.rubygems.sources = old - end - - def use_api - return @use_api if defined?(@use_api) - - fetchers.shift until fetchers.first.available? - - @use_api = if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint - false - else - fetchers.first.api_fetcher? - end - end - - def user_agent - @user_agent ||= begin - ruby = Bundler::RubyVersion.system - - agent = String.new("bundler/#{Bundler::VERSION}") - agent << " rubygems/#{Gem::VERSION}" - agent << " ruby/#{ruby.versions_string(ruby.versions)}" - agent << " (#{ruby.host})" - agent << " command/#{ARGV.first}" - - if ruby.engine != "ruby" - # engine_version raises on unknown engines - engine_version = begin - ruby.engine_versions - rescue - "???" - end - agent << " #{ruby.engine}/#{ruby.versions_string(engine_version)}" - end - - agent << " options/#{Bundler.settings.all.join(",")}" - - agent << " ci/#{cis.join(",")}" if cis.any? - - # add a random ID so we can consolidate runs server-side - agent << " " << SecureRandom.hex(8) - - # add any user agent strings set in the config - extra_ua = Bundler.settings[:user_agent] - agent << " " << extra_ua if extra_ua - - agent - end - end - - def fetchers - @fetchers ||= FETCHERS.map {|f| f.new(downloader, @remote, uri) } - end - - def http_proxy - return unless uri = connection.proxy_uri - uri.to_s - end - - def inspect - "#<#{self.class}:0x#{object_id} uri=#{uri}>" - end - - private - - FETCHERS = [CompactIndex, Dependency, Index].freeze - - def cis - env_cis = { - "TRAVIS" => "travis", - "CIRCLECI" => "circle", - "SEMAPHORE" => "semaphore", - "JENKINS_URL" => "jenkins", - "BUILDBOX" => "buildbox", - "GO_SERVER_URL" => "go", - "SNAP_CI" => "snap", - "CI_NAME" => ENV["CI_NAME"], - "CI" => "ci" - } - env_cis.find_all {|env, _| ENV[env] }.map {|_, ci| ci } - end - - def connection - @connection ||= begin - needs_ssl = remote_uri.scheme == "https" || - Bundler.settings[:ssl_verify_mode] || - Bundler.settings[:ssl_client_cert] - raise SSLError if needs_ssl && !defined?(OpenSSL::SSL) - - con = PersistentHTTP.new "bundler", :ENV - if gem_proxy = Bundler.rubygems.configuration[:http_proxy] - con.proxy = URI.parse(gem_proxy) if gem_proxy != :no_proxy - end - - if remote_uri.scheme == "https" - con.verify_mode = (Bundler.settings[:ssl_verify_mode] || - OpenSSL::SSL::VERIFY_PEER) - con.cert_store = bundler_cert_store - end - - ssl_client_cert = Bundler.settings[:ssl_client_cert] || - (Bundler.rubygems.configuration.ssl_client_cert if - Bundler.rubygems.configuration.respond_to?(:ssl_client_cert)) - if ssl_client_cert - pem = File.read(ssl_client_cert) - con.cert = OpenSSL::X509::Certificate.new(pem) - con.key = OpenSSL::PKey::RSA.new(pem) - end - - con.read_timeout = Fetcher.api_timeout - con.open_timeout = Fetcher.api_timeout - con.override_headers["User-Agent"] = user_agent - con.override_headers["X-Gemfile-Source"] = @remote.original_uri.to_s if @remote.original_uri - con - end - end - - # cached gem specification path, if one exists - def gemspec_cached_path(spec_file_name) - paths = Bundler.rubygems.spec_cache_dirs.map {|dir| File.join(dir, spec_file_name) } - paths = paths.select {|path| File.file? path } - paths.first - end - - HTTP_ERRORS = [ - Timeout::Error, EOFError, SocketError, Errno::ENETDOWN, Errno::ENETUNREACH, - Errno::EINVAL, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EAGAIN, - Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, - PersistentHTTP::Error, Zlib::BufError, Errno::EHOSTUNREACH - ].freeze - - def bundler_cert_store - store = OpenSSL::X509::Store.new - ssl_ca_cert = Bundler.settings[:ssl_ca_cert] || - (Bundler.rubygems.configuration.ssl_ca_cert if - Bundler.rubygems.configuration.respond_to?(:ssl_ca_cert)) - if ssl_ca_cert - if File.directory? ssl_ca_cert - store.add_path ssl_ca_cert - else - store.add_file ssl_ca_cert - end - else - store.set_default_paths - certs = File.expand_path("../ssl_certs/*/*.pem", __FILE__) - Dir.glob(certs).each {|c| store.add_file c } - end - store - end - - private - - def remote_uri - @remote.uri - end - - def downloader - @downloader ||= Downloader.new(connection, self.class.redirect_limit) - end - end -end diff --git a/lib/bundler/fetcher/base.rb b/lib/bundler/fetcher/base.rb deleted file mode 100644 index 27987f670a..0000000000 --- a/lib/bundler/fetcher/base.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Fetcher - class Base - attr_reader :downloader - attr_reader :display_uri - attr_reader :remote - - def initialize(downloader, remote, display_uri) - raise "Abstract class" if self.class == Base - @downloader = downloader - @remote = remote - @display_uri = display_uri - end - - def remote_uri - @remote.uri - end - - def fetch_uri - @fetch_uri ||= begin - if remote_uri.host == "rubygems.org" - uri = remote_uri.dup - uri.host = "index.rubygems.org" - uri - else - remote_uri - end - end - end - - def available? - true - end - - def api_fetcher? - false - end - - private - - def log_specs(debug_msg) - if Bundler.ui.debug? - Bundler.ui.debug debug_msg - else - Bundler.ui.info ".", false - end - end - end - end -end diff --git a/lib/bundler/fetcher/compact_index.rb b/lib/bundler/fetcher/compact_index.rb deleted file mode 100644 index cfc74d642c..0000000000 --- a/lib/bundler/fetcher/compact_index.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -require "bundler/fetcher/base" -require "bundler/worker" - -module Bundler - autoload :CompactIndexClient, "bundler/compact_index_client" - - class Fetcher - class CompactIndex < Base - def self.compact_index_request(method_name) - method = instance_method(method_name) - undef_method(method_name) - define_method(method_name) do |*args, &blk| - begin - method.bind(self).call(*args, &blk) - rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e - raise HTTPError, e.message - rescue AuthenticationRequiredError - # Fail since we got a 401 from the server. - raise - rescue HTTPError => e - Bundler.ui.trace(e) - nil - end - end - end - - def specs(gem_names) - specs_for_names(gem_names) - end - compact_index_request :specs - - def specs_for_names(gem_names) - gem_info = [] - complete_gems = [] - remaining_gems = gem_names.dup - - until remaining_gems.empty? - log_specs "Looking up gems #{remaining_gems.inspect}" - - deps = compact_index_client.dependencies(remaining_gems) - next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq - deps.each {|dep| gem_info << dep } - complete_gems.concat(deps.map(&:first)).uniq! - remaining_gems = next_gems - complete_gems - end - @bundle_worker.stop if @bundle_worker - @bundle_worker = nil # reset it. Not sure if necessary - - gem_info - end - - def fetch_spec(spec) - spec -= [nil, "ruby", ""] - contents = compact_index_client.spec(*spec) - return nil if contents.nil? - contents.unshift(spec.first) - contents[3].map! {|d| Gem::Dependency.new(*d) } - EndpointSpecification.new(*contents) - end - compact_index_request :fetch_spec - - def available? - return nil unless SharedHelpers.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 - fetch_uri.scheme != "file" && compact_index_client.update_and_parse_checksums! - rescue CompactIndexClient::Updater::MisMatchedChecksumError => e - Bundler.ui.debug(e.message) - nil - end - compact_index_request :available? - - def api_fetcher? - true - end - - private - - def compact_index_client - @compact_index_client ||= begin - SharedHelpers.filesystem_access(cache_path) do - CompactIndexClient.new(cache_path, client_fetcher) - end.tap do |client| - client.in_parallel = lambda do |inputs, &blk| - func = lambda {|object, _index| blk.call(object) } - worker = bundle_worker(func) - inputs.each {|input| worker.enq(input) } - inputs.map { worker.deq } - end - end - end - end - - def bundle_worker(func = nil) - @bundle_worker ||= begin - worker_name = "Compact Index (#{display_uri.host})" - Bundler::Worker.new(Bundler.current_ruby.rbx? ? 1 : 25, worker_name, func) - end - @bundle_worker.tap do |worker| - worker.instance_variable_set(:@func, func) if func - end - end - - def cache_path - Bundler.user_cache.join("compact_index", remote.cache_slug) - end - - def client_fetcher - ClientFetcher.new(self, Bundler.ui) - end - - ClientFetcher = Struct.new(:fetcher, :ui) do - def call(path, headers) - fetcher.downloader.fetch(fetcher.fetch_uri + path, headers) - rescue NetworkDownError => e - raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"] - ui.warn "Using the cached data for the new index because of a network error: #{e}" - Net::HTTPNotModified.new(nil, nil, nil) - end - end - end - end -end diff --git a/lib/bundler/fetcher/dependency.rb b/lib/bundler/fetcher/dependency.rb deleted file mode 100644 index 1430d1ebeb..0000000000 --- a/lib/bundler/fetcher/dependency.rb +++ /dev/null @@ -1,82 +0,0 @@ -# frozen_string_literal: true - -require "bundler/fetcher/base" -require "cgi" - -module Bundler - class Fetcher - class Dependency < Base - def available? - @available ||= fetch_uri.scheme != "file" && downloader.fetch(dependency_api_uri) - rescue NetworkDownError => e - raise HTTPError, e.message - rescue AuthenticationRequiredError - # Fail since we got a 401 from the server. - raise - rescue HTTPError - false - end - - def api_fetcher? - true - end - - def specs(gem_names, full_dependency_list = [], last_spec_list = []) - query_list = gem_names.uniq - full_dependency_list - - log_specs "Query List: #{query_list.inspect}" - - return last_spec_list if query_list.empty? - - spec_list, deps_list = Bundler::Retry.new("dependency api", FAIL_ERRORS).attempts do - dependency_specs(query_list) - end - - returned_gems = spec_list.map(&:first).uniq - specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list) - rescue MarshalError - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - Bundler.ui.debug "could not fetch from the dependency API, trying the full index" - nil - rescue HTTPError, GemspecError - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - Bundler.ui.debug "could not fetch from the dependency API\nit's suggested to retry using the full index via `bundle install --full-index`" - nil - end - - def dependency_specs(gem_names) - Bundler.ui.debug "Query Gemcutter Dependency Endpoint API: #{gem_names.join(",")}" - - gem_list = unmarshalled_dep_gems(gem_names) - get_formatted_specs_and_deps(gem_list) - end - - def unmarshalled_dep_gems(gem_names) - gem_list = [] - gem_names.each_slice(Source::Rubygems::API_REQUEST_SIZE) do |names| - marshalled_deps = downloader.fetch(dependency_api_uri(names)).body - gem_list.concat(Bundler.load_marshal(marshalled_deps)) - end - gem_list - end - - def get_formatted_specs_and_deps(gem_list) - deps_list = [] - spec_list = [] - - gem_list.each do |s| - deps_list.concat(s[:dependencies].map(&:first)) - deps = s[:dependencies].map {|n, d| [n, d.split(", ")] } - spec_list.push([s[:name], s[:number], s[:platform], deps]) - end - [spec_list, deps_list] - end - - def dependency_api_uri(gem_names = []) - uri = fetch_uri + "api/v1/dependencies" - uri.query = "gems=#{CGI.escape(gem_names.sort.join(","))}" if gem_names.any? - uri - end - end - end -end diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb deleted file mode 100644 index cbc5e220bd..0000000000 --- a/lib/bundler/fetcher/downloader.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Fetcher - class Downloader - attr_reader :connection - attr_reader :redirect_limit - - def initialize(connection, redirect_limit) - @connection = connection - @redirect_limit = redirect_limit - end - - def fetch(uri, options = {}, counter = 0) - raise HTTPError, "Too many redirects" if counter >= redirect_limit - - response = request(uri, options) - Bundler.ui.debug("HTTP #{response.code} #{response.message} #{uri}") - - case response - when Net::HTTPSuccess, Net::HTTPNotModified - response - when Net::HTTPRedirection - new_uri = URI.parse(response["location"]) - if new_uri.host == uri.host - new_uri.user = uri.user - new_uri.password = uri.password - end - fetch(new_uri, options, counter + 1) - when Net::HTTPRequestEntityTooLarge - raise FallbackError, response.body - when Net::HTTPUnauthorized - raise AuthenticationRequiredError, uri.host - when Net::HTTPNotFound - raise FallbackError, "Net::HTTPNotFound" - else - raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}" - end - end - - def request(uri, options) - validate_uri_scheme!(uri) - - Bundler.ui.debug "HTTP GET #{uri}" - req = Net::HTTP::Get.new uri.request_uri, options - if uri.user - user = CGI.unescape(uri.user) - password = uri.password ? CGI.unescape(uri.password) : nil - req.basic_auth(user, password) - end - connection.request(uri, req) - rescue NoMethodError => e - raise unless ["undefined method", "use_ssl="].all? {|snippet| e.message.include? snippet } - raise LoadError.new("cannot load such file -- openssl") - rescue OpenSSL::SSL::SSLError - raise CertificateFailureError.new(uri) - rescue *HTTP_ERRORS => e - Bundler.ui.trace e - case e.message - when /host down:/, /getaddrinfo: nodename nor servname provided/ - raise NetworkDownError, "Could not reach host #{uri.host}. Check your network " \ - "connection and try again." - else - raise HTTPError, "Network error while fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" \ - " (#{e})" - end - end - - private - - def validate_uri_scheme!(uri) - return if uri.scheme =~ /\Ahttps?\z/ - raise InvalidOption, - "The request uri `#{uri}` has an invalid scheme (`#{uri.scheme}`). " \ - "Did you mean `http` or `https`?" - end - end - end -end diff --git a/lib/bundler/fetcher/index.rb b/lib/bundler/fetcher/index.rb deleted file mode 100644 index 9529944391..0000000000 --- a/lib/bundler/fetcher/index.rb +++ /dev/null @@ -1,52 +0,0 @@ -# frozen_string_literal: true - -require "bundler/fetcher/base" -require "rubygems/remote_fetcher" - -module Bundler - class Fetcher - class Index < Base - def specs(_gem_names) - Bundler.rubygems.fetch_all_remote_specs(remote) - rescue Gem::RemoteFetcher::FetchError, OpenSSL::SSL::SSLError, Net::HTTPFatalError => e - case e.message - when /certificate verify failed/ - raise CertificateFailureError.new(display_uri) - when /401/ - raise AuthenticationRequiredError, remote_uri - when /403/ - raise BadAuthenticationError, remote_uri if remote_uri.userinfo - raise AuthenticationRequiredError, remote_uri - else - Bundler.ui.trace e - raise HTTPError, "Could not fetch specs from #{display_uri}" - end - end - - def fetch_spec(spec) - spec -= [nil, "ruby", ""] - spec_file_name = "#{spec.join "-"}.gemspec" - - uri = URI.parse("#{remote_uri}#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}.rz") - if uri.scheme == "file" - Bundler.load_marshal Gem.inflate(Gem.read_binary(uri.path)) - elsif cached_spec_path = gemspec_cached_path(spec_file_name) - Bundler.load_gemspec(cached_spec_path) - else - Bundler.load_marshal Gem.inflate(downloader.fetch(uri).body) - end - rescue MarshalError - raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \ - "Your network or your gem server is probably having issues right now." - end - - private - - # cached gem specification path, if one exists - def gemspec_cached_path(spec_file_name) - paths = Bundler.rubygems.spec_cache_dirs.map {|dir| File.join(dir, spec_file_name) } - paths.find {|path| File.file? path } - end - end - end -end diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb deleted file mode 100644 index f624185773..0000000000 --- a/lib/bundler/friendly_errors.rb +++ /dev/null @@ -1,129 +0,0 @@ -# encoding: utf-8 -# frozen_string_literal: true - -require "cgi" -require "bundler/vendored_thor" - -module Bundler - module FriendlyErrors - module_function - - def log_error(error) - case error - when YamlSyntaxError - Bundler.ui.error error.message - Bundler.ui.trace error.orig_exception - when Dsl::DSLError, GemspecError - Bundler.ui.error error.message - when GemRequireError - Bundler.ui.error error.message - Bundler.ui.trace error.orig_exception, nil, true - when BundlerError - Bundler.ui.error error.message, :wrap => true - Bundler.ui.trace error - when Thor::Error - Bundler.ui.error error.message - when LoadError - raise error unless error.message =~ /cannot load such file -- openssl|openssl.so|libcrypto.so/ - Bundler.ui.error "\nCould not load OpenSSL." - Bundler.ui.warn <<-WARN, :wrap => true - You must recompile Ruby with OpenSSL support or change the sources in your \ - Gemfile from 'https' to 'http'. Instructions for compiling with OpenSSL \ - using RVM are available at http://rvm.io/packages/openssl. - WARN - Bundler.ui.trace error - when Interrupt - Bundler.ui.error "\nQuitting..." - Bundler.ui.trace error - when Gem::InvalidSpecificationException - Bundler.ui.error error.message, :wrap => true - when SystemExit - when *[defined?(Java::JavaLang::OutOfMemoryError) && Java::JavaLang::OutOfMemoryError].compact - Bundler.ui.error "\nYour JVM has run out of memory, and Bundler cannot continue. " \ - "You can decrease the amount of memory Bundler needs by removing gems from your Gemfile, " \ - "especially large gems. (Gems can be as large as hundreds of megabytes, and Bundler has to read those files!). " \ - "Alternatively, you can increase the amount of memory the JVM is able to use by running Bundler with jruby -J-Xmx1024m -S bundle (JRuby defaults to 500MB)." - else request_issue_report_for(error) - end - end - - def exit_status(error) - case error - when BundlerError then error.status_code - when Thor::Error then 15 - when SystemExit then error.status - else 1 - end - end - - def request_issue_report_for(e) - Bundler.ui.info <<-EOS.gsub(/^ {8}/, "") - --- ERROR REPORT TEMPLATE ------------------------------------------------------- - # Error Report - - ## Questions - - Please fill out answers to these questions, it'll help us figure out - why things are going wrong. - - - **What did you do?** - - I ran the command `#{$PROGRAM_NAME} #{ARGV.join(" ")}` - - - **What did you expect to happen?** - - I expected Bundler to... - - - **What happened instead?** - - Instead, what happened was... - - - **Have you tried any solutions posted on similar issues in our issue tracker, stack overflow, or google?** - - I tried... - - - **Have you read our issues document, https://github.com/bundler/bundler/blob/master/doc/contributing/ISSUES.md?** - - ... - - ## Backtrace - - ``` - #{e.class}: #{e.message} - #{e.backtrace && e.backtrace.join("\n ").chomp} - ``` - - #{Bundler::Env.report} - --- TEMPLATE END ---------------------------------------------------------------- - - EOS - - Bundler.ui.error "Unfortunately, an unexpected error occurred, and Bundler cannot continue." - - Bundler.ui.warn <<-EOS.gsub(/^ {8}/, "") - - First, try this link to see if there are any existing issue reports for this error: - #{issues_url(e)} - - If there aren't any reports for this error yet, please create copy and paste the report template above into a new issue. Don't forget to anonymize any private data! The new issue form is located at: - https://github.com/bundler/bundler/issues/new - EOS - end - - def issues_url(exception) - message = exception.message.lines.first.tr(":", " ").chomp - message = message.split("-").first if exception.is_a?(Errno) - "https://github.com/bundler/bundler/search?q=" \ - "#{CGI.escape(message)}&type=Issues" - end - end - - def self.with_friendly_errors - yield - rescue SignalException - raise - rescue Exception => e - FriendlyErrors.log_error(e) - exit FriendlyErrors.exit_status(e) - end -end diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb deleted file mode 100644 index 1d7fc508d5..0000000000 --- a/lib/bundler/gem_helper.rb +++ /dev/null @@ -1,202 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_thor" unless defined?(Thor) -require "bundler" - -module Bundler - class GemHelper - include Rake::DSL if defined? Rake::DSL - - class << self - # set when install'd. - attr_accessor :instance - - def install_tasks(opts = {}) - new(opts[:dir], opts[:name]).install - end - - def gemspec(&block) - gemspec = instance.gemspec - block.call(gemspec) if block - gemspec - end - end - - attr_reader :spec_path, :base, :gemspec - - def initialize(base = nil, name = nil) - Bundler.ui = UI::Shell.new - @base = (base ||= SharedHelpers.pwd) - gemspecs = name ? [File.join(base, "#{name}.gemspec")] : Dir[File.join(base, "{,*}.gemspec")] - raise "Unable to determine name from existing gemspec. Use :name => 'gemname' in #install_tasks to manually set it." unless gemspecs.size == 1 - @spec_path = gemspecs.first - @gemspec = Bundler.load_gemspec(@spec_path) - end - - def install - built_gem_path = nil - - desc "Build #{name}-#{version}.gem into the pkg directory." - task "build" do - built_gem_path = build_gem - end - - desc "Build and install #{name}-#{version}.gem into system gems." - task "install" => "build" do - install_gem(built_gem_path) - end - - desc "Build and install #{name}-#{version}.gem into system gems without network access." - task "install:local" => "build" do - install_gem(built_gem_path, :local) - end - - desc "Create tag #{version_tag} and build and push #{name}-#{version}.gem to #{gem_push_host}\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 - - task "release:guard_clean" do - guard_clean - end - - task "release:source_control_push", [:remote] do |_, args| - tag_version { git_push(args[:remote]) } unless already_tagged? - end - - task "release:rubygem_push" do - rubygem_push(built_gem_path) if gem_push? - end - - GemHelper.instance = self - end - - def build_gem - file_name = nil - sh("gem build -V '#{spec_path}'") do - file_name = File.basename(built_gem_path) - SharedHelpers.filesystem_access(File.join(base, "pkg")) {|p| FileUtils.mkdir_p(p) } - FileUtils.mv(built_gem_path, "pkg") - Bundler.ui.confirm "#{name} #{version} built to pkg/#{file_name}." - end - File.join(base, "pkg", file_name) - end - - def install_gem(built_gem_path = nil, local = false) - built_gem_path ||= build_gem - out, _ = sh_with_code("gem install '#{built_gem_path}'#{" --local" if local}") - raise "Couldn't install gem, run `gem install #{built_gem_path}' for more detailed output" unless out[/Successfully installed/] - Bundler.ui.confirm "#{name} (#{version}) installed." - end - - protected - - def rubygem_push(path) - gem_command = "gem push '#{path}'" - gem_command += " --key #{gem_key}" if gem_key - gem_command += " --host #{allowed_push_host}" if allowed_push_host - 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 #{gem_push_host}" - end - - def built_gem_path - Dir[File.join(base, "#{name}-*.gem")].sort_by {|f| File.mtime(f) }.last - end - - def git_push(remote = "") - perform_git_push remote - perform_git_push "#{remote} --tags" - Bundler.ui.confirm "Pushed git commits and tags." - end - - def allowed_push_host - @gemspec.metadata["allowed_push_host"] if @gemspec.respond_to?(:metadata) - end - - def gem_push_host - env_rubygems_host = ENV["RUBYGEMS_HOST"] - env_rubygems_host = nil if - env_rubygems_host && env_rubygems_host.empty? - - allowed_push_host || env_rubygems_host || "rubygems.org" - end - - def perform_git_push(options = "") - cmd = "git push #{options}" - out, code = sh_with_code(cmd) - raise "Couldn't git push. `#{cmd}' failed with the following output:\n\n#{out}\n" unless code == 0 - end - - def already_tagged? - return false unless sh("git tag").split(/\n/).include?(version_tag) - Bundler.ui.confirm "Tag #{version_tag} has already been created." - true - end - - def guard_clean - clean? && committed? || raise("There are files that need to be committed first.") - end - - def clean? - sh_with_code("git diff --exit-code")[1] == 0 - end - - def committed? - sh_with_code("git diff-index --quiet --cached HEAD")[1] == 0 - end - - def tag_version - sh "git tag -m \"Version #{version}\" #{version_tag}" - Bundler.ui.confirm "Tagged #{version_tag}." - yield if block_given? - rescue - Bundler.ui.error "Untagging #{version_tag} due to error." - sh_with_code "git tag -d #{version_tag}" - raise - end - - def version - gemspec.version - end - - def version_tag - "v#{version}" - end - - def name - gemspec.name - end - - def sh(cmd, &block) - out, code = sh_with_code(cmd, &block) - unless code.zero? - raise(out.empty? ? "Running `#{cmd}` failed. Run this command directly for more detailed output." : out) - end - out - end - - def sh_with_code(cmd, &block) - cmd += " 2>&1" - outbuf = String.new - Bundler.ui.debug(cmd) - SharedHelpers.chdir(base) do - outbuf = `#{cmd}` - status = $?.exitstatus - block.call(outbuf) if status.zero? && block - [outbuf, status] - end - end - - def gem_key - Bundler.settings["gem.push_key"].to_s.downcase if Bundler.settings["gem.push_key"] - end - - def gem_push? - !%w[n no nil false off 0].include?(ENV["gem_push"].to_s.downcase) - end - end -end diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb deleted file mode 100644 index 019ae10c66..0000000000 --- a/lib/bundler/gem_helpers.rb +++ /dev/null @@ -1,101 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module GemHelpers - GENERIC_CACHE = {} # rubocop:disable MutableConstant - GENERICS = [ - [Gem::Platform.new("java"), Gem::Platform.new("java")], - [Gem::Platform.new("mswin32"), Gem::Platform.new("mswin32")], - [Gem::Platform.new("mswin64"), Gem::Platform.new("mswin64")], - [Gem::Platform.new("universal-mingw32"), Gem::Platform.new("universal-mingw32")], - [Gem::Platform.new("x64-mingw32"), Gem::Platform.new("x64-mingw32")], - [Gem::Platform.new("x86_64-mingw32"), Gem::Platform.new("x64-mingw32")], - [Gem::Platform.new("mingw32"), Gem::Platform.new("x86-mingw32")] - ].freeze - - def generic(p) - return p if p == Gem::Platform::RUBY - - GENERIC_CACHE[p] ||= begin - _, found = GENERICS.find do |match, _generic| - p.os == match.os && (!match.cpu || p.cpu == match.cpu) - end - found || Gem::Platform::RUBY - end - end - module_function :generic - - def generic_local_platform - generic(Bundler.local_platform) - end - module_function :generic_local_platform - - def platform_specificity_match(spec_platform, user_platform) - spec_platform = Gem::Platform.new(spec_platform) - return PlatformMatch::EXACT_MATCH if spec_platform == user_platform - return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY - - PlatformMatch.new( - PlatformMatch.os_match(spec_platform, user_platform), - PlatformMatch.cpu_match(spec_platform, user_platform), - PlatformMatch.platform_version_match(spec_platform, user_platform) - ) - end - module_function :platform_specificity_match - - def select_best_platform_match(specs, platform) - specs.select {|spec| spec.match_platform(platform) }. - min_by {|spec| platform_specificity_match(spec.platform, platform) } - end - module_function :select_best_platform_match - - PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match) - class PlatformMatch - def <=>(other) - return nil unless other.is_a?(PlatformMatch) - - m = os_match <=> other.os_match - return m unless m.zero? - - m = cpu_match <=> other.cpu_match - return m unless m.zero? - - m = platform_version_match <=> other.platform_version_match - m - end - - EXACT_MATCH = new(-1, -1, -1).freeze - WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze - - def self.os_match(spec_platform, user_platform) - if spec_platform.os == user_platform.os - 0 - else - 1 - end - end - - def self.cpu_match(spec_platform, user_platform) - if spec_platform.cpu == user_platform.cpu - 0 - elsif spec_platform.cpu == "arm" && user_platform.cpu.to_s.start_with?("arm") - 0 - elsif spec_platform.cpu.nil? || spec_platform.cpu == "universal" - 1 - else - 2 - end - end - - def self.platform_version_match(spec_platform, user_platform) - if spec_platform.version == user_platform.version - 0 - elsif spec_platform.version.nil? - 1 - else - 2 - end - end - end - end -end diff --git a/lib/bundler/gem_remote_fetcher.rb b/lib/bundler/gem_remote_fetcher.rb deleted file mode 100644 index 9577535d63..0000000000 --- a/lib/bundler/gem_remote_fetcher.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require "rubygems/remote_fetcher" - -module Bundler - # Adds support for setting custom HTTP headers when fetching gems from the - # server. - # - # TODO: Get rid of this when and if gemstash only supports RubyGems versions - # that contain https://github.com/rubygems/rubygems/commit/3db265cc20b2f813. - class GemRemoteFetcher < Gem::RemoteFetcher - attr_accessor :headers - - # Extracted from RubyGems 2.4. - def fetch_http(uri, last_modified = nil, head = false, depth = 0) - fetch_type = head ? Net::HTTP::Head : Net::HTTP::Get - # beginning of change - response = request uri, fetch_type, last_modified do |req| - headers.each {|k, v| req.add_field(k, v) } if headers - end - # end of change - - case response - when Net::HTTPOK, Net::HTTPNotModified then - response.uri = uri if response.respond_to? :uri - head ? response : response.body - when Net::HTTPMovedPermanently, Net::HTTPFound, Net::HTTPSeeOther, - Net::HTTPTemporaryRedirect then - raise FetchError.new("too many redirects", uri) if depth > 10 - - location = URI.parse response["Location"] - - if https?(uri) && !https?(location) - raise FetchError.new("redirecting to non-https resource: #{location}", uri) - end - - fetch_http(location, last_modified, head, depth + 1) - else - raise FetchError.new("bad response #{response.message} #{response.code}", uri) - end - end - end -end diff --git a/lib/bundler/gem_tasks.rb b/lib/bundler/gem_tasks.rb deleted file mode 100644 index f736517bd7..0000000000 --- a/lib/bundler/gem_tasks.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -require "rake/clean" -CLOBBER.include "pkg" - -require "bundler/gem_helper" -Bundler::GemHelper.install_tasks diff --git a/lib/bundler/gem_version_promoter.rb b/lib/bundler/gem_version_promoter.rb deleted file mode 100644 index 52b5386045..0000000000 --- a/lib/bundler/gem_version_promoter.rb +++ /dev/null @@ -1,176 +0,0 @@ -# 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 (patch, minor, major). - # Primarily designed to work with Resolver which will provide it the list of - # available dependency versions as found in its index, before returning it to - # to the resolution engine to select the best version. - class GemVersionPromoter - attr_reader :level, :locked_specs, :unlock_gems - - # By default, strict is false, meaning every available version of a gem - # is returned from sort_versions. The order gives preference to the - # requested level (:patch, :minor, :major) but in complicated requirement - # cases some gems will by necessity by promoted past the requested level, - # or even reverted to older versions. - # - # If strict is set to true, the results from sort_versions will be - # truncated, eliminating any version outside the current level scope. - # This can lead to unexpected outcomes or even VersionConflict exceptions - # that report a version of a gem not existing for versions that indeed do - # existing in the referenced source. - attr_accessor :strict - - # Given a list of locked_specs and a list of gems to unlock creates a - # GemVersionPromoter instance. - # - # @param locked_specs [SpecSet] All current locked specs. Unlike Definition - # where this list is empty if all gems are being updated, this should - # always be populated for all gems so this class can properly function. - # @param unlock_gems [String] List of gem names being unlocked. If empty, - # all gems will be considered unlocked. - # @return [GemVersionPromoter] - def initialize(locked_specs = SpecSet.new([]), unlock_gems = []) - @level = :major - @strict = false - @locked_specs = locked_specs - @unlock_gems = unlock_gems - @sort_versions = {} - end - - # @param value [Symbol] One of three Symbols: :major, :minor or :patch. - def level=(value) - v = case value - when String, Symbol - value.to_sym - end - - raise ArgumentError, "Unexpected level #{v}. Must be :major, :minor or :patch" unless [:major, :minor, :patch].include?(v) - @level = v - end - - # Given a Dependency and an Array of SpecGroups of available versions for a - # gem, this method will return the Array of SpecGroups sorted (and possibly - # truncated if strict is true) in an order to give preference to the current - # level (:major, :minor or :patch) when resolution is deciding what versions - # best resolve all dependencies in the bundle. - # @param dep [Dependency] The Dependency of the gem. - # @param spec_groups [SpecGroup] An array of SpecGroups for the same gem - # named in the @dep param. - # @return [SpecGroup] A new instance of the SpecGroup Array sorted and - # possibly filtered. - def sort_versions(dep, spec_groups) - before_result = "before sort_versions: #{debug_format_result(dep, spec_groups).inspect}" if ENV["DEBUG_RESOLVER"] - - @sort_versions[dep] ||= begin - gem_name = dep.name - - # An Array per version returned, different entries for different platforms. - # We only need the version here so it's ok to hard code this to the first instance. - locked_spec = locked_specs[gem_name].first - - if strict - filter_dep_specs(spec_groups, locked_spec) - else - sort_dep_specs(spec_groups, locked_spec) - end.tap do |specs| - if ENV["DEBUG_RESOLVER"] - STDERR.puts before_result - STDERR.puts " after sort_versions: #{debug_format_result(dep, specs).inspect}" - end - end - end - end - - # @return [bool] Convenience method for testing value of level variable. - def major? - level == :major - end - - # @return [bool] Convenience method for testing value of level variable. - def minor? - level == :minor - end - - private - - def filter_dep_specs(spec_groups, locked_spec) - res = spec_groups.select do |spec_group| - if locked_spec && !major? - gsv = spec_group.version - lsv = locked_spec.version - - must_match = minor? ? [0] : [0, 1] - - matches = must_match.map {|idx| gsv.segments[idx] == lsv.segments[idx] } - (matches.uniq == [true]) ? (gsv >= lsv) : false - else - true - end - end - - sort_dep_specs(res, locked_spec) - end - - def sort_dep_specs(spec_groups, locked_spec) - return spec_groups unless locked_spec - @gem_name = locked_spec.name - @locked_version = locked_spec.version - - result = spec_groups.sort do |a, b| - @a_ver = a.version - @b_ver = b.version - if major? - @a_ver <=> @b_ver - elsif either_version_older_than_locked - @a_ver <=> @b_ver - elsif segments_do_not_match(:major) - @b_ver <=> @a_ver - elsif !minor? && segments_do_not_match(:minor) - @b_ver <=> @a_ver - else - @a_ver <=> @b_ver - end - end - post_sort(result) - end - - def either_version_older_than_locked - @a_ver < @locked_version || @b_ver < @locked_version - end - - def segments_do_not_match(level) - index = [:major, :minor].index(level) - @a_ver.segments[index] != @b_ver.segments[index] - end - - def unlocking_gem? - unlock_gems.empty? || unlock_gems.include?(@gem_name) - end - - # Specific version moves can't always reliably be done during sorting - # as not all elements are compared against each other. - def post_sort(result) - # default :major behavior in Bundler does not do this - return result if major? - if unlocking_gem? - result - else - move_version_to_end(result, @locked_version) - end - end - - def move_version_to_end(result, version) - move, keep = result.partition {|s| s.version.to_s == version.to_s } - keep.concat(move) - end - - def debug_format_result(dep, spec_groups) - a = [dep.to_s, - spec_groups.map {|sg| [sg.version, sg.dependencies_for_activated_platforms.map {|dp| [dp.name, dp.requirement.to_s] }] }] - last_map = a.last.map {|sg_data| [sg_data.first.version, sg_data.last.map {|aa| aa.join(" ") }] } - [a.first, last_map, level, strict ? :strict : :not_strict] - end - end -end diff --git a/lib/bundler/gemdeps.rb b/lib/bundler/gemdeps.rb deleted file mode 100644 index cd4b25d0e6..0000000000 --- a/lib/bundler/gemdeps.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Gemdeps - def initialize(runtime) - @runtime = runtime - end - - def requested_specs - @runtime.requested_specs - end - - def specs - @runtime.specs - end - - def dependencies - @runtime.dependencies - end - - def current_dependencies - @runtime.current_dependencies - end - - def requires - @runtime.requires - end - end -end diff --git a/lib/bundler/graph.rb b/lib/bundler/graph.rb deleted file mode 100644 index de6bba0214..0000000000 --- a/lib/bundler/graph.rb +++ /dev/null @@ -1,152 +0,0 @@ -# frozen_string_literal: true - -require "set" -module Bundler - class Graph - GRAPH_NAME = :Gemfile - - def initialize(env, output_file, show_version = false, show_requirements = false, output_format = "png", without = []) - @env = env - @output_file = output_file - @show_version = show_version - @show_requirements = show_requirements - @output_format = output_format - @without_groups = without.map(&:to_sym) - - @groups = [] - @relations = Hash.new {|h, k| h[k] = Set.new } - @node_options = {} - @edge_options = {} - - _populate_relations - end - - attr_reader :groups, :relations, :node_options, :edge_options, :output_file, :output_format - - def viz - GraphVizClient.new(self).run - end - - private - - def _populate_relations - parent_dependencies = _groups.values.to_set.flatten - loop do - break if parent_dependencies.empty? - - tmp = Set.new - parent_dependencies.each do |dependency| - child_dependencies = spec_for_dependency(dependency).runtime_dependencies.to_set - @relations[dependency.name] += child_dependencies.map(&:name).to_set - tmp += child_dependencies - - @node_options[dependency.name] = _make_label(dependency, :node) - child_dependencies.each do |c_dependency| - @edge_options["#{dependency.name}_#{c_dependency.name}"] = _make_label(c_dependency, :edge) - end - end - parent_dependencies = tmp - end - end - - def _groups - relations = Hash.new {|h, k| h[k] = Set.new } - @env.current_dependencies.each do |dependency| - dependency.groups.each do |group| - next if @without_groups.include?(group) - - relations[group.to_s].add(dependency) - @relations[group.to_s].add(dependency.name) - - @node_options[group.to_s] ||= _make_label(group, :node) - @edge_options["#{group}_#{dependency.name}"] = _make_label(dependency, :edge) - end - end - @groups = relations.keys - relations - end - - def _make_label(symbol_or_string_or_dependency, element_type) - case element_type.to_sym - when :node - if symbol_or_string_or_dependency.is_a?(Gem::Dependency) - label = symbol_or_string_or_dependency.name.dup - label << "\n#{spec_for_dependency(symbol_or_string_or_dependency).version}" if @show_version - else - label = symbol_or_string_or_dependency.to_s - end - when :edge - label = nil - if symbol_or_string_or_dependency.respond_to?(:requirements_list) && @show_requirements - tmp = symbol_or_string_or_dependency.requirements_list.join(", ") - label = tmp if tmp != ">= 0" - end - else - raise ArgumentError, "2nd argument is invalid" - end - label.nil? ? {} : { :label => label } - end - - def spec_for_dependency(dependency) - @env.requested_specs.find {|s| s.name == dependency.name } - end - - class GraphVizClient - def initialize(graph_instance) - @graph_name = graph_instance.class::GRAPH_NAME - @groups = graph_instance.groups - @relations = graph_instance.relations - @node_options = graph_instance.node_options - @edge_options = graph_instance.edge_options - @output_file = graph_instance.output_file - @output_format = graph_instance.output_format - end - - def g - @g ||= ::GraphViz.digraph(@graph_name, :concentrate => true, :normalize => true, :nodesep => 0.55) do |g| - g.edge[:weight] = 2 - g.edge[:fontname] = g.node[:fontname] = "Arial, Helvetica, SansSerif" - g.edge[:fontsize] = 12 - end - end - - def run - @groups.each do |group| - g.add_nodes( - group, { - :style => "filled", - :fillcolor => "#B9B9D5", - :shape => "box3d", - :fontsize => 16 - }.merge(@node_options[group]) - ) - end - - @relations.each do |parent, children| - children.each do |child| - if @groups.include?(parent) - g.add_nodes(child, { :style => "filled", :fillcolor => "#B9B9D5" }.merge(@node_options[child])) - g.add_edges(parent, child, { :constraint => false }.merge(@edge_options["#{parent}_#{child}"])) - else - g.add_nodes(child, @node_options[child]) - g.add_edges(parent, child, @edge_options["#{parent}_#{child}"]) - end - end - end - - if @output_format.to_s == "debug" - $stdout.puts g.output :none => String - Bundler.ui.info "debugging bundle viz..." - else - begin - g.output @output_format.to_sym => "#{@output_file}.#{@output_format}" - Bundler.ui.info "#{@output_file}.#{@output_format}" - rescue ArgumentError => e - $stderr.puts "Unsupported output format. See Ruby-Graphviz/lib/graphviz/constants.rb" - raise e - end - end - end - end - end -end diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb deleted file mode 100644 index 9166a92738..0000000000 --- a/lib/bundler/index.rb +++ /dev/null @@ -1,213 +0,0 @@ -# frozen_string_literal: true - -require "set" - -module Bundler - class Index - include Enumerable - - def self.build - i = new - yield i - i - end - - attr_reader :specs, :all_specs, :sources - protected :specs, :all_specs - - RUBY = "ruby".freeze - NULL = "\0".freeze - - def initialize - @sources = [] - @cache = {} - @specs = Hash.new {|h, k| h[k] = {} } - @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH } - end - - def initialize_copy(o) - @sources = o.sources.dup - @cache = {} - @specs = Hash.new {|h, k| h[k] = {} } - @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH } - - o.specs.each do |name, hash| - @specs[name] = hash.dup - end - o.all_specs.each do |name, array| - @all_specs[name] = array.dup - end - end - - def inspect - "#<#{self.class}:0x#{object_id} sources=#{sources.map(&:inspect)} specs.size=#{specs.size}>" - end - - def empty? - each { return false } - true - end - - def search_all(name) - all_matches = local_search(name) + @all_specs[name] - @sources.each do |source| - all_matches.concat(source.search_all(name)) - end - all_matches - end - - # Search this index's specs, and any source indexes that this index knows - # about, returning all of the results. - def search(query, base = nil) - sort_specs(unsorted_search(query, base)) - end - - def unsorted_search(query, base) - results = local_search(query, base) - - seen = results.map(&:full_name).to_set unless @sources.empty? - - @sources.each do |source| - source.unsorted_search(query, base).each do |spec| - results << spec if seen.add?(spec.full_name) - end - end - - results - end - protected :unsorted_search - - def self.sort_specs(specs) - specs.sort_by do |s| - platform_string = s.platform.to_s - [s.version, platform_string == RUBY ? NULL : platform_string] - end - end - - def sort_specs(specs) - self.class.sort_specs(specs) - end - - def local_search(query, base = nil) - case query - when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query) - when String then specs_by_name(query) - when Gem::Dependency then search_by_dependency(query, base) - when DepProxy then search_by_dependency(query.dep, base) - else - raise "You can't search for a #{query.inspect}." - end - end - - alias_method :[], :search - - def <<(spec) - @specs[spec.name][spec.full_name] = spec - spec - end - - def each(&blk) - return enum_for(:each) unless blk - specs.values.each do |spec_sets| - spec_sets.values.each(&blk) - end - sources.each {|s| s.each(&blk) } - self - end - - def spec_names - names = specs.keys + sources.map(&:spec_names) - names.uniq! - names - end - - # returns a list of the dependencies - def unmet_dependency_names - dependency_names.select do |name| - name != "bundler" && search(name).empty? - end - end - - def dependency_names - names = [] - each do |spec| - spec.dependencies.each do |dep| - next if dep.type == :development - names << dep.name - end - end - names.uniq - end - - def use(other, override_dupes = false) - return unless other - other.each do |s| - if (dupes = search_by_spec(s)) && !dupes.empty? - # safe to << since it's a new array when it has contents - @all_specs[s.name] = dupes << s - next unless override_dupes - end - self << s - end - self - end - - def size - @sources.inject(@specs.size) do |size, source| - size += source.size - end - end - - # Whether all the specs in self are in other - # TODO: rename to #include? - def ==(other) - all? do |spec| - other_spec = other[spec].first - other_spec && dependencies_eql?(spec, other_spec) && spec.source == other_spec.source - end - end - - def dependencies_eql?(spec, other_spec) - deps = spec.dependencies.select {|d| d.type != :development } - other_deps = other_spec.dependencies.select {|d| d.type != :development } - Set.new(deps) == Set.new(other_deps) - end - - def add_source(index) - raise ArgumentError, "Source must be an index, not #{index.class}" unless index.is_a?(Index) - @sources << index - @sources.uniq! # need to use uniq! here instead of checking for the item before adding - end - - private - - def specs_by_name(name) - @specs[name].values - end - - def search_by_dependency(dependency, base = nil) - @cache[base || false] ||= {} - @cache[base || false][dependency] ||= begin - specs = specs_by_name(dependency.name) - specs += base if base - found = specs.select do |spec| - next true if spec.source.is_a?(Source::Gemspec) - if base # allow all platforms when searching from a lockfile - dependency.matches_spec?(spec) - else - dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform) - end - end - - found - end - end - - EMPTY_SEARCH = [].freeze - - def search_by_spec(spec) - spec = @specs[spec.name][spec.full_name] - spec ? [spec] : EMPTY_SEARCH - end - end -end diff --git a/lib/bundler/injector.rb b/lib/bundler/injector.rb deleted file mode 100644 index 7fe6a91ddd..0000000000 --- a/lib/bundler/injector.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Injector - def self.inject(new_deps, options = {}) - injector = new(new_deps, options) - injector.inject(Bundler.default_gemfile, Bundler.default_lockfile) - end - - def initialize(new_deps, options = {}) - @new_deps = new_deps - @options = options - end - - def inject(gemfile_path, lockfile_path) - if Bundler.frozen? - # ensure the lock and Gemfile are synced - Bundler.definition.ensure_equivalent_gemfile_and_lockfile(true) - end - - # temporarily unfreeze - Bundler.settings.temporary(:deployment => false, :frozen => false) do - # 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 versioning 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) - - # invalidate the cached Bundler.definition - Bundler.reset_paths! - - # return an array of the deps that we added - @new_deps - end - end - - private - - def conservative_version(spec) - version = spec.version - return ">= 0" if version.nil? - segments = version.segments - seg_end_index = version >= Gem::Version.new("1.0") ? 1 : 2 - - prerelease_suffix = version.to_s.gsub(version.release.to_s, "") if version.prerelease? - "~> #{segments[0..seg_end_index].join(".")}#{prerelease_suffix}" - end - - def build_gem_lines(conservative_versioning) - @new_deps.map do |d| - name = d.name.dump - - requirement = if conservative_versioning - ", \"#{conservative_version(@definition.specs[d.name][0])}\"" - else - ", #{d.requirement.as_list.map(&:dump).join(", ")}" - end - - if d.groups != Array(:default) - group = d.groups.size == 1 ? ", :group => #{d.groups.inspect}" : ", :groups => #{d.groups.inspect}" - end - - source = ", :source => \"#{d.source}\"" unless d.source.nil? - - %(gem #{name}#{requirement}#{group}#{source}) - end.join("\n") - end - - def append_to(gemfile_path, new_gem_lines) - gemfile_path.open("a") do |f| - f.puts - if @options["timestamp"] || @options["timestamp"].nil? - f.puts "# Added at #{Time.now} by #{`whoami`.chomp}:" - end - f.puts new_gem_lines - end - end - end -end diff --git a/lib/bundler/inline.rb b/lib/bundler/inline.rb deleted file mode 100644 index 9d25f3261a..0000000000 --- a/lib/bundler/inline.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require "bundler/compatibility_guard" - -# Allows for declaring a Gemfile inline in a ruby script, optionally installing -# any gems that aren't already installed on the user's system. -# -# @note Every gem that is specified in this 'Gemfile' will be `require`d, as if -# the user had manually called `Bundler.require`. To avoid a requested gem -# being automatically required, add the `:require => false` option to the -# `gem` dependency declaration. -# -# @param install [Boolean] whether gems that aren't already installed on the -# user's system should be installed. -# Defaults to `false`. -# -# @param gemfile [Proc] a block that is evaluated as a `Gemfile`. -# -# @example Using an inline Gemfile -# -# #!/usr/bin/env ruby -# -# require 'bundler/inline' -# -# gemfile do -# source 'https://rubygems.org' -# gem 'json', require: false -# gem 'nap', require: 'rest' -# gem 'cocoapods', '~> 0.34.1' -# end -# -# puts Pod::VERSION # => "0.34.4" -# -def gemfile(install = false, options = {}, &gemfile) - require "bundler" - - opts = options.dup - ui = opts.delete(:ui) { Bundler::UI::Shell.new } - raise ArgumentError, "Unknown options: #{opts.keys.join(", ")}" unless opts.empty? - - old_root = Bundler.method(:root) - def Bundler.root - Bundler::SharedHelpers.pwd.expand_path - end - Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", "Gemfile" - - Bundler::Plugin.gemfile_install(&gemfile) if Bundler.feature_flag.plugins? - builder = Bundler::Dsl.new - builder.instance_eval(&gemfile) - - definition = builder.to_definition(nil, true) - def definition.lock(*); end - definition.validate_runtime! - - missing_specs = proc do - definition.missing_specs? - end - - Bundler.ui = ui if install - if install || missing_specs.call - Bundler.settings.temporary(:inline => true) do - installer = Bundler::Installer.install(Bundler.root, definition, :system => true) - installer.post_install_messages.each do |name, message| - Bundler.ui.info "Post-install message from #{name}:\n#{message}" - end - end - end - - runtime = Bundler::Runtime.new(nil, definition) - runtime.setup.require -ensure - bundler_module = class << Bundler; self; end - bundler_module.send(:define_method, :root, old_root) if old_root -end diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb deleted file mode 100644 index d1066c9c19..0000000000 --- a/lib/bundler/installer.rb +++ /dev/null @@ -1,287 +0,0 @@ -# frozen_string_literal: true - -require "erb" -require "rubygems/dependency_installer" -require "bundler/worker" -require "bundler/installer/parallel_installer" -require "bundler/installer/standalone" -require "bundler/installer/gem_installer" - -module Bundler - class Installer - class << self - attr_accessor :ambiguous_gems - - Installer.ambiguous_gems = [] - end - - attr_reader :post_install_messages - - # Begins the installation process for Bundler. - # For more information see the #run method on this class. - def self.install(root, definition, options = {}) - installer = new(root, definition) - Plugin.hook("before-install-all", definition.dependencies) - installer.run(options) - installer - end - - def initialize(root, definition) - @root = root - @definition = definition - @post_install_messages = {} - end - - # Runs the install procedures for a specific Gemfile. - # - # Firstly, this method will check to see if `Bundler.bundle_path` exists - # and if not then Bundler will create the directory. This is usually the same - # location as RubyGems which typically is the `~/.gem` directory - # unless other specified. - # - # 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. - # If there are no dependencies specified then Bundler returns a warning message stating - # so and this method returns. - # - # Fourthly, Bundler checks if the Gemfile.lock exists, and if so - # then proceeds to set up a definition based on the Gemfile and the Gemfile.lock. - # During this step Bundler will also download information about any new gems - # that are not in the Gemfile.lock and resolve any dependencies if needed. - # - # 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, - # but only if the --binstubs option has been passed or Bundler.options[:bin] has been set - # earlier. - # - # 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, if the user has specified the standalone flag, Bundler will generate the needed - # require paths and save them in a `setup.rb` file. See `bundle standalone --help` for more - # information. - def run(options) - create_bundle_path - - ProcessLock.lock do - if Bundler.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 - - if resolve_if_needed(options) - ensure_specs_are_compatible! - warn_on_incompatible_bundler_deps - load_plugins - options.delete(:jobs) - else - options[:jobs] = 1 # to avoid the overhead of Bundler::Worker - end - install(options) - - lock unless Bundler.frozen? - Standalone.new(options[:standalone], @definition).generate if options[:standalone] - end - end - - def generate_bundler_executable_stubs(spec, options = {}) - if options[:binstubs_cmd] && spec.executables.empty? - options = {} - spec.runtime_dependencies.each do |dep| - bins = @definition.specs[dep].first.executables - options[dep.name] = bins unless bins.empty? - end - if options.any? - Bundler.ui.warn "#{spec.name} has no executables, but you may want " \ - "one from a gem it depends on." - options.each {|name, bins| Bundler.ui.warn " #{name} has: #{bins.join(", ")}" } - else - Bundler.ui.warn "There are no executables for the gem #{spec.name}." - end - return - end - - # double-assignment to avoid warnings about variables that will be used by ERB - bin_path = Bundler.bin_path - bin_path = bin_path - relative_gemfile_path = Bundler.default_gemfile.relative_path_from(bin_path) - relative_gemfile_path = relative_gemfile_path - ruby_command = Thor::Util.ruby_command - ruby_command = ruby_command - template_path = File.expand_path("../templates/Executable", __FILE__) - if spec.name == "bundler" - template_path += ".bundler" - spec.executables = %(bundle) - end - template = File.read(template_path) - - exists = [] - spec.executables.each do |executable| - binstub_path = "#{bin_path}/#{executable}" - if File.exist?(binstub_path) && !options[:force] - exists << executable - next - end - - File.open(binstub_path, "w", 0o777 & ~File.umask) do |f| - f.puts ERB.new(template, nil, "-").result(binding) - end - end - - if options[:binstubs_cmd] && exists.any? - case exists.size - when 1 - Bundler.ui.warn "Skipped #{exists[0]} since it already exists." - when 2 - Bundler.ui.warn "Skipped #{exists.join(" and ")} since they already exist." - else - items = exists[0...-1].empty? ? nil : exists[0...-1].join(", ") - skipped = [items, exists[-1]].compact.join(" and ") - Bundler.ui.warn "Skipped #{skipped} since they already exist." - end - Bundler.ui.warn "If you want to overwrite skipped stubs, use --force." - end - end - - 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 - unless path = Bundler.settings[:path] - raise "Can't standalone without an explicit path set" - end - standalone_path = Bundler.root.join(path).relative_path_from(bin_path) - standalone_path = standalone_path - template = File.read(File.expand_path("../templates/Executable.standalone", __FILE__)) - ruby_command = Thor::Util.ruby_command - ruby_command = ruby_command - - spec.executables.each do |executable| - next if executable == "bundle" - executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path) - executable_path = executable_path - File.open "#{bin_path}/#{executable}", "w", 0o755 do |f| - f.puts ERB.new(template, nil, "-").result(binding) - end - end - end - - private - - # the order that the resolver provides is significant, since - # dependencies might affect the installation of a gem. - # 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) - force = options["force"] - jobs = options.delete(:jobs) do - if can_install_in_parallel? - [Bundler.settings[:jobs].to_i - 1, 1].max - else - 1 - end - end - install_in_parallel jobs, options[:standalone], force - end - - def load_plugins - Bundler.rubygems.load_plugins - - requested_path_gems = @definition.requested_specs.select {|s| s.source.is_a?(Source::Path) } - path_plugin_files = requested_path_gems.map do |spec| - begin - Bundler.rubygems.spec_matches_for_glob(spec, "rubygems_plugin#{Bundler.rubygems.suffix_pattern}") - rescue TypeError - error_message = "#{spec.name} #{spec.version} has an invalid gemspec" - raise Gem::InvalidSpecificationException, error_message - end - end.flatten - Bundler.rubygems.load_plugin_files(path_plugin_files) - end - - def ensure_specs_are_compatible! - system_ruby = Bundler::RubyVersion.system - rubygems_version = Gem::Version.create(Gem::VERSION) - @definition.specs.each do |spec| - if required_ruby_version = spec.required_ruby_version - unless required_ruby_version.satisfied_by?(system_ruby.gem_version) - raise InstallError, "#{spec.full_name} requires ruby version #{required_ruby_version}, " \ - "which is incompatible with the current version, #{system_ruby}" - end - end - next unless required_rubygems_version = spec.required_rubygems_version - unless required_rubygems_version.satisfied_by?(rubygems_version) - raise InstallError, "#{spec.full_name} requires rubygems version #{required_rubygems_version}, " \ - "which is incompatible with the current version, #{rubygems_version}" - end - end - end - - def warn_on_incompatible_bundler_deps - bundler_version = Gem::Version.create(Bundler::VERSION) - @definition.specs.each do |spec| - spec.dependencies.each do |dep| - next if dep.type == :development - next unless dep.name == "bundler".freeze - next if dep.requirement.satisfied_by?(bundler_version) - - Bundler.ui.warn "#{spec.name} (#{spec.version}) has dependency" \ - " #{SharedHelpers.pretty_dependency(dep)}" \ - ", which is unsatisfied by the current bundler version #{VERSION}" \ - ", so the dependency is being ignored" - end - 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 - end - - def install_in_parallel(size, standalone, force = false) - spec_installations = ParallelInstaller.call(self, @definition.specs, size, standalone, force) - spec_installations.each do |installation| - post_install_messages[installation.name] = installation.post_install_message if installation.has_post_install_message? - end - end - - def create_bundle_path - SharedHelpers.filesystem_access(Bundler.bundle_path.to_s) do |p| - Bundler.mkdir_p(p) - end unless Bundler.bundle_path.exist? - rescue Errno::EEXIST - raise PathError, "Could not install to path `#{Bundler.bundle_path}` " \ - "because a file already exists at that path. Either remove or rename the file so the directory can be created." - end - - # returns whether or not a re-resolve was needed - def resolve_if_needed(options) - if !@definition.unlocking? && !options["force"] && !Bundler.settings[:inline] && Bundler.default_lockfile.file? - return false if @definition.nothing_changed? && !@definition.missing_specs? - end - - options["local"] ? @definition.resolve_with_cache! : @definition.resolve_remotely! - true - end - - def lock(opts = {}) - @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections]) - end - end -end diff --git a/lib/bundler/installer/gem_installer.rb b/lib/bundler/installer/gem_installer.rb deleted file mode 100644 index 086b763d20..0000000000 --- a/lib/bundler/installer/gem_installer.rb +++ /dev/null @@ -1,78 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class GemInstaller - attr_reader :spec, :standalone, :worker, :force, :installer - - def initialize(spec, installer, standalone = false, worker = 0, force = false) - @spec = spec - @installer = installer - @standalone = standalone - @worker = worker - @force = force - end - - def install_from_spec - post_install_message = spec_settings ? install_with_settings : install - Bundler.ui.debug "#{worker}: #{spec.name} (#{spec.version}) from #{spec.loaded_from}" - generate_executable_stubs - return true, post_install_message - rescue Bundler::InstallHookError, Bundler::SecurityError, APIResponseMismatchError - raise - rescue Errno::ENOSPC - return false, out_of_space_message - rescue => e - return false, specific_failure_message(e) - end - - private - - def specific_failure_message(e) - message = "#{e.class}: #{e.message}\n" - message += " " + e.backtrace.join("\n ") + "\n\n" if Bundler.ui.debug? - message = message.lines.first + Bundler.ui.add_color(message.lines.drop(1).join, :clear) - message + Bundler.ui.add_color(failure_message, :red) - end - - def failure_message - return install_error_message if spec.source.options["git"] - "#{install_error_message}\n#{gem_install_message}" - end - - def install_error_message - "An error occurred while installing #{spec.name} (#{spec.version}), and Bundler cannot continue." - end - - def gem_install_message - "Make sure that `gem install #{spec.name} -v '#{spec.version}'` succeeds before bundling." - end - - def spec_settings - # Fetch the build settings, if there are any - Bundler.settings["build.#{spec.name}"] - end - - def install - spec.source.install(spec, :force => force, :ensure_builtin_gems_cached => standalone, :build_args => Array(spec_settings)) - end - - def install_with_settings - # Build arguments are global, so this is mutexed - Bundler.rubygems.install_with_build_args([spec_settings]) { install } - end - - def out_of_space_message - "#{install_error_message}\nYour disk is out of space. Free some space to be able to install your bundle." - end - - def generate_executable_stubs - return if Bundler.feature_flag.forget_cli_options? - return if Bundler.settings[:inline] - if Bundler.settings[:bin] && standalone - installer.generate_standalone_bundler_executable_stubs(spec) - elsif Bundler.settings[:bin] - installer.generate_bundler_executable_stubs(spec, :force => true) - end - end - end -end diff --git a/lib/bundler/installer/parallel_installer.rb b/lib/bundler/installer/parallel_installer.rb deleted file mode 100644 index 95d9575c44..0000000000 --- a/lib/bundler/installer/parallel_installer.rb +++ /dev/null @@ -1,228 +0,0 @@ -# frozen_string_literal: true - -require "bundler/worker" -require "bundler/installer/gem_installer" - -module Bundler - class ParallelInstaller - class SpecInstallation - attr_accessor :spec, :name, :post_install_message, :state, :error - def initialize(spec) - @spec = spec - @name = spec.name - @state = :none - @post_install_message = "" - @error = nil - end - - def installed? - state == :installed - end - - def enqueued? - state == :enqueued - end - - def failed? - state == :failed - end - - def installation_attempted? - installed? || failed? - end - - # Only true when spec in neither installed nor already enqueued - def ready_to_enqueue? - !enqueued? && !installation_attempted? - end - - def has_post_install_message? - !post_install_message.empty? - end - - def ignorable_dependency?(dep) - dep.type == :development || dep.name == @name - end - - # Checks installed dependencies against spec's dependencies to make - # sure needed dependencies have been installed. - def dependencies_installed?(all_specs) - installed_specs = all_specs.select(&:installed?).map(&:name) - dependencies.all? {|d| installed_specs.include? d.name } - end - - # Represents only the non-development dependencies, the ones that are - # itself and are in the total list. - def dependencies - @dependencies ||= begin - all_dependencies.reject {|dep| ignorable_dependency? dep } - end - end - - def missing_lockfile_dependencies(all_spec_names) - deps = all_dependencies.reject {|dep| ignorable_dependency? dep } - deps.reject {|dep| all_spec_names.include? dep.name } - end - - # Represents all dependencies - def all_dependencies - @spec.dependencies - end - - def to_s - "#<#{self.class} #{@spec.full_name} (#{state})>" - end - end - - def self.call(*args) - new(*args).call - end - - attr_reader :size - - def initialize(installer, all_specs, size, standalone, force) - @installer = installer - @size = size - @standalone = standalone - @force = force - @specs = all_specs.map {|s| SpecInstallation.new(s) } - @spec_set = all_specs - end - - def call - # Since `autoload` has the potential for threading issues on 1.8.7 - # TODO: remove in bundler 2.0 - require "bundler/gem_remote_fetcher" if RUBY_VERSION < "1.9" - - check_for_corrupt_lockfile - - if @size > 1 - install_with_worker - else - install_serially - end - - handle_error if @specs.any?(&:failed?) - @specs - ensure - worker_pool && worker_pool.stop - end - - def check_for_corrupt_lockfile - missing_dependencies = @specs.map do |s| - [ - s, - s.missing_lockfile_dependencies(@specs.map(&:name)), - ] - end.reject { |a| a.last.empty? } - return if missing_dependencies.empty? - - warning = [] - warning << "Your lockfile was created by an old Bundler that left some things out." - if @size != 1 - warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time." - @size = 1 - end - warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile." - warning << "The missing gems are:" - - missing_dependencies.each do |spec, missing| - warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}" - end - - Bundler.ui.warn(warning.join("\n")) - end - - private - - def install_with_worker - enqueue_specs - process_specs until finished_installing? - end - - def install_serially - until finished_installing? - raise "failed to find a spec to enqueue while installing serially" unless spec_install = @specs.find(&:ready_to_enqueue?) - spec_install.state = :enqueued - do_install(spec_install, 0) - end - end - - def worker_pool - @worker_pool ||= Bundler::Worker.new @size, "Parallel Installer", lambda { |spec_install, worker_num| - do_install(spec_install, worker_num) - } - end - - def do_install(spec_install, worker_num) - gem_installer = Bundler::GemInstaller.new( - spec_install.spec, @installer, @standalone, worker_num, @force - ) - success, message = begin - gem_installer.install_from_spec - rescue => e - raise e, "#{e}\n\n#{require_tree_for_spec(spec_install.spec)}" - end - if success - spec_install.state = :installed - spec_install.post_install_message = message unless message.nil? - else - 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 - worker_pool.deq - enqueue_specs - end - - def finished_installing? - @specs.all? do |spec| - return true if spec.failed? - spec.installed? - end - 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 require_tree_for_spec(spec) - tree = @spec_set.what_required(spec) - t = String.new("In #{File.basename(SharedHelpers.default_gemfile)}:\n") - tree.each_with_index do |s, depth| - t << " " * depth.succ << s.name - unless tree.last == s - t << %( was resolved to #{s.version}, which depends on) - end - t << %(\n) - end - t - end - - # Keys in the remains hash represent uninstalled gems specs. - # We enqueue all gem specs that do not have any dependencies. - # Later we call this lambda again to install specs that depended on - # previously installed specifications. We continue until all specs - # are installed. - def enqueue_specs - @specs.select(&:ready_to_enqueue?).each do |spec| - if spec.dependencies_installed? @specs - spec.state = :enqueued - worker_pool.enq spec - end - end - end - end -end diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb deleted file mode 100644 index ce0c9df1eb..0000000000 --- a/lib/bundler/installer/standalone.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Standalone - def initialize(groups, definition) - @specs = groups.empty? ? definition.requested_specs : definition.specs_for(groups.map(&:to_sym)) - end - - def generate - SharedHelpers.filesystem_access(bundler_path) do |p| - FileUtils.mkdir_p(p) - end - File.open File.join(bundler_path, "setup.rb"), "w" do |file| - file.puts "require 'rbconfig'" - file.puts "# ruby 1.8.7 doesn't define RUBY_ENGINE" - file.puts "ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'ruby'" - file.puts "ruby_version = RbConfig::CONFIG[\"ruby_version\"]" - file.puts "path = File.expand_path('..', __FILE__)" - paths.each do |path| - file.puts %($:.unshift "\#{path}/#{path}") - end - end - end - - private - - def paths - @specs.map do |spec| - next if spec.name == "bundler" - Array(spec.require_paths).map do |path| - gem_path(path, spec).sub(version_dir, '#{ruby_engine}/#{ruby_version}') - # This is a static string intentionally. It's interpolated at a later time. - end - end.flatten - end - - def version_dir - "#{Bundler::RubyVersion.system.engine}/#{RbConfig::CONFIG["ruby_version"]}" - end - - def bundler_path - Bundler.root.join(Bundler.settings[:path], "bundler") - end - - def gem_path(path, spec) - full_path = Pathname.new(path).absolute? ? path : File.join(spec.full_gem_path, path) - Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s - rescue TypeError - error_message = "#{spec.name} #{spec.version} has an invalid gemspec" - raise Gem::InvalidSpecificationException.new(error_message) - end - end -end diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb deleted file mode 100644 index 993952c23b..0000000000 --- a/lib/bundler/lazy_specification.rb +++ /dev/null @@ -1,123 +0,0 @@ -# frozen_string_literal: true - -require "uri" -require "bundler/match_platform" - -module Bundler - class LazySpecification - Identifier = Struct.new(:name, :version, :source, :platform, :dependencies) - class Identifier - include Comparable - def <=>(other) - return unless other.is_a?(Identifier) - [name, version, platform_string] <=> [other.name, other.version, other.platform_string] - end - - protected - - def platform_string - platform_string = platform.to_s - platform_string == Index::RUBY ? Index::NULL : platform_string - end - end - - include MatchPlatform - - attr_reader :name, :version, :dependencies, :platform - attr_accessor :source, :remote - - def initialize(name, version, platform, source = nil) - @name = name - @version = version - @dependencies = [] - @platform = platform || Gem::Platform::RUBY - @source = source - @specification = nil - end - - def full_name - if platform == Gem::Platform::RUBY || platform.nil? - "#{@name}-#{@version}" - else - "#{@name}-#{@version}-#{platform}" - end - end - - def ==(other) - identifier == other.identifier - end - - def satisfies?(dependency) - @name == dependency.name && dependency.requirement.satisfied_by?(Gem::Version.new(@version)) - end - - def to_lock - out = String.new - - if platform == Gem::Platform::RUBY || platform.nil? - out << " #{name} (#{version})\n" - else - out << " #{name} (#{version}-#{platform})\n" - end - - dependencies.sort_by(&:to_s).uniq.each do |dep| - next if dep.type == :development - out << " #{dep.to_lock}\n" - end - - out - end - - def __materialize__ - search_object = Bundler.feature_flag.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 - search = source.specs.search(search_object).last - if search && Gem::Platform.new(search.platform) != Gem::Platform.new(platform) && !search.runtime_dependencies.-(dependencies.reject {|d| d.type == :development }).empty? - Bundler.ui.warn "Unable to use the platform-specific (#{search.platform}) version of #{name} (#{version}) " \ - "because it has different dependencies from the #{platform} version. " \ - "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again." - search = source.specs.search(self).last - end - search.dependencies = dependencies if search.is_a?(RemoteSpecification) || search.is_a?(EndpointSpecification) - search - end - end - - def respond_to?(*args) - super || @specification ? @specification.respond_to?(*args) : nil - end - - def to_s - @__to_s ||= if platform == Gem::Platform::RUBY || platform.nil? - "#{name} (#{version})" - else - "#{name} (#{version}-#{platform})" - end - end - - def identifier - @__identifier ||= Identifier.new(name, version, source, platform, dependencies) - end - - def git_version - return unless source.is_a?(Bundler::Source::Git) - " #{source.revision[0..6]}" - end - - private - - def to_ary - nil - end - - def method_missing(method, *args, &blk) - raise "LazySpecification has not been materialized yet (calling :#{method} #{args.inspect})" unless @specification - - return super unless respond_to?(method) - - @specification.send(method, *args, &blk) - end - end -end diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb deleted file mode 100644 index 585077d18d..0000000000 --- a/lib/bundler/lockfile_generator.rb +++ /dev/null @@ -1,95 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class LockfileGenerator - attr_reader :definition - attr_reader :out - - # @private - def initialize(definition) - @definition = definition - @out = String.new - end - - def self.generate(definition) - new(definition).generate! - end - - def generate! - add_sources - add_platforms - add_dependencies - add_locked_ruby_version - add_bundled_with - - out - end - - private - - def add_sources - definition.send(:sources).lock_sources.each_with_index do |source, idx| - out << "\n" unless idx.zero? - - # Add the source header - out << source.to_lock - - # Find all specs for this source - specs = definition.resolve.select {|s| source.can_lock?(s) } - add_specs(specs) - end - end - - def add_specs(specs) - # This needs to be sorted by full name so that - # gems with the same name, but different platform - # are ordered consistently - specs.sort_by(&:full_name).each do |spec| - next if spec.name == "bundler".freeze - out << spec.to_lock - end - end - - def add_platforms - add_section("PLATFORMS", definition.platforms) - end - - def add_dependencies - out << "\nDEPENDENCIES\n" - - handled = [] - definition.dependencies.sort_by(&:to_s).each do |dep| - next if handled.include?(dep.name) - out << dep.to_lock - handled << dep.name - end - end - - def add_locked_ruby_version - return unless locked_ruby_version = definition.locked_ruby_version - add_section("RUBY VERSION", locked_ruby_version.to_s) - end - - def add_bundled_with - add_section("BUNDLED WITH", definition.locked_bundler_version.to_s) - end - - def add_section(name, value) - out << "\n#{name}\n" - case value - when Array - value.map(&:to_s).sort.each do |val| - out << " #{val}\n" - end - when Hash - value.to_a.sort_by {|k, _| k.to_s }.each do |key, val| - out << " #{key}: #{val}\n" - end - when String - out << " #{value}\n" - else - raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile" - end - end - end -end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb deleted file mode 100644 index ff706fca1d..0000000000 --- a/lib/bundler/lockfile_parser.rb +++ /dev/null @@ -1,256 +0,0 @@ -# frozen_string_literal: true - -# Some versions of the Bundler 1.1 RC series introduced corrupted -# lockfiles. There were two major problems: -# -# * multiple copies of the same GIT section appeared in the lockfile -# * when this happened, those sections got multiple copies of gems -# in those sections. -# -# As a result, Bundler 1.1 contains code that fixes the earlier -# corruption. We will remove this fix-up code in Bundler 1.2. - -module Bundler - class LockfileParser - attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version - - BUNDLED = "BUNDLED WITH".freeze - DEPENDENCIES = "DEPENDENCIES".freeze - PLATFORMS = "PLATFORMS".freeze - RUBY = "RUBY VERSION".freeze - GIT = "GIT".freeze - GEM = "GEM".freeze - PATH = "PATH".freeze - PLUGIN = "PLUGIN SOURCE".freeze - SPECS = " specs:".freeze - OPTIONS = /^ ([a-z]+): (.*)$/i - SOURCE = [GIT, GEM, PATH, PLUGIN].freeze - - SECTIONS_BY_VERSION_INTRODUCED = { - # The strings have to be dup'ed for old RG on Ruby 2.3+ - # TODO: remove dup in Bundler 2.0 - Gem::Version.create("1.0".dup) => [DEPENDENCIES, PLATFORMS, GIT, GEM, PATH].freeze, - Gem::Version.create("1.10".dup) => [BUNDLED].freeze, - Gem::Version.create("1.12".dup) => [RUBY].freeze, - Gem::Version.create("1.13".dup) => [PLUGIN].freeze, - }.freeze - - KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze - - ENVIRONMENT_VERSION_SECTIONS = [BUNDLED, RUBY].freeze - - def self.sections_in_lockfile(lockfile_contents) - lockfile_contents.scan(/^\w[\w ]*$/).uniq - end - - def self.unknown_sections_in_lockfile(lockfile_contents) - sections_in_lockfile(lockfile_contents) - KNOWN_SECTIONS - end - - def self.sections_to_ignore(base_version = nil) - base_version &&= base_version.release - base_version ||= Gem::Version.create("1.0".dup) - attributes = [] - SECTIONS_BY_VERSION_INTRODUCED.each do |version, introduced| - next if version <= base_version - attributes += introduced - end - attributes - end - - def initialize(lockfile) - @platforms = [] - @sources = [] - @dependencies = {} - @state = nil - @specs = {} - - @rubygems_aggregate = Source::Rubygems.new - - if lockfile.match(/<<<<<<<|=======|>>>>>>>|\|\|\|\|\|\|\|/) - raise LockfileError, "Your #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)} contains merge conflicts.\n" \ - "Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock." - end - - lockfile.split(/(?:\r?\n)+/).each do |line| - if SOURCE.include?(line) - @state = :source - parse_source(line) - elsif line == DEPENDENCIES - @state = :dependency - elsif line == PLATFORMS - @state = :platform - elsif line == RUBY - @state = :ruby - elsif line == BUNDLED - @state = :bundled_with - elsif line =~ /^[^\s]/ - @state = nil - elsif @state - send("parse_#{@state}", line) - end - end - @sources << @rubygems_aggregate unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - @specs = @specs.values.sort_by(&:identifier) - warn_for_outdated_bundler_version - rescue ArgumentError => e - Bundler.ui.debug(e) - raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \ - "and then `bundle install` to generate a new lockfile." - end - - def warn_for_outdated_bundler_version - return unless bundler_version - prerelease_text = bundler_version.prerelease? ? " --pre" : "" - current_version = Gem::Version.create(Bundler::VERSION) - case current_version.segments.first <=> bundler_version.segments.first - when -1 - raise LockfileError, "You must use Bundler #{bundler_version.segments.first} or greater with this lockfile." - when 0 - if current_version < bundler_version - Bundler.ui.warn "Warning: the running version of Bundler (#{current_version}) is older " \ - "than the version that created the lockfile (#{bundler_version}). We suggest you " \ - "upgrade to the latest version of Bundler by running `gem " \ - "install bundler#{prerelease_text}`.\n" - end - end - end - - private - - TYPES = { - GIT => Bundler::Source::Git, - GEM => Bundler::Source::Rubygems, - PATH => Bundler::Source::Path, - PLUGIN => Bundler::Plugin, - }.freeze - - def parse_source(line) - case line - when SPECS - case @type - when PATH - @current_source = TYPES[@type].from_lock(@opts) - @sources << @current_source - when GIT - @current_source = TYPES[@type].from_lock(@opts) - # Strip out duplicate GIT sections - if @sources.include?(@current_source) - @current_source = @sources.find {|s| s == @current_source } - else - @sources << @current_source - end - when GEM - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - @opts["remotes"] = @opts.delete("remote") - @current_source = TYPES[@type].from_lock(@opts) - @sources << @current_source - else - Array(@opts["remote"]).each do |url| - @rubygems_aggregate.add_remote(url) - end - @current_source = @rubygems_aggregate - end - when PLUGIN - @current_source = Plugin.source_from_lock(@opts) - @sources << @current_source - end - when OPTIONS - value = $2 - value = true if value == "true" - value = false if value == "false" - - key = $1 - - if @opts[key] - @opts[key] = Array(@opts[key]) - @opts[key] << value - else - @opts[key] = value - end - when *SOURCE - @current_source = nil - @opts = {} - @type = line - else - parse_spec(line) - end - end - - space = / / - NAME_VERSION = / - ^(#{space}{2}|#{space}{4}|#{space}{6})(?!#{space}) # Exactly 2, 4, or 6 spaces at the start of the line - (.*?) # Name - (?:#{space}\(([^-]*) # Space, followed by version - (?:-(.*))?\))? # Optional platform - (!)? # Optional pinned marker - $ # Line end - /xo - - def parse_dependency(line) - return unless line =~ NAME_VERSION - spaces = $1 - return unless spaces.size == 2 - name = $2 - version = $3 - pinned = $5 - - version = version.split(",").map(&:strip) if version - - dep = Bundler::Dependency.new(name, version) - - if pinned && dep.name != "bundler" - spec = @specs.find {|_, v| v.name == dep.name } - dep.source = spec.last.source if spec - - # Path sources need to know what the default name / version - # to use in the case that there are no gemspecs present. A fake - # gemspec is created based on the version set on the dependency - # TODO: Use the version from the spec instead of from the dependency - if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path) - dep.source.name = name - dep.source.version = $1 - end - end - - @dependencies[dep.name] = dep - end - - def parse_spec(line) - return unless line =~ NAME_VERSION - spaces = $1 - name = $2 - version = $3 - platform = $4 - - if spaces.size == 4 - version = Gem::Version.new(version) - platform = platform ? Gem::Platform.new(platform) : Gem::Platform::RUBY - @current_spec = LazySpecification.new(name, version, platform) - @current_spec.source = @current_source - - # Avoid introducing multiple copies of the same spec (caused by - # duplicate GIT sections) - @specs[@current_spec.identifier] ||= @current_spec - elsif spaces.size == 6 - version = version.split(",").map(&:strip) if version - dep = Gem::Dependency.new(name, version) - @current_spec.dependencies << dep - end - end - - def parse_platform(line) - @platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/ - end - - def parse_bundled_with(line) - line = line.strip - return unless Gem::Version.correct?(line) - @bundler_version = Gem::Version.create(line) - end - - def parse_ruby(line) - @ruby_version = line.strip - end - end -end diff --git a/lib/bundler/match_platform.rb b/lib/bundler/match_platform.rb deleted file mode 100644 index 56cbbfb95d..0000000000 --- a/lib/bundler/match_platform.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -require "bundler/gem_helpers" - -module Bundler - module MatchPlatform - include GemHelpers - - def match_platform(p) - MatchPlatform.platforms_match?(platform, p) - end - - def self.platforms_match?(gemspec_platform, local_platform) - return true if gemspec_platform.nil? - return true if Gem::Platform::RUBY == gemspec_platform - return true if local_platform == gemspec_platform - gemspec_platform = Gem::Platform.new(gemspec_platform) - return true if GemHelpers.generic(gemspec_platform) === local_platform - return true if gemspec_platform === local_platform - - false - end - end -end diff --git a/lib/bundler/mirror.rb b/lib/bundler/mirror.rb deleted file mode 100644 index a6fa070eb8..0000000000 --- a/lib/bundler/mirror.rb +++ /dev/null @@ -1,223 +0,0 @@ -# frozen_string_literal: true - -require "socket" - -module Bundler - class Settings - # Class used to build the mirror set and then find a mirror for a given URI - # - # @param prober [Prober object, nil] by default a TCPSocketProbe, this object - # will be used to probe the mirror address to validate that the mirror replies. - class Mirrors - def initialize(prober = nil) - @all = Mirror.new - @prober = prober || TCPSocketProbe.new - @mirrors = {} - end - - # Returns a mirror for the given uri. - # - # Depending on the uri having a valid mirror or not, it may be a - # mirror that points to the provided uri - def for(uri) - if @all.validate!(@prober).valid? - @all - else - fetch_valid_mirror_for(Settings.normalize_uri(uri)) - end - end - - def each - @mirrors.each do |k, v| - yield k, v.uri.to_s - end - end - - def parse(key, value) - config = MirrorConfig.new(key, value) - mirror = if config.all? - @all - else - @mirrors[config.uri] ||= Mirror.new - end - config.update_mirror(mirror) - end - - private - - def fetch_valid_mirror_for(uri) - downcased = uri.to_s.downcase - mirror = @mirrors[downcased] || @mirrors[URI(downcased).host] || Mirror.new(uri) - mirror.validate!(@prober) - mirror = Mirror.new(uri) unless mirror.valid? - mirror - end - end - - # A mirror - # - # Contains both the uri that should be used as a mirror and the - # fallback timeout which will be used for probing if the mirror - # replies on time or not. - class Mirror - DEFAULT_FALLBACK_TIMEOUT = 0.1 - - attr_reader :uri, :fallback_timeout - - def initialize(uri = nil, fallback_timeout = 0) - self.uri = uri - self.fallback_timeout = fallback_timeout - @valid = nil - end - - def uri=(uri) - @uri = if uri.nil? - nil - else - URI(uri.to_s) - end - @valid = nil - end - - def fallback_timeout=(timeout) - case timeout - when true, "true" - @fallback_timeout = DEFAULT_FALLBACK_TIMEOUT - when false, "false" - @fallback_timeout = 0 - else - @fallback_timeout = timeout.to_i - end - @valid = nil - end - - def ==(other) - !other.nil? && uri == other.uri && fallback_timeout == other.fallback_timeout - end - - def valid? - return false if @uri.nil? - return @valid unless @valid.nil? - false - end - - def validate!(probe = nil) - @valid = false if uri.nil? - if @valid.nil? - @valid = fallback_timeout == 0 || (probe || TCPSocketProbe.new).replies?(self) - end - self - end - end - - # Class used to parse one configuration line - # - # Gets the configuration line and the value. - # This object provides a `update_mirror` method - # used to setup the given mirror value. - class MirrorConfig - attr_accessor :uri, :value - - def initialize(config_line, value) - uri, fallback = - config_line.match(%r{\Amirror\.(all|.+?)(\.fallback_timeout)?\/?\z}).captures - @fallback = !fallback.nil? - @all = false - if uri == "all" - @all = true - else - @uri = URI(uri).absolute? ? Settings.normalize_uri(uri) : uri - end - @value = value - end - - def all? - @all - end - - def update_mirror(mirror) - if @fallback - mirror.fallback_timeout = @value - else - mirror.uri = Settings.normalize_uri(@value) - end - end - end - - # Class used for probing TCP availability for a given mirror. - class TCPSocketProbe - def replies?(mirror) - MirrorSockets.new(mirror).any? do |socket, address, timeout| - begin - socket.connect_nonblock(address) - rescue Errno::EINPROGRESS - wait_for_writtable_socket(socket, address, timeout) - rescue # Connection failed somehow, again - false - end - end - end - - private - - def wait_for_writtable_socket(socket, address, timeout) - if IO.select(nil, [socket], nil, timeout) - probe_writtable_socket(socket, address) - else # TCP Handshake timed out, or there is something dropping packets - false - end - end - - def probe_writtable_socket(socket, address) - socket.connect_nonblock(address) - rescue Errno::EISCONN - true - rescue # Connection failed - false - end - end - end - - # Class used to build the list of sockets that correspond to - # a given mirror. - # - # One mirror may correspond to many different addresses, both - # because of it having many dns entries or because - # the network interface is both ipv4 and ipv5 - class MirrorSockets - def initialize(mirror) - @timeout = mirror.fallback_timeout - @addresses = Socket.getaddrinfo(mirror.uri.host, mirror.uri.port).map do |address| - SocketAddress.new(address[0], address[3], address[1]) - end - end - - def any? - @addresses.any? do |address| - socket = Socket.new(Socket.const_get(address.type), Socket::SOCK_STREAM, 0) - socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) - value = yield socket, address.to_socket_address, @timeout - socket.close unless socket.closed? - value - end - end - end - - # Socket address builder. - # - # Given a socket type, a host and a port, - # provides a method to build sockaddr string - class SocketAddress - attr_reader :type, :host, :port - - def initialize(type, host, port) - @type = type - @host = host - @port = port - end - - def to_socket_address - Socket.pack_sockaddr_in(@port, @host) - end - end -end diff --git a/lib/bundler/plugin.rb b/lib/bundler/plugin.rb deleted file mode 100644 index 99c9a867b0..0000000000 --- a/lib/bundler/plugin.rb +++ /dev/null @@ -1,285 +0,0 @@ -# frozen_string_literal: true - -require "bundler/plugin/api" - -module Bundler - module Plugin - autoload :DSL, "bundler/plugin/dsl" - autoload :Index, "bundler/plugin/index" - autoload :Installer, "bundler/plugin/installer" - autoload :SourceList, "bundler/plugin/source_list" - - class MalformattedPlugin < PluginError; end - class UndefinedCommandError < PluginError; end - class UnknownSourceError < PluginError; end - - PLUGIN_FILE_NAME = "plugins.rb".freeze - - module_function - - def reset! - instance_variables.each {|i| remove_instance_variable(i) } - - @sources = {} - @commands = {} - @hooks_by_event = Hash.new {|h, k| h[k] = [] } - @loaded_plugin_names = [] - end - - reset! - - # Installs a new plugin by the given name - # - # @param [Array<String>] names the name of plugin to be installed - # @param [Hash] options various parameters as described in description. - # Refer to cli/plugin for available options - def install(names, options) - specs = Installer.new.install(names, options) - - save_plugins names, specs - rescue PluginError => e - if specs - specs_to_delete = Hash[specs.select {|k, _v| names.include?(k) && !index.commands.values.include?(k) }] - specs_to_delete.values.each {|spec| Bundler.rm_rf(spec.full_gem_path) } - end - - Bundler.ui.error "Failed to install plugin #{name}: #{e.message}\n #{e.backtrace.join("\n ")}" - end - - # Evaluates the Gemfile with a limited DSL and installs the plugins - # specified by plugin method - # - # @param [Pathname] gemfile path - # @param [Proc] block that can be evaluated for (inline) Gemfile - def gemfile_install(gemfile = nil, &inline) - builder = DSL.new - if block_given? - builder.instance_eval(&inline) - else - builder.eval_gemfile(gemfile) - end - definition = builder.to_definition(nil, true) - - return if definition.dependencies.empty? - - plugins = definition.dependencies.map(&:name).reject {|p| index.installed? p } - installed_specs = Installer.new.install_definition(definition) - - save_plugins plugins, installed_specs, builder.inferred_plugins - rescue => e - unless e.is_a?(GemfileError) - Bundler.ui.error "Failed to install plugin: #{e.message}\n #{e.backtrace[0]}" - end - raise - end - - # The index object used to store the details about the plugin - def index - @index ||= Index.new - end - - # The directory root for all plugin related data - # - # Points to root in app_config_path if ran in an app else points to the one - # in user_bundle_path - def root - @root ||= if SharedHelpers.in_bundle? - local_root - else - global_root - end - end - - def local_root - Bundler.app_config_path.join("plugin") - end - - # The global directory root for all plugin related data - def global_root - Bundler.user_bundle_path.join("plugin") - end - - # The cache directory for plugin stuffs - def cache - @cache ||= root.join("cache") - end - - # To be called via the API to register to handle a command - def add_command(command, cls) - @commands[command] = cls - end - - # Checks if any plugin handles the command - def command?(command) - !index.command_plugin(command).nil? - end - - # To be called from Cli class to pass the command and argument to - # approriate plugin class - def exec_command(command, args) - raise UndefinedCommandError, "Command `#{command}` not found" unless command? command - - load_plugin index.command_plugin(command) unless @commands.key? command - - @commands[command].new.exec(command, args) - end - - # To be called via the API to register to handle a source plugin - def add_source(source, cls) - @sources[source] = cls - end - - # Checks if any plugin declares the source - def source?(name) - !index.source_plugin(name.to_s).nil? - end - - # @return [Class] that handles the source. The calss includes API::Source - def source(name) - raise UnknownSourceError, "Source #{name} not found" unless source? name - - load_plugin(index.source_plugin(name)) unless @sources.key? name - - @sources[name] - end - - # @param [Hash] The options that are present in the lock file - # @return [API::Source] the instance of the class that handles the source - # type passed in locked_opts - def source_from_lock(locked_opts) - src = source(locked_opts["type"]) - - src.new(locked_opts.merge("uri" => locked_opts["remote"])) - end - - # To be called via the API to register a hooks and corresponding block that - # will be called to handle the hook - def add_hook(event, &block) - @hooks_by_event[event.to_s] << block - end - - # Runs all the hooks that are registered for the passed event - # - # It passes the passed arguments and block to the block registered with - # the api. - # - # @param [String] event - def hook(event, *args, &arg_blk) - return unless Bundler.feature_flag.plugins? - - plugins = index.hook_plugins(event) - return unless plugins.any? - - (plugins - @loaded_plugin_names).each {|name| load_plugin(name) } - - @hooks_by_event[event].each {|blk| blk.call(*args, &arg_blk) } - end - - # currently only intended for specs - # - # @return [String, nil] installed path - def installed?(plugin) - Index.new.installed?(plugin) - end - - # Post installation processing and registering with index - # - # @param [Array<String>] plugins list to be installed - # @param [Hash] specs of plugins mapped to installation path (currently they - # contain all the installed specs, including plugins) - # @param [Array<String>] names of inferred source plugins that can be ignored - def save_plugins(plugins, specs, optional_plugins = []) - plugins.each do |name| - spec = specs[name] - validate_plugin! Pathname.new(spec.full_gem_path) - installed = register_plugin(name, spec, optional_plugins.include?(name)) - Bundler.ui.info "Installed plugin #{name}" if installed - end - end - - # Checks if the gem is good to be a plugin - # - # At present it only checks whether it contains plugins.rb file - # - # @param [Pathname] plugin_path the path plugin is installed at - # @raise [MalformattedPlugin] if plugins.rb file is not found - def validate_plugin!(plugin_path) - plugin_file = plugin_path.join(PLUGIN_FILE_NAME) - raise MalformattedPlugin, "#{PLUGIN_FILE_NAME} was not found in the plugin." unless plugin_file.file? - end - - # Runs the plugins.rb file in an isolated namespace, records the plugin - # actions it registers for and then passes the data to index to be stored. - # - # @param [String] name the name of the plugin - # @param [Specification] spec of installed plugin - # @param [Boolean] optional_plugin, removed if there is conflict with any - # other plugin (used for default source plugins) - # - # @raise [MalformattedPlugin] if plugins.rb raises any error - def register_plugin(name, spec, optional_plugin = false) - commands = @commands - sources = @sources - hooks = @hooks_by_event - - @commands = {} - @sources = {} - @hooks_by_event = Hash.new {|h, k| h[k] = [] } - - load_paths = spec.load_paths - add_to_load_path(load_paths) - path = Pathname.new spec.full_gem_path - - begin - load path.join(PLUGIN_FILE_NAME), true - rescue StandardError => e - raise MalformattedPlugin, "#{e.class}: #{e.message}" - end - - if optional_plugin && @sources.keys.any? {|s| source? s } - Bundler.rm_rf(path) - false - else - index.register_plugin(name, path.to_s, load_paths, @commands.keys, - @sources.keys, @hooks_by_event.keys) - true - end - ensure - @commands = commands - @sources = sources - @hooks_by_event = hooks - end - - # Executes the plugins.rb file - # - # @param [String] name of the plugin - def load_plugin(name) - # Need to ensure before this that plugin root where the rest of gems - # are installed to be on load path to support plugin deps. Currently not - # done to avoid conflicts - path = index.plugin_path(name) - - add_to_load_path(index.load_paths(name)) - - load path.join(PLUGIN_FILE_NAME) - - @loaded_plugin_names << name - rescue => e - Bundler.ui.error "Failed loading plugin #{name}: #{e.message}" - raise - end - - def add_to_load_path(load_paths) - if insert_index = Bundler.rubygems.load_path_insert_index - $LOAD_PATH.insert(insert_index, *load_paths) - else - $LOAD_PATH.unshift(*load_paths) - end - end - - class << self - private :load_plugin, :register_plugin, :save_plugins, :validate_plugin!, - :add_to_load_path - end - end -end diff --git a/lib/bundler/plugin/api.rb b/lib/bundler/plugin/api.rb deleted file mode 100644 index a2d5cbb4ac..0000000000 --- a/lib/bundler/plugin/api.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # This is the interfacing class represents the API that we intend to provide - # the plugins to use. - # - # For plugins to be independent of the Bundler internals they shall limit their - # interactions to methods of this class only. This will save them from breaking - # when some internal change. - # - # Currently we are delegating the methods defined in Bundler class to - # itself. So, this class acts as a buffer. - # - # If there is some change in the Bundler class that is incompatible to its - # previous behavior or if otherwise desired, we can reimplement(or implement) - # the method to preserve compatibility. - # - # To use this, either the class can inherit this class or use it directly. - # For example of both types of use, refer the file `spec/plugins/command.rb` - # - # To use it without inheriting, you will have to create an object of this - # to use the functions (except for declaration functions like command, source, - # and hooks). - module Plugin - class API - autoload :Source, "bundler/plugin/api/source" - - # The plugins should declare that they handle a command through this helper. - # - # @param [String] command being handled by them - # @param [Class] (optional) class that handles the command. If not - # provided, the `self` class will be used. - def self.command(command, cls = self) - Plugin.add_command command, cls - end - - # The plugins should declare that they provide a installation source - # through this helper. - # - # @param [String] the source type they provide - # @param [Class] (optional) class that handles the source. If not - # provided, the `self` class will be used. - def self.source(source, cls = self) - cls.send :include, Bundler::Plugin::API::Source - Plugin.add_source source, cls - end - - def self.hook(event, &block) - Plugin.add_hook(event, &block) - end - - # The cache dir to be used by the plugins for storage - # - # @return [Pathname] path of the cache dir - def cache_dir - Plugin.cache.join("plugins") - end - - # A tmp dir to be used by plugins - # Accepts names that get concatenated as suffix - # - # @return [Pathname] object for the new directory created - def tmp(*names) - Bundler.tmp(["plugin", *names].join("-")) - end - - def method_missing(name, *args, &blk) - return Bundler.send(name, *args, &blk) if Bundler.respond_to?(name) - - return SharedHelpers.send(name, *args, &blk) if SharedHelpers.respond_to?(name) - - super - end - - def respond_to_missing?(name, include_private = false) - SharedHelpers.respond_to?(name, include_private) || - Bundler.respond_to?(name, include_private) || super - end - end - end -end diff --git a/lib/bundler/plugin/api/source.rb b/lib/bundler/plugin/api/source.rb deleted file mode 100644 index 586477efb5..0000000000 --- a/lib/bundler/plugin/api/source.rb +++ /dev/null @@ -1,306 +0,0 @@ -# frozen_string_literal: true - -require "uri" - -module Bundler - module Plugin - class API - # This class provides the base to build source plugins - # All the method here are required to build a source plugin (except - # `uri_hash`, `gem_install_dir`; they are helpers). - # - # Defaults for methods, where ever possible are provided which is - # expected to work. But, all source plugins have to override - # `fetch_gemspec_files` and `install`. Defaults are also not provided for - # `remote!`, `cache!` and `unlock!`. - # - # The defaults shall work for most situations but nevertheless they can - # be (preferably should be) overridden as per the plugins' needs safely - # (as long as they behave as expected). - # On overriding `initialize` you should call super first. - # - # If required plugin should override `hash`, `==` and `eql?` methods to be - # able to match objects representing same sources, but may be created in - # different situation (like form gemfile and lockfile). The default ones - # checks only for class and uri, but elaborate source plugins may need - # more comparisons (e.g. git checking on branch or tag). - # - # @!attribute [r] uri - # @return [String] the remote specified with `source` block in Gemfile - # - # @!attribute [r] options - # @return [String] options passed during initialization (either from - # lockfile or Gemfile) - # - # @!attribute [r] name - # @return [String] name that can be used to uniquely identify a source - # - # @!attribute [rw] dependency_names - # @return [Array<String>] Names of dependencies that the source should - # try to resolve. It is not necessary to use this list intenally. This - # is present to be compatible with `Definition` and is used by - # rubygems source. - module Source - attr_reader :uri, :options, :name - attr_accessor :dependency_names - - def initialize(opts) - @options = opts - @dependency_names = [] - @uri = opts["uri"] - @type = opts["type"] - @name = opts["name"] || "#{@type} at #{@uri}" - end - - # This is used by the default `spec` method to constructs the - # Specification objects for the gems and versions that can be installed - # by this source plugin. - # - # Note: If the spec method is overridden, this function is not necessary - # - # @return [Array<String>] paths of the gemspec files for gems that can - # be installed - def fetch_gemspec_files - [] - end - - # Options to be saved in the lockfile so that the source plugin is able - # to check out same version of gem later. - # - # There options are passed when the source plugin is created from the - # lock file. - # - # @return [Hash] - def options_to_lock - {} - end - - # Install the gem specified by the spec at appropriate path. - # `install_path` provides a sufficient default, if the source can only - # satisfy one gem, but is not binding. - # - # @return [String] post installation message (if any) - def install(spec, opts) - raise MalformattedPlugin, "Source plugins need to override the install method." - end - - # It builds extensions, generates bins and installs them for the spec - # provided. - # - # It depends on `spec.loaded_from` to get full_gem_path. The source - # plugins should set that. - # - # It should be called in `install` after the plugin is done placing the - # gem at correct install location. - # - # It also runs Gem hooks `pre_install`, `post_build` and `post_install` - # - # Note: Do not override if you don't know what you are doing. - def post_install(spec, disable_exts = false) - opts = { :env_shebang => false, :disable_extensions => disable_exts } - installer = Bundler::Source::Path::Installer.new(spec, opts) - installer.post_install - end - - # A default installation path to install a single gem. If the source - # servers multiple gems, it's not of much use and the source should one - # of its own. - def install_path - @install_path ||= - begin - base_name = File.basename(URI.parse(uri).normalize.path) - - gem_install_dir.join("#{base_name}-#{uri_hash[0..11]}") - end - end - - # Parses the gemspec files to find the specs for the gems that can be - # satisfied by the source. - # - # Few important points to keep in mind: - # - If the gems are not installed then it shall return specs for all - # the gems it can satisfy - # - If gem is installed (that is to be detected by the plugin itself) - # then it shall return at least the specs that are installed. - # - The `loaded_from` for each of the specs shall be correct (it is - # used to find the load path) - # - # @return [Bundler::Index] index containing the specs - def specs - files = fetch_gemspec_files - - Bundler::Index.build do |index| - files.each do |file| - next unless spec = Bundler.load_gemspec(file) - Bundler.rubygems.set_installed_by_version(spec) - - spec.source = self - Bundler.rubygems.validate(spec) - - index << spec - end - end - end - - # Set internal representation to fetch the gems/specs from remote. - # - # When this is called, the source should try to fetch the specs and - # install from remote path. - def remote! - end - - # Set internal representation to fetch the gems/specs from app cache. - # - # When this is called, the source should try to fetch the specs and - # install from the path provided by `app_cache_path`. - def cached! - end - - # This is called to update the spec and installation. - # - # If the source plugin is loaded from lockfile or otherwise, it shall - # refresh the cache/specs (e.g. git sources can make a fresh clone). - def unlock! - end - - # Name of directory where plugin the is expected to cache the gems when - # #cache is called. - # - # Also this name is matched against the directories in cache for pruning - # - # This is used by `app_cache_path` - def app_cache_dirname - base_name = File.basename(URI.parse(uri).normalize.path) - "#{base_name}-#{uri_hash}" - end - - # This method is called while caching to save copy of the gems that the - # source can resolve to path provided by `app_cache_app`so that they can - # be reinstalled from the cache without querying the remote (i.e. an - # alternative to remote) - # - # This is stored with the app and source plugins should try to provide - # specs and install only from this cache when `cached!` is called. - # - # This cache is different from the internal caching that can be done - # at sub paths of `cache_path` (from API). This can be though as caching - # by bundler. - def cache(spec, custom_path = nil) - new_cache_path = app_cache_path(custom_path) - - FileUtils.rm_rf(new_cache_path) - FileUtils.cp_r(install_path, new_cache_path) - FileUtils.touch(app_cache_path.join(".bundlecache")) - end - - # This shall check if two source object represent the same source. - # - # The comparison shall take place only on the attribute that can be - # inferred from the options passed from Gemfile and not on attibutes - # that are used to pin down the gem to specific version (e.g. Git - # sources should compare on branch and tag but not on commit hash) - # - # The sources objects are constructed from Gemfile as well as from - # lockfile. To converge the sources, it is necessary that they match. - # - # The same applies for `eql?` and `hash` - def ==(other) - other.is_a?(self.class) && uri == other.uri - end - - # When overriding `eql?` please preserve the behaviour as mentioned in - # docstring for `==` method. - alias_method :eql?, :== - - # When overriding `hash` please preserve the behaviour as mentioned in - # docstring for `==` method, i.e. two methods equal by above comparison - # should have same hash. - def hash - [self.class, uri].hash - end - - # A helper method, not necessary if not used internally. - def installed? - File.directory?(install_path) - end - - # The full path where the plugin should cache the gem so that it can be - # installed latter. - # - # Note: Do not override if you don't know what you are doing. - def app_cache_path(custom_path = nil) - @app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname) - end - - # Used by definition. - # - # Note: Do not override if you don't know what you are doing. - def unmet_deps - specs.unmet_dependency_names - end - - # Note: Do not override if you don't know what you are doing. - def can_lock?(spec) - spec.source == self - end - - # Generates the content to be entered into the lockfile. - # Saves type and remote and also calls to `options_to_lock`. - # - # Plugin should use `options_to_lock` to save information in lockfile - # and not override this. - # - # Note: Do not override if you don't know what you are doing. - def to_lock - out = String.new("#{LockfileParser::PLUGIN}\n") - out << " remote: #{@uri}\n" - out << " type: #{@type}\n" - options_to_lock.each do |opt, value| - out << " #{opt}: #{value}\n" - end - out << " specs:\n" - end - - def to_s - "plugin source for #{options[:type]} with uri #{uri}" - end - - # Note: Do not override if you don't know what you are doing. - def include?(other) - other == self - end - - def uri_hash - SharedHelpers.digest(:SHA1).hexdigest(uri) - end - - # Note: Do not override if you don't know what you are doing. - def gem_install_dir - Bundler.install_path - end - - # It is used to obtain the full_gem_path. - # - # spec's loaded_from path is expanded against this to get full_gem_path - # - # Note: Do not override if you don't know what you are doing. - def root - Bundler.root - end - - # @private - # Returns true - def bundler_plugin_api_source? - true - end - - # @private - # This API on source might not be stable, and for now we expect plugins - # to download all specs in `#specs`, so we implement the method for - # compatibility purposes and leave it undocumented (and don't support) - # overriding it) - def double_check_for(*); end - end - end - end -end diff --git a/lib/bundler/plugin/dsl.rb b/lib/bundler/plugin/dsl.rb deleted file mode 100644 index 4bfc8437e0..0000000000 --- a/lib/bundler/plugin/dsl.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module Plugin - # Dsl to parse the Gemfile looking for plugins to install - class DSL < Bundler::Dsl - class PluginGemfileError < PluginError; end - alias_method :_gem, :gem # To use for plugin installation as gem - - # So that we don't have to override all there methods to dummy ones - # explicitly. - # They will be handled by method_missing - [:gemspec, :gem, :path, :install_if, :platforms, :env].each {|m| undef_method m } - - # This lists the plugins that was added automatically and not specified by - # the user. - # - # When we encounter :type attribute with a source block, we add a plugin - # by name bundler-source-<type> to list of plugins to be installed. - # - # These plugins are optional and are not installed when there is conflict - # with any other plugin. - attr_reader :inferred_plugins - - def initialize - super - @sources = Plugin::SourceList.new - @inferred_plugins = [] # The source plugins inferred from :type - end - - def plugin(name, *args) - _gem(name, *args) - end - - def method_missing(name, *args) - raise PluginGemfileError, "Undefined local variable or method `#{name}' for Gemfile" unless Bundler::Dsl.method_defined? name - end - - def source(source, *args, &blk) - options = args.last.is_a?(Hash) ? args.pop.dup : {} - options = normalize_hash(options) - return super unless options.key?("type") - - plugin_name = "bundler-source-#{options["type"]}" - - return if @dependencies.any? {|d| d.name == plugin_name } - - plugin(plugin_name) - @inferred_plugins << plugin_name - end - end - end -end diff --git a/lib/bundler/plugin/index.rb b/lib/bundler/plugin/index.rb deleted file mode 100644 index 8dde072f16..0000000000 --- a/lib/bundler/plugin/index.rb +++ /dev/null @@ -1,157 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # Manages which plugins are installed and their sources. This also is supposed to map - # which plugin does what (currently the features are not implemented so this class is - # now a stub class). - module Plugin - class Index - class CommandConflict < PluginError - def initialize(plugin, commands) - msg = "Command(s) `#{commands.join("`, `")}` declared by #{plugin} are already registered." - super msg - end - end - - class SourceConflict < PluginError - def initialize(plugin, sources) - msg = "Source(s) `#{sources.join("`, `")}` declared by #{plugin} are already registered." - super msg - end - end - - attr_reader :commands - - def initialize - @plugin_paths = {} - @commands = {} - @sources = {} - @hooks = {} - @load_paths = {} - - load_index(global_index_file, true) - load_index(local_index_file) if SharedHelpers.in_bundle? - end - - # This function is to be called when a new plugin is installed. This - # function shall add the functions of the plugin to existing maps and also - # the name to source location. - # - # @param [String] name of the plugin to be registered - # @param [String] path where the plugin is installed - # @param [Array<String>] load_paths for the plugin - # @param [Array<String>] commands that are handled by the plugin - # @param [Array<String>] sources that are handled by the plugin - def register_plugin(name, path, load_paths, commands, sources, hooks) - old_commands = @commands.dup - - common = commands & @commands.keys - raise CommandConflict.new(name, common) unless common.empty? - commands.each {|c| @commands[c] = name } - - common = sources & @sources.keys - raise SourceConflict.new(name, common) unless common.empty? - sources.each {|k| @sources[k] = name } - - hooks.each {|e| (@hooks[e] ||= []) << name } - - @plugin_paths[name] = path - @load_paths[name] = load_paths - save_index - rescue - @commands = old_commands - raise - end - - # Path of default index file - def index_file - Plugin.root.join("index") - end - - # Path where the global index file is stored - def global_index_file - Plugin.global_root.join("index") - end - - # Path where the local index file is stored - def local_index_file - Plugin.local_root.join("index") - end - - def plugin_path(name) - Pathname.new @plugin_paths[name] - end - - def load_paths(name) - @load_paths[name] - end - - # Fetch the name of plugin handling the command - def command_plugin(command) - @commands[command] - end - - def installed?(name) - @plugin_paths[name] - end - - def source?(source) - @sources.key? source - end - - def source_plugin(name) - @sources[name] - end - - # Returns the list of plugin names handling the passed event - def hook_plugins(event) - @hooks[event] || [] - end - - private - - # Reads the index file from the directory and initializes the instance - # variables. - # - # It skips the sources if the second param is true - # @param [Pathname] index file path - # @param [Boolean] is the index file global index - def load_index(index_file, global = false) - SharedHelpers.filesystem_access(index_file, :read) do |index_f| - valid_file = index_f && index_f.exist? && !index_f.size.zero? - break unless valid_file - - data = index_f.read - - require "bundler/yaml_serializer" - index = YAMLSerializer.load(data) - - @commands.merge!(index["commands"]) - @hooks.merge!(index["hooks"]) - @load_paths.merge!(index["load_paths"]) - @plugin_paths.merge!(index["plugin_paths"]) - @sources.merge!(index["sources"]) unless global - end - end - - # Should be called when any of the instance variables change. Stores the - # instance variables in YAML format. (The instance variables are supposed - # to be only String key value pairs) - def save_index - index = { - "commands" => @commands, - "hooks" => @hooks, - "load_paths" => @load_paths, - "plugin_paths" => @plugin_paths, - "sources" => @sources, - } - - require "bundler/yaml_serializer" - SharedHelpers.filesystem_access(index_file) do |index_f| - FileUtils.mkdir_p(index_f.dirname) - File.open(index_f, "w") {|f| f.puts YAMLSerializer.dump(index) } - end - end - end - end -end diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb deleted file mode 100644 index 5379c38979..0000000000 --- a/lib/bundler/plugin/installer.rb +++ /dev/null @@ -1,96 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # Handles the installation of plugin in appropriate directories. - # - # This class is supposed to be wrapper over the existing gem installation infra - # but currently it itself handles everything as the Source's subclasses (e.g. Source::RubyGems) - # are heavily dependent on the Gemfile. - module Plugin - class Installer - autoload :Rubygems, "bundler/plugin/installer/rubygems" - autoload :Git, "bundler/plugin/installer/git" - - def install(names, options) - version = options[:version] || [">= 0"] - Bundler.settings.temporary(:lockfile_uses_separate_rubygems_sources => false, :disable_multisource => false) do - if options[:git] - install_git(names, version, options) - else - sources = options[:source] || Bundler.rubygems.sources - install_rubygems(names, version, sources) - end - end - end - - # Installs the plugin from Definition object created by limited parsing of - # Gemfile searching for plugins to be installed - # - # @param [Definition] definition object - # @return [Hash] map of names to their specs they are installed with - def install_definition(definition) - def definition.lock(*); end - definition.resolve_remotely! - specs = definition.specs - - install_from_specs specs - end - - private - - def install_git(names, version, options) - uri = options.delete(:git) - options["uri"] = uri - - source_list = SourceList.new - source_list.add_git_source(options) - - # To support both sources - if options[:source] - source_list.add_rubygems_source("remotes" => options[:source]) - end - - deps = names.map {|name| Dependency.new name, version } - - definition = Definition.new(nil, deps, source_list, true) - install_definition(definition) - end - - # Installs the plugin from rubygems source and returns the path where the - # plugin was installed - # - # @param [String] name of the plugin gem to search in the source - # @param [Array] version of the gem to install - # @param [String, Array<String>] source(s) to resolve the gem - # - # @return [Hash] map of names to the specs of plugins installed - def install_rubygems(names, version, sources) - deps = names.map {|name| Dependency.new name, version } - - source_list = SourceList.new - source_list.add_rubygems_source("remotes" => sources) - - definition = Definition.new(nil, deps, source_list, true) - install_definition(definition) - end - - # Installs the plugins and deps from the provided specs and returns map of - # gems to their paths - # - # @param specs to install - # - # @return [Hash] map of names to the specs - def install_from_specs(specs) - paths = {} - - specs.each do |spec| - spec.source.install spec - - paths[spec.name] = spec - end - - paths - end - end - end -end diff --git a/lib/bundler/plugin/installer/git.rb b/lib/bundler/plugin/installer/git.rb deleted file mode 100644 index fbb6c5e40e..0000000000 --- a/lib/bundler/plugin/installer/git.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module Plugin - class Installer - class Git < Bundler::Source::Git - def cache_path - @cache_path ||= begin - git_scope = "#{base_name}-#{uri_hash}" - - Plugin.cache.join("bundler", "git", git_scope) - end - end - - def install_path - @install_path ||= begin - git_scope = "#{base_name}-#{shortref_for_path(revision)}" - - Plugin.root.join("bundler", "gems", git_scope) - end - end - - def version_message(spec) - "#{spec.name} #{spec.version}" - end - - def root - Plugin.root - end - - def generate_bin(spec, disable_extensions = false) - # Need to find a way without code duplication - # For now, we can ignore this - end - end - end - end -end diff --git a/lib/bundler/plugin/installer/rubygems.rb b/lib/bundler/plugin/installer/rubygems.rb deleted file mode 100644 index 7ae74fa93b..0000000000 --- a/lib/bundler/plugin/installer/rubygems.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module Plugin - class Installer - class Rubygems < Bundler::Source::Rubygems - def version_message(spec) - "#{spec.name} #{spec.version}" - end - - private - - def requires_sudo? - false # Will change on implementation of project level plugins - end - - def rubygems_dir - Plugin.root - end - - def cache_path - Plugin.cache - end - end - end - end -end diff --git a/lib/bundler/plugin/source_list.rb b/lib/bundler/plugin/source_list.rb deleted file mode 100644 index f0e212205f..0000000000 --- a/lib/bundler/plugin/source_list.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # SourceList object to be used while parsing the Gemfile, setting the - # approptiate options to be used with Source classes for plugin installation - module Plugin - class SourceList < Bundler::SourceList - def add_git_source(options = {}) - add_source_to_list Plugin::Installer::Git.new(options), git_sources - end - - def add_rubygems_source(options = {}) - add_source_to_list Plugin::Installer::Rubygems.new(options), @rubygems_sources - end - - def all_sources - path_sources + git_sources + rubygems_sources + [metadata_source] - end - - private - - def rubygems_aggregate_class - Plugin::Installer::Rubygems - end - end - end -end diff --git a/lib/bundler/process_lock.rb b/lib/bundler/process_lock.rb deleted file mode 100644 index 4bd6931577..0000000000 --- a/lib/bundler/process_lock.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class ProcessLock - def self.lock(bundle_path = Bundler.bundle_path) - lock_file_path = File.join(bundle_path, "bundler.lock") - has_lock = false - - File.open(lock_file_path, "w") do |f| - f.flock(File::LOCK_EX) - has_lock = true - yield - f.flock(File::LOCK_UN) - end - rescue Errno::EACCES, Errno::ENOLCK - # In the case the user does not have access to - # create the lock file or is using NFS where - # locks are not available we skip locking. - yield - ensure - FileUtils.rm_f(lock_file_path) if has_lock - end - end -end diff --git a/lib/bundler/psyched_yaml.rb b/lib/bundler/psyched_yaml.rb deleted file mode 100644 index e654416a5a..0000000000 --- a/lib/bundler/psyched_yaml.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -# Psych could be a gem, so try to ask for it -begin - gem "psych" -rescue LoadError -end if defined?(gem) - -# Psych could be in the stdlib -# but it's too late if Syck is already loaded -begin - require "psych" unless defined?(Syck) -rescue LoadError - # Apparently Psych wasn't available. Oh well. -end - -# At least load the YAML stdlib, whatever that may be -require "yaml" unless defined?(YAML.dump) - -module Bundler - # On encountering invalid YAML, - # Psych raises Psych::SyntaxError - if defined?(::Psych::SyntaxError) - YamlLibrarySyntaxError = ::Psych::SyntaxError - else # Syck raises ArgumentError - YamlLibrarySyntaxError = ::ArgumentError - end -end - -require "bundler/deprecate" -begin - Bundler::Deprecate.skip_during do - require "rubygems/safe_yaml" - end -rescue LoadError - # it's OK if the file isn't there -end diff --git a/lib/bundler/remote_specification.rb b/lib/bundler/remote_specification.rb deleted file mode 100644 index 23e1234330..0000000000 --- a/lib/bundler/remote_specification.rb +++ /dev/null @@ -1,114 +0,0 @@ -# frozen_string_literal: true - -require "uri" - -module Bundler - # Represents a lazily loaded gem specification, where the full specification - # is on the source server in rubygems' "quick" index. The proxy object is to - # be seeded with what we're given from the source's abbreviated index - the - # full specification will only be fetched when necessary. - class RemoteSpecification - include MatchPlatform - include Comparable - - attr_reader :name, :version, :platform - attr_writer :dependencies - attr_accessor :source, :remote - - def initialize(name, version, platform, spec_fetcher) - @name = name - @version = Gem::Version.create version - @platform = platform - @spec_fetcher = spec_fetcher - @dependencies = nil - end - - # Needed before installs, since the arch matters then and quick - # specs don't bother to include the arch in the platform string - def fetch_platform - @platform = _remote_specification.platform - end - - def full_name - if platform == Gem::Platform::RUBY || platform.nil? - "#{@name}-#{@version}" - else - "#{@name}-#{@version}-#{platform}" - end - end - - # Compare this specification against another object. Using sort_obj - # is compatible with Gem::Specification and other Bundler or RubyGems - # objects. Otherwise, use the default Object comparison. - def <=>(other) - if other.respond_to?(:sort_obj) - sort_obj <=> other.sort_obj - else - super - end - end - - # Because Rubyforge cannot be trusted to provide valid specifications - # once the remote gem is downloaded, the backend specification will - # be swapped out. - def __swap__(spec) - SharedHelpers.ensure_same_dependencies(self, dependencies, spec.dependencies) - @_remote_specification = spec - end - - # Create a delegate used for sorting. This strategy is copied from - # RubyGems 2.23 and ensures that Bundler's specifications can be - # compared and sorted with RubyGems' own specifications. - # - # @see #<=> - # @see Gem::Specification#sort_obj - # - # @return [Array] an object you can use to compare and sort this - # specification against other specifications - def sort_obj - [@name, @version, @platform == Gem::Platform::RUBY ? -1 : 1] - end - - def to_s - "#<#{self.class} name=#{name} version=#{version} platform=#{platform}>" - end - - def dependencies - @dependencies ||= begin - deps = method_missing(:dependencies) - - # allow us to handle when the specs dependencies are an array of array of string - # see https://github.com/bundler/bundler/issues/5797 - deps = deps.map {|d| d.is_a?(Gem::Dependency) ? d : Gem::Dependency.new(*d) } - - deps - end - end - - def git_version - return unless loaded_from && source.is_a?(Bundler::Source::Git) - " #{source.revision[0..6]}" - end - - private - - def to_ary - nil - end - - def _remote_specification - @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform]) - @_remote_specification || raise(GemspecError, "Gemspec data for #{full_name} was" \ - " missing from the server! Try installing with `--full-index` as a workaround.") - end - - def method_missing(method, *args, &blk) - _remote_specification.send(method, *args, &blk) - end - - def respond_to?(method, include_all = false) - super || _remote_specification.respond_to?(method, include_all) - end - public :respond_to? - end -end diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb deleted file mode 100644 index 052d776183..0000000000 --- a/lib/bundler/resolver.rb +++ /dev/null @@ -1,372 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Resolver - require "bundler/vendored_molinillo" - require "bundler/resolver/spec_group" - - # Figures out the best possible configuration of gems that satisfies - # the list of passed dependencies and any child dependencies without - # causing any gem activation errors. - # - # ==== Parameters - # *dependencies<Gem::Dependency>:: The list of dependencies to resolve - # - # ==== Returns - # <GemBundle>,nil:: If the list of dependencies can be resolved, a - # collection of gemspecs is returned. Otherwise, nil is returned. - def self.resolve(requirements, index, source_requirements = {}, base = [], gem_version_promoter = GemVersionPromoter.new, additional_base_requirements = [], platforms = nil) - platforms = Set.new(platforms) if platforms - base = SpecSet.new(base) unless base.is_a?(SpecSet) - resolver = new(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) - result = resolver.start(requirements) - SpecSet.new(result) - end - - def initialize(index, source_requirements, base, gem_version_promoter, additional_base_requirements, platforms) - @index = index - @source_requirements = source_requirements - @base = base - @resolver = Molinillo::Resolver.new(self, self) - @search_for = {} - @base_dg = Molinillo::DependencyGraph.new - @base.each do |ls| - dep = Dependency.new(ls.name, ls.version) - @base_dg.add_vertex(ls.name, DepProxy.new(dep, ls.platform), true) - end - additional_base_requirements.each {|d| @base_dg.add_vertex(d.name, d) } - @platforms = platforms - @gem_version_promoter = gem_version_promoter - @allow_bundler_dependency_conflicts = Bundler.feature_flag.allow_bundler_dependency_conflicts? - @lockfile_uses_separate_rubygems_sources = Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - end - - def start(requirements) - @prerelease_specified = {} - requirements.each {|dep| @prerelease_specified[dep.name] ||= dep.prerelease? } - - 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 - message = version_conflict_message(e) - raise VersionConflict.new(e.conflicts.keys.uniq, message) - rescue Molinillo::CircularDependencyError => e - names = e.dependencies.sort_by(&:name).map {|d| "gem '#{d.name}'" } - raise CyclicDependencyError, "Your bundle requires gems that depend" \ - " on each other, creating an infinite loop. Please remove" \ - " #{names.count > 1 ? "either " : ""}#{names.join(" or ")}" \ - " and try again." - end - - include Molinillo::UI - - # Conveys debug information to the user. - # - # @param [Integer] depth the current depth of the resolution process. - # @return [void] - def debug(depth = 0) - return unless debug? - debug_info = yield - debug_info = debug_info.inspect unless debug_info.is_a?(String) - STDERR.puts debug_info.split("\n").map {|s| " " * depth + s } - end - - def debug? - return @debug_mode if defined?(@debug_mode) - @debug_mode = ENV["DEBUG_RESOLVER"] || ENV["DEBUG_RESOLVER_TREE"] || false - end - - def before_resolution - Bundler.ui.info "Resolving dependencies...", debug? - end - - def after_resolution - Bundler.ui.info "" - end - - def indicate_progress - Bundler.ui.info ".", false unless debug? - end - - include Molinillo::SpecificationProvider - - def dependencies_for(specification) - specification.dependencies_for_activated_platforms - end - - def search_for(dependency) - platform = dependency.__platform - dependency = dependency.dep unless dependency.is_a? Gem::Dependency - search = @search_for[dependency] ||= begin - index = index_for(dependency) - results = index.search(dependency, @base[dependency.name]) - - unless @prerelease_specified[dependency.name] - # Move prereleases to the beginning of the list, so they're considered - # last during resolution. - pre, results = results.partition {|spec| spec.version.prerelease? } - results = pre + results - end - - if vertex = @base_dg.vertex_named(dependency.name) - locked_requirement = vertex.payload.requirement - end - spec_groups = if results.any? - nested = [] - results.each do |spec| - version, specs = nested.last - if version == spec.version - specs << spec - else - nested << [spec.version, [spec]] - end - end - nested.reduce([]) do |groups, (version, specs)| - next groups if locked_requirement && !locked_requirement.satisfied_by?(version) - spec_group = SpecGroup.new(specs) - spec_group.ignores_bundler_dependencies = @allow_bundler_dependency_conflicts - groups << spec_group - end - else - [] - end - # GVP handles major itself, but it's still a bit risky to trust it with it - # until we get it settled with new behavior. For 2.x it can take over all cases. - if @gem_version_promoter.major? - spec_groups - else - @gem_version_promoter.sort_versions(dependency, spec_groups) - end - end - search.select {|sg| sg.for?(platform) }.each {|sg| sg.activate_platform!(platform) } - end - - def index_for(dependency) - source = @source_requirements[dependency.name] - if source - source.specs - elsif @lockfile_uses_separate_rubygems_sources - Index.build do |idx| - if dependency.all_sources - dependency.all_sources.each {|s| idx.add_source(s.specs) if s } - else - idx.add_source @source_requirements[:default].specs - end - end - else - @index - end - end - - def name_for(dependency) - dependency.name - end - - def name_for_explicit_dependency_source - Bundler.default_gemfile.basename.to_s - rescue - "Gemfile" - end - - def name_for_locking_dependency_source - Bundler.default_lockfile.basename.to_s - rescue - "Gemfile.lock" - end - - def requirement_satisfied_by?(requirement, activated, spec) - return false unless requirement.matches_spec?(spec) || spec.source.is_a?(Source::Gemspec) - if spec.version.prerelease? && !requirement.prerelease? && search_for(requirement).any? {|sg| !sg.version.prerelease? } - vertex = activated.vertex_named(spec.name) - return false if vertex.requirements.none?(&:prerelease?) - end - spec.activate_platform!(requirement.__platform) if !@platforms || @platforms.include?(requirement.__platform) - true - end - - def relevant_sources_for_vertex(vertex) - if vertex.root? - [@source_requirements[vertex.name]] - elsif @lockfile_uses_separate_rubygems_sources - vertex.recursive_predecessors.map do |v| - @source_requirements[v.name] - end << @source_requirements[:default] - end - end - - def sort_dependencies(dependencies, activated, conflicts) - dependencies.sort_by do |dependency| - dependency.all_sources = relevant_sources_for_vertex(activated.vertex_named(dependency.name)) - name = name_for(dependency) - vertex = activated.vertex_named(name) - [ - @base_dg.vertex_named(name) ? 0 : 1, - vertex.payload ? 0 : 1, - vertex.root? ? 0 : 1, - amount_constrained(dependency), - conflicts[name] ? 0 : 1, - vertex.payload ? 0 : search_for(dependency).count, - self.class.platform_sort_key(dependency.__platform), - ] - end - end - - # Sort platforms from most general to most specific - def self.sort_platforms(platforms) - platforms.sort_by do |platform| - platform_sort_key(platform) - end - end - - def self.platform_sort_key(platform) - return ["", "", ""] if Gem::Platform::RUBY == platform - platform.to_a.map {|part| part || "" } - end - - private - - # returns an integer \in (-\infty, 0] - # a number closer to 0 means the dependency is less constraining - # - # dependencies w/ 0 or 1 possibilities (ignoring version requirements) - # are given very negative values, so they _always_ sort first, - # before dependencies that are unconstrained - def amount_constrained(dependency) - @amount_constrained ||= {} - @amount_constrained[dependency.name] ||= begin - if (base = @base[dependency.name]) && !base.empty? - dependency.requirement.satisfied_by?(base.first.version) ? 0 : 1 - else - all = index_for(dependency).search(dependency.name).size - - if all <= 1 - all - 1_000_000 - else - search = search_for(dependency) - search = @prerelease_specified[dependency.name] ? search.count : search.count {|s| !s.version.prerelease? } - search - all - end - end - end - end - - def verify_gemfile_dependencies_are_found!(requirements) - requirements.each do |requirement| - name = requirement.name - next if name == "bundler" - next unless search_for(requirement).empty? - - cache_message = begin - " or in gems cached in #{Bundler.settings.app_cache_path}" if Bundler.app_cache.exist? - rescue GemfileNotFound - nil - end - - if (base = @base[name]) && !base.empty? - version = base.first.version - message = "You have requested:\n" \ - " #{name} #{requirement.requirement}\n\n" \ - "The bundle currently has #{name} locked at #{version}.\n" \ - "Try running `bundle update #{name}`\n\n" \ - "If you are updating multiple gems in your Gemfile at once,\n" \ - "try passing them all to `bundle update`" - elsif source = @source_requirements[name] - specs = source.specs[name] - versions_with_platforms = specs.map {|s| [s.version, s.platform] } - message = String.new("Could not find gem '#{SharedHelpers.pretty_dependency(requirement)}' in #{source}#{cache_message}.\n") - message << if versions_with_platforms.any? - "The source contains '#{name}' at: #{formatted_versions_with_platforms(versions_with_platforms)}" - else - "The source does not contain any versions of '#{name}'" - end - else - message = "Could not find gem '#{requirement}' in any of the gem sources " \ - "listed in your Gemfile#{cache_message}." - end - raise GemNotFound, message - end - end - - def formatted_versions_with_platforms(versions_with_platforms) - version_platform_strs = versions_with_platforms.map do |vwp| - version = vwp.first - platform = vwp.last - version_platform_str = String.new(version.to_s) - version_platform_str << " #{platform}" unless platform.nil? || platform == Gem::Platform::RUBY - version_platform_str - end - version_platform_strs.join(", ") - end - - def version_conflict_message(e) - e.message_with_trees( - :solver_name => "Bundler", - :possibility_type => "gem", - :reduce_trees => lambda do |trees| - # bail out if tree size is too big for Array#combination to make any sense - return trees if trees.size > 15 - 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 - - trees = trees.sort_by {|t| t.flatten.map(&:to_s) } - trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } - - trees.sort_by {|t| t.reverse.map(&:name) } - end, - :printable_requirement => lambda {|req| SharedHelpers.pretty_dependency(req) }, - :additional_message_for_conflict => lambda do |o, name, conflict| - if name == "bundler" - o << %(\n Current Bundler version:\n bundler (#{Bundler::VERSION})) - other_bundler_required = !conflict.requirement.requirement.satisfied_by?(Gem::Version.new Bundler::VERSION) - 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" - - relevant_sources = if conflict.requirement.source - [conflict.requirement.source] - elsif conflict.requirement.all_sources - conflict.requirement.all_sources - elsif @lockfile_uses_separate_rubygems_sources - # every conflict should have an explicit group of sources when we - # enforce strict pinning - raise "no source set for #{conflict}" - else - [] - end.compact.map(&:to_s).uniq.sort - - o << "Could not find gem '#{SharedHelpers.pretty_dependency(conflict.requirement)}'" - if conflict.requirement_trees.first.size > 1 - o << ", which is required by " - o << "gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}'," - end - o << " " - - o << if relevant_sources.empty? - "in any of the sources.\n" - else - "in any of the relevant sources:\n #{relevant_sources * "\n "}\n" - end - end - end, - :version_for_spec => lambda {|spec| spec.version } - ) - end - end -end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb deleted file mode 100644 index 9c10a4b733..0000000000 --- a/lib/bundler/resolver/spec_group.rb +++ /dev/null @@ -1,111 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Resolver - class SpecGroup - include GemHelpers - - attr_accessor :name, :version, :source - attr_accessor :ignores_bundler_dependencies - - def initialize(all_specs) - raise ArgumentError, "cannot initialize with an empty value" unless exemplary_spec = all_specs.first - @name = exemplary_spec.name - @version = exemplary_spec.version - @source = exemplary_spec.source - - @required_by = [] - @activated_platforms = [] - @dependencies = nil - @specs = Hash.new do |specs, platform| - specs[platform] = select_best_platform_match(all_specs, platform) - end - @ignores_bundler_dependencies = true - 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 for?(platform) - spec = @specs[platform] - !spec.nil? - end - - def to_s - @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 - - def ==(other) - return unless other.is_a?(SpecGroup) - name == other.name && - version == other.version && - source == other.source - end - - def eql?(other) - name.eql?(other.name) && - version.eql?(other.version) && - source.eql?(other.source) - end - - def hash - to_s.hash ^ source.hash - 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 - next if @ignores_bundler_dependencies && dep.name == "bundler".freeze - 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 - end -end diff --git a/lib/bundler/retry.rb b/lib/bundler/retry.rb deleted file mode 100644 index 244606dcc9..0000000000 --- a/lib/bundler/retry.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # General purpose class for retrying code that may fail - class Retry - attr_accessor :name, :total_runs, :current_run - - class << self - def default_attempts - default_retries + 1 - end - alias_method :attempts, :default_attempts - - def default_retries - Bundler.settings[:retry] - end - end - - def initialize(name, exceptions = nil, retries = self.class.default_retries) - @name = name - @retries = retries - @exceptions = Array(exceptions) || [] - @total_runs = @retries + 1 # will run once, then upto attempts.times - end - - def attempt(&block) - @current_run = 0 - @failed = false - @error = nil - run(&block) while keep_trying? - @result - end - alias_method :attempts, :attempt - - private - - def run(&block) - @failed = false - @current_run += 1 - @result = block.call - rescue => e - fail_attempt(e) - end - - def fail_attempt(e) - @failed = true - if last_attempt? || @exceptions.any? {|k| e.is_a?(k) } - Bundler.ui.info "" unless Bundler.ui.debug? - raise e - end - return true unless name - Bundler.ui.info "" unless Bundler.ui.debug? # Add new line incase dots preceded this - Bundler.ui.warn "Retrying #{name} due to error (#{current_run.next}/#{total_runs}): #{e.class} #{e.message}", Bundler.ui.debug? - end - - def keep_trying? - return true if current_run.zero? - return false if last_attempt? - return true if @failed - end - - def last_attempt? - current_run >= total_runs - end - end -end diff --git a/lib/bundler/ruby_dsl.rb b/lib/bundler/ruby_dsl.rb deleted file mode 100644 index f6ba220cd5..0000000000 --- a/lib/bundler/ruby_dsl.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module RubyDsl - def ruby(*ruby_version) - options = ruby_version.last.is_a?(Hash) ? ruby_version.pop : {} - ruby_version.flatten! - raise GemfileError, "Please define :engine_version" if options[:engine] && options[:engine_version].nil? - raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil? - - if options[:engine] == "ruby" && options[:engine_version] && - ruby_version != Array(options[:engine_version]) - raise GemfileEvalError, "ruby_version must match the :engine_version for MRI" - end - @ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version]) - end - end -end diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb deleted file mode 100644 index d4e1bdbfd5..0000000000 --- a/lib/bundler/ruby_version.rb +++ /dev/null @@ -1,152 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class RubyVersion - attr_reader :versions, - :patchlevel, - :engine, - :engine_versions, - :gem_version, - :engine_gem_version - - def initialize(versions, patchlevel, engine, engine_version) - # The parameters to this method must satisfy the - # following constraints, which are verified in - # the DSL: - # - # * If an engine is specified, an engine version - # must also be specified - # * If an engine version is specified, an engine - # must also be specified - # * If the engine is "ruby", the engine version - # must not be specified, or the engine version - # specified must match the version. - - @versions = Array(versions).map do |v| - op, v = Gem::Requirement.parse(v) - op == "=" ? v.to_s : "#{op} #{v}" - end - - @gem_version = Gem::Requirement.create(@versions.first).requirements.first.last - @input_engine = engine && engine.to_s - @engine = engine && engine.to_s || "ruby" - @engine_versions = (engine_version && Array(engine_version)) || @versions - @engine_gem_version = Gem::Requirement.create(@engine_versions.first).requirements.first.last - @patchlevel = patchlevel - end - - def to_s(versions = self.versions) - output = String.new("ruby #{versions_string(versions)}") - output << "p#{patchlevel}" if patchlevel - output << " (#{engine} #{versions_string(engine_versions)})" unless engine == "ruby" - - output - end - - # @private - PATTERN = / - ruby\s - ([\d.]+) # ruby version - (?:p(-?\d+))? # optional patchlevel - (?:\s\((\S+)\s(.+)\))? # optional engine info - /xo - - # Returns a RubyVersion from the given string. - # @param [String] the version string to match. - # @return [RubyVersion,Nil] The version if the string is a valid RubyVersion - # description, and nil otherwise. - def self.from_string(string) - new($1, $2, $3, $4) if string =~ PATTERN - end - - def single_version_string - to_s(gem_version) - end - - def ==(other) - versions == other.versions && - engine == other.engine && - engine_versions == other.engine_versions && - patchlevel == other.patchlevel - end - - def host - @host ||= [ - RbConfig::CONFIG["host_cpu"], - RbConfig::CONFIG["host_vendor"], - RbConfig::CONFIG["host_os"] - ].join("-") - end - - # Returns a tuple of these things: - # [diff, this, other] - # The priority of attributes are - # 1. engine - # 2. ruby_version - # 3. engine_version - def diff(other) - raise ArgumentError, "Can only diff with a RubyVersion, not a #{other.class}" unless other.is_a?(RubyVersion) - if engine != other.engine && @input_engine - [:engine, engine, other.engine] - elsif versions.empty? || !matches?(versions, other.gem_version) - [:version, versions_string(versions), versions_string(other.versions)] - elsif @input_engine && !matches?(engine_versions, other.engine_gem_version) - [:engine_version, versions_string(engine_versions), versions_string(other.engine_versions)] - elsif patchlevel && (!patchlevel.is_a?(String) || !other.patchlevel.is_a?(String) || !matches?(patchlevel, other.patchlevel)) - [:patchlevel, patchlevel, other.patchlevel] - end - end - - def versions_string(versions) - Array(versions).join(", ") - end - - def self.system - ruby_engine = if defined?(RUBY_ENGINE) && !RUBY_ENGINE.nil? - RUBY_ENGINE.dup - else - # not defined in ruby 1.8.7 - "ruby" - end - # :sob: mocking RUBY_VERSION breaks stuff on 1.8.7 - ruby_version = ENV.fetch("BUNDLER_SPEC_RUBY_VERSION") { RUBY_VERSION }.dup - ruby_engine_version = case ruby_engine - when "ruby" - ruby_version - when "rbx" - Rubinius::VERSION.dup - when "jruby" - JRUBY_VERSION.dup - else - raise BundlerError, "RUBY_ENGINE value #{RUBY_ENGINE} is not recognized" - end - patchlevel = RUBY_PATCHLEVEL.to_s - - @ruby_version ||= RubyVersion.new(ruby_version, patchlevel, ruby_engine, ruby_engine_version) - end - - def to_gem_version_with_patchlevel - @gem_version_with_patch ||= begin - Gem::Version.create("#{@gem_version}.#{@patchlevel}") - rescue ArgumentError - @gem_version - end - end - - def exact? - return @exact if defined?(@exact) - @exact = versions.all? {|v| Gem::Requirement.create(v).exact? } - end - - private - - def matches?(requirements, version) - # Handles RUBY_PATCHLEVEL of -1 for instances like ruby-head - return requirements == version if requirements.to_s == "-1" || version.to_s == "-1" - - Array(requirements).all? do |requirement| - Gem::Requirement.create(requirement).satisfied_by?(Gem::Version.create(version)) - end - end - end -end diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb deleted file mode 100644 index e9f0eac355..0000000000 --- a/lib/bundler/rubygems_ext.rb +++ /dev/null @@ -1,210 +0,0 @@ -# frozen_string_literal: true - -require "pathname" - -if defined?(Gem::QuickLoader) - # Gem Prelude makes me a sad panda :'( - Gem::QuickLoader.load_full_rubygems_library -end - -require "rubygems" -require "rubygems/specification" - -begin - # Possible use in Gem::Specification#source below and require - # shouldn't be deferred. - require "rubygems/source" -rescue LoadError - # Not available before RubyGems 2.0.0, ignore - nil -end - -require "bundler/match_platform" - -module Gem - @loaded_stacks = Hash.new {|h, k| h[k] = [] } - - class Specification - attr_accessor :remote, :location, :relative_loaded_from - - if instance_methods(false).map(&:to_sym).include?(:source) - remove_method :source - attr_writer :source - def source - (defined?(@source) && @source) || Gem::Source::Installed.new - end - else - attr_accessor :source - end - - alias_method :rg_full_gem_path, :full_gem_path - alias_method :rg_loaded_from, :loaded_from - - attr_writer :full_gem_path unless instance_methods.include?(:full_gem_path=) - - def full_gem_path - # this cannot check source.is_a?(Bundler::Plugin::API::Source) - # because that _could_ trip the autoload, and if there are unresolved - # gems at that time, this method could be called inside another require, - # thus raising with that constant being undefined. Better to check a method - if source.respond_to?(:path) || (source.respond_to?(:bundler_plugin_api_source?) && source.bundler_plugin_api_source?) - Pathname.new(loaded_from).dirname.expand_path(source.root).to_s.untaint - else - rg_full_gem_path - end - end - - def loaded_from - if relative_loaded_from - source.path.join(relative_loaded_from).to_s - else - rg_loaded_from - end - end - - def load_paths - return full_require_paths if respond_to?(:full_require_paths) - - require_paths.map do |require_path| - if require_path.include?(full_gem_path) - require_path - else - File.join(full_gem_path, require_path) - end - end - end - - if method_defined?(:extension_dir) - alias_method :rg_extension_dir, :extension_dir - def extension_dir - @bundler_extension_dir ||= if source.respond_to?(:extension_dir_name) - File.expand_path(File.join(extensions_dir, source.extension_dir_name)) - else - rg_extension_dir - end - end - end - - # RubyGems 1.8+ used only. - methods = instance_methods(false) - gem_dir = methods.first.is_a?(String) ? "gem_dir" : :gem_dir - remove_method :gem_dir if methods.include?(gem_dir) - def gem_dir - full_gem_path - end - - def groups - @groups ||= [] - end - - def git_version - return unless loaded_from && source.is_a?(Bundler::Source::Git) - " #{source.revision[0..6]}" - end - - def to_gemfile(path = nil) - gemfile = String.new("source 'https://rubygems.org'\n") - gemfile << dependencies_to_gemfile(nondevelopment_dependencies) - unless development_dependencies.empty? - gemfile << "\n" - gemfile << dependencies_to_gemfile(development_dependencies, :development) - end - gemfile - end - - def nondevelopment_dependencies - dependencies - development_dependencies - end - - private - - def dependencies_to_gemfile(dependencies, group = nil) - gemfile = String.new - if dependencies.any? - gemfile << "group :#{group} do\n" if group - dependencies.each do |dependency| - gemfile << " " if group - gemfile << %(gem "#{dependency.name}") - req = dependency.requirements_list.first - gemfile << %(, "#{req}") if req - gemfile << "\n" - end - gemfile << "end\n" if group - end - gemfile - end - end - - class Dependency - attr_accessor :source, :groups, :all_sources - - alias_method :eql?, :== - - def encode_with(coder) - to_yaml_properties.each do |ivar| - coder[ivar.to_s.sub(/^@/, "")] = instance_variable_get(ivar) - end - end - - def to_yaml_properties - instance_variables.reject {|p| ["@source", "@groups", "@all_sources"].include?(p.to_s) } - end - - def to_lock - out = String.new(" #{name}") - unless requirement.none? - reqs = requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse - out << " (#{reqs.join(", ")})" - end - 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 - return true if requirement.none? - - requirement.satisfied_by?(spec.version) - end unless allocate.respond_to?(:matches_spec?) - end - - class Requirement - # Backport of performance enhancement added to RubyGems 1.4 - def none? - # note that it might be tempting to replace with with RubyGems 2.0's - # improved implementation. Don't. It requires `DefaultRequirement` to be - # defined, and more importantantly, these overrides are not used when the - # running RubyGems defines these methods - to_s == ">= 0" - end unless allocate.respond_to?(:none?) - - # Backport of performance enhancement added to RubyGems 2.2 - def exact? - return false unless @requirements.size == 1 - @requirements[0][0] == "=" - end unless allocate.respond_to?(:exact?) - end - - class Platform - JAVA = Gem::Platform.new("java") unless defined?(JAVA) - MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) - MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64) - MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW) - X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW) - - undef_method :hash if method_defined? :hash - def hash - @cpu.hash ^ @os.hash ^ @version.hash - end - - undef_method :eql? if method_defined? :eql? - alias_method :eql?, :== - end -end - -module Gem - class Specification - include ::Bundler::MatchPlatform - end -end diff --git a/lib/bundler/rubygems_gem_installer.rb b/lib/bundler/rubygems_gem_installer.rb deleted file mode 100644 index 2b7fa8e0f6..0000000000 --- a/lib/bundler/rubygems_gem_installer.rb +++ /dev/null @@ -1,99 +0,0 @@ -# frozen_string_literal: true - -require "rubygems/installer" - -module Bundler - class RubyGemsGemInstaller < Gem::Installer - unless respond_to?(:at) - def self.at(*args) - new(*args) - end - end - - def check_executable_overwrite(filename) - # Bundler needs to install gems regardless of binstub overwriting - end - - def pre_install_checks - super && validate_bundler_checksum(options[:bundler_expected_checksum]) - end - - def build_extensions - extension_cache_path = options[:bundler_extension_cache_path] - return super unless extension_cache_path && extension_dir = Bundler.rubygems.spec_extension_dir(spec) - - extension_dir = Pathname.new(extension_dir) - build_complete = SharedHelpers.filesystem_access(extension_cache_path.join("gem.build_complete"), :read, &:file?) - if build_complete && !options[:force] - SharedHelpers.filesystem_access(extension_dir.parent, &:mkpath) - SharedHelpers.filesystem_access(extension_cache_path) do - FileUtils.cp_r extension_cache_path, spec.extension_dir - end - else - super - if extension_dir.directory? # not made for gems without extensions - SharedHelpers.filesystem_access(extension_cache_path.parent, &:mkpath) - SharedHelpers.filesystem_access(extension_cache_path) do - FileUtils.cp_r extension_dir, extension_cache_path - end - end - end - end - - private - - def validate_bundler_checksum(checksum) - return true if Bundler.settings[:disable_checksum_validation] - return true unless checksum - 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 = SharedHelpers.digest(:SHA256).new - digest << io.read(16_384) until io.eof? - io.rewind - send(checksum_type(checksum), digest) - end - unless digest == checksum - raise SecurityError, <<-MESSAGE - Bundler cannot continue installing #{spec.name} (#{spec.version}). - The checksum for the downloaded `#{spec.full_name}.gem` does not match \ - the checksum given by the server. This means the contents of the downloaded \ - gem is different from what was uploaded to the server, and could be a potential security issue. - - To resolve this issue: - 1. delete the downloaded gem located at: `#{spec.gem_dir}/#{spec.full_name}.gem` - 2. run `bundle install` - - If you wish to continue installing the downloaded gem, and are certain it does not pose a \ - security issue despite the mismatching checksum, do the following: - 1. run `bundle config disable_checksum_validation true` to turn off checksum verification - 2. run `bundle install` - - (More info: The expected SHA256 checksum was #{checksum.inspect}, but the \ - checksum for the downloaded gem was #{digest.inspect}.) - MESSAGE - end - true - end - - def checksum_type(checksum) - case checksum.length - when 64 then :hexdigest! - when 44 then :base64digest! - else raise InstallError, "The given checksum for #{spec.full_name} (#{checksum.inspect}) is not a valid SHA256 hexdigest nor base64digest" - end - end - - def hexdigest!(digest) - digest.hexdigest! - end - - def base64digest!(digest) - if digest.respond_to?(:base64digest!) - digest.base64digest! - else - [digest.digest!].pack("m0") - end - end - end -end diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb deleted file mode 100644 index 0f16b6231d..0000000000 --- a/lib/bundler/rubygems_integration.rb +++ /dev/null @@ -1,892 +0,0 @@ -# frozen_string_literal: true - -require "monitor" -require "rubygems" -require "rubygems/config_file" - -module Bundler - class RubygemsIntegration - if defined?(Gem::Ext::Builder::CHDIR_MONITOR) - EXT_LOCK = Gem::Ext::Builder::CHDIR_MONITOR - else - EXT_LOCK = Monitor.new - end - - def self.version - @version ||= Gem::Version.new(Gem::VERSION) - end - - def self.provides?(req_str) - Gem::Requirement.new(req_str).satisfied_by?(version) - end - - def initialize - @replaced_methods = {} - end - - def version - self.class.version - end - - def provides?(req_str) - self.class.provides?(req_str) - end - - def build_args - Gem::Command.build_args - end - - def build_args=(args) - Gem::Command.build_args = args - end - - def load_path_insert_index - Gem.load_path_insert_index - end - - def loaded_specs(name) - Gem.loaded_specs[name] - end - - def mark_loaded(spec) - if spec.respond_to?(:activated=) - current = Gem.loaded_specs[spec.name] - current.activated = false if current - spec.activated = true - end - Gem.loaded_specs[spec.name] = spec - end - - def validate(spec) - Bundler.ui.silence { spec.validate(false) } - rescue Gem::InvalidSpecificationException => e - error_message = "The gemspec at #{spec.loaded_from} is not valid. Please fix this gemspec.\n" \ - "The validation error was '#{e.message}'\n" - raise Gem::InvalidSpecificationException.new(error_message) - rescue Errno::ENOENT - nil - end - - def set_installed_by_version(spec, installed_by_version = Gem::VERSION) - return unless spec.respond_to?(:installed_by_version=) - spec.installed_by_version = Gem::Version.create(installed_by_version) - end - - def spec_missing_extensions?(spec, default = true) - return spec.missing_extensions? if spec.respond_to?(:missing_extensions?) - - return false if spec_default_gem?(spec) - return false if spec.extensions.empty? - - default - end - - def spec_default_gem?(spec) - spec.respond_to?(:default_gem?) && spec.default_gem? - end - - def spec_matches_for_glob(spec, glob) - return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob) - - spec.load_paths.map do |lp| - Dir["#{lp}/#{glob}#{suffix_pattern}"] - end.flatten(1) - end - - def spec_extension_dir(spec) - return unless spec.respond_to?(:extension_dir) - spec.extension_dir - end - - def stub_set_spec(stub, spec) - stub.instance_variable_set(:@spec, spec) - end - - def path(obj) - obj.to_s - end - - def platforms - return [Gem::Platform::RUBY] if Bundler.settings[:force_ruby_platform] - Gem.platforms - end - - def configuration - require "bundler/psyched_yaml" - Gem.configuration - rescue Gem::SystemExitException, LoadError => e - Bundler.ui.error "#{e.class}: #{e.message}" - Bundler.ui.trace e - raise - rescue YamlLibrarySyntaxError => e - raise YamlSyntaxError.new(e, "Your RubyGems configuration, which is " \ - "usually located in ~/.gemrc, contains invalid YAML syntax.") - end - - def ruby_engine - Gem.ruby_engine - end - - def read_binary(path) - Gem.read_binary(path) - end - - def inflate(obj) - Gem.inflate(obj) - end - - def sources=(val) - # Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc - # If that file exists, its settings (including sources) will overwrite the values we - # are about to set here. In order to avoid that, we force memoizing the config file now. - configuration - - Gem.sources = val - end - - def sources - Gem.sources - end - - def gem_dir - Gem.dir - end - - def gem_bindir - Gem.bindir - end - - def user_home - Gem.user_home - end - - def gem_path - Gem.path - end - - def reset - Gem::Specification.reset - end - - def post_reset_hooks - Gem.post_reset_hooks - end - - def suffix_pattern - Gem.suffix_pattern - end - - def gem_cache - gem_path.map {|p| File.expand_path("cache", p) } - end - - 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 - - def marshal_spec_dir - Gem::MARSHAL_SPEC_DIR - end - - def config_map - Gem::ConfigMap - end - - def repository_subdirectories - %w[cache doc gems specifications] - end - - def clear_paths - Gem.clear_paths - end - - def bin_path(gem, bin, ver) - Gem.bin_path(gem, bin, ver) - end - - def path_separator - File::PATH_SEPARATOR - end - - def preserve_paths - # this is a no-op outside of RubyGems 1.8 - yield - end - - def loaded_gem_paths - # RubyGems 2.2+ can put binary extension into dedicated folders, - # therefore use RubyGems facilities to obtain their load paths. - if Gem::Specification.method_defined? :full_require_paths - loaded_gem_paths = Gem.loaded_specs.map {|_, s| s.full_require_paths } - loaded_gem_paths.flatten - else - $LOAD_PATH.select do |p| - Bundler.rubygems.gem_path.any? {|gp| p =~ /^#{Regexp.escape(gp)}/ } - end - end - end - - def load_plugins - Gem.load_plugins if Gem.respond_to?(:load_plugins) - end - - def load_plugin_files(files) - Gem.load_plugin_files(files) if Gem.respond_to?(:load_plugin_files) - end - - def ui=(obj) - Gem::DefaultUserInteraction.ui = obj - end - - def ext_lock - EXT_LOCK - end - - def fetch_specs(all, pre, &blk) - require "rubygems/spec_fetcher" - specs = Gem::SpecFetcher.new.list(all, pre) - specs.each { yield } if block_given? - specs - end - - def fetch_prerelease_specs - fetch_specs(false, true) - rescue Gem::RemoteFetcher::FetchError - {} # 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 - Bundler.rubygems.sources = [remote.uri.to_s] - # Fetch all specs, minus prerelease specs - spec_list = fetch_specs(true, false) - # Then fetch the prerelease specs - fetch_prerelease_specs.each {|k, v| spec_list[k].concat(v) } - - spec_list.values.first - ensure - Bundler.rubygems.sources = old_sources - end - - def with_build_args(args) - ext_lock.synchronize do - old_args = build_args - begin - self.build_args = args - yield - ensure - self.build_args = old_args - end - end - end - - def install_with_build_args(args) - with_build_args(args) { yield } - end - - def gem_from_path(path, policy = nil) - require "rubygems/format" - Gem::Format.from_file_by_path(path, policy) - end - - def spec_from_gem(path, policy = nil) - require "rubygems/security" - require "bundler/psyched_yaml" - gem_from_path(path, security_policies[policy]).spec - rescue Gem::Package::FormatError - raise GemspecError, "Could not read gem at #{path}. It may be corrupted." - rescue Exception, Gem::Exception, Gem::Security::Exception => e - if e.is_a?(Gem::Security::Exception) || - e.message =~ /unknown trust policy|unsigned gem/i || - e.message =~ /couldn't verify (meta)?data signature/i - raise SecurityError, - "The gem #{File.basename(path, ".gem")} can't be installed because " \ - "the security policy didn't allow it, with the message: #{e.message}" - else - raise e - end - end - - def build(spec, skip_validation = false) - require "rubygems/builder" - Gem::Builder.new(spec).build - end - - def build_gem(gem_dir, spec) - build(spec) - end - - def download_gem(spec, uri, path) - uri = Bundler.settings.mirror_for(uri) - fetcher = Gem::RemoteFetcher.new(configuration[:http_proxy]) - Bundler::Retry.new("download gem from #{uri}").attempts do - fetcher.download(spec, uri, path) - end - end - - def security_policy_keys - %w[High Medium Low AlmostNo No].map {|level| "#{level}Security" } - end - - def security_policies - @security_policies ||= begin - require "rubygems/security" - Gem::Security::Policies - rescue LoadError, NameError - {} - end - end - - def reverse_rubygems_kernel_mixin - # Disable rubygems' gem activation system - kernel = (class << ::Kernel; self; end) - [kernel, ::Kernel].each do |k| - if k.private_method_defined?(:gem_original_require) - redefine_method(k, :require, k.instance_method(:gem_original_require)) - end - end - end - - def binstubs_call_gem? - true - end - - def stubs_provide_full_functionality? - false - end - - def replace_gem(specs, specs_by_name) - reverse_rubygems_kernel_mixin - - executables = nil - - kernel = (class << ::Kernel; self; end) - [kernel, ::Kernel].each do |kernel_class| - redefine_method(kernel_class, :gem) do |dep, *reqs| - executables ||= specs.map(&:executables).flatten if ::Bundler.rubygems.binstubs_call_gem? - if executables && executables.include?(File.basename(caller.first.split(":").first)) - break - end - - reqs.pop if reqs.last.is_a?(Hash) - - unless dep.respond_to?(:name) && dep.respond_to?(:requirement) - dep = Gem::Dependency.new(dep, reqs) - end - - if spec = specs_by_name[dep.name] - return true if dep.matches_spec?(spec) - end - - message = if spec.nil? - "#{dep.name} is not part of the bundle." \ - " Add it to your #{Bundler.default_gemfile.basename}." - else - "can't activate #{dep}, already activated #{spec.full_name}. " \ - "Make sure all dependencies are added to Gemfile." - end - - e = Gem::LoadError.new(message) - e.name = dep.name - if e.respond_to?(:requirement=) - e.requirement = dep.requirement - elsif e.respond_to?(:version_requirement=) - e.version_requirement = dep.requirement - end - raise e - end - - # backwards compatibility shim, see https://github.com/bundler/bundler/issues/5102 - kernel_class.send(:public, :gem) if Bundler.feature_flag.setup_makes_kernel_gem_public? - end - end - - def stub_source_index(specs) - Gem::SourceIndex.send(:alias_method, :old_initialize, :initialize) - redefine_method(Gem::SourceIndex, :initialize) do |*args| - @gems = {} - # You're looking at this thinking: Oh! This is how I make those - # rubygems deprecations go away! - # - # You'd be correct BUT using of this method in production code - # must be approved by the rubygems team itself! - # - # This is your warning. If you use this and don't have approval - # we can't protect you. - # - Deprecate.skip_during do - self.spec_dirs = *args - add_specs(*specs) - end - end - end - - # Used to make bin stubs that are not created by bundler work - # under bundler. The new Gem.bin_path only considers gems in - # +specs+ - def replace_bin_path(specs, specs_by_name) - gem_class = (class << Gem; self; end) - - redefine_method(gem_class, :find_spec_for_exe) do |gem_name, *args| - exec_name = args.first - - spec_with_name = specs_by_name[gem_name] - spec = if exec_name - if spec_with_name && spec_with_name.executables.include?(exec_name) - spec_with_name - else - specs.find {|s| s.executables.include?(exec_name) } - end - else - spec_with_name - end - - unless spec - message = "can't find executable #{exec_name} for gem #{gem_name}" - if !exec_name || spec_with_name.nil? - message += ". #{gem_name} is not currently included in the bundle, " \ - "perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?" - end - raise Gem::Exception, message - end - - raise Gem::Exception, "no default executable for #{spec.full_name}" unless exec_name ||= spec.default_executable - - unless spec.name == gem_name - Bundler::SharedHelpers.major_deprecation 2, - "Bundler is using a binstub that was created for a different gem (#{spec.name}).\n" \ - "You should run `bundle binstub #{gem_name}` " \ - "to work around a system/bundle conflict." - end - spec - end - - redefine_method(gem_class, :activate_bin_path) do |name, *args| - exec_name = args.first - return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle" - - # Copy of Rubygems activate_bin_path impl - requirement = args.last - spec = find_spec_for_exe name, exec_name, [requirement] - - gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name) - gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name) - File.exist?(gem_bin) ? gem_bin : gem_from_path_bin - end - - redefine_method(gem_class, :bin_path) do |name, *args| - exec_name = args.first - return ENV["BUNDLE_BIN_PATH"] if exec_name == "bundle" - - spec = find_spec_for_exe(name, *args) - exec_name ||= spec.default_executable - - gem_bin = File.join(spec.full_gem_path, spec.bindir, exec_name) - gem_from_path_bin = File.join(File.dirname(spec.loaded_from), spec.bindir, exec_name) - File.exist?(gem_bin) ? gem_bin : gem_from_path_bin - end - end - - # Because Bundler has a static view of what specs are available, - # we don't #refresh, so stub it out. - def replace_refresh - gem_class = (class << Gem; self; end) - 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| - h[s.name] = s - h - end - - replace_gem(specs, specs_by_name) - stub_rubygems(specs) - replace_bin_path(specs, specs_by_name) - replace_refresh - - Gem.clear_paths - end - - # This backports the correct segment generation code from RubyGems 1.4+ - # by monkeypatching 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| - /^\d+$/ =~ s ? s.to_i : s - end - end - end - - # This backport fixes the marshaling of @segments. - def backport_yaml_initialize - redefine_method(Gem::Version, :yaml_initialize) do |_, map| - @version = map["version"] - @segments = nil - @hash = nil - end - 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 - File.dirname File.dirname loaded_from - end - end - - def backport_cache_file - redefine_method(Gem::Specification, :cache_dir) do - @cache_dir ||= File.join base_dir, "cache" - end - - redefine_method(Gem::Specification, :cache_file) do - @cache_file ||= File.join cache_dir, "#{full_name}.gem" - end - end - - def backport_spec_file - redefine_method(Gem::Specification, :spec_dir) do - @spec_dir ||= File.join base_dir, "specifications" - end - - redefine_method(Gem::Specification, :spec_file) do - @spec_file ||= File.join spec_dir, "#{full_name}.gemspec" - end - end - - def undo_replacements - @replaced_methods.each do |(sym, klass), method| - redefine_method(klass, sym, method) - end - post_reset_hooks.reject! do |proc| - proc.binding.eval("__FILE__") == __FILE__ - end - @replaced_methods.clear - end - - def redefine_method(klass, method, unbound_method = nil, &block) - visibility = method_visibility(klass, method) - begin - if (instance_method = klass.instance_method(method)) && method != :initialize - # doing this to ensure we also get private methods - klass.send(:remove_method, method) - end - rescue NameError - # method isn't defined - nil - end - @replaced_methods[[method, klass]] = instance_method - if unbound_method - klass.send(:define_method, method, unbound_method) - klass.send(visibility, method) - elsif block - klass.send(:define_method, method, &block) - klass.send(visibility, method) - end - end - - def method_visibility(klass, method) - if klass.private_method_defined?(method) - :private - elsif klass.protected_method_defined?(method) - :protected - else - :public - end - end - - # RubyGems 1.4 through 1.6 - class Legacy < RubygemsIntegration - def initialize - super - backport_base_dir - backport_cache_file - backport_spec_file - backport_yaml_initialize - 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| - source_index.spec_dirs = *args - source_index.add_specs(*specs) - end - end - end - - def all_specs - Gem.source_index.gems.values - end - - def find_name(name) - Gem.source_index.find_name(name) - end - - def validate(spec) - # These versions of RubyGems always validate in "packaging" mode, - # which is too strict for the kinds of checks we care about. As a - # result, validation is disabled on versions of RubyGems below 1.7. - end - - def post_reset_hooks - [] - end - - def reset - end - end - - # RubyGems versions 1.3.6 and 1.3.7 - class Ancient < Legacy - def initialize - super - backport_segment_generation - end - end - - # RubyGems 1.7 - class Transitional < Legacy - def stub_rubygems(specs) - stub_source_index(specs) - end - - def validate(spec) - # Missing summary is downgraded to a warning in later versions, - # so we set it to an empty string to prevent an exception here. - spec.summary ||= "" - RubygemsIntegration.instance_method(:validate).bind(self).call(spec) - end - end - - # RubyGems 1.8.5-1.8.19 - class Modern < RubygemsIntegration - def stub_rubygems(specs) - Gem::Specification.all = specs - - Gem.post_reset do - Gem::Specification.all = specs - end - - stub_source_index(specs) - end - - def all_specs - Gem::Specification.to_a - end - - def find_name(name) - Gem::Specification.find_all_by_name name - 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 - old_dir = gem_dir - old_path = gem_path - yield - Gem.use_paths(old_dir, old_path) - 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" - Gem::Builder.new(spec).build(skip_validation) - end - end - - # RubyGems 2.0 - class Future < RubygemsIntegration - def stub_rubygems(specs) - Gem::Specification.all = specs - - Gem.post_reset do - Gem::Specification.all = specs - end - - redefine_method((class << Gem; self; end), :finish_resolve) do |*| - [] - end - end - - def all_specs - Gem::Specification.to_a - end - - def find_name(name) - Gem::Specification.find_all_by_name name - end - - def fetch_specs(source, remote, name) - path = source + "#{name}.#{Gem.marshal_version}.gz" - fetcher = gem_remote_fetcher - fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri - string = fetcher.fetch_path(path) - Bundler.load_marshal(string) - rescue Gem::RemoteFetcher::FetchError => e - # it's okay for prerelease to fail - raise e unless name == "prerelease_specs" - end - - def fetch_all_remote_specs(remote) - source = remote.uri.is_a?(URI) ? remote.uri : URI.parse(source.to_s) - - specs = fetch_specs(source, remote, "specs") - pres = fetch_specs(source, remote, "prerelease_specs") || [] - - specs.concat(pres) - end - - def download_gem(spec, uri, path) - uri = Bundler.settings.mirror_for(uri) - fetcher = gem_remote_fetcher - fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri - Bundler::Retry.new("download gem from #{uri}").attempts do - fetcher.download(spec, uri, path) - end - end - - def gem_remote_fetcher - require "resolv" - proxy = configuration[:http_proxy] - dns = Resolv::DNS.new - Bundler::GemRemoteFetcher.new(proxy, dns) - end - - def gem_from_path(path, policy = nil) - require "rubygems/package" - p = Gem::Package.new(path) - p.security_policy = policy if policy - p - end - - def build(spec, skip_validation = false) - require "rubygems/package" - Gem::Package.build(spec, skip_validation) - end - - def repository_subdirectories - Gem::REPOSITORY_SUBDIRECTORIES - end - - def install_with_build_args(args) - yield - end - - def path_separator - Gem.path_separator - end - end - - # RubyGems 2.1.0 - class MoreFuture < Future - def initialize - super - backport_ext_builder_monitor - end - - def all_specs - require "bundler/remote_specification" - Gem::Specification.stubs.map do |stub| - StubSpecification.from_stub(stub) - end - end - - def backport_ext_builder_monitor - # So we can avoid requiring "rubygems/ext" in its entirety - Gem.module_eval <<-RB, __FILE__, __LINE__ + 1 - module Ext - end - RB - - require "rubygems/ext/builder" - - Gem::Ext::Builder.class_eval do - unless const_defined?(:CHDIR_MONITOR) - const_set(:CHDIR_MONITOR, EXT_LOCK) - end - - remove_const(:CHDIR_MUTEX) if const_defined?(:CHDIR_MUTEX) - const_set(:CHDIR_MUTEX, const_get(:CHDIR_MONITOR)) - end - end - - if Gem::Specification.respond_to?(:stubs_for) - def find_name(name) - Gem::Specification.stubs_for(name).map(&:to_spec) - end - else - def find_name(name) - Gem::Specification.stubs.find_all do |spec| - spec.name == name - end.map(&:to_spec) - end - end - - def use_gemdeps(gemfile) - ENV["BUNDLE_GEMFILE"] ||= File.expand_path(gemfile) - require "bundler/gemdeps" - runtime = Bundler.setup - Bundler.ui = nil - activated_spec_names = runtime.requested_specs.map(&:to_spec).sort_by(&:name) - [Gemdeps.new(runtime), activated_spec_names] - end - - if provides?(">= 2.5.2") - # RubyGems-generated binstubs call Kernel#gem - def binstubs_call_gem? - false - end - - # only 2.5.2+ has all of the stub methods we want to use, and since this - # is a performance optimization _only_, - # we'll restrict ourselves to the most - # recent RG versions instead of all versions that have stubs - def stubs_provide_full_functionality? - true - end - end - end - end - - def self.rubygems - @rubygems ||= if RubygemsIntegration.provides?(">= 2.1.0") - RubygemsIntegration::MoreFuture.new - elsif RubygemsIntegration.provides?(">= 1.99.99") - RubygemsIntegration::Future.new - elsif RubygemsIntegration.provides?(">= 1.8.20") - RubygemsIntegration::MoreModern.new - elsif RubygemsIntegration.provides?(">= 1.8.5") - RubygemsIntegration::Modern.new - elsif RubygemsIntegration.provides?(">= 1.8.0") - RubygemsIntegration::AlmostModern.new - elsif RubygemsIntegration.provides?(">= 1.7.0") - 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 -end diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb deleted file mode 100644 index f27597b854..0000000000 --- a/lib/bundler/runtime.rb +++ /dev/null @@ -1,316 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Runtime - include SharedHelpers - - def initialize(root, definition) - @root = root - @definition = definition - end - - def setup(*groups) - @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen? - - groups.map!(&:to_sym) - - # Has to happen first - clean_load_path - - specs = groups.any? ? @definition.specs_for(groups) : requested_specs - - SharedHelpers.set_bundle_environment - Bundler.rubygems.replace_entrypoints(specs) - - # Activate the specs - load_paths = specs.map do |spec| - unless spec.loaded_from - raise GemNotFound, "#{spec.full_name} is missing. Run `bundle install` to get it." - end - - check_for_activated_spec!(spec) - - Bundler.rubygems.mark_loaded(spec) - spec.load_paths.reject {|path| $LOAD_PATH.include?(path) } - end.reverse.flatten - - # See Gem::Specification#add_self_to_load_path (since RubyGems 1.8) - if insert_index = Bundler.rubygems.load_path_insert_index - # Gem directories must come after -I and ENV['RUBYLIB'] - $LOAD_PATH.insert(insert_index, *load_paths) - else - # We are probably testing in core, -I and RUBYLIB don't apply - $LOAD_PATH.unshift(*load_paths) - end - - setup_manpath - - lock(:preserve_unknown_sections => true) - - self - end - - REQUIRE_ERRORS = [ - /^no such file to load -- (.+)$/i, - /^Missing \w+ (?:file\s*)?([^\s]+.rb)$/i, - /^Missing API definition file in (.+)$/i, - /^cannot load such file -- (.+)$/i, - /^dlopen\([^)]*\): Library not loaded: (.+)$/i, - ].freeze - - def require(*groups) - groups.map!(&:to_sym) - groups = [:default] if groups.empty? - - @definition.dependencies.each do |dep| - # Skip the dependency if it is not in any of the requested groups, or - # not for the current platform, or doesn't match the gem constraints. - next unless (dep.groups & groups).any? && dep.should_include? - - required_file = nil - - begin - # Loop through all the specified autorequires for the - # dependency. If there are none, use the dependency's name - # as the autorequire. - Array(dep.autorequire || dep.name).each do |file| - # Allow `require: true` as an alias for `require: <name>` - file = dep.name if file == true - required_file = file - begin - Kernel.require file - rescue => e - raise e if e.is_a?(LoadError) # we handle this a little later - raise Bundler::GemRequireError.new e, - "There was an error while trying to load the gem '#{file}'." - end - end - rescue LoadError => e - REQUIRE_ERRORS.find {|r| r =~ e.message } - raise if dep.autorequire || $1 != required_file - - if dep.autorequire.nil? && dep.name.include?("-") - begin - namespaced_file = dep.name.tr("-", "/") - Kernel.require namespaced_file - rescue LoadError => e - REQUIRE_ERRORS.find {|r| r =~ e.message } - raise if $1 != namespaced_file - end - end - end - end - end - - def self.definition_method(meth) - define_method(meth) do - raise ArgumentError, "no definition when calling Runtime##{meth}" unless @definition - @definition.send(meth) - end - end - private_class_method :definition_method - - definition_method :requested_specs - definition_method :specs - definition_method :dependencies - definition_method :current_dependencies - definition_method :requires - - def lock(opts = {}) - return if @definition.nothing_changed? && !@definition.unlocking? - @definition.lock(Bundler.default_lockfile, opts[:preserve_unknown_sections]) - end - - alias_method :gems, :specs - - def cache(custom_path = nil) - cache_path = Bundler.app_cache(custom_path) - SharedHelpers.filesystem_access(cache_path) do |p| - FileUtils.mkdir_p(p) - end unless File.exist?(cache_path) - - Bundler.ui.info "Updating files in #{Bundler.settings.app_cache_path}" - - specs_to_cache = Bundler.settings[:cache_all_platforms] ? @definition.resolve.materialized_for_all_platforms : specs - specs_to_cache.each do |spec| - next if spec.name == "bundler" - next if spec.source.is_a?(Source::Gemspec) - spec.source.send(:fetch_gem, spec) if Bundler.settings[:cache_all_platforms] && spec.source.respond_to?(:fetch_gem, true) - spec.source.cache(spec, custom_path) if spec.source.respond_to?(:cache) - end - - Dir[cache_path.join("*/.git")].each do |git_dir| - FileUtils.rm_rf(git_dir) - FileUtils.touch(File.expand_path("../.bundlecache", git_dir)) - end - - prune_cache(cache_path) unless Bundler.settings[:no_prune] - end - - def prune_cache(cache_path) - SharedHelpers.filesystem_access(cache_path) do |p| - FileUtils.mkdir_p(p) - end unless File.exist?(cache_path) - resolve = @definition.resolve - prune_gem_cache(resolve, cache_path) - prune_git_and_path_cache(resolve, cache_path) - end - - def clean(dry_run = false) - gem_bins = Dir["#{Gem.dir}/bin/*"] - git_dirs = Dir["#{Gem.dir}/bundler/gems/*"] - git_cache_dirs = Dir["#{Gem.dir}/cache/bundler/git/*"] - gem_dirs = Dir["#{Gem.dir}/gems/*"] - gem_files = Dir["#{Gem.dir}/cache/*.gem"] - gemspec_files = Dir["#{Gem.dir}/specifications/*.gemspec"] - spec_gem_paths = [] - # need to keep git sources around - spec_git_paths = @definition.spec_git_paths - spec_git_cache_dirs = [] - spec_gem_executables = [] - spec_cache_paths = [] - spec_gemspec_paths = [] - specs.each do |spec| - spec_gem_paths << spec.full_gem_path - # need to check here in case gems are nested like for the rails git repo - md = %r{(.+bundler/gems/.+-[a-f0-9]{7,12})}.match(spec.full_gem_path) - spec_git_paths << md[1] if md - spec_gem_executables << spec.executables.collect do |executable| - e = "#{Bundler.rubygems.gem_bindir}/#{executable}" - [e, "#{e}.bat"] - end - spec_cache_paths << spec.cache_file - spec_gemspec_paths << spec.spec_file - spec_git_cache_dirs << spec.source.cache_path.to_s if spec.source.is_a?(Bundler::Source::Git) - end - spec_gem_paths.uniq! - spec_gem_executables.flatten! - - stale_gem_bins = gem_bins - spec_gem_executables - stale_git_dirs = git_dirs - spec_git_paths - ["#{Gem.dir}/bundler/gems/extensions"] - stale_git_cache_dirs = git_cache_dirs - spec_git_cache_dirs - stale_gem_dirs = gem_dirs - spec_gem_paths - stale_gem_files = gem_files - spec_cache_paths - stale_gemspec_files = gemspec_files - spec_gemspec_paths - - removed_stale_gem_dirs = stale_gem_dirs.collect {|dir| remove_dir(dir, dry_run) } - removed_stale_git_dirs = stale_git_dirs.collect {|dir| remove_dir(dir, dry_run) } - output = removed_stale_gem_dirs + removed_stale_git_dirs - - unless dry_run - stale_files = stale_gem_bins + stale_gem_files + stale_gemspec_files - stale_files.each do |file| - SharedHelpers.filesystem_access(File.dirname(file)) do |_p| - FileUtils.rm(file) if File.exist?(file) - end - end - stale_git_cache_dirs.each do |cache_dir| - SharedHelpers.filesystem_access(cache_dir) do |dir| - FileUtils.rm_rf(dir) if File.exist?(dir) - end - end - end - - output - end - - private - - def prune_gem_cache(resolve, cache_path) - cached = Dir["#{cache_path}/*.gem"] - - cached = cached.delete_if do |path| - spec = Bundler.rubygems.spec_from_gem path - - resolve.any? do |s| - s.name == spec.name && s.version == spec.version && !s.source.is_a?(Bundler::Source::Git) - end - end - - if cached.any? - Bundler.ui.info "Removing outdated .gem files from #{Bundler.settings.app_cache_path}" - - cached.each do |path| - Bundler.ui.info " * #{File.basename(path)}" - File.delete(path) - end - end - end - - def prune_git_and_path_cache(resolve, cache_path) - cached = Dir["#{cache_path}/*/.bundlecache"] - - cached = cached.delete_if do |path| - name = File.basename(File.dirname(path)) - - resolve.any? do |s| - source = s.source - source.respond_to?(:app_cache_dirname) && source.app_cache_dirname == name - end - end - - if cached.any? - Bundler.ui.info "Removing outdated git and path gems from #{Bundler.settings.app_cache_path}" - - cached.each do |path| - path = File.dirname(path) - Bundler.ui.info " * #{File.basename(path)}" - FileUtils.rm_rf(path) - end - end - end - - def setup_manpath - # Add man/ subdirectories from activated bundles to MANPATH for man(1) - manuals = $LOAD_PATH.map do |path| - man_subdir = path.sub(/lib$/, "man") - man_subdir unless Dir[man_subdir + "/man?/"].empty? - end.compact - - return if manuals.empty? - Bundler::SharedHelpers.set_env "MANPATH", manuals.concat( - ENV["MANPATH"].to_s.split(File::PATH_SEPARATOR) - ).uniq.join(File::PATH_SEPARATOR) - end - - def remove_dir(dir, dry_run) - full_name = Pathname.new(dir).basename.to_s - - parts = full_name.split("-") - name = parts[0..-2].join("-") - version = parts.last - output = "#{name} (#{version})" - - if dry_run - Bundler.ui.info "Would have removed #{output}" - else - Bundler.ui.info "Removing #{output}" - FileUtils.rm_rf(dir) - end - - output - end - - def check_for_activated_spec!(spec) - return unless activated_spec = Bundler.rubygems.loaded_specs(spec.name) - return if activated_spec.version == spec.version - - suggestion = if Bundler.rubygems.spec_default_gem?(activated_spec) - "Since #{spec.name} is a default gem, you can either remove your dependency on it" \ - " or try updating to a newer version of bundler that supports #{spec.name} as a default gem." - else - "Prepending `bundle exec` to your command may solve this." - end - - e = Gem::LoadError.new "You have already activated #{activated_spec.name} #{activated_spec.version}, " \ - "but your Gemfile requires #{spec.name} #{spec.version}. #{suggestion}" - e.name = spec.name - if e.respond_to?(:requirement=) - e.requirement = Gem::Requirement.new(spec.version.to_s) - else - e.version_requirement = Gem::Requirement.new(spec.version.to_s) - end - raise e - end - end -end diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb deleted file mode 100644 index f33e9453be..0000000000 --- a/lib/bundler/settings.rb +++ /dev/null @@ -1,442 +0,0 @@ -# frozen_string_literal: true - -require "uri" - -module Bundler - class Settings - autoload :Mirror, "bundler/mirror" - autoload :Mirrors, "bundler/mirror" - autoload :Validator, "bundler/settings/validator" - - BOOL_KEYS = %w[ - allow_bundler_dependency_conflicts - allow_deployment_source_credential_changes - allow_offline_install - auto_clean_without_path - auto_install - cache_all - cache_all_platforms - cache_command_is_package - console_command - default_install_uses_path - deployment - deployment_means_frozen - disable_checksum_validation - disable_exec_load - disable_local_branch_check - disable_multisource - disable_shared_gems - disable_version_check - error_on_stderr - force_ruby_platform - forget_cli_options - frozen - gem.coc - gem.mit - global_gem_cache - ignore_messages - init_gems_rb - list_command - lockfile_uses_separate_rubygems_sources - major_deprecations - no_install - no_prune - only_update_to_newer_versions - path.system - plugins - prefer_gems_rb - print_only_version_number - setup_makes_kernel_gem_public - silence_root_warning - skip_default_git_sources - specific_platform - suppress_install_using_messages - unlock_source_unlocks_spec - update_requires_all_flag - ].freeze - - NUMBER_KEYS = %w[ - redirect - retry - ssl_verify_mode - timeout - ].freeze - - ARRAY_KEYS = %w[ - with - without - ].freeze - - DEFAULT_CONFIG = { - :disable_version_check => true, - :redirect => 5, - :retry => 3, - :timeout => 10, - }.freeze - - def initialize(root = nil) - @root = root - @local_config = load_config(local_config_file) - @global_config = load_config(global_config_file) - @temporary = {} - end - - def [](name) - key = key_for(name) - value = @temporary.fetch(key) do - @local_config.fetch(key) do - ENV.fetch(key) do - @global_config.fetch(key) do - DEFAULT_CONFIG.fetch(name) do - nil - end end end end end - - converted_value(value, name) - end - - def set_command_option(key, value) - if Bundler.feature_flag.forget_cli_options? - temporary(key => value) - value - else - command = if value.nil? - "bundle config --delete #{key}" - else - "bundle config #{key} #{Array(value).join(":")}" - end - - Bundler::SharedHelpers.major_deprecation 2,\ - "flags passed to commands " \ - "will no longer be automatically remembered. Instead please set flags " \ - "you want remembered between commands using `bundle config " \ - "<setting name> <setting value>`, i.e. `#{command}`" - - set_local(key, value) - end - end - - def set_command_option_if_given(key, value) - return if value.nil? - set_command_option(key, value) - end - - def set_local(key, value) - local_config_file || raise(GemfileNotFound, "Could not locate Gemfile") - - set_key(key, value, @local_config, local_config_file) - end - - def temporary(update) - existing = Hash[update.map {|k, _| [k, @temporary[key_for(k)]] }] - update.each do |k, v| - set_key(k, v, @temporary, nil) - end - return unless block_given? - begin - yield - ensure - existing.each {|k, v| set_key(k, v, @temporary, nil) } - end - end - - def set_global(key, value) - set_key(key, value, @global_config, global_config_file) - end - - def all - env_keys = ENV.keys.grep(/\ABUNDLE_.+/) - - keys = @temporary.keys | @global_config.keys | @local_config.keys | env_keys - - keys.map do |key| - key.sub(/^BUNDLE_/, "").gsub(/__/, ".").downcase - end - end - - def local_overrides - repos = {} - all.each do |k| - repos[$'] = self[k] if k =~ /^local\./ - end - repos - end - - def mirror_for(uri) - uri = URI(uri.to_s) unless uri.is_a?(URI) - gem_mirrors.for(uri.to_s).uri - end - - def credentials_for(uri) - self[uri.to_s] || self[uri.host] - end - - def gem_mirrors - all.inject(Mirrors.new) do |mirrors, k| - mirrors.parse(k, self[k]) if k.start_with?("mirror.") - mirrors - end - end - - def locations(key) - key = key_for(key) - locations = {} - locations[:temporary] = @temporary[key] if @temporary.key?(key) - 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) - locations[:default] = DEFAULT_CONFIG[key] if DEFAULT_CONFIG.key?(key) - locations - end - - def pretty_values_for(exposed_key) - key = key_for(exposed_key) - - locations = [] - - if @temporary.key?(key) - locations << "Set for the current command: #{converted_value(@temporary[key], exposed_key).inspect}" - end - - if @local_config.key?(key) - locations << "Set for your local app (#{local_config_file}): #{converted_value(@local_config[key], exposed_key).inspect}" - end - - if value = ENV[key] - locations << "Set via #{key}: #{converted_value(value, exposed_key).inspect}" - end - - if @global_config.key?(key) - locations << "Set for the current user (#{global_config_file}): #{converted_value(@global_config[key], exposed_key).inspect}" - end - - return ["You have not configured a value for `#{exposed_key}`"] if locations.empty? - locations - end - - # for legacy reasons, the ruby scope isnt appended when the setting comes from ENV or the global config, - # nor do we respect :disable_shared_gems - def path - key = key_for(:path) - path = ENV[key] || @global_config[key] - if path && !@temporary.key?(key) && !@local_config.key?(key) - return Path.new(path, false, false, false) - end - - system_path = self["path.system"] || (self[:disable_shared_gems] == false) - Path.new(self[:path], true, system_path, Bundler.feature_flag.default_install_uses_path?) - end - - Path = Struct.new(:explicit_path, :append_ruby_scope, :system_path, :default_install_uses_path) do - def path - path = base_path - path = File.join(path, Bundler.ruby_scope) if append_ruby_scope && !use_system_gems? - path - end - - def use_system_gems? - return true if system_path - return false if explicit_path - !default_install_uses_path - end - - def base_path - path = explicit_path - path ||= ".bundle" unless use_system_gems? - path ||= Bundler.rubygems.gem_dir - path - end - - def validate! - return unless explicit_path && system_path - path = Bundler.settings.pretty_values_for(:path) - path.unshift(nil, "path:") unless path.empty? - system_path = Bundler.settings.pretty_values_for("path.system") - system_path.unshift(nil, "path.system:") unless system_path.empty? - disable_shared_gems = Bundler.settings.pretty_values_for(:disable_shared_gems) - disable_shared_gems.unshift(nil, "disable_shared_gems:") unless disable_shared_gems.empty? - raise InvalidOption, - "Using a custom path while using system gems is unsupported.\n#{path.join("\n")}\n#{system_path.join("\n")}\n#{disable_shared_gems.join("\n")}" - end - end - - def allow_sudo? - key = key_for(:path) - path_configured = @temporary.key?(key) || @local_config.key?(key) - !path_configured - end - - def ignore_config? - ENV["BUNDLE_IGNORE_CONFIG"] - end - - def app_cache_path - @app_cache_path ||= self[:cache_path] || "vendor/cache" - end - - def validate! - all.each do |raw_key| - [@local_config, ENV, @global_config].each do |settings| - value = converted_value(settings[key_for(raw_key)], raw_key) - Validator.validate!(raw_key, value, settings.to_hash.dup) - end - end - end - - def key_for(key) - key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key - key = key.to_s.gsub(".", "__").upcase - "BUNDLE_#{key}" - end - - private - - def parent_setting_for(name) - split_specific_setting_for(name)[0] - end - - def specific_gem_for(name) - split_specific_setting_for(name)[1] - end - - def split_specific_setting_for(name) - name.split(".") - end - - def is_bool(name) - BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s)) - end - - def to_bool(value) - case value - when nil, /\A(false|f|no|n|0|)\z/i, false - false - else - true - end - end - - def is_num(key) - NUMBER_KEYS.include?(key.to_s) - end - - def is_array(key) - ARRAY_KEYS.include?(key.to_s) - end - - def to_array(value) - return [] unless value - value.split(":").map(&:to_sym) - end - - def array_to_s(array) - array = Array(array) - return nil if array.empty? - array.join(":").tr(" ", ":") - end - - def set_key(raw_key, value, hash, file) - raw_key = raw_key.to_s - value = array_to_s(value) if is_array(raw_key) - - key = key_for(raw_key) - - return if hash[key] == value - - hash[key] = value - hash.delete(key) if value.nil? - - Validator.validate!(raw_key, converted_value(value, raw_key), hash) - - return unless file - 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 - - def converted_value(value, key) - if is_array(key) - to_array(value) - elsif value.nil? - nil - elsif is_bool(key) || value == "false" - to_bool(value) - elsif is_num(key) - value.to_i - else - value.to_s - end - end - - def global_config_file - if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty? - Pathname.new(ENV["BUNDLE_CONFIG"]) - else - begin - Bundler.user_bundle_path.join("config") - rescue PermissionError, GenericSystemCallError - nil - end - end - end - - def local_config_file - Pathname.new(@root).join("config") if @root - end - - CONFIG_REGEX = %r{ # rubocop:disable Style/RegexpLiteral - ^ - (BUNDLE_.+):\s # the key - (?: !\s)? # optional exclamation mark found with ruby 1.9.3 - (['"]?) # optional opening quote - (.* # contents of the value - (?: # optionally, up until the next key - (\n(?!BUNDLE).+)* - ) - ) - \2 # matching closing quote - $ - }xo - - def load_config(config_file) - return {} if !config_file || ignore_config? - SharedHelpers.filesystem_access(config_file, :read) do |file| - valid_file = file.exist? && !file.size.zero? - return {} unless valid_file - require "bundler/yaml_serializer" - YAMLSerializer.load file.read - end - end - - PER_URI_OPTIONS = %w[ - fallback_timeout - ].freeze - - NORMALIZE_URI_OPTIONS_PATTERN = - / - \A - (\w+\.)? # optional prefix key - (https?.*?) # URI - (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key - \z - /ix - - # TODO: duplicates Rubygems#normalize_uri - # TODO: is this the correct place to validate mirror URIs? - def self.normalize_uri(uri) - uri = uri.to_s - if uri =~ NORMALIZE_URI_OPTIONS_PATTERN - prefix = $1 - uri = $2 - suffix = $3 - end - uri = "#{uri}/" unless uri.end_with?("/") - uri = URI(uri) - unless uri.absolute? - raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri) - end - "#{prefix}#{uri}#{suffix}" - end - end -end diff --git a/lib/bundler/settings/validator.rb b/lib/bundler/settings/validator.rb deleted file mode 100644 index 9aa1627fb2..0000000000 --- a/lib/bundler/settings/validator.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Settings - class Validator - class Rule - attr_reader :description - - def initialize(keys, description, &validate) - @keys = keys - @description = description - @validate = validate - end - - def validate!(key, value, settings) - instance_exec(key, value, settings, &@validate) - end - - def fail!(key, value, *reasons) - reasons.unshift @description - raise InvalidOption, "Setting `#{key}` to #{value.inspect} failed:\n#{reasons.map {|r| " - #{r}" }.join("\n")}" - end - - def set(settings, key, value, *reasons) - hash_key = k(key) - return if settings[hash_key] == value - reasons.unshift @description - Bundler.ui.info "Setting `#{key}` to #{value.inspect}, since #{reasons.join(", ")}" - if value.nil? - settings.delete(hash_key) - else - settings[hash_key] = value - end - end - - def k(key) - Bundler.settings.key_for(key) - end - end - - def self.rules - @rules ||= Hash.new {|h, k| h[k] = [] } - end - private_class_method :rules - - def self.rule(keys, description, &blk) - rule = Rule.new(keys, description, &blk) - keys.each {|k| rules[k] << rule } - end - private_class_method :rule - - def self.validate!(key, value, settings) - rules_to_validate = rules[key] - rules_to_validate.each {|rule| rule.validate!(key, value, settings) } - end - - rule %w[path path.system], "path and path.system are mutually exclusive" do |key, value, settings| - if key == "path" && value - set(settings, "path.system", nil) - elsif key == "path.system" && value - set(settings, :path, nil) - end - end - - rule %w[with without], "a group cannot be in both `with` & `without` simultaneously" do |key, value, settings| - with = settings.fetch(k(:with), "").split(":").map(&:to_sym) - without = settings.fetch(k(:without), "").split(":").map(&:to_sym) - - other_key = key == "with" ? :without : :with - other_setting = key == "with" ? without : with - - conflicting = with & without - if conflicting.any? - fail!(key, value, "`#{other_key}` is current set to #{other_setting.inspect}", "the `#{conflicting.join("`, `")}` groups conflict") - end - end - end - end -end diff --git a/lib/bundler/setup.rb b/lib/bundler/setup.rb deleted file mode 100644 index ac6a5bf861..0000000000 --- a/lib/bundler/setup.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require "bundler/shared_helpers" - -if Bundler::SharedHelpers.in_bundle? - require "bundler" - - if STDOUT.tty? || ENV["BUNDLER_FORCE_TTY"] - begin - Bundler.setup - rescue Bundler::BundlerError => e - puts "\e[31m#{e.message}\e[0m" - puts e.backtrace.join("\n") if ENV["DEBUG"] - if e.is_a?(Bundler::GemNotFound) - puts "\e[33mRun `bundle install` to install missing gems.\e[0m" - end - exit e.status_code - end - else - Bundler.setup - end - - # Add bundler to the load path after disabling system gems - bundler_lib = File.expand_path("../..", __FILE__) - $LOAD_PATH.unshift(bundler_lib) unless $LOAD_PATH.include?(bundler_lib) - - Bundler.ui = nil -end diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb deleted file mode 100644 index 5566bc5832..0000000000 --- a/lib/bundler/shared_helpers.rb +++ /dev/null @@ -1,366 +0,0 @@ -# frozen_string_literal: true - -require "bundler/compatibility_guard" - -require "pathname" -require "rubygems" - -require "bundler/version" -require "bundler/constants" -require "bundler/rubygems_integration" -require "bundler/current_ruby" - -module Gem - class Dependency - # This is only needed for RubyGems < 1.4 - unless method_defined? :requirement - def requirement - version_requirements - end - end - end -end - -module Bundler - module SharedHelpers - def root - gemfile = find_gemfile - raise GemfileNotFound, "Could not locate Gemfile" unless gemfile - Pathname.new(gemfile).untaint.expand_path.parent - end - - def default_gemfile - gemfile = find_gemfile(:order_matters) - raise GemfileNotFound, "Could not locate Gemfile" unless gemfile - Pathname.new(gemfile).untaint.expand_path - end - - def default_lockfile - gemfile = default_gemfile - - case gemfile.basename.to_s - when "gems.rb" then Pathname.new(gemfile.sub(/.rb$/, ".locked")) - else Pathname.new("#{gemfile}.lock") - end.untaint - end - - def default_bundle_dir - bundle_dir = find_directory(".bundle") - return nil unless bundle_dir - - bundle_dir = Pathname.new(bundle_dir) - - global_bundle_dir = Bundler.user_home.join(".bundle") - return nil if bundle_dir == global_bundle_dir - - bundle_dir - end - - def in_bundle? - find_gemfile - end - - def chdir(dir, &blk) - Bundler.rubygems.ext_lock.synchronize do - Dir.chdir dir, &blk - end - end - - def pwd - Bundler.rubygems.ext_lock.synchronize do - Pathname.pwd - end - 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 - - keys.each {|key| ENV.delete(key) } - - block.call - ensure - keys.each {|key| ENV[key] = old_env[key] } - end - - def set_bundle_environment - set_bundle_variables - set_path - set_rubyopt - set_rubylib - end - - # Rescues permissions errors raised by file system operations - # (ie. Errno:EACCESS, Errno::EAGAIN) and raises more friendly errors instead. - # - # @param path [String] the path that the action will be attempted to - # @param action [Symbol, #to_s] the type of operation that will be - # performed. For example: :write, :read, :exec - # - # @yield path - # - # @raise [Bundler::PermissionError] if Errno:EACCES is raised in the - # given block - # @raise [Bundler::TemporaryResourceError] if Errno:EAGAIN is raised in the - # given block - # - # @example - # filesystem_access("vendor/cache", :write) do - # FileUtils.mkdir_p("vendor/cache") - # end - # - # @see {Bundler::PermissionError} - def filesystem_access(path, action = :write, &block) - # Use block.call instead of yield because of a bug in Ruby 2.2.2 - # See https://github.com/bundler/bundler/issues/5341 for details - block.call(path.dup.untaint) - rescue Errno::EACCES - raise PermissionError.new(path, action) - rescue Errno::EAGAIN - raise TemporaryResourceError.new(path, action) - rescue Errno::EPROTO - raise VirtualProtocolError.new - rescue Errno::ENOSPC - raise NoSpaceOnDeviceError.new(path, action) - rescue *[const_get_safely(:ENOTSUP, Errno)].compact - raise OperationNotSupportedError.new(path, action) - rescue Errno::EEXIST, Errno::ENOENT - raise - rescue SystemCallError => e - raise GenericSystemCallError.new(e, "There was an error accessing `#{path}`.") - end - - def const_get_safely(constant_name, namespace) - const_in_namespace = namespace.constants.include?(constant_name.to_s) || - namespace.constants.include?(constant_name.to_sym) - return nil unless const_in_namespace - namespace.const_get(constant_name) - end - - def major_deprecation(major_version, message) - if Bundler.bundler_major_version >= major_version - require "bundler/errors" - raise DeprecatedError, "[REMOVED FROM #{major_version}.0] #{message}" - end - - 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 #{major_version}.0] #{message}") - end - - def print_major_deprecations! - multiple_gemfiles = search_up(".") do |dir| - gemfiles = gemfile_names.select {|gf| File.file? File.expand_path(gf, dir) } - next if gemfiles.empty? - break false if gemfiles.size == 1 - end - if multiple_gemfiles && Bundler.bundler_major_version == 1 - Bundler::SharedHelpers.major_deprecation 2, \ - "gems.rb and gems.locked will be preferred to Gemfile and Gemfile.lock." - end - - if RUBY_VERSION < "2" - major_deprecation(2, "Bundler will only support ruby >= 2.0, you are running #{RUBY_VERSION}") - end - return if Bundler.rubygems.provides?(">= 2") - major_deprecation(2, "Bundler will only support rubygems >= 2.0, you are running #{Bundler.rubygems.version}") - end - - def trap(signal, override = false, &block) - prior = Signal.trap(signal) do - block.call - prior.call unless override - end - end - - def ensure_same_dependencies(spec, old_deps, new_deps) - new_deps = new_deps.reject {|d| d.type == :development } - old_deps = old_deps.reject {|d| d.type == :development } - - without_type = proc {|d| Gem::Dependency.new(d.name, d.requirements_list.sort) } - new_deps.map!(&without_type) - old_deps.map!(&without_type) - - extra_deps = new_deps - old_deps - return if extra_deps.empty? - - Bundler.ui.debug "#{spec.full_name} from #{spec.remote} has either corrupted API or lockfile dependencies" \ - " (was expecting #{old_deps.map(&:to_s)}, but the real spec has #{new_deps.map(&:to_s)})" - raise APIResponseMismatchError, - "Downloading #{spec.full_name} revealed dependencies not in the API or the lockfile (#{extra_deps.join(", ")})." \ - "\nEither installing with `--full-index` or running `bundle update #{spec.name}` should fix the problem." - end - - def pretty_dependency(dep, print_source = false) - msg = String.new(dep.name) - msg << " (#{dep.requirement})" unless dep.requirement == Gem::Requirement.default - if dep.is_a?(Bundler::Dependency) - platform_string = dep.platforms.join(", ") - msg << " " << platform_string if !platform_string.empty? && platform_string != Gem::Platform::RUBY - end - msg << " from the `#{dep.source}` source" if print_source && dep.source - msg - end - - def md5_available? - return @md5_available if defined?(@md5_available) - @md5_available = begin - require "openssl" - OpenSSL::Digest::MD5.digest("") - true - rescue LoadError - true - rescue OpenSSL::Digest::DigestError - false - end - end - - def digest(name) - require "digest" - Digest(name) - end - - private - - def validate_bundle_path - path_separator = Bundler.rubygems.path_separator - return unless Bundler.bundle_path.to_s.split(path_separator).size > 1 - message = "Your bundle path contains text matching #{path_separator.inspect}, " \ - "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 match #{path_separator.inspect}." \ - "\nYour current bundle path is '#{Bundler.bundle_path}'." - raise Bundler::PathError, message - end - - def find_gemfile(order_matters = false) - given = ENV["BUNDLE_GEMFILE"] - return given if given && !given.empty? - names = gemfile_names - names.reverse! if order_matters && Bundler.feature_flag.prefer_gems_rb? - find_file(*names) - end - - def gemfile_names - ["Gemfile", "gems.rb"] - end - - def find_file(*names) - search_up(*names) do |filename| - return filename if File.file?(filename) - end - end - - def find_directory(*names) - search_up(*names) do |dirname| - return dirname if File.directory?(dirname) - end - end - - def search_up(*names) - previous = nil - current = File.expand_path(SharedHelpers.pwd).untaint - - until !File.directory?(current) || current == previous - if ENV["BUNDLE_SPEC_RUN"] - # avoid stepping above the tmp directory when testing - if !!(ENV["BUNDLE_RUBY"] && ENV["BUNDLE_GEM"]) - # for Ruby Core - gemspec = "lib/bundler.gemspec" - else - gemspec = "bundler.gemspec" - end - return nil if File.file?(File.join(current, gemspec)) - end - - names.each do |name| - filename = File.join(current, name) - yield filename - end - previous = current - current = File.expand_path("..", current) - end - end - - def set_env(key, value) - raise ArgumentError, "new key #{key}" unless EnvironmentPreserver::BUNDLER_KEYS.include?(key) - orig_key = "#{EnvironmentPreserver::BUNDLER_PREFIX}#{key}" - orig = ENV[key] - orig ||= EnvironmentPreserver::INTENTIONALLY_NIL - ENV[orig_key] ||= orig - - ENV[key] = value - end - public :set_env - - def set_bundle_variables - begin - Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", Bundler.rubygems.bin_path("bundler", "bundle", VERSION) - rescue Gem::GemNotFoundException - if File.exist?(File.expand_path("../../../exe/bundle", __FILE__)) - Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", File.expand_path("../../../exe/bundle", __FILE__) - else - Bundler::SharedHelpers.set_env "BUNDLE_BIN_PATH", File.expand_path("../../../../bin/bundle", __FILE__) - end - end - - # Set BUNDLE_GEMFILE - Bundler::SharedHelpers.set_env "BUNDLE_GEMFILE", find_gemfile(:order_matters).to_s - Bundler::SharedHelpers.set_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" - Bundler::SharedHelpers.set_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) - Bundler::SharedHelpers.set_env "RUBYOPT", rubyopt.join(" ") - end - - def set_rubylib - rubylib = (ENV["RUBYLIB"] || "").split(File::PATH_SEPARATOR) - rubylib.unshift bundler_ruby_lib - Bundler::SharedHelpers.set_env "RUBYLIB", rubylib.uniq.join(File::PATH_SEPARATOR) - end - - def bundler_ruby_lib - File.expand_path("../..", __FILE__) - end - - def clean_load_path - # handle 1.9 where system gems are always on the load path - return unless defined?(::Gem) - - bundler_lib = bundler_ruby_lib - - loaded_gem_paths = Bundler.rubygems.loaded_gem_paths - - $LOAD_PATH.reject! do |p| - next if File.expand_path(p).start_with?(bundler_lib) - loaded_gem_paths.delete(p) - end - $LOAD_PATH.uniq! - end - - def prints_major_deprecations? - require "bundler" - deprecation_release = Bundler::VERSION.split(".").drop(1).include?("99") - return false if !deprecation_release && !Bundler.settings[:major_deprecations] - require "bundler/deprecate" - return false if Bundler::Deprecate.skip - true - end - - extend self - end -end diff --git a/lib/bundler/similarity_detector.rb b/lib/bundler/similarity_detector.rb deleted file mode 100644 index b7f3ee7afa..0000000000 --- a/lib/bundler/similarity_detector.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class SimilarityDetector - SimilarityScore = Struct.new(:string, :distance) - - # initialize with an array of words to be matched against - def initialize(corpus) - @corpus = corpus - end - - # return an array of words similar to 'word' from the corpus - def similar_words(word, limit = 3) - words_by_similarity = @corpus.map {|w| SimilarityScore.new(w, levenshtein_distance(word, w)) } - words_by_similarity.select {|s| s.distance <= limit }.sort_by(&:distance).map(&:string) - end - - # return the result of 'similar_words', concatenated into a list - # (eg "a, b, or c") - def similar_word_list(word, limit = 3) - words = similar_words(word, limit) - if words.length == 1 - words[0] - elsif words.length > 1 - [words[0..-2].join(", "), words[-1]].join(" or ") - end - end - - protected - - # http://www.informit.com/articles/article.aspx?p=683059&seqNum=36 - def levenshtein_distance(this, that, ins = 2, del = 2, sub = 1) - # ins, del, sub are weighted costs - return nil if this.nil? - return nil if that.nil? - dm = [] # distance matrix - - # Initialize first row values - dm[0] = (0..this.length).collect {|i| i * ins } - fill = [0] * (this.length - 1) - - # Initialize first column values - (1..that.length).each do |i| - dm[i] = [i * del, fill.flatten] - end - - # populate matrix - (1..that.length).each do |i| - (1..this.length).each do |j| - # critical comparison - dm[i][j] = [ - dm[i - 1][j - 1] + (this[j - 1] == that[i - 1] ? 0 : sub), - dm[i][j - 1] + ins, - dm[i - 1][j] + del - ].min - end - end - - # The last value in matrix is the Levenshtein distance between the strings - dm[that.length][this.length] - end - end -end diff --git a/lib/bundler/source.rb b/lib/bundler/source.rb deleted file mode 100644 index 5a1f05098b..0000000000 --- a/lib/bundler/source.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - autoload :Gemspec, "bundler/source/gemspec" - autoload :Git, "bundler/source/git" - autoload :Metadata, "bundler/source/metadata" - autoload :Path, "bundler/source/path" - autoload :Rubygems, "bundler/source/rubygems" - - attr_accessor :dependency_names - - def unmet_deps - specs.unmet_dependency_names - end - - def version_message(spec) - message = "#{spec.name} #{spec.version}" - message += " (#{spec.platform})" if spec.platform != Gem::Platform::RUBY && !spec.platform.nil? - - if Bundler.locked_gems - locked_spec = Bundler.locked_gems.specs.find {|s| s.name == spec.name } - locked_spec_version = locked_spec.version if locked_spec - if locked_spec_version && spec.version != locked_spec_version - message += Bundler.ui.add_color(" (was #{locked_spec_version})", version_color(spec.version, locked_spec_version)) - end - end - - message - end - - def can_lock?(spec) - spec.source == self - end - - # 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. - def double_check_for(*); end - - def dependency_names_to_double_check - specs.dependency_names - end - - def include?(other) - other == self - end - - def inspect - "#<#{self.class}:0x#{object_id} #{self}>" - end - - def path? - instance_of?(Bundler::Source::Path) - end - - private - - def version_color(spec_version, locked_spec_version) - if Gem::Version.correct?(spec_version) && Gem::Version.correct?(locked_spec_version) - # display yellow if there appears to be a regression - earlier_version?(spec_version, locked_spec_version) ? :yellow : :green - else - # default to green if the versions cannot be directly compared - :green - end - end - - def earlier_version?(spec_version, locked_spec_version) - Gem::Version.new(spec_version) < Gem::Version.new(locked_spec_version) - end - - def print_using_message(message) - if !message.include?("(was ") && Bundler.feature_flag.suppress_install_using_messages? - Bundler.ui.debug message - else - Bundler.ui.info message - end - end - - def extension_cache_path(spec) - return unless Bundler.feature_flag.global_gem_cache? - return unless source_slug = extension_cache_slug(spec) - Bundler.user_cache.join( - "extensions", Gem::Platform.local.to_s, Bundler.ruby_scope, - source_slug, spec.full_name - ) - end - - def extension_cache_slug(_) - nil - end - end -end diff --git a/lib/bundler/source/gemspec.rb b/lib/bundler/source/gemspec.rb deleted file mode 100644 index 7e3447e776..0000000000 --- a/lib/bundler/source/gemspec.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - class Gemspec < Path - attr_reader :gemspec - - def initialize(options) - super - @gemspec = options["gemspec"] - end - - def as_path_source - Path.new(options) - end - end - end -end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb deleted file mode 100644 index a1a59ddce5..0000000000 --- a/lib/bundler/source/git.rb +++ /dev/null @@ -1,328 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_fileutils" -require "uri" - -module Bundler - class Source - class Git < Path - autoload :GitProxy, "bundler/source/git/git_proxy" - - attr_reader :uri, :ref, :branch, :options, :submodules - - def initialize(options) - @options = options - @glob = options["glob"] || DEFAULT_GLOB - - @allow_cached = false - @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"] - @ref = options["ref"] || options["branch"] || options["tag"] || "master" - @submodules = options["submodules"] - @name = options["name"] - @version = options["version"].to_s.strip.gsub("-", ".pre.") - - @copied = false - @local = false - end - - def self.from_lock(options) - new(options.merge("uri" => options.delete("remote"))) - end - - def to_lock - 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 - out << " specs:\n" - end - - def hash - [self.class, uri, ref, branch, name, version, submodules].hash - end - - def eql?(other) - other.is_a?(Git) && uri == other.uri && ref == other.ref && - branch == other.branch && name == other.name && - version == other.version && submodules == other.submodules - end - - alias_method :==, :eql? - - def to_s - at = if local? - path - elsif user_ref = options["ref"] - if ref =~ /\A[a-z0-9]{4,}\z/i - shortref_for_display(user_ref) - else - user_ref - end - else - ref - end - - rev = begin - "@#{shortref_for_display(revision)}" - rescue GitError - nil - end - - "#{uri} (at #{at}#{rev})" - end - - def name - File.basename(@uri, ".git") - end - - # This is the path which is going to contain a specific - # checkout of the git repository. When using local git - # repos, this is set to the local repo. - def install_path - @install_path ||= begin - git_scope = "#{base_name}-#{shortref_for_path(revision)}" - - path = Bundler.install_path.join(git_scope) - - if !path.exist? && Bundler.requires_sudo? - Bundler.user_bundle_path.join(Bundler.ruby_scope).join(git_scope) - else - path - end - end - end - - alias_method :path, :install_path - - def extension_dir_name - "#{base_name}-#{shortref_for_path(revision)}" - end - - def unlock! - git_proxy.revision = nil - options["revision"] = nil - - @unlocked = true - end - - def local_override!(path) - return false if local? - - path = Pathname.new(path) - path = path.expand_path(Bundler.root) unless path.relative? - - unless options["branch"] || Bundler.settings[:disable_local_branch_check] - raise GitError, "Cannot use local override for #{name} at #{path} because " \ - ":branch is not specified in Gemfile. Specify a branch or use " \ - "`bundle config --delete` to remove the local override" - end - - unless path.exist? - raise GitError, "Cannot use local override for #{name} because #{path} " \ - "does not exist. Check `bundle config --delete` to remove the local override" - end - - set_local!(path) - - # Create a new git proxy without the cached revision - # so the Gemfile.lock always picks up the new revision. - @git_proxy = GitProxy.new(path, uri, ref) - - if git_proxy.branch != options["branch"] && !Bundler.settings[:disable_local_branch_check] - raise GitError, "Local override for #{name} at #{path} is using branch " \ - "#{git_proxy.branch} but Gemfile specifies #{options["branch"]}" - end - - changed = cached_revision && cached_revision != git_proxy.revision - - if changed && !@unlocked && !git_proxy.contains?(cached_revision) - raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \ - "but the current branch in your local override for #{name} does not contain such commit. " \ - "Please make sure your branch is up to date." - end - - changed - end - - def specs(*) - set_local!(app_cache_path) if has_app_cache? && !local? - - if requires_checkout? && !@copied - fetch - git_proxy.copy_to(install_path, submodules) - serialize_gemspecs_in(install_path) - @copied = true - end - - local_specs - end - - def install(spec, options = {}) - force = options[:force] - - print_using_message "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 - end - - generate_bin_options = { :disable_extensions => !Bundler.rubygems.spec_missing_extensions?(spec), :build_args => options[:build_args] } - generate_bin(spec, generate_bin_options) - - requires_checkout? ? spec.post_install_message : nil - end - - def cache(spec, custom_path = nil) - app_cache_path = app_cache_path(custom_path) - return unless Bundler.feature_flag.cache_all? - return if path == app_cache_path - cached! - FileUtils.rm_rf(app_cache_path) - git_proxy.checkout if requires_checkout? - git_proxy.copy_to(app_cache_path, @submodules) - serialize_gemspecs_in(app_cache_path) - end - - def load_spec_files - super - rescue PathError => e - Bundler.ui.trace e - raise GitError, "#{self} is not yet checked out. Run `bundle install` first." - end - - # This is the path which is going to contain a cache - # of the git repository. When using the same git repository - # across different projects, this cache will be shared. - # When using local git repos, this is set to the local repo. - def cache_path - @cache_path ||= begin - if Bundler.requires_sudo? || Bundler.feature_flag.global_gem_cache? - Bundler.user_cache - else - Bundler.bundle_path.join("cache", "bundler") - end.join("git", git_scope) - end - end - - def app_cache_dirname - "#{base_name}-#{shortref_for_path(cached_revision || revision)}" - end - - def revision - git_proxy.revision - end - - def allow_git_ops? - @allow_remote || @allow_cached - end - - private - - def serialize_gemspecs_in(destination) - destination = destination.expand_path(Bundler.root) if destination.relative? - Dir["#{destination}/#{@glob}"].each do |spec_path| - # Evaluate gemspecs and cache the result. Gemspecs - # in git might require git or other dependencies. - # The gemspecs we cache should already be evaluated. - spec = Bundler.load_gemspec(spec_path) - next unless spec - Bundler.rubygems.set_installed_by_version(spec) - Bundler.rubygems.validate(spec) - File.open(spec_path, "wb") {|file| file.write(spec.to_ruby) } - end - end - - def set_local!(path) - @local = true - @local_specs = @git_proxy = nil - @cache_path = @install_path = path - end - - def has_app_cache? - cached_revision && super - end - - def local? - @local - end - - def requires_checkout? - allow_git_ops? && !local? - end - - def base_name - File.basename(uri.sub(%r{^(\w+://)?([^/:]+:)?(//\w*/)?(\w*/)*}, ""), ".git") - end - - def shortref_for_display(ref) - ref[0..6] - end - - def shortref_for_path(ref) - ref[0..11] - end - - def uri_hash - if uri =~ %r{^\w+://(\w+@)?} - # Downcase the domain component of the URI - # and strip off a trailing slash, if one is present - input = URI.parse(uri).normalize.to_s.sub(%r{/$}, "") - else - # If there is no URI scheme, assume it is an ssh/git URI - input = uri - end - SharedHelpers.digest(:SHA1).hexdigest(input) - end - - def cached_revision - options["revision"] - end - - def cached? - cache_path.exist? - end - - def git_proxy - @git_proxy ||= GitProxy.new(cache_path, uri, ref, cached_revision, self) - end - - def fetch - git_proxy.checkout - rescue GitError => e - raise unless Bundler.feature_flag.allow_offline_install? - Bundler.ui.warn "Using cached git data because of network errors:\n#{e}" - end - - # no-op, since we validate when re-serializing the gemspec - def validate_spec(_spec); end - - if Bundler.rubygems.stubs_provide_full_functionality? - def load_gemspec(file) - stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent) - stub.full_gem_path = Pathname.new(file).dirname.expand_path(root).to_s.untaint - StubSpecification.from_stub(stub) - end - end - - def git_scope - "#{base_name}-#{uri_hash}" - end - - def extension_cache_slug(_) - extension_dir_name - end - end - end -end diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb deleted file mode 100644 index c56dda66ea..0000000000 --- a/lib/bundler/source/git/git_proxy.rb +++ /dev/null @@ -1,257 +0,0 @@ -# frozen_string_literal: true - -require "shellwords" -require "tempfile" -module Bundler - class Source - class Git - class GitNotInstalledError < GitError - def initialize - msg = String.new - msg << "You need to install git to be able to use gems from git repositories. " - msg << "For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git" - super msg - end - end - - class GitNotAllowedError < GitError - def initialize(command) - msg = String.new - msg << "Bundler is trying to run a `git #{command}` at runtime. You probably need to run `bundle install`. However, " - msg << "this error message could probably be more useful. Please submit a ticket at http://github.com/bundler/bundler/issues " - msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}" - super msg - end - end - - class GitCommandError < GitError - def initialize(command, path = nil, extra_info = nil) - msg = String.new - msg << "Git error: command `git #{command}` in directory #{SharedHelpers.pwd} has failed." - msg << "\n#{extra_info}" if extra_info - msg << "\nIf this error persists you could try removing the cache directory '#{path}'" if path && path.exist? - super msg - end - end - - class MissingGitRevisionError < GitError - def initialize(ref, repo) - msg = "Revision #{ref} does not exist in the repository #{repo}. Maybe you misspelled it?" - super msg - end - end - - # The GitProxy is responsible to interact with git repositories. - # All actions required by the Git source is encapsulated in this - # object. - class GitProxy - attr_accessor :path, :uri, :ref - attr_writer :revision - - def initialize(path, uri, ref, revision = nil, git = nil) - @path = path - @uri = uri - @ref = ref - @revision = revision - @git = git - raise GitNotInstalledError.new if allow? && !Bundler.git_present? - end - - def revision - return @revision if @revision - - begin - @revision ||= find_local_revision - rescue GitCommandError - raise MissingGitRevisionError.new(ref, URICredentialsFilter.credential_filtered_uri(uri)) - end - - @revision - end - - def branch - @branch ||= allowed_in_path do - git("rev-parse --abbrev-ref HEAD").strip - end - end - - def contains?(commit) - allowed_in_path do - result = git_null("branch --contains #{commit}") - $? == 0 && result =~ /^\* (.*)$/ - end - end - - def version - git("--version").match(/(git version\s*)?((\.?\d+)+).*/)[2] - end - - def full_version - git("--version").sub("git version", "").strip - end - - def checkout - return if path.exist? && has_revision_cached? - extra_ref = "#{Shellwords.shellescape(ref)}:#{Shellwords.shellescape(ref)}" if ref && ref.start_with?("refs/") - - Bundler.ui.info "Fetching #{URICredentialsFilter.credential_filtered_uri(uri)}" - - unless path.exist? - 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) - return unless extra_ref - end - - in_path do - git_retry %(fetch --force --quiet --tags #{uri_escaped_with_configured_credentials} "refs/heads/*:refs/heads/*" #{extra_ref}) - end - end - - def copy_to(destination, submodules = false) - # method 1 - unless File.exist?(destination.join(".git")) - begin - SharedHelpers.filesystem_access(destination.dirname) do |p| - FileUtils.mkdir_p(p) - end - SharedHelpers.filesystem_access(destination) do |p| - FileUtils.rm_rf(p) - end - git_retry %(clone --no-checkout --quiet "#{path}" "#{destination}") - File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination) - rescue Errno::EEXIST => e - file_path = e.message[%r{.*?(/.*)}, 1] - raise GitError, "Bundler could not install a gem because it needs to " \ - "create a directory, but a file exists - #{file_path}. Please delete " \ - "this file and try again." - end - end - # method 2 - SharedHelpers.chdir(destination) do - git_retry %(fetch --force --quiet --tags "#{path}") - git "reset --hard #{@revision}" - - if submodules - git_retry "submodule update --init --recursive" - elsif Gem::Version.create(version) >= Gem::Version.create("2.9.0") - git_retry "submodule deinit --all --force" - end - end - end - - private - - # TODO: Do not rely on /dev/null. - # Given that open3 is not cross platform until Ruby 1.9.3, - # the best solution is to pipe to /dev/null if it exists. - # If it doesn't, everything will work fine, but the user - # will get the $stderr messages as well. - def git_null(command) - git("#{command} 2>#{Bundler::NULL}", false) - end - - def git_retry(command) - Bundler::Retry.new("`git #{URICredentialsFilter.credential_filtered_string(command, uri)}`", GitNotAllowedError).attempts do - git(command) - end - end - - def git(command, check_errors = true, error_msg = nil) - command_with_no_credentials = URICredentialsFilter.credential_filtered_string(command, uri) - raise GitNotAllowedError.new(command_with_no_credentials) unless allow? - - out = SharedHelpers.with_clean_git_env do - capture_and_filter_stderr(uri) { `git #{command}` } - end - - stdout_with_no_credentials = URICredentialsFilter.credential_filtered_string(out, uri) - raise GitCommandError.new(command_with_no_credentials, path, error_msg) if check_errors && !$?.success? - stdout_with_no_credentials - end - - def has_revision_cached? - return unless @revision - in_path { git("cat-file -e #{@revision}") } - true - rescue GitError - false - end - - def remove_cache - FileUtils.rm_rf(path) - end - - def find_local_revision - allowed_in_path do - git("rev-parse --verify #{Shellwords.shellescape(ref)}", true).strip - end - end - - # Escape the URI for git commands - def uri_escaped_with_configured_credentials - remote = configured_uri_for(uri) - if Bundler::WINDOWS - # Windows quoting requires double quotes only, with double quotes - # inside the string escaped by being doubled. - '"' + remote.gsub('"') { '""' } + '"' - else - # Bash requires single quoted strings, with the single quotes escaped - # by ending the string, escaping the quote, and restarting the string. - "'" + remote.gsub("'") { "'\\''" } + "'" - end - end - - # Adds credentials to the URI as Fetcher#configured_uri_for does - def configured_uri_for(uri) - if /https?:/ =~ uri - remote = URI(uri) - config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host] - remote.userinfo ||= config_auth - remote.to_s - else - uri - end - end - - def allow? - @git ? @git.allow_git_ops? : true - end - - def in_path(&blk) - checkout unless path.exist? - _ = URICredentialsFilter # load it before we chdir - SharedHelpers.chdir(path, &blk) - end - - def allowed_in_path - return in_path { yield } if allow? - raise GitError, "The git source #{uri} is not yet checked out. Please run `bundle install` before trying to start your application" - end - - # TODO: Replace this with Open3 when upgrading to bundler 2 - # Similar to #git_null, as Open3 is not cross-platform, - # a temporary way is to use Tempfile to capture the stderr. - # When replacing this using Open3, make sure git_null is - # also replaced by Open3, so stdout and stderr all got handled properly. - def capture_and_filter_stderr(uri) - return_value, captured_err = "" - backup_stderr = STDERR.dup - begin - Tempfile.open("captured_stderr") do |f| - STDERR.reopen(f) - return_value = yield - f.rewind - captured_err = f.read - end - ensure - STDERR.reopen backup_stderr - end - $stderr.puts URICredentialsFilter.credential_filtered_string(captured_err, uri) if uri && !captured_err.empty? - return_value - end - end - end - end -end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb deleted file mode 100644 index 93909002c7..0000000000 --- a/lib/bundler/source/metadata.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - class Metadata < Source - def specs - @specs ||= Index.build do |idx| - idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) - idx << Gem::Specification.new("rubygems\0", Gem::VERSION) - - idx << Gem::Specification.new do |s| - s.name = "bundler" - s.version = VERSION - s.platform = Gem::Platform::RUBY - s.source = self - s.authors = ["bundler team"] - s.bindir = "exe" - s.executables = %w[bundle] - # can't point to the actual gemspec or else the require paths will be wrong - s.loaded_from = File.expand_path("..", __FILE__) - end - if loaded_spec = nil && Bundler.rubygems.loaded_specs("bundler") - idx << loaded_spec # this has to come after the fake gemspec, to override it - elsif local_spec = Bundler.rubygems.find_name("bundler").find {|s| s.version.to_s == VERSION } - idx << local_spec - end - - idx.each {|s| s.source = self } - end - end - - def cached!; end - - def remote!; end - - def options - {} - end - - def install(spec, _opts = {}) - print_using_message "Using #{version_message(spec)}" - nil - end - - def to_s - "the local ruby installation" - end - - def ==(other) - self.class == other.class - end - alias_method :eql?, :== - - def hash - self.class.hash - end - - def version_message(spec) - "#{spec.name} #{spec.version}" - end - end - end -end diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb deleted file mode 100644 index ed734bf549..0000000000 --- a/lib/bundler/source/path.rb +++ /dev/null @@ -1,249 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - class Path < Source - autoload :Installer, "bundler/source/path/installer" - - attr_reader :path, :options, :root_path, :original_path - attr_writer :name - attr_accessor :version - - protected :original_path - - DEFAULT_GLOB = "{,*,*/*}.gemspec".freeze - - def initialize(options) - @options = options.dup - @glob = options["glob"] || DEFAULT_GLOB - - @allow_cached = false - @allow_remote = false - - @root_path = options["root_path"] || Bundler.root - - if options["path"] - @path = Pathname.new(options["path"]) - @path = expand(@path) unless @path.relative? - end - - @name = options["name"] - @version = options["version"] - - # Stores the original path. If at any point we move to the - # cached directory, we still have the original path to copy from. - @original_path = @path - end - - def remote! - @local_specs = nil - @allow_remote = true - end - - def cached! - @local_specs = nil - @allow_cached = true - end - - def self.from_lock(options) - new(options.merge("path" => options.delete("remote"))) - end - - def to_lock - out = String.new("PATH\n") - out << " remote: #{lockfile_path}\n" - out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB - out << " specs:\n" - end - - def to_s - "source at `#{@path}`" - end - - def hash - [self.class, expanded_path, version].hash - end - - def eql?(other) - return unless other.class == self.class - expanded_original_path == other.expanded_original_path && - version == other.version - end - - alias_method :==, :eql? - - def name - File.basename(expanded_path.to_s) - end - - def install(spec, options = {}) - print_using_message "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.feature_flag.cache_all? - return if expand(@original_path).to_s.index(root_path.to_s + "/") == 0 - - unless @original_path.exist? - raise GemNotFound, "Can't cache gem #{version_message(spec)} because #{self} is missing!" - end - - FileUtils.rm_rf(app_cache_path) - FileUtils.cp_r("#{@original_path}/.", app_cache_path) - FileUtils.touch(app_cache_path.join(".bundlecache")) - end - - def local_specs(*) - @local_specs ||= load_spec_files - end - - def specs - if has_app_cache? - @path = app_cache_path - @expanded_path = nil # Invalidate - end - local_specs - end - - def app_cache_dirname - name - end - - def root - Bundler.root - end - - def expanded_original_path - @expanded_original_path ||= expand(original_path) - end - - private - - def expanded_path - @expanded_path ||= expand(path) - end - - def expand(somepath) - somepath.expand_path(root_path) - rescue ArgumentError => e - Bundler.ui.debug(e) - raise PathError, "There was an error while trying to use the path " \ - "`#{somepath}`.\nThe error message was: #{e.message}." - end - - def lockfile_path - return relative_path(original_path) if original_path.absolute? - expand(original_path).relative_path_from(Bundler.root) - end - - def app_cache_path(custom_path = nil) - @app_cache_path ||= Bundler.app_cache(custom_path).join(app_cache_dirname) - end - - def has_app_cache? - SharedHelpers.in_bundle? && app_cache_path.exist? - end - - def load_gemspec(file) - return unless spec = Bundler.load_gemspec(file) - Bundler.rubygems.set_installed_by_version(spec) - spec - end - - def validate_spec(spec) - Bundler.rubygems.validate(spec) - end - - def load_spec_files - index = Index.new - - if File.directory?(expanded_path) - # We sort depth-first since `<<` will override the earlier-found specs - Dir["#{expanded_path}/#{@glob}"].sort_by {|p| -p.split(File::SEPARATOR).size }.each do |file| - next unless spec = load_gemspec(file) - spec.source = self - - # Validation causes extension_dir to be calculated, which depends - # on #source, so we validate here instead of load_gemspec - validate_spec(spec) - index << spec - end - - if index.empty? && @name && @version - index << Gem::Specification.new do |s| - s.name = @name - s.source = self - s.version = Gem::Version.new(@version) - s.platform = Gem::Platform::RUBY - s.summary = "Fake gemspec for #{@name}" - s.relative_loaded_from = "#{@name}.gemspec" - s.authors = ["no one"] - if expanded_path.join("bin").exist? - executables = expanded_path.join("bin").children - executables.reject! {|p| File.directory?(p) } - s.executables = executables.map {|c| c.basename.to_s } - end - end - end - else - message = String.new("The path `#{expanded_path}` ") - message << if File.exist?(expanded_path) - "is not a directory." - else - "does not exist." - end - raise PathError, message - end - - index - end - - def relative_path(path = self.path) - if path.to_s.start_with?(root_path.to_s) - return path.relative_path_from(root_path) - end - path - end - - def generate_bin(spec, options = {}) - gem_dir = Pathname.new(spec.full_gem_path) - - # Some gem authors put absolute paths in their gemspec - # and we have to save them from themselves - spec.files = spec.files.map do |p| - next p unless p =~ /\A#{Pathname::SEPARATOR_PAT}/ - next if File.directory?(p) - begin - Pathname.new(p).relative_path_from(gem_dir).to_s - rescue ArgumentError - p - end - end.compact - - installer = Path::Installer.new( - spec, - :env_shebang => false, - :disable_extensions => options[:disable_extensions], - :build_args => options[:build_args], - :bundler_extension_cache_path => extension_cache_path(spec) - ) - installer.post_install - rescue Gem::InvalidSpecificationException => e - Bundler.ui.warn "\n#{spec.name} at #{spec.full_gem_path} did not have a valid gemspec.\n" \ - "This prevents bundler from installing bins or native extensions, but " \ - "that may not affect its functionality." - - if !spec.extensions.empty? && !spec.email.empty? - Bundler.ui.warn "If you need to use this package without installing it from a gem " \ - "repository, please contact #{spec.email} and ask them " \ - "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 -end diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb deleted file mode 100644 index a0357ffa39..0000000000 --- a/lib/bundler/source/path/installer.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - class Path - class Installer < Bundler::RubyGemsGemInstaller - attr_reader :spec - - def initialize(spec, options = {}) - @options = options - @spec = spec - @gem_dir = Bundler.rubygems.path(spec.full_gem_path) - @wrappers = true - @env_shebang = true - @format_executable = options[:format_executable] || false - @build_args = options[:build_args] || Bundler.rubygems.build_args - @gem_bin_dir = "#{Bundler.rubygems.gem_dir}/bin" - @disable_extensions = options[:disable_extensions] - - if Bundler.requires_sudo? - @tmp_dir = Bundler.tmp(spec.full_name).to_s - @bin_dir = "#{@tmp_dir}/bin" - else - @bin_dir = @gem_bin_dir - end - end - - def post_install - SharedHelpers.chdir(@gem_dir) do - run_hooks(:pre_install) - - unless @disable_extensions - build_extensions - run_hooks(:post_build) - end - - generate_bin unless spec.executables.nil? || spec.executables.empty? - - run_hooks(:post_install) - end - ensure - Bundler.rm_rf(@tmp_dir) if Bundler.requires_sudo? - end - - private - - def generate_bin - super - - if Bundler.requires_sudo? - SharedHelpers.filesystem_access(@gem_bin_dir) do |p| - Bundler.mkdir_p(p) - end - spec.executables.each do |exe| - Bundler.sudo "cp -R #{@bin_dir}/#{exe} #{@gem_bin_dir}" - end - end - end - - def run_hooks(type) - hooks_meth = "#{type}_hooks" - return unless Gem.respond_to?(hooks_meth) - Gem.send(hooks_meth).each do |hook| - result = hook.call(self) - next unless result == false - location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/ - message = "#{type} hook#{location} failed for #{spec.full_name}" - raise InstallHookError, message - end - end - end - end - end -end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb deleted file mode 100644 index 3cf22a50f1..0000000000 --- a/lib/bundler/source/rubygems.rb +++ /dev/null @@ -1,530 +0,0 @@ -# frozen_string_literal: true - -require "uri" -require "rubygems/user_interaction" - -module Bundler - class Source - class Rubygems < Source - autoload :Remote, "bundler/source/rubygems/remote" - - # Use the API when installing less than X gems - API_REQUEST_LIMIT = 500 - # Ask for X gems per API request - API_REQUEST_SIZE = 50 - - attr_reader :remotes, :caches - - def initialize(options = {}) - @options = options - @remotes = [] - @dependency_names = [] - @allow_remote = false - @allow_cached = false - @caches = [cache_path, *Bundler.rubygems.gem_cache] - - Array(options["remotes"] || []).reverse_each {|r| add_remote(r) } - end - - def remote! - @specs = nil - @allow_remote = true - end - - def cached! - @specs = nil - @allow_cached = true - end - - def hash - @remotes.hash - end - - def eql?(other) - other.is_a?(Rubygems) && other.credless_remotes == credless_remotes - end - - alias_method :==, :eql? - - def include?(o) - o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty? - end - - def can_lock?(spec) - return super if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - spec.source.is_a?(Rubygems) - end - - def options - { "remotes" => @remotes.map(&:to_s) } - end - - def self.from_lock(options) - new(options) - end - - def to_lock - out = String.new("GEM\n") - remotes.reverse_each do |remote| - out << " remote: #{suppress_configured_credentials remote}\n" - end - out << " specs:\n" - end - - def to_s - if remotes.empty? - "locally installed gems" - else - remote_names = remotes.map(&:to_s).join(", ") - "rubygems repository #{remote_names} or installed locally" - end - end - alias_method :name, :to_s - - def specs - @specs ||= begin - # remote_specs usually generates a way larger Index than the other - # sources, and large_idx.use small_idx is way faster than - # small_idx.use large_idx. - idx = @allow_remote ? remote_specs.dup : Index.new - idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote - idx.use(installed_specs, :override_dupes) - idx - end - end - - def install(spec, opts = {}) - force = opts[:force] - ensure_builtin_gems_cached = opts[:ensure_builtin_gems_cached] - - if ensure_builtin_gems_cached && builtin_gem?(spec) - if !cached_path(spec) - cached_built_in_gem(spec) unless spec.remote - force = true - else - spec.loaded_from = loaded_from(spec) - end - end - - if installed?(spec) && !force - print_using_message "Using #{version_message(spec)}" - return nil # no post-install message - end - - # Download the gem to get the spec, because some specs that are returned - # by rubygems.org are broken and wrong. - if spec.remote - # Check for this spec from other sources - uris = [spec.remote.anonymized_uri] - uris += remotes_for_spec(spec).map(&:anonymized_uri) - uris.uniq! - Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1 - - s = Bundler.rubygems.spec_from_gem(fetch_gem(spec), Bundler.settings["trust-policy"]) - spec.__swap__(s) - end - - unless Bundler.settings[:no_install] - message = "Installing #{version_message(spec)}" - message += " with native extensions" if spec.extensions.any? - Bundler.ui.confirm message - - path = cached_gem(spec) - if requires_sudo? - install_path = Bundler.tmp(spec.full_name) - bin_path = install_path.join("bin") - else - install_path = rubygems_dir - bin_path = Bundler.system_bindir - end - - installed_spec = nil - Bundler.rubygems.preserve_paths do - installed_spec = Bundler::RubyGemsGemInstaller.at( - path, - :install_dir => install_path.to_s, - :bin_dir => bin_path.to_s, - :ignore_dependencies => true, - :wrappers => true, - :env_shebang => true, - :build_args => opts[:build_args], - :bundler_expected_checksum => spec.respond_to?(:checksum) && spec.checksum, - :bundler_extension_cache_path => extension_cache_path(spec) - ).install - end - spec.full_gem_path = installed_spec.full_gem_path - - # SUDO HAX - if requires_sudo? - Bundler.rubygems.repository_subdirectories.each do |name| - src = File.join(install_path, name, "*") - dst = File.join(rubygems_dir, name) - if name == "extensions" && Dir.glob(src).any? - src = File.join(src, "*/*") - ext_src = Dir.glob(src).first - ext_src.gsub!(src[0..-6], "") - dst = File.dirname(File.join(dst, ext_src)) - end - SharedHelpers.filesystem_access(dst) do |p| - Bundler.mkdir_p(p) - end - Bundler.sudo "cp -R #{src} #{dst}" if Dir[src].any? - end - - spec.executables.each do |exe| - SharedHelpers.filesystem_access(Bundler.system_bindir) do |p| - Bundler.mkdir_p(p) - end - Bundler.sudo "cp -R #{install_path}/bin/#{exe} #{Bundler.system_bindir}/" - end - end - installed_spec.loaded_from = loaded_from(spec) - end - spec.loaded_from = loaded_from(spec) - - spec.post_install_message - ensure - Bundler.rm_rf(install_path) if requires_sudo? - end - - def cache(spec, custom_path = nil) - if builtin_gem?(spec) - cached_path = cached_built_in_gem(spec) - else - cached_path = cached_gem(spec) - end - raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path - return if File.dirname(cached_path) == Bundler.app_cache.to_s - Bundler.ui.info " * #{File.basename(cached_path)}" - FileUtils.cp(cached_path, Bundler.app_cache(custom_path)) - rescue Errno::EACCES => e - Bundler.ui.debug(e) - raise InstallError, e.message - end - - def cached_built_in_gem(spec) - cached_path = cached_path(spec) - if cached_path.nil? - remote_spec = remote_specs.search(spec).first - if remote_spec - cached_path = fetch_gem(remote_spec) - else - Bundler.ui.warn "#{spec.full_name} is built in to Ruby, and can't be cached because your Gemfile doesn't have any sources that contain it." - end - end - cached_path - end - - def add_remote(source) - uri = normalize_uri(source) - @remotes.unshift(uri) unless @remotes.include?(uri) - end - - def equivalent_remotes?(other_remotes) - other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth)) - end - - def replace_remotes(other_remotes, allow_equivalent = false) - return false if other_remotes == @remotes - - equivalent = allow_equivalent && equivalent_remotes?(other_remotes) - - @remotes = [] - other_remotes.reverse_each do |r| - add_remote r.to_s - end - - !equivalent - end - - def unmet_deps - if @allow_remote && api_fetchers.any? - remote_specs.unmet_dependency_names - else - [] - end - end - - def fetchers - @fetchers ||= remotes.map do |uri| - remote = Source::Rubygems::Remote.new(uri) - Bundler::Fetcher.new(remote) - end - end - - def double_check_for(unmet_dependency_names) - return unless @allow_remote - return unless api_fetchers.any? - - unmet_dependency_names = unmet_dependency_names.call - unless unmet_dependency_names.nil? - if api_fetchers.size <= 1 - # can't do this when there are multiple fetchers because then we might not fetch from _all_ - # of them - unmet_dependency_names -= remote_specs.spec_names # avoid re-fetching things we've already gotten - end - return if unmet_dependency_names.empty? - end - - Bundler.ui.debug "Double checking for #{unmet_dependency_names || "all specs (due to the size of the request)"} in #{self}" - - fetch_names(api_fetchers, unmet_dependency_names, specs, false) - end - - def dependency_names_to_double_check - names = [] - remote_specs.each do |spec| - case spec - when EndpointSpecification, Gem::Specification, StubSpecification, LazySpecification - names.concat(spec.runtime_dependencies) - when RemoteSpecification # from the full index - return nil - else - raise "unhandled spec type (#{spec.inspect})" - end - end - names.map!(&:name) if names - names - end - - protected - - def credless_remotes - remotes.map(&method(:suppress_configured_credentials)) - end - - def remotes_for_spec(spec) - specs.search_all(spec.name).inject([]) do |uris, s| - uris << s.remote if s.remote - uris - end - end - - def loaded_from(spec) - "#{rubygems_dir}/specifications/#{spec.full_name}.gemspec" - end - - def cached_gem(spec) - cached_gem = cached_path(spec) - unless cached_gem - raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation" - end - cached_gem - end - - def cached_path(spec) - possibilities = @caches.map {|p| "#{p}/#{spec.file_name}" } - possibilities.find {|p| File.exist?(p) } - end - - def normalize_uri(uri) - uri = uri.to_s - uri = "#{uri}/" unless uri =~ %r{/$} - uri = URI(uri) - raise ArgumentError, "The source must be an absolute URI. For example:\n" \ - "source 'https://rubygems.org'" if !uri.absolute? || (uri.is_a?(URI::HTTP) && uri.host.nil?) - uri - end - - def suppress_configured_credentials(remote) - remote_nouser = remove_auth(remote) - if remote.userinfo && remote.userinfo == Bundler.settings[remote_nouser] - remote_nouser - else - remote - end - end - - def remove_auth(remote) - remote.dup.tap {|uri| uri.user = uri.password = nil }.to_s - end - - def installed_specs - @installed_specs ||= Index.build do |idx| - Bundler.rubygems.all_specs.reverse_each do |spec| - next if spec.name == "bundler" - spec.source = self - if Bundler.rubygems.spec_missing_extensions?(spec, false) - Bundler.ui.debug "Source #{self} is ignoring #{spec} because it is missing extensions" - next - end - idx << spec - end - end - end - - def cached_specs - @cached_specs ||= begin - idx = installed_specs.dup - - Dir["#{cache_path}/*.gem"].each do |gemfile| - next if gemfile =~ /^bundler\-[\d\.]+?\.gem/ - s ||= Bundler.rubygems.spec_from_gem(gemfile) - s.source = self - if Bundler.rubygems.spec_missing_extensions?(s, false) - Bundler.ui.debug "Source #{self} is ignoring #{s} because it is missing extensions" - next - end - idx << s - end - - idx - end - end - - def api_fetchers - fetchers.select {|f| f.use_api && f.fetchers.first.api_fetcher? } - end - - def remote_specs - @remote_specs ||= Index.build do |idx| - index_fetchers = fetchers - api_fetchers - - # gather lists from non-api sites - fetch_names(index_fetchers, nil, idx, false) - - # 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 500 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 - - fetch_names(api_fetchers, allow_api && dependency_names, idx, false) - end - end - - def fetch_names(fetchers, dependency_names, index, override_dupes) - fetchers.each do |f| - if dependency_names - Bundler.ui.info "Fetching gem metadata from #{f.uri}", Bundler.ui.debug? - index.use f.specs_with_retry(dependency_names, self), override_dupes - Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over - else - Bundler.ui.info "Fetching source index from #{f.uri}" - index.use f.specs_with_retry(nil, self), override_dupes - end - end - end - - def fetch_gem(spec) - return false unless spec.remote - - spec.fetch_platform - - download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir - gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem" - - SharedHelpers.filesystem_access("#{download_path}/cache") do |p| - FileUtils.mkdir_p(p) - end - download_gem(spec, download_path) - - if requires_sudo? - SharedHelpers.filesystem_access("#{rubygems_dir}/cache") do |p| - Bundler.mkdir_p(p) - end - Bundler.sudo "mv #{download_path}/cache/#{spec.full_name}.gem #{gem_path}" - end - - gem_path - ensure - Bundler.rm_rf(download_path) if requires_sudo? - end - - def builtin_gem?(spec) - # Ruby 2.1, where all included gems have this summary - return true if spec.summary =~ /is bundled with Ruby/ - - # Ruby 2.0, where gemspecs are stored in specifications/default/ - spec.loaded_from && spec.loaded_from.include?("specifications/default/") - end - - def installed?(spec) - installed_specs[spec].any? - end - - def requires_sudo? - Bundler.requires_sudo? - end - - def rubygems_dir - Bundler.rubygems.gem_dir - end - - def cache_path - Bundler.app_cache - end - - private - - # Checks if the requested spec exists in the global cache. If it does, - # we copy it to the download path, and if it does not, we download it. - # - # @param [Specification] spec - # the spec we want to download or retrieve from the cache. - # - # @param [String] download_path - # the local directory the .gem will end up in. - # - def download_gem(spec, download_path) - local_path = File.join(download_path, "cache/#{spec.full_name}.gem") - - if (cache_path = download_cache_path(spec)) && cache_path.file? - SharedHelpers.filesystem_access(local_path) do - FileUtils.cp(cache_path, local_path) - end - else - uri = spec.remote.uri - Bundler.ui.confirm("Fetching #{version_message(spec)}") - Bundler.rubygems.download_gem(spec, uri, download_path) - cache_globally(spec, local_path) - end - end - - # Checks if the requested spec exists in the global cache. If it does - # not, we create the relevant global cache subdirectory if it does not - # exist and copy the spec from the local cache to the global cache. - # - # @param [Specification] spec - # the spec we want to copy to the global cache. - # - # @param [String] local_cache_path - # the local directory from which we want to copy the .gem. - # - def cache_globally(spec, local_cache_path) - return unless cache_path = download_cache_path(spec) - return if cache_path.exist? - - SharedHelpers.filesystem_access(cache_path.dirname, &:mkpath) - SharedHelpers.filesystem_access(cache_path) do - FileUtils.cp(local_cache_path, cache_path) - end - end - - # Returns the global cache path of the calling Rubygems::Source object. - # - # Note that the Source determines the path's subdirectory. We use this - # subdirectory in the global cache path so that gems with the same name - # -- and possibly different versions -- from different sources are saved - # to their respective subdirectories and do not override one another. - # - # @param [Gem::Specification] specification - # - # @return [Pathname] The global cache path. - # - def download_cache_path(spec) - return unless Bundler.feature_flag.global_gem_cache? - return unless remote = spec.remote - return unless cache_slug = remote.cache_slug - - Bundler.user_cache.join("gems", cache_slug, spec.file_name) - end - - def extension_cache_slug(spec) - return unless remote = spec.remote - remote.cache_slug - end - end - end -end diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb deleted file mode 100644 index e73baaa992..0000000000 --- a/lib/bundler/source/rubygems/remote.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class Source - class Rubygems - class Remote - attr_reader :uri, :anonymized_uri, :original_uri - - def initialize(uri) - orig_uri = uri - uri = Bundler.settings.mirror_for(uri) - @original_uri = orig_uri if orig_uri != uri - fallback_auth = Bundler.settings.credentials_for(uri) - - @uri = apply_auth(uri, fallback_auth).freeze - @anonymized_uri = remove_auth(@uri).freeze - end - - # @return [String] A slug suitable for use as a cache key for this - # remote. - # - def cache_slug - @cache_slug ||= begin - return nil unless SharedHelpers.md5_available? - - cache_uri = original_uri || uri - - uri_parts = [cache_uri.host, cache_uri.user, cache_uri.port, cache_uri.path] - uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.compact.join(".")) - - uri_parts[-1] = uri_digest - uri_parts.compact.join(".") - end - end - - def to_s - "rubygems remote at #{anonymized_uri}" - end - - private - - def apply_auth(uri, auth) - if auth && uri.userinfo.nil? - uri = uri.dup - uri.userinfo = auth - end - - uri - rescue URI::InvalidComponentError - error_message = "Please CGI escape your usernames and passwords before " \ - "setting them for authentication." - raise HTTPError.new(error_message) - end - - def remove_auth(uri) - if uri.userinfo - uri = uri.dup - uri.user = uri.password = nil - end - - uri - end - end - end - end -end diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb deleted file mode 100644 index ac2adacb3d..0000000000 --- a/lib/bundler/source_list.rb +++ /dev/null @@ -1,186 +0,0 @@ -# frozen_string_literal: true - -module Bundler - class SourceList - attr_reader :path_sources, - :git_sources, - :plugin_sources, - :global_rubygems_source, - :metadata_source - - def initialize - @path_sources = [] - @git_sources = [] - @plugin_sources = [] - @global_rubygems_source = nil - @rubygems_aggregate = rubygems_aggregate_class.new - @rubygems_sources = [] - @metadata_source = Source::Metadata.new - end - - def add_path_source(options = {}) - if options["gemspec"] - add_source_to_list Source::Gemspec.new(options), path_sources - else - add_source_to_list Source::Path.new(options), path_sources - end - end - - def add_git_source(options = {}) - add_source_to_list(Source::Git.new(options), git_sources).tap do |source| - warn_on_git_protocol(source) - end - end - - def add_rubygems_source(options = {}) - add_source_to_list Source::Rubygems.new(options), @rubygems_sources - end - - def add_plugin_source(source, options = {}) - add_source_to_list Plugin.source(source).new(options), @plugin_sources - end - - def global_rubygems_source=(uri) - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri) - end - add_rubygems_remote(uri) - end - - def add_rubygems_remote(uri) - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - return if Bundler.feature_flag.disable_multisource? - raise InvalidOption, "`lockfile_uses_separate_rubygems_sources` cannot be set without `disable_multisource` being set" - end - @rubygems_aggregate.add_remote(uri) - @rubygems_aggregate - end - - def default_source - global_rubygems_source || @rubygems_aggregate - end - - def rubygems_sources - @rubygems_sources + [default_source] - end - - def rubygems_remotes - rubygems_sources.map(&:remotes).flatten.uniq - end - - def all_sources - path_sources + git_sources + plugin_sources + rubygems_sources + [metadata_source] - end - - def get(source) - source_list_for(source).find {|s| equal_source?(source, s) || equivalent_source?(source, s) } - end - - def lock_sources - if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? - [[default_source], @rubygems_sources, git_sources, path_sources, plugin_sources].map do |sources| - sources.sort_by(&:to_s) - end.flatten(1) - else - lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s) - lock_sources << combine_rubygems_sources - end - end - - # Returns true if there are changes - def replace_sources!(replacement_sources) - return true if replacement_sources.empty? - - [path_sources, git_sources, plugin_sources].each do |source_list| - source_list.map! do |source| - replacement_sources.find {|s| s == source } || source - end - end - - replacement_rubygems = !Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? && - replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } - @rubygems_aggregate = replacement_rubygems if replacement_rubygems - - return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources) - return true if replacement_rubygems && rubygems_remotes.to_set != replacement_rubygems.remotes.to_set - - false - end - - def cached! - all_sources.each(&:cached!) - end - - def remote! - all_sources.each(&:remote!) - end - - def rubygems_primary_remotes - @rubygems_aggregate.remotes - end - - private - - def rubygems_aggregate_class - Source::Rubygems - end - - def add_source_to_list(source, list) - list.unshift(source).uniq! - source - end - - def source_list_for(source) - case source - when Source::Git then git_sources - when Source::Path then path_sources - when Source::Rubygems then rubygems_sources - when Plugin::API::Source then plugin_sources - else raise ArgumentError, "Invalid source: #{source.inspect}" - end - end - - def combine_rubygems_sources - Source::Rubygems.new("remotes" => rubygems_remotes) - end - - def warn_on_git_protocol(source) - return if Bundler.settings["git.allow_insecure"] - - if source.uri =~ /^git\:/ - Bundler.ui.warn "The git source `#{source.uri}` uses the `git` protocol, " \ - "which transmits data without encryption. Disable this warning with " \ - "`bundle config git.allow_insecure true`, or switch to the `https` " \ - "protocol to keep your data secure." - end - end - - def equal_sources?(lock_sources, replacement_sources) - lock_sources.to_set == replacement_sources.to_set - end - - def equal_source?(source, other_source) - source == other_source - end - - def equivalent_source?(source, other_source) - return false unless Bundler.settings[:allow_deployment_source_credential_changes] && source.is_a?(Source::Rubygems) - - equivalent_rubygems_sources?([source], [other_source]) - end - - def equivalent_sources?(lock_sources, replacement_sources) - return false unless Bundler.settings[:allow_deployment_source_credential_changes] - - lock_rubygems_sources, lock_other_sources = lock_sources.partition {|s| s.is_a?(Source::Rubygems) } - replacement_rubygems_sources, replacement_other_sources = replacement_sources.partition {|s| s.is_a?(Source::Rubygems) } - - equivalent_rubygems_sources?(lock_rubygems_sources, replacement_rubygems_sources) && equal_sources?(lock_other_sources, replacement_other_sources) - end - - def equivalent_rubygems_sources?(lock_sources, replacement_sources) - actual_remotes = replacement_sources.map(&:remotes).flatten.uniq - lock_sources.all? {|s| s.equivalent_remotes?(actual_remotes) } - end - end -end diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb deleted file mode 100644 index 7cd3021997..0000000000 --- a/lib/bundler/spec_set.rb +++ /dev/null @@ -1,189 +0,0 @@ -# frozen_string_literal: true - -require "tsort" -require "forwardable" -require "set" - -module Bundler - class SpecSet - extend Forwardable - include TSort, Enumerable - - def_delegators :@specs, :<<, :length, :add, :remove, :size, :empty? - def_delegators :sorted, :each - - def initialize(specs) - @specs = specs - end - - def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true) - handled = Set.new - deps = dependencies.dup - specs = [] - skip += ["bundler"] - - loop do - break unless dep = deps.shift - next if !handled.add?(dep) || skip.include?(dep.name) - - if spec = spec_for_dependency(dep, match_current_platform) - specs << spec - - spec.dependencies.each do |d| - next if d.type == :development - d = DepProxy.new(d, dep.__platform) unless match_current_platform - deps << d - end - elsif check - return false - elsif raise_on_missing - raise "Unable to find a spec satisfying #{dep} in the set. Perhaps the lockfile is corrupted?" - end - end - - if spec = lookup["bundler"].first - specs << spec - end - - check ? true : SpecSet.new(specs) - end - - def valid_for?(deps) - self.for(deps, [], true) - end - - def [](key) - key = key.name if key.respond_to?(:name) - lookup[key].reverse - end - - def []=(key, value) - @specs << value - @lookup = nil - @sorted = nil - value - end - - def sort! - self - end - - def to_a - sorted.dup - end - - def to_hash - lookup.dup - 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) - s.source.dependency_names = deps if s.source.respond_to?(:dependency_names=) - spec = s.__materialize__ - unless spec - unless missing_specs - raise GemNotFound, "Could not find #{s.full_name} in any of the sources" - end - missing_specs << s - end - spec - end - SpecSet.new(missing_specs ? materialized.compact : materialized) - end - - # Materialize for all the specs in the spec set, regardless of what platform they're for - # This is in contrast to how for does platform filtering (and specifically different from how `materialize` calls `for` only for the current platform) - # @return [Array<Gem::Specification>] - def materialized_for_all_platforms - names = @specs.map(&:name).uniq - @specs.map do |s| - next s unless s.is_a?(LazySpecification) - s.source.dependency_names = names if s.source.respond_to?(:dependency_names=) - spec = s.__materialize__ - raise GemNotFound, "Could not find #{s.full_name} in any of the sources" unless spec - spec - end - end - - def merge(set) - arr = sorted.dup - set.each do |set_spec| - full_name = set_spec.full_name - next if arr.any? {|spec| spec.full_name == full_name } - arr << set_spec - end - SpecSet.new(arr) - end - - def find_by_name_and_platform(name, platform) - @specs.detect {|spec| spec.name == name && spec.match_platform(platform) } - end - - def what_required(spec) - unless req = find {|s| s.dependencies.any? {|d| d.type == :runtime && d.name == spec.name } } - return [spec] - end - what_required(req) << spec - end - - private - - def sorted - rake = @specs.find {|s| s.name == "rake" } - begin - @sorted ||= ([rake] + tsort).compact.uniq - rescue TSort::Cyclic => error - cgems = extract_circular_gems(error) - raise CyclicDependencyError, "Your bundle requires gems that depend" \ - " on each other, creating an infinite loop. Please remove either" \ - " gem '#{cgems[1]}' or gem '#{cgems[0]}' and try again." - end - end - - def extract_circular_gems(error) - if Bundler.current_ruby.mri? && Bundler.current_ruby.on_19? - error.message.scan(/(\w+) \([^)]/).flatten - else - error.message.scan(/@name="(.*?)"/).flatten - end - end - - def lookup - @lookup ||= begin - lookup = Hash.new {|h, k| h[k] = [] } - Index.sort_specs(@specs).reverse_each do |s| - lookup[s.name] << s - end - lookup - end - end - - def tsort_each_node - # MUST sort by name for backwards compatibility - @specs.sort_by(&:name).each {|s| yield s } - end - - def spec_for_dependency(dep, match_current_platform) - specs_for_platforms = lookup[dep.name] - if match_current_platform - Bundler.rubygems.platforms.reverse_each do |pl| - match = GemHelpers.select_best_platform_match(specs_for_platforms, pl) - return match if match - end - nil - else - GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform) - end - end - - def tsort_each_child(s) - s.dependencies.sort_by(&:name).each do |d| - next if d.type == :development - lookup[d.name].each {|s2| yield s2 } - end - end - end -end diff --git a/lib/bundler/ssl_certs/.document b/lib/bundler/ssl_certs/.document deleted file mode 100644 index fb66f13c33..0000000000 --- a/lib/bundler/ssl_certs/.document +++ /dev/null @@ -1 +0,0 @@ -# Ignore all files in this directory diff --git a/lib/bundler/ssl_certs/certificate_manager.rb b/lib/bundler/ssl_certs/certificate_manager.rb deleted file mode 100644 index 26fc38ec18..0000000000 --- a/lib/bundler/ssl_certs/certificate_manager.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require "bundler/vendored_fileutils" -require "net/https" -require "openssl" - -module Bundler - module SSLCerts - class CertificateManager - attr_reader :bundler_cert_path, :bundler_certs, :rubygems_certs - - def self.update_from!(rubygems_path) - new(rubygems_path).update! - end - - def initialize(rubygems_path = nil) - if rubygems_path - rubygems_cert_path = File.join(rubygems_path, "lib/rubygems/ssl_certs") - @rubygems_certs = certificates_in(rubygems_cert_path) - end - - @bundler_cert_path = File.expand_path("..", __FILE__) - @bundler_certs = certificates_in(bundler_cert_path) - end - - def up_to_date? - rubygems_certs.all? do |rc| - bundler_certs.find do |bc| - File.basename(bc) == File.basename(rc) && FileUtils.compare_file(bc, rc) - end - end - end - - def update! - return if up_to_date? - - FileUtils.rm bundler_certs - FileUtils.cp rubygems_certs, bundler_cert_path - end - - def connect_to(host) - http = Net::HTTP.new(host, 443) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_PEER - http.cert_store = store - http.head("/") - end - - private - - def certificates_in(path) - Dir[File.join(path, "**/*.pem")].sort - end - - def store - @store ||= begin - store = OpenSSL::X509::Store.new - bundler_certs.each do |cert| - store.add_file cert - end - store - end - end - end - end -end diff --git a/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem deleted file mode 100644 index f4ce4ca43d..0000000000 --- a/lib/bundler/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- diff --git a/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem b/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem deleted file mode 100644 index 9e6810ab70..0000000000 --- a/lib/bundler/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- diff --git a/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem b/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem deleted file mode 100644 index 20585f1c01..0000000000 --- a/lib/bundler/ssl_certs/rubygems.org/AddTrustExternalCARoot.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- diff --git a/lib/bundler/stub_specification.rb b/lib/bundler/stub_specification.rb deleted file mode 100644 index 0dd024024a..0000000000 --- a/lib/bundler/stub_specification.rb +++ /dev/null @@ -1,108 +0,0 @@ -# frozen_string_literal: true - -require "bundler/remote_specification" - -module Bundler - class StubSpecification < RemoteSpecification - def self.from_stub(stub) - return stub if stub.is_a?(Bundler::StubSpecification) - spec = new(stub.name, stub.version, stub.platform, nil) - spec.stub = stub - spec - end - - attr_accessor :stub, :ignored - - # Pre 2.2.0 did not include extension_dir - # https://github.com/rubygems/rubygems/commit/9485ca2d101b82a946d6f327f4bdcdea6d4946ea - if Bundler.rubygems.provides?(">= 2.2.0") - def source=(source) - super - # Stub has no concept of source, which means that extension_dir may be wrong - # This is the case for git-based gems. So, instead manually assign the extension dir - return unless source.respond_to?(:extension_dir_name) - path = File.join(stub.extensions_dir, source.extension_dir_name) - stub.extension_dir = File.expand_path(path) - end - end - - def to_yaml - _remote_specification.to_yaml - end - - # @!group Stub Delegates - - if Bundler.rubygems.provides?(">= 2.3") - # This is defined directly to avoid having to load every installed spec - def missing_extensions? - stub.missing_extensions? - end - end - - def activated - stub.activated - end - - def activated=(activated) - stub.instance_variable_set(:@activated, activated) - end - - def default_gem - stub.default_gem - end - - def full_gem_path - # deleted gems can have their stubs return nil, so in that case grab the - # expired path from the full spec - stub.full_gem_path || method_missing(:full_gem_path) - end - - if Bundler.rubygems.provides?(">= 2.2.0") - def full_require_paths - stub.full_require_paths - end - - # This is what we do in bundler/rubygems_ext - # full_require_paths is always implemented in >= 2.2.0 - def load_paths - full_require_paths - end - end - - def loaded_from - stub.loaded_from - end - - if Bundler.rubygems.stubs_provide_full_functionality? - def matches_for_glob(glob) - stub.matches_for_glob(glob) - end - end - - def raw_require_paths - stub.raw_require_paths - end - - private - - def _remote_specification - @_remote_specification ||= begin - rs = stub.to_spec - if rs.equal?(self) # happens when to_spec gets the spec from Gem.loaded_specs - rs = Gem::Specification.load(loaded_from) - Bundler.rubygems.stub_set_spec(stub, rs) - end - - unless rs - raise GemspecError, "The gemspec for #{full_name} at #{loaded_from}" \ - " was missing or broken. Try running `gem pristine #{name} -v #{version}`" \ - " to fix the cached spec." - end - - rs.source = source - - rs - end - end - end -end diff --git a/lib/bundler/templates/.document b/lib/bundler/templates/.document deleted file mode 100644 index fb66f13c33..0000000000 --- a/lib/bundler/templates/.document +++ /dev/null @@ -1 +0,0 @@ -# Ignore all files in this directory diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable deleted file mode 100755 index 414a75898d..0000000000 --- a/lib/bundler/templates/Executable +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %> -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application '<%= executable %>' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "pathname" -ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../<%= relative_gemfile_path %>", - Pathname.new(__FILE__).realpath) - -bundle_binstub = File.expand_path("../bundle", __FILE__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - -require "rubygems" -require "bundler/setup" - -load Gem.bin_path("<%= spec.name %>", "<%= executable %>") diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler deleted file mode 100644 index eeda90b584..0000000000 --- a/lib/bundler/templates/Executable.bundler +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %> -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application '<%= executable %>' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "rubygems" - -m = Module.new do - module_function - - def invoked_as_script? - File.expand_path($0) == File.expand_path(__FILE__) - end - - def env_var_version - ENV["BUNDLER_VERSION"] - end - - def cli_arg_version - return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` - bundler_version = nil - update_index = nil - ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN - bundler_version = a - end - next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 || ">= 0.a" - update_index = i - end - bundler_version - end - - def gemfile - gemfile = ENV["BUNDLE_GEMFILE"] - return gemfile if gemfile && !gemfile.empty? - - File.expand_path("../<%= relative_gemfile_path %>", __FILE__) - end - - def lockfile - lockfile = - case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) - else "#{gemfile}.lock" - end - File.expand_path(lockfile) - end - - def lockfile_version - return unless File.file?(lockfile) - lockfile_contents = File.read(lockfile) - return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ - Regexp.last_match(1) - end - - def bundler_version - @bundler_version ||= begin - env_var_version || cli_arg_version || - lockfile_version || "#{Gem::Requirement.default}.a" - end - end - - def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile - - # must dup string for RG < 1.8 compatibility - activate_bundler(bundler_version.dup) - end - - def activate_bundler(bundler_version) - if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0") - bundler_version = "< 2" - end - gem_error = activation_error_handling do - gem "bundler", bundler_version - end - return if gem_error.nil? - require_error = activation_error_handling do - require "bundler/version" - end - return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION)) - warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`" - exit 42 - end - - def activation_error_handling - yield - nil - rescue StandardError, LoadError => e - e - end -end - -m.load_bundler! - -if m.invoked_as_script? - load Gem.bin_path("<%= spec.name %>", "<%= executable %>") -end diff --git a/lib/bundler/templates/Executable.standalone b/lib/bundler/templates/Executable.standalone deleted file mode 100644 index 4bf0753f44..0000000000 --- a/lib/bundler/templates/Executable.standalone +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %> -# -# This file was generated by Bundler. -# -# The application '<%= executable %>' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "pathname" -path = Pathname.new(__FILE__) -$:.unshift File.expand_path "../<%= standalone_path %>", path.realpath - -require "bundler/setup" -load File.expand_path "../<%= executable_path %>", path.realpath diff --git a/lib/bundler/templates/Gemfile b/lib/bundler/templates/Gemfile deleted file mode 100644 index 1afd2cce67..0000000000 --- a/lib/bundler/templates/Gemfile +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true - -source "https://rubygems.org" - -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - -# gem "rails" diff --git a/lib/bundler/templates/gems.rb b/lib/bundler/templates/gems.rb deleted file mode 100644 index 547cd6e8d9..0000000000 --- a/lib/bundler/templates/gems.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -# A sample gems.rb -source "https://rubygems.org" - -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - -# gem "rails" diff --git a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt b/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt deleted file mode 100644 index a3833d29d7..0000000000 --- a/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +++ /dev/null @@ -1,74 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at <%= config[:email] %>. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/lib/bundler/templates/newgem/Gemfile.tt b/lib/bundler/templates/newgem/Gemfile.tt deleted file mode 100644 index c114bd6665..0000000000 --- a/lib/bundler/templates/newgem/Gemfile.tt +++ /dev/null @@ -1,6 +0,0 @@ -source "https://rubygems.org" - -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - -# Specify your gem's dependencies in <%= config[:name] %>.gemspec -gemspec diff --git a/lib/bundler/templates/newgem/LICENSE.txt.tt b/lib/bundler/templates/newgem/LICENSE.txt.tt deleted file mode 100644 index 76ef4b0191..0000000000 --- a/lib/bundler/templates/newgem/LICENSE.txt.tt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) <%= Time.now.year %> <%= config[:author] %> - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/lib/bundler/templates/newgem/README.md.tt b/lib/bundler/templates/newgem/README.md.tt deleted file mode 100644 index 868a0afe67..0000000000 --- a/lib/bundler/templates/newgem/README.md.tt +++ /dev/null @@ -1,47 +0,0 @@ -# <%= config[:constant_name] %> - -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/<%= config[:namespaced_path] %>`. To experiment with that code, run `bin/console` for an interactive prompt. - -TODO: Delete this and the text above, and describe your gem - -## Installation - -Add this line to your application's Gemfile: - -```ruby -gem '<%= config[:name] %>' -``` - -And then execute: - - $ bundle - -Or install it yourself as: - - $ gem install <%= config[:name] %> - -## Usage - -TODO: Write usage instructions here - -## Development - -After checking out the repo, run `bin/setup` to install dependencies.<% if config[:test] %> Then, run `rake <%= config[:test].sub('mini', '').sub('rspec', 'spec') %>` to run the tests.<% end %> You can also run `bin/console` for an interactive prompt that will allow you to experiment.<% if config[:bin] %> Run `bundle exec <%= config[:name] %>` to use the gem in this directory, ignoring other installed copies of this gem.<% end %> - -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). - -## Contributing - -Bug reports and pull requests are welcome on GitHub at https://github.com/<%= config[:github_username] %>/<%= config[:name] %>.<% if config[:coc] %> This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.<% end %> -<% if config[:mit] -%> - -## License - -The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). -<% end -%> -<% if config[:coc] -%> - -## Code of Conduct - -Everyone interacting in the <%= config[:constant_name] %> project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/<%= config[:github_username] %>/<%= config[:name] %>/blob/master/CODE_OF_CONDUCT.md). -<% end -%> diff --git a/lib/bundler/templates/newgem/Rakefile.tt b/lib/bundler/templates/newgem/Rakefile.tt deleted file mode 100644 index 099da6f3ec..0000000000 --- a/lib/bundler/templates/newgem/Rakefile.tt +++ /dev/null @@ -1,29 +0,0 @@ -require "bundler/gem_tasks" -<% if config[:test] == "minitest" -%> -require "rake/testtask" - -Rake::TestTask.new(:test) do |t| - t.libs << "test" - t.libs << "lib" - t.test_files = FileList["test/**/*_test.rb"] -end - -<% elsif config[:test] == "rspec" -%> -require "rspec/core/rake_task" - -RSpec::Core::RakeTask.new(:spec) - -<% end -%> -<% if config[:ext] -%> -require "rake/extensiontask" - -task :build => :compile - -Rake::ExtensionTask.new("<%= config[:underscored_name] %>") do |ext| - ext.lib_dir = "lib/<%= config[:namespaced_path] %>" -end - -task :default => [:clobber, :compile, :<%= config[:test_task] %>] -<% else -%> -task :default => :<%= config[:test_task] %> -<% end -%> diff --git a/lib/bundler/templates/newgem/bin/console.tt b/lib/bundler/templates/newgem/bin/console.tt deleted file mode 100644 index a27f82430f..0000000000 --- a/lib/bundler/templates/newgem/bin/console.tt +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env ruby - -require "bundler/setup" -require "<%= config[:namespaced_path] %>" - -# You can add fixtures and/or initialization code here to make experimenting -# with your gem easier. You can also use a different console, if you like. - -# (If you use this, don't forget to add pry to your Gemfile!) -# require "pry" -# Pry.start - -require "irb" -IRB.start(__FILE__) diff --git a/lib/bundler/templates/newgem/bin/setup.tt b/lib/bundler/templates/newgem/bin/setup.tt deleted file mode 100644 index dce67d860a..0000000000 --- a/lib/bundler/templates/newgem/bin/setup.tt +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' -set -vx - -bundle install - -# Do any other automated setup that you need to do here diff --git a/lib/bundler/templates/newgem/exe/newgem.tt b/lib/bundler/templates/newgem/exe/newgem.tt deleted file mode 100644 index a8339bb79f..0000000000 --- a/lib/bundler/templates/newgem/exe/newgem.tt +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby - -require "<%= config[:namespaced_path] %>" diff --git a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt b/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt deleted file mode 100644 index 8cfc828f94..0000000000 --- a/lib/bundler/templates/newgem/ext/newgem/extconf.rb.tt +++ /dev/null @@ -1,3 +0,0 @@ -require "mkmf" - -create_makefile(<%= config[:makefile_path].inspect %>) diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt deleted file mode 100644 index 8177c4d202..0000000000 --- a/lib/bundler/templates/newgem/ext/newgem/newgem.c.tt +++ /dev/null @@ -1,9 +0,0 @@ -#include "<%= config[:underscored_name] %>.h" - -VALUE rb_m<%= config[:constant_array].join %>; - -void -Init_<%= config[:underscored_name] %>(void) -{ - rb_m<%= config[:constant_array].join %> = rb_define_module(<%= config[:constant_name].inspect %>); -} diff --git a/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt b/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt deleted file mode 100644 index c6e420b66e..0000000000 --- a/lib/bundler/templates/newgem/ext/newgem/newgem.h.tt +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef <%= config[:underscored_name].upcase %>_H -#define <%= config[:underscored_name].upcase %>_H 1 - -#include "ruby.h" - -#endif /* <%= config[:underscored_name].upcase %>_H */ diff --git a/lib/bundler/templates/newgem/gitignore.tt b/lib/bundler/templates/newgem/gitignore.tt deleted file mode 100644 index b1c9f9986c..0000000000 --- a/lib/bundler/templates/newgem/gitignore.tt +++ /dev/null @@ -1,20 +0,0 @@ -/.bundle/ -/.yardoc -/_yardoc/ -/coverage/ -/doc/ -/pkg/ -/spec/reports/ -/tmp/ -<%- if config[:ext] -%> -*.bundle -*.so -*.o -*.a -mkmf.log -<%- end -%> -<%- if config[:test] == "rspec" -%> - -# rspec failure tracking -.rspec_status -<%- end -%> diff --git a/lib/bundler/templates/newgem/lib/newgem.rb.tt b/lib/bundler/templates/newgem/lib/newgem.rb.tt deleted file mode 100644 index 7d8ad90ab0..0000000000 --- a/lib/bundler/templates/newgem/lib/newgem.rb.tt +++ /dev/null @@ -1,12 +0,0 @@ -require "<%= config[:namespaced_path] %>/version" -<%- if config[:ext] -%> -require "<%= config[:namespaced_path] %>/<%= config[:underscored_name] %>" -<%- end -%> - -<%- config[:constant_array].each_with_index do |c, i| -%> -<%= " " * i %>module <%= c %> -<%- end -%> -<%= " " * config[:constant_array].size %># Your code goes here... -<%- (config[:constant_array].size-1).downto(0) do |i| -%> -<%= " " * i %>end -<%- end -%> diff --git a/lib/bundler/templates/newgem/lib/newgem/version.rb.tt b/lib/bundler/templates/newgem/lib/newgem/version.rb.tt deleted file mode 100644 index 389daf5048..0000000000 --- a/lib/bundler/templates/newgem/lib/newgem/version.rb.tt +++ /dev/null @@ -1,7 +0,0 @@ -<%- config[:constant_array].each_with_index do |c, i| -%> -<%= " " * i %>module <%= c %> -<%- end -%> -<%= " " * config[:constant_array].size %>VERSION = "0.1.0" -<%- (config[:constant_array].size-1).downto(0) do |i| -%> -<%= " " * i %>end -<%- end -%> diff --git a/lib/bundler/templates/newgem/newgem.gemspec.tt b/lib/bundler/templates/newgem/newgem.gemspec.tt deleted file mode 100644 index 9a87a1374a..0000000000 --- a/lib/bundler/templates/newgem/newgem.gemspec.tt +++ /dev/null @@ -1,49 +0,0 @@ -<%- if RUBY_VERSION < "2.0.0" -%> -# coding: utf-8 -<%- end -%> - -lib = File.expand_path("../lib", __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require "<%= config[:namespaced_path] %>/version" - -Gem::Specification.new do |spec| - spec.name = <%= config[:name].inspect %> - spec.version = <%= config[:constant_name] %>::VERSION - 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] -%> - spec.license = "MIT" -<%- end -%> - - # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' - # to allow pushing to a single host or delete this section to allow pushing to any host. - if spec.respond_to?(:metadata) - spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" - else - raise "RubyGems 2.0 or newer is required to protect against " \ - "public gem pushes." - end - - spec.files = `git ls-files -z`.split("\x0").reject do |f| - f.match(%r{^(test|spec|features)/}) - end - spec.bindir = "exe" - spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } - spec.require_paths = ["lib"] -<%- if config[:ext] -%> - spec.extensions = ["ext/<%= config[:underscored_name] %>/extconf.rb"] -<%- end -%> - - spec.add_development_dependency "bundler", "~> <%= config[:bundler_version] %>" - spec.add_development_dependency "rake", "~> 10.0" -<%- if config[:ext] -%> - spec.add_development_dependency "rake-compiler" -<%- end -%> -<%- if config[:test] -%> - spec.add_development_dependency "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>" -<%- end -%> -end diff --git a/lib/bundler/templates/newgem/rspec.tt b/lib/bundler/templates/newgem/rspec.tt deleted file mode 100644 index 34c5164d9b..0000000000 --- a/lib/bundler/templates/newgem/rspec.tt +++ /dev/null @@ -1,3 +0,0 @@ ---format documentation ---color ---require spec_helper diff --git a/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt b/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt deleted file mode 100644 index c63b487830..0000000000 --- a/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +++ /dev/null @@ -1,9 +0,0 @@ -RSpec.describe <%= config[:constant_name] %> do - it "has a version number" do - expect(<%= config[:constant_name] %>::VERSION).not_to be nil - end - - it "does something useful" do - expect(false).to eq(true) - end -end diff --git a/lib/bundler/templates/newgem/spec/spec_helper.rb.tt b/lib/bundler/templates/newgem/spec/spec_helper.rb.tt deleted file mode 100644 index 805cf57e01..0000000000 --- a/lib/bundler/templates/newgem/spec/spec_helper.rb.tt +++ /dev/null @@ -1,14 +0,0 @@ -require "bundler/setup" -require "<%= config[:namespaced_path] %>" - -RSpec.configure do |config| - # Enable flags like --only-failures and --next-failure - config.example_status_persistence_file_path = ".rspec_status" - - # Disable RSpec exposing methods globally on `Module` and `main` - config.disable_monkey_patching! - - config.expect_with :rspec do |c| - c.syntax = :expect - end -end diff --git a/lib/bundler/templates/newgem/test/newgem_test.rb.tt b/lib/bundler/templates/newgem/test/newgem_test.rb.tt deleted file mode 100644 index f2af9f90e0..0000000000 --- a/lib/bundler/templates/newgem/test/newgem_test.rb.tt +++ /dev/null @@ -1,11 +0,0 @@ -require "test_helper" - -class <%= config[:constant_name] %>Test < Minitest::Test - def test_that_it_has_a_version_number - refute_nil ::<%= config[:constant_name] %>::VERSION - end - - def test_it_does_something_useful - assert false - end -end diff --git a/lib/bundler/templates/newgem/test/test_helper.rb.tt b/lib/bundler/templates/newgem/test/test_helper.rb.tt deleted file mode 100644 index 725e3e4647..0000000000 --- a/lib/bundler/templates/newgem/test/test_helper.rb.tt +++ /dev/null @@ -1,4 +0,0 @@ -$LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) -require "<%= config[:namespaced_path] %>" - -require "minitest/autorun" diff --git a/lib/bundler/templates/newgem/travis.yml.tt b/lib/bundler/templates/newgem/travis.yml.tt deleted file mode 100644 index fe0761cc23..0000000000 --- a/lib/bundler/templates/newgem/travis.yml.tt +++ /dev/null @@ -1,5 +0,0 @@ -sudo: false -language: ruby -rvm: - - <%= RUBY_VERSION %> -before_install: gem install bundler -v <%= Bundler::VERSION %> diff --git a/lib/bundler/ui.rb b/lib/bundler/ui.rb deleted file mode 100644 index 8138b30d38..0000000000 --- a/lib/bundler/ui.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module UI - autoload :RGProxy, "bundler/ui/rg_proxy" - autoload :Shell, "bundler/ui/shell" - autoload :Silent, "bundler/ui/silent" - end -end diff --git a/lib/bundler/ui/rg_proxy.rb b/lib/bundler/ui/rg_proxy.rb deleted file mode 100644 index e2f98481db..0000000000 --- a/lib/bundler/ui/rg_proxy.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -require "bundler/ui" -require "rubygems/user_interaction" - -module Bundler - module UI - class RGProxy < ::Gem::SilentUI - def initialize(ui) - @ui = ui - super() - end - - def say(message) - @ui && @ui.debug(message) - end - end - end -end diff --git a/lib/bundler/ui/shell.rb b/lib/bundler/ui/shell.rb deleted file mode 100644 index 3b3b6bfb53..0000000000 --- a/lib/bundler/ui/shell.rb +++ /dev/null @@ -1,144 +0,0 @@ -# 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 - @level = ENV["DEBUG"] ? "debug" : "info" - @warning_history = [] - end - - def add_color(string, *color) - @shell.set_color(string, *color) - end - - def info(msg, newline = nil) - tell_me(msg, nil, newline) if level("info") - end - - def confirm(msg, newline = nil) - tell_me(msg, :green, newline) if level("confirm") - end - - def warn(msg, newline = nil) - return unless level("warn") - return if @warning_history.include? msg - @warning_history << msg - - return tell_err(msg, :yellow, newline) if Bundler.feature_flag.error_on_stderr? - tell_me(msg, :yellow, newline) - end - - def error(msg, newline = nil) - return unless level("error") - return tell_err(msg, :red, newline) if Bundler.feature_flag.error_on_stderr? - tell_me(msg, :red, newline) - end - - def debug(msg, newline = nil) - tell_me(msg, nil, newline) if debug? - end - - def debug? - level("debug") - end - - def quiet? - level("quiet") - end - - def ask(msg) - @shell.ask(msg) - end - - def yes?(msg) - @shell.yes?(msg) - end - - def no? - @shell.no?(msg) - end - - def level=(level) - raise ArgumentError unless LEVELS.include?(level.to_s) - @level = level.to_s - end - - def level(name = nil) - return @level unless name - unless index = LEVELS.index(name) - raise "#{name.inspect} is not a valid level" - end - index <= LEVELS.index(@level) - end - - def trace(e, newline = nil, force = false) - return unless debug? || force - msg = "#{e.class}: #{e.message}\n#{e.backtrace.join("\n ")}" - tell_me(msg, nil, newline) - end - - def silence(&blk) - with_level("silent", &blk) - end - - def unprinted_warnings - [] - end - - private - - # valimism - def tell_me(msg, color = nil, newline = nil) - msg = word_wrap(msg) if newline.is_a?(Hash) && newline[:wrap] - if newline.nil? - @shell.say(msg, color) - else - @shell.say(msg, color, newline) - end - end - - def tell_err(message, color = nil, newline = nil) - newline = message.to_s !~ /( |\t)\Z/ unless newline - message = word_wrap(message) if newline.is_a?(Hash) && newline[:wrap] - - color = nil if color && !$stderr.tty? - - buffer = @shell.send(:prepare_message, message, *color) - buffer << "\n" if newline && !message.to_s.end_with?("\n") - - @shell.send(:stderr).print(buffer) - @shell.send(:stderr).flush - end - - def strip_leading_spaces(text) - spaces = text[/\A\s+/, 0] - spaces ? text.gsub(/#{spaces}/, "") : text - end - - def word_wrap(text, line_width = @shell.terminal_width) - strip_leading_spaces(text).split("\n").collect do |line| - line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1\n").strip : line - end * "\n" - end - - def with_level(level) - original = @level - @level = level - yield - ensure - @level = original - end - end - end -end diff --git a/lib/bundler/ui/silent.rb b/lib/bundler/ui/silent.rb deleted file mode 100644 index dca1b2ac86..0000000000 --- a/lib/bundler/ui/silent.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module UI - class Silent - attr_writer :shell - - def initialize - @warnings = [] - end - - def add_color(string, color) - string - end - - def info(message, newline = nil) - end - - def confirm(message, newline = nil) - end - - def warn(message, newline = nil) - @warnings |= [message] - end - - def error(message, newline = nil) - end - - def debug(message, newline = nil) - end - - def debug? - false - end - - def quiet? - false - end - - def ask(message) - end - - def yes?(msg) - raise "Cannot ask yes? with a silent shell" - end - - def no? - raise "Cannot ask no? with a silent shell" - end - - def level=(name) - end - - def level(name = nil) - end - - def trace(message, newline = nil, force = false) - end - - def silence - yield - end - - def unprinted_warnings - @warnings - end - end - end -end diff --git a/lib/bundler/uri_credentials_filter.rb b/lib/bundler/uri_credentials_filter.rb deleted file mode 100644 index ee3692268c..0000000000 --- a/lib/bundler/uri_credentials_filter.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module URICredentialsFilter - module_function - - def credential_filtered_uri(uri_to_anonymize) - return uri_to_anonymize if uri_to_anonymize.nil? - uri = uri_to_anonymize.dup - uri = URI(uri.to_s) unless uri.is_a?(URI) - if uri.userinfo - # oauth authentication - if uri.password == "x-oauth-basic" || uri.password == "x" - # URI as string does not display with password if no user is set - oauth_designation = uri.password - uri.user = oauth_designation - end - uri.password = nil - end - return uri if uri_to_anonymize.is_a?(URI) - return uri.to_s if uri_to_anonymize.is_a?(String) - rescue URI::InvalidURIError # uri is not canonical uri scheme - uri - end - - def credential_filtered_string(str_to_filter, uri) - return str_to_filter if uri.nil? || str_to_filter.nil? - str_with_no_credentials = str_to_filter.dup - anonymous_uri_str = credential_filtered_uri(uri).to_s - uri_str = uri.to_s - if anonymous_uri_str != uri_str - str_with_no_credentials = str_with_no_credentials.gsub(uri_str, anonymous_uri_str) - end - str_with_no_credentials - end - end -end diff --git a/lib/bundler/vendor/fileutils/lib/fileutils.rb b/lib/bundler/vendor/fileutils/lib/fileutils.rb deleted file mode 100644 index cc69740845..0000000000 --- a/lib/bundler/vendor/fileutils/lib/fileutils.rb +++ /dev/null @@ -1,1638 +0,0 @@ -# frozen_string_literal: true -# -# = fileutils.rb -# -# Copyright (c) 2000-2007 Minero Aoki -# -# This program is free software. -# You can distribute/modify this program under the same terms of ruby. -# -# == module Bundler::FileUtils -# -# Namespace for several file utility methods for copying, moving, removing, etc. -# -# === Module Functions -# -# require 'bundler/vendor/fileutils/lib/fileutils' -# -# Bundler::FileUtils.cd(dir, options) -# Bundler::FileUtils.cd(dir, options) {|dir| block } -# Bundler::FileUtils.pwd() -# Bundler::FileUtils.mkdir(dir, options) -# Bundler::FileUtils.mkdir(list, options) -# Bundler::FileUtils.mkdir_p(dir, options) -# Bundler::FileUtils.mkdir_p(list, options) -# Bundler::FileUtils.rmdir(dir, options) -# Bundler::FileUtils.rmdir(list, options) -# Bundler::FileUtils.ln(target, link, options) -# Bundler::FileUtils.ln(targets, dir, options) -# Bundler::FileUtils.ln_s(target, link, options) -# Bundler::FileUtils.ln_s(targets, dir, options) -# Bundler::FileUtils.ln_sf(target, link, options) -# Bundler::FileUtils.cp(src, dest, options) -# Bundler::FileUtils.cp(list, dir, options) -# Bundler::FileUtils.cp_r(src, dest, options) -# Bundler::FileUtils.cp_r(list, dir, options) -# Bundler::FileUtils.mv(src, dest, options) -# Bundler::FileUtils.mv(list, dir, options) -# Bundler::FileUtils.rm(list, options) -# Bundler::FileUtils.rm_r(list, options) -# Bundler::FileUtils.rm_rf(list, options) -# Bundler::FileUtils.install(src, dest, options) -# Bundler::FileUtils.chmod(mode, list, options) -# Bundler::FileUtils.chmod_R(mode, list, options) -# Bundler::FileUtils.chown(user, group, list, options) -# Bundler::FileUtils.chown_R(user, group, list, options) -# Bundler::FileUtils.touch(list, options) -# -# The <tt>options</tt> parameter is a hash of options, taken from the list -# <tt>:force</tt>, <tt>:noop</tt>, <tt>:preserve</tt>, and <tt>:verbose</tt>. -# <tt>:noop</tt> means that no changes are made. The other three are obvious. -# Each method documents the options that it honours. -# -# All methods that have the concept of a "source" file or directory can take -# either one file or a list of files in that argument. See the method -# documentation for examples. -# -# There are some `low level' methods, which do not accept any option: -# -# Bundler::FileUtils.copy_entry(src, dest, preserve = false, dereference = false) -# Bundler::FileUtils.copy_file(src, dest, preserve = false, dereference = true) -# Bundler::FileUtils.copy_stream(srcstream, deststream) -# Bundler::FileUtils.remove_entry(path, force = false) -# Bundler::FileUtils.remove_entry_secure(path, force = false) -# Bundler::FileUtils.remove_file(path, force = false) -# Bundler::FileUtils.compare_file(path_a, path_b) -# Bundler::FileUtils.compare_stream(stream_a, stream_b) -# Bundler::FileUtils.uptodate?(file, cmp_list) -# -# == module Bundler::FileUtils::Verbose -# -# This module has all methods of Bundler::FileUtils module, but it outputs messages -# before acting. This equates to passing the <tt>:verbose</tt> flag to methods -# in Bundler::FileUtils. -# -# == module Bundler::FileUtils::NoWrite -# -# This module has all methods of Bundler::FileUtils module, but never changes -# files/directories. This equates to passing the <tt>:noop</tt> flag to methods -# in Bundler::FileUtils. -# -# == module Bundler::FileUtils::DryRun -# -# This module has all methods of Bundler::FileUtils module, but never changes -# files/directories. This equates to passing the <tt>:noop</tt> and -# <tt>:verbose</tt> flags to methods in Bundler::FileUtils. -# - -module Bundler::FileUtils - - def self.private_module_function(name) #:nodoc: - module_function name - private_class_method name - end - - # - # Returns the name of the current directory. - # - def pwd - Dir.pwd - end - module_function :pwd - - alias getwd pwd - module_function :getwd - - # - # Changes the current directory to the directory +dir+. - # - # If this method is called with block, resumes to the old - # working directory after the block execution finished. - # - # Bundler::FileUtils.cd('/', :verbose => true) # chdir and report it - # - # Bundler::FileUtils.cd('/') do # chdir - # # ... # do something - # end # return to original directory - # - def cd(dir, verbose: nil, &block) # :yield: dir - fu_output_message "cd #{dir}" if verbose - Dir.chdir(dir, &block) - fu_output_message 'cd -' if verbose and block - end - module_function :cd - - alias chdir cd - module_function :chdir - - # - # Returns true if +new+ is newer than all +old_list+. - # Non-existent files are older than any file. - # - # Bundler::FileUtils.uptodate?('hello.o', %w(hello.c hello.h)) or \ - # system 'make hello.o' - # - def uptodate?(new, old_list) - return false unless File.exist?(new) - new_time = File.mtime(new) - old_list.each do |old| - if File.exist?(old) - return false unless new_time > File.mtime(old) - end - end - true - end - module_function :uptodate? - - def remove_trailing_slash(dir) #:nodoc: - dir == '/' ? dir : dir.chomp(?/) - end - private_module_function :remove_trailing_slash - - # - # Creates one or more directories. - # - # Bundler::FileUtils.mkdir 'test' - # Bundler::FileUtils.mkdir %w( tmp data ) - # Bundler::FileUtils.mkdir 'notexist', :noop => true # Does not really create. - # Bundler::FileUtils.mkdir 'tmp', :mode => 0700 - # - def mkdir(list, mode: nil, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message "mkdir #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose - return if noop - - list.each do |dir| - fu_mkdir dir, mode - end - end - module_function :mkdir - - # - # Creates a directory and all its parent directories. - # For example, - # - # Bundler::FileUtils.mkdir_p '/usr/local/lib/ruby' - # - # causes to make following directories, if it does not exist. - # - # * /usr - # * /usr/local - # * /usr/local/lib - # * /usr/local/lib/ruby - # - # You can pass several directories at a time in a list. - # - def mkdir_p(list, mode: nil, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message "mkdir -p #{mode ? ('-m %03o ' % mode) : ''}#{list.join ' '}" if verbose - return *list if noop - - list.map {|path| remove_trailing_slash(path)}.each do |path| - # optimize for the most common case - begin - fu_mkdir path, mode - next - rescue SystemCallError - next if File.directory?(path) - end - - stack = [] - until path == stack.last # dirname("/")=="/", dirname("C:/")=="C:/" - stack.push path - path = File.dirname(path) - end - stack.pop # root directory should exist - stack.reverse_each do |dir| - begin - fu_mkdir dir, mode - rescue SystemCallError - raise unless File.directory?(dir) - end - end - end - - return *list - end - module_function :mkdir_p - - alias mkpath mkdir_p - alias makedirs mkdir_p - module_function :mkpath - module_function :makedirs - - def fu_mkdir(path, mode) #:nodoc: - path = remove_trailing_slash(path) - if mode - Dir.mkdir path, mode - File.chmod mode, path - else - Dir.mkdir path - end - end - private_module_function :fu_mkdir - - # - # Removes one or more directories. - # - # Bundler::FileUtils.rmdir 'somedir' - # Bundler::FileUtils.rmdir %w(somedir anydir otherdir) - # # Does not really remove directory; outputs message. - # Bundler::FileUtils.rmdir 'somedir', :verbose => true, :noop => true - # - def rmdir(list, parents: nil, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message "rmdir #{parents ? '-p ' : ''}#{list.join ' '}" if verbose - return if noop - list.each do |dir| - begin - Dir.rmdir(dir = remove_trailing_slash(dir)) - if parents - until (parent = File.dirname(dir)) == '.' or parent == dir - dir = parent - Dir.rmdir(dir) - end - end - rescue Errno::ENOTEMPTY, Errno::EEXIST, Errno::ENOENT - end - end - end - module_function :rmdir - - # - # :call-seq: - # Bundler::FileUtils.ln(target, link, force: nil, noop: nil, verbose: nil) - # Bundler::FileUtils.ln(target, dir, force: nil, noop: nil, verbose: nil) - # Bundler::FileUtils.ln(targets, dir, force: nil, noop: nil, verbose: nil) - # - # In the first form, creates a hard link +link+ which points to +target+. - # If +link+ already exists, raises Errno::EEXIST. - # But if the :force option is set, overwrites +link+. - # - # Bundler::FileUtils.ln 'gcc', 'cc', verbose: true - # Bundler::FileUtils.ln '/usr/bin/emacs21', '/usr/bin/emacs' - # - # In the second form, creates a link +dir/target+ pointing to +target+. - # In the third form, creates several hard links in the directory +dir+, - # pointing to each item in +targets+. - # If +dir+ is not a directory, raises Errno::ENOTDIR. - # - # Bundler::FileUtils.cd '/sbin' - # Bundler::FileUtils.ln %w(cp mv mkdir), '/bin' # Now /sbin/cp and /bin/cp are linked. - # - def ln(src, dest, force: nil, noop: nil, verbose: nil) - fu_output_message "ln#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose - return if noop - fu_each_src_dest0(src, dest) do |s,d| - remove_file d, true if force - File.link s, d - end - end - module_function :ln - - alias link ln - module_function :link - - # - # :call-seq: - # Bundler::FileUtils.ln_s(target, link, force: nil, noop: nil, verbose: nil) - # Bundler::FileUtils.ln_s(target, dir, force: nil, noop: nil, verbose: nil) - # Bundler::FileUtils.ln_s(targets, dir, force: nil, noop: nil, verbose: nil) - # - # In the first form, creates a symbolic link +link+ which points to +target+. - # If +link+ already exists, raises Errno::EEXIST. - # But if the :force option is set, overwrites +link+. - # - # Bundler::FileUtils.ln_s '/usr/bin/ruby', '/usr/local/bin/ruby' - # Bundler::FileUtils.ln_s 'verylongsourcefilename.c', 'c', force: true - # - # In the second form, creates a link +dir/target+ pointing to +target+. - # In the third form, creates several symbolic links in the directory +dir+, - # pointing to each item in +targets+. - # If +dir+ is not a directory, raises Errno::ENOTDIR. - # - # Bundler::FileUtils.ln_s Dir.glob('/bin/*.rb'), '/home/foo/bin' - # - def ln_s(src, dest, force: nil, noop: nil, verbose: nil) - fu_output_message "ln -s#{force ? 'f' : ''} #{[src,dest].flatten.join ' '}" if verbose - return if noop - fu_each_src_dest0(src, dest) do |s,d| - remove_file d, true if force - File.symlink s, d - end - end - module_function :ln_s - - alias symlink ln_s - module_function :symlink - - # - # :call-seq: - # Bundler::FileUtils.ln_sf(*args) - # - # Same as - # - # Bundler::FileUtils.ln_s(*args, force: true) - # - def ln_sf(src, dest, noop: nil, verbose: nil) - ln_s src, dest, force: true, noop: noop, verbose: verbose - end - module_function :ln_sf - - # - # Copies a file content +src+ to +dest+. If +dest+ is a directory, - # copies +src+ to +dest/src+. - # - # If +src+ is a list of files, then +dest+ must be a directory. - # - # Bundler::FileUtils.cp 'eval.c', 'eval.c.org' - # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6' - # Bundler::FileUtils.cp %w(cgi.rb complex.rb date.rb), '/usr/lib/ruby/1.6', :verbose => true - # Bundler::FileUtils.cp 'symlink', 'dest' # copy content, "dest" is not a symlink - # - def cp(src, dest, preserve: nil, noop: nil, verbose: nil) - fu_output_message "cp#{preserve ? ' -p' : ''} #{[src,dest].flatten.join ' '}" if verbose - return if noop - fu_each_src_dest(src, dest) do |s, d| - copy_file s, d, preserve - end - end - module_function :cp - - alias copy cp - module_function :copy - - # - # Copies +src+ to +dest+. If +src+ is a directory, this method copies - # all its contents recursively. If +dest+ is a directory, copies - # +src+ to +dest/src+. - # - # +src+ can be a list of files. - # - # # Installing Ruby library "mylib" under the site_ruby - # Bundler::FileUtils.rm_r site_ruby + '/mylib', :force - # Bundler::FileUtils.cp_r 'lib/', site_ruby + '/mylib' - # - # # Examples of copying several files to target directory. - # Bundler::FileUtils.cp_r %w(mail.rb field.rb debug/), site_ruby + '/tmail' - # Bundler::FileUtils.cp_r Dir.glob('*.rb'), '/home/foo/lib/ruby', :noop => true, :verbose => true - # - # # If you want to copy all contents of a directory instead of the - # # directory itself, c.f. src/x -> dest/x, src/y -> dest/y, - # # use following code. - # Bundler::FileUtils.cp_r 'src/.', 'dest' # cp_r('src', 'dest') makes dest/src, - # # but this doesn't. - # - def cp_r(src, dest, preserve: nil, noop: nil, verbose: nil, - dereference_root: true, remove_destination: nil) - fu_output_message "cp -r#{preserve ? 'p' : ''}#{remove_destination ? ' --remove-destination' : ''} #{[src,dest].flatten.join ' '}" if verbose - return if noop - fu_each_src_dest(src, dest) do |s, d| - copy_entry s, d, preserve, dereference_root, remove_destination - end - end - module_function :cp_r - - # - # Copies a file system entry +src+ to +dest+. - # If +src+ is a directory, this method copies its contents recursively. - # This method preserves file types, c.f. symlink, directory... - # (FIFO, device files and etc. are not supported yet) - # - # Both of +src+ and +dest+ must be a path name. - # +src+ must exist, +dest+ must not exist. - # - # If +preserve+ is true, this method preserves owner, group, and - # modified time. Permissions are copied regardless +preserve+. - # - # If +dereference_root+ is true, this method dereference tree root. - # - # If +remove_destination+ is true, this method removes each destination file before copy. - # - def copy_entry(src, dest, preserve = false, dereference_root = false, remove_destination = false) - Entry_.new(src, nil, dereference_root).wrap_traverse(proc do |ent| - destent = Entry_.new(dest, ent.rel, false) - File.unlink destent.path if remove_destination && File.file?(destent.path) - ent.copy destent.path - end, proc do |ent| - destent = Entry_.new(dest, ent.rel, false) - ent.copy_metadata destent.path if preserve - end) - end - module_function :copy_entry - - # - # Copies file contents of +src+ to +dest+. - # Both of +src+ and +dest+ must be a path name. - # - def copy_file(src, dest, preserve = false, dereference = true) - ent = Entry_.new(src, nil, dereference) - ent.copy_file dest - ent.copy_metadata dest if preserve - end - module_function :copy_file - - # - # Copies stream +src+ to +dest+. - # +src+ must respond to #read(n) and - # +dest+ must respond to #write(str). - # - def copy_stream(src, dest) - IO.copy_stream(src, dest) - end - module_function :copy_stream - - # - # Moves file(s) +src+ to +dest+. If +file+ and +dest+ exist on the different - # disk partition, the file is copied then the original file is removed. - # - # Bundler::FileUtils.mv 'badname.rb', 'goodname.rb' - # Bundler::FileUtils.mv 'stuff.rb', '/notexist/lib/ruby', :force => true # no error - # - # Bundler::FileUtils.mv %w(junk.txt dust.txt), '/home/foo/.trash/' - # Bundler::FileUtils.mv Dir.glob('test*.rb'), 'test', :noop => true, :verbose => true - # - def mv(src, dest, force: nil, noop: nil, verbose: nil, secure: nil) - fu_output_message "mv#{force ? ' -f' : ''} #{[src,dest].flatten.join ' '}" if verbose - return if noop - fu_each_src_dest(src, dest) do |s, d| - destent = Entry_.new(d, nil, true) - begin - if destent.exist? - if destent.directory? - raise Errno::EEXIST, d - else - destent.remove_file if rename_cannot_overwrite_file? - end - end - begin - File.rename s, d - rescue Errno::EXDEV - copy_entry s, d, true - if secure - remove_entry_secure s, force - else - remove_entry s, force - end - end - rescue SystemCallError - raise unless force - end - end - end - module_function :mv - - alias move mv - module_function :move - - def rename_cannot_overwrite_file? #:nodoc: - /emx/ =~ RUBY_PLATFORM - end - private_module_function :rename_cannot_overwrite_file? - - # - # Remove file(s) specified in +list+. This method cannot remove directories. - # All StandardErrors are ignored when the :force option is set. - # - # Bundler::FileUtils.rm %w( junk.txt dust.txt ) - # Bundler::FileUtils.rm Dir.glob('*.so') - # Bundler::FileUtils.rm 'NotExistFile', :force => true # never raises exception - # - def rm(list, force: nil, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message "rm#{force ? ' -f' : ''} #{list.join ' '}" if verbose - return if noop - - list.each do |path| - remove_file path, force - end - end - module_function :rm - - alias remove rm - module_function :remove - - # - # Equivalent to - # - # Bundler::FileUtils.rm(list, :force => true) - # - def rm_f(list, noop: nil, verbose: nil) - rm list, force: true, noop: noop, verbose: verbose - end - module_function :rm_f - - alias safe_unlink rm_f - module_function :safe_unlink - - # - # remove files +list+[0] +list+[1]... If +list+[n] is a directory, - # removes its all contents recursively. This method ignores - # StandardError when :force option is set. - # - # Bundler::FileUtils.rm_r Dir.glob('/tmp/*') - # Bundler::FileUtils.rm_r 'some_dir', :force => true - # - # WARNING: This method causes local vulnerability - # if one of parent directories or removing directory tree are world - # writable (including /tmp, whose permission is 1777), and the current - # process has strong privilege such as Unix super user (root), and the - # system has symbolic link. For secure removing, read the documentation - # of #remove_entry_secure carefully, and set :secure option to true. - # Default is :secure=>false. - # - # NOTE: This method calls #remove_entry_secure if :secure option is set. - # See also #remove_entry_secure. - # - def rm_r(list, force: nil, noop: nil, verbose: nil, secure: nil) - list = fu_list(list) - fu_output_message "rm -r#{force ? 'f' : ''} #{list.join ' '}" if verbose - return if noop - list.each do |path| - if secure - remove_entry_secure path, force - else - remove_entry path, force - end - end - end - module_function :rm_r - - # - # Equivalent to - # - # Bundler::FileUtils.rm_r(list, :force => true) - # - # WARNING: This method causes local vulnerability. - # Read the documentation of #rm_r first. - # - def rm_rf(list, noop: nil, verbose: nil, secure: nil) - rm_r list, force: true, noop: noop, verbose: verbose, secure: secure - end - module_function :rm_rf - - alias rmtree rm_rf - module_function :rmtree - - # - # This method removes a file system entry +path+. +path+ shall be a - # regular file, a directory, or something. If +path+ is a directory, - # remove it recursively. This method is required to avoid TOCTTOU - # (time-of-check-to-time-of-use) local security vulnerability of #rm_r. - # #rm_r causes security hole when: - # - # * Parent directory is world writable (including /tmp). - # * Removing directory tree includes world writable directory. - # * The system has symbolic link. - # - # To avoid this security hole, this method applies special preprocess. - # If +path+ is a directory, this method chown(2) and chmod(2) all - # removing directories. This requires the current process is the - # owner of the removing whole directory tree, or is the super user (root). - # - # WARNING: You must ensure that *ALL* parent directories cannot be - # moved by other untrusted users. For example, parent directories - # should not be owned by untrusted users, and should not be world - # writable except when the sticky bit set. - # - # WARNING: Only the owner of the removing directory tree, or Unix super - # user (root) should invoke this method. Otherwise this method does not - # work. - # - # For details of this security vulnerability, see Perl's case: - # - # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2005-0448 - # * http://www.cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2004-0452 - # - # For fileutils.rb, this vulnerability is reported in [ruby-dev:26100]. - # - def remove_entry_secure(path, force = false) - unless fu_have_symlink? - remove_entry path, force - return - end - fullpath = File.expand_path(path) - st = File.lstat(fullpath) - unless st.directory? - File.unlink fullpath - return - end - # is a directory. - parent_st = File.stat(File.dirname(fullpath)) - unless parent_st.world_writable? - remove_entry path, force - return - end - unless parent_st.sticky? - raise ArgumentError, "parent directory is world writable, Bundler::FileUtils#remove_entry_secure does not work; abort: #{path.inspect} (parent directory mode #{'%o' % parent_st.mode})" - end - # freeze tree root - euid = Process.euid - File.open(fullpath + '/.') {|f| - unless fu_stat_identical_entry?(st, f.stat) - # symlink (TOC-to-TOU attack?) - File.unlink fullpath - return - end - f.chown euid, -1 - f.chmod 0700 - unless fu_stat_identical_entry?(st, File.lstat(fullpath)) - # TOC-to-TOU attack? - File.unlink fullpath - return - end - } - # ---- tree root is frozen ---- - root = Entry_.new(path) - root.preorder_traverse do |ent| - if ent.directory? - ent.chown euid, -1 - ent.chmod 0700 - end - end - root.postorder_traverse do |ent| - begin - ent.remove - rescue - raise unless force - end - end - rescue - raise unless force - end - module_function :remove_entry_secure - - def fu_have_symlink? #:nodoc: - File.symlink nil, nil - rescue NotImplementedError - return false - rescue TypeError - return true - end - private_module_function :fu_have_symlink? - - def fu_stat_identical_entry?(a, b) #:nodoc: - a.dev == b.dev and a.ino == b.ino - end - private_module_function :fu_stat_identical_entry? - - # - # This method removes a file system entry +path+. - # +path+ might be a regular file, a directory, or something. - # If +path+ is a directory, remove it recursively. - # - # See also #remove_entry_secure. - # - def remove_entry(path, force = false) - Entry_.new(path).postorder_traverse do |ent| - begin - ent.remove - rescue - raise unless force - end - end - rescue - raise unless force - end - module_function :remove_entry - - # - # Removes a file +path+. - # This method ignores StandardError if +force+ is true. - # - def remove_file(path, force = false) - Entry_.new(path).remove_file - rescue - raise unless force - end - module_function :remove_file - - # - # Removes a directory +dir+ and its contents recursively. - # This method ignores StandardError if +force+ is true. - # - def remove_dir(path, force = false) - remove_entry path, force # FIXME?? check if it is a directory - end - module_function :remove_dir - - # - # Returns true if the contents of a file +a+ and a file +b+ are identical. - # - # Bundler::FileUtils.compare_file('somefile', 'somefile') #=> true - # Bundler::FileUtils.compare_file('/dev/null', '/dev/urandom') #=> false - # - def compare_file(a, b) - return false unless File.size(a) == File.size(b) - File.open(a, 'rb') {|fa| - File.open(b, 'rb') {|fb| - return compare_stream(fa, fb) - } - } - end - module_function :compare_file - - alias identical? compare_file - alias cmp compare_file - module_function :identical? - module_function :cmp - - # - # Returns true if the contents of a stream +a+ and +b+ are identical. - # - def compare_stream(a, b) - bsize = fu_stream_blksize(a, b) - sa = String.new(capacity: bsize) - sb = String.new(capacity: bsize) - begin - a.read(bsize, sa) - b.read(bsize, sb) - return true if sa.empty? && sb.empty? - end while sa == sb - false - end - module_function :compare_stream - - # - # If +src+ is not same as +dest+, copies it and changes the permission - # mode to +mode+. If +dest+ is a directory, destination is +dest+/+src+. - # This method removes destination before copy. - # - # Bundler::FileUtils.install 'ruby', '/usr/local/bin/ruby', :mode => 0755, :verbose => true - # Bundler::FileUtils.install 'lib.rb', '/usr/local/lib/ruby/site_ruby', :verbose => true - # - def install(src, dest, mode: nil, owner: nil, group: nil, preserve: nil, - noop: nil, verbose: nil) - if verbose - msg = +"install -c" - msg << ' -p' if preserve - msg << ' -m ' << mode_to_s(mode) if mode - msg << " -o #{owner}" if owner - msg << " -g #{group}" if group - msg << ' ' << [src,dest].flatten.join(' ') - fu_output_message msg - end - return if noop - uid = fu_get_uid(owner) - gid = fu_get_gid(group) - fu_each_src_dest(src, dest) do |s, d| - st = File.stat(s) - unless File.exist?(d) and compare_file(s, d) - remove_file d, true - copy_file s, d - File.utime st.atime, st.mtime, d if preserve - File.chmod fu_mode(mode, st), d if mode - File.chown uid, gid, d if uid or gid - end - end - end - module_function :install - - def user_mask(target) #:nodoc: - target.each_char.inject(0) do |mask, chr| - case chr - when "u" - mask | 04700 - when "g" - mask | 02070 - when "o" - mask | 01007 - when "a" - mask | 07777 - else - raise ArgumentError, "invalid `who' symbol in file mode: #{chr}" - end - end - end - private_module_function :user_mask - - def apply_mask(mode, user_mask, op, mode_mask) #:nodoc: - case op - when '=' - (mode & ~user_mask) | (user_mask & mode_mask) - when '+' - mode | (user_mask & mode_mask) - when '-' - mode & ~(user_mask & mode_mask) - end - end - private_module_function :apply_mask - - def symbolic_modes_to_i(mode_sym, path) #:nodoc: - mode = if File::Stat === path - path.mode - else - File.stat(path).mode - end - mode_sym.split(/,/).inject(mode & 07777) do |current_mode, clause| - target, *actions = clause.split(/([=+-])/) - raise ArgumentError, "invalid file mode: #{mode_sym}" if actions.empty? - target = 'a' if target.empty? - user_mask = user_mask(target) - actions.each_slice(2) do |op, perm| - need_apply = op == '=' - mode_mask = (perm || '').each_char.inject(0) do |mask, chr| - case chr - when "r" - mask | 0444 - when "w" - mask | 0222 - when "x" - mask | 0111 - when "X" - if FileTest.directory? path - mask | 0111 - else - mask - end - when "s" - mask | 06000 - when "t" - mask | 01000 - when "u", "g", "o" - if mask.nonzero? - current_mode = apply_mask(current_mode, user_mask, op, mask) - end - need_apply = false - copy_mask = user_mask(chr) - (current_mode & copy_mask) / (copy_mask & 0111) * (user_mask & 0111) - else - raise ArgumentError, "invalid `perm' symbol in file mode: #{chr}" - end - end - - if mode_mask.nonzero? || need_apply - current_mode = apply_mask(current_mode, user_mask, op, mode_mask) - end - end - current_mode - end - end - private_module_function :symbolic_modes_to_i - - def fu_mode(mode, path) #:nodoc: - mode.is_a?(String) ? symbolic_modes_to_i(mode, path) : mode - end - private_module_function :fu_mode - - def mode_to_s(mode) #:nodoc: - mode.is_a?(String) ? mode : "%o" % mode - end - private_module_function :mode_to_s - - # - # Changes permission bits on the named files (in +list+) to the bit pattern - # represented by +mode+. - # - # +mode+ is the symbolic and absolute mode can be used. - # - # Absolute mode is - # Bundler::FileUtils.chmod 0755, 'somecommand' - # Bundler::FileUtils.chmod 0644, %w(my.rb your.rb his.rb her.rb) - # Bundler::FileUtils.chmod 0755, '/usr/bin/ruby', :verbose => true - # - # Symbolic mode is - # Bundler::FileUtils.chmod "u=wrx,go=rx", 'somecommand' - # Bundler::FileUtils.chmod "u=wr,go=rr", %w(my.rb your.rb his.rb her.rb) - # Bundler::FileUtils.chmod "u=wrx,go=rx", '/usr/bin/ruby', :verbose => true - # - # "a" :: is user, group, other mask. - # "u" :: is user's mask. - # "g" :: is group's mask. - # "o" :: is other's mask. - # "w" :: is write permission. - # "r" :: is read permission. - # "x" :: is execute permission. - # "X" :: - # is execute permission for directories only, must be used in conjunction with "+" - # "s" :: is uid, gid. - # "t" :: is sticky bit. - # "+" :: is added to a class given the specified mode. - # "-" :: Is removed from a given class given mode. - # "=" :: Is the exact nature of the class will be given a specified mode. - - def chmod(mode, list, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message sprintf('chmod %s %s', mode_to_s(mode), list.join(' ')) if verbose - return if noop - list.each do |path| - Entry_.new(path).chmod(fu_mode(mode, path)) - end - end - module_function :chmod - - # - # Changes permission bits on the named files (in +list+) - # to the bit pattern represented by +mode+. - # - # Bundler::FileUtils.chmod_R 0700, "/tmp/app.#{$$}" - # Bundler::FileUtils.chmod_R "u=wrx", "/tmp/app.#{$$}" - # - def chmod_R(mode, list, noop: nil, verbose: nil, force: nil) - list = fu_list(list) - fu_output_message sprintf('chmod -R%s %s %s', - (force ? 'f' : ''), - mode_to_s(mode), list.join(' ')) if verbose - return if noop - list.each do |root| - Entry_.new(root).traverse do |ent| - begin - ent.chmod(fu_mode(mode, ent.path)) - rescue - raise unless force - end - end - end - end - module_function :chmod_R - - # - # Changes owner and group on the named files (in +list+) - # to the user +user+ and the group +group+. +user+ and +group+ - # may be an ID (Integer/String) or a name (String). - # If +user+ or +group+ is nil, this method does not change - # the attribute. - # - # Bundler::FileUtils.chown 'root', 'staff', '/usr/local/bin/ruby' - # Bundler::FileUtils.chown nil, 'bin', Dir.glob('/usr/bin/*'), :verbose => true - # - def chown(user, group, list, noop: nil, verbose: nil) - list = fu_list(list) - fu_output_message sprintf('chown %s %s', - (group ? "#{user}:#{group}" : user || ':'), - list.join(' ')) if verbose - return if noop - uid = fu_get_uid(user) - gid = fu_get_gid(group) - list.each do |path| - Entry_.new(path).chown uid, gid - end - end - module_function :chown - - # - # Changes owner and group on the named files (in +list+) - # to the user +user+ and the group +group+ recursively. - # +user+ and +group+ may be an ID (Integer/String) or - # a name (String). If +user+ or +group+ is nil, this - # method does not change the attribute. - # - # Bundler::FileUtils.chown_R 'www', 'www', '/var/www/htdocs' - # Bundler::FileUtils.chown_R 'cvs', 'cvs', '/var/cvs', :verbose => true - # - def chown_R(user, group, list, noop: nil, verbose: nil, force: nil) - list = fu_list(list) - fu_output_message sprintf('chown -R%s %s %s', - (force ? 'f' : ''), - (group ? "#{user}:#{group}" : user || ':'), - list.join(' ')) if verbose - return if noop - uid = fu_get_uid(user) - gid = fu_get_gid(group) - list.each do |root| - Entry_.new(root).traverse do |ent| - begin - ent.chown uid, gid - rescue - raise unless force - end - end - end - end - module_function :chown_R - - begin - require 'etc' - rescue LoadError # rescue LoadError for miniruby - end - - def fu_get_uid(user) #:nodoc: - return nil unless user - case user - when Integer - user - when /\A\d+\z/ - user.to_i - else - Etc.getpwnam(user) ? Etc.getpwnam(user).uid : nil - end - end - private_module_function :fu_get_uid - - def fu_get_gid(group) #:nodoc: - return nil unless group - case group - when Integer - group - when /\A\d+\z/ - group.to_i - else - Etc.getgrnam(group) ? Etc.getgrnam(group).gid : nil - end - end - private_module_function :fu_get_gid - - # - # Updates modification time (mtime) and access time (atime) of file(s) in - # +list+. Files are created if they don't exist. - # - # Bundler::FileUtils.touch 'timestamp' - # Bundler::FileUtils.touch Dir.glob('*.c'); system 'make' - # - def touch(list, noop: nil, verbose: nil, mtime: nil, nocreate: nil) - list = fu_list(list) - t = mtime - if verbose - fu_output_message "touch #{nocreate ? '-c ' : ''}#{t ? t.strftime('-t %Y%m%d%H%M.%S ') : ''}#{list.join ' '}" - end - return if noop - list.each do |path| - created = nocreate - begin - File.utime(t, t, path) - rescue Errno::ENOENT - raise if created - File.open(path, 'a') { - ; - } - created = true - retry if t - end - end - end - module_function :touch - - private - - module StreamUtils_ - private - - def fu_windows? - /mswin|mingw|bccwin|emx/ =~ RUBY_PLATFORM - end - - def fu_copy_stream0(src, dest, blksize = nil) #:nodoc: - IO.copy_stream(src, dest) - end - - def fu_stream_blksize(*streams) - streams.each do |s| - next unless s.respond_to?(:stat) - size = fu_blksize(s.stat) - return size if size - end - fu_default_blksize() - end - - def fu_blksize(st) - s = st.blksize - return nil unless s - return nil if s == 0 - s - end - - def fu_default_blksize - 1024 - end - end - - include StreamUtils_ - extend StreamUtils_ - - class Entry_ #:nodoc: internal use only - include StreamUtils_ - - def initialize(a, b = nil, deref = false) - @prefix = @rel = @path = nil - if b - @prefix = a - @rel = b - else - @path = a - end - @deref = deref - @stat = nil - @lstat = nil - end - - def inspect - "\#<#{self.class} #{path()}>" - end - - def path - if @path - File.path(@path) - else - join(@prefix, @rel) - end - end - - def prefix - @prefix || @path - end - - def rel - @rel - end - - def dereference? - @deref - end - - def exist? - begin - lstat - true - rescue Errno::ENOENT - false - end - end - - def file? - s = lstat! - s and s.file? - end - - def directory? - s = lstat! - s and s.directory? - end - - def symlink? - s = lstat! - s and s.symlink? - end - - def chardev? - s = lstat! - s and s.chardev? - end - - def blockdev? - s = lstat! - s and s.blockdev? - end - - def socket? - s = lstat! - s and s.socket? - end - - def pipe? - s = lstat! - s and s.pipe? - end - - S_IF_DOOR = 0xD000 - - def door? - s = lstat! - s and (s.mode & 0xF000 == S_IF_DOOR) - end - - def entries - opts = {} - opts[:encoding] = ::Encoding::UTF_8 if fu_windows? - Dir.entries(path(), opts)\ - .reject {|n| n == '.' or n == '..' }\ - .map {|n| Entry_.new(prefix(), join(rel(), n.untaint)) } - end - - def stat - return @stat if @stat - if lstat() and lstat().symlink? - @stat = File.stat(path()) - else - @stat = lstat() - end - @stat - end - - def stat! - return @stat if @stat - if lstat! and lstat!.symlink? - @stat = File.stat(path()) - else - @stat = lstat! - end - @stat - rescue SystemCallError - nil - end - - def lstat - if dereference? - @lstat ||= File.stat(path()) - else - @lstat ||= File.lstat(path()) - end - end - - def lstat! - lstat() - rescue SystemCallError - nil - end - - def chmod(mode) - if symlink? - File.lchmod mode, path() if have_lchmod? - else - File.chmod mode, path() - end - end - - def chown(uid, gid) - if symlink? - File.lchown uid, gid, path() if have_lchown? - else - File.chown uid, gid, path() - end - end - - def copy(dest) - lstat - case - when file? - copy_file dest - when directory? - if !File.exist?(dest) and descendant_directory?(dest, path) - raise ArgumentError, "cannot copy directory %s to itself %s" % [path, dest] - end - begin - Dir.mkdir dest - rescue - raise unless File.directory?(dest) - end - when symlink? - File.symlink File.readlink(path()), dest - when chardev? - raise "cannot handle device file" unless File.respond_to?(:mknod) - mknod dest, ?c, 0666, lstat().rdev - when blockdev? - raise "cannot handle device file" unless File.respond_to?(:mknod) - mknod dest, ?b, 0666, lstat().rdev - when socket? - raise "cannot handle socket" unless File.respond_to?(:mknod) - mknod dest, nil, lstat().mode, 0 - when pipe? - raise "cannot handle FIFO" unless File.respond_to?(:mkfifo) - mkfifo dest, 0666 - when door? - raise "cannot handle door: #{path()}" - else - raise "unknown file type: #{path()}" - end - end - - def copy_file(dest) - File.open(path()) do |s| - File.open(dest, 'wb', s.stat.mode) do |f| - IO.copy_stream(s, f) - end - end - end - - def copy_metadata(path) - st = lstat() - if !st.symlink? - File.utime st.atime, st.mtime, path - end - mode = st.mode - begin - if st.symlink? - begin - File.lchown st.uid, st.gid, path - rescue NotImplementedError - end - else - File.chown st.uid, st.gid, path - end - rescue Errno::EPERM, Errno::EACCES - # clear setuid/setgid - mode &= 01777 - end - if st.symlink? - begin - File.lchmod mode, path - rescue NotImplementedError - end - else - File.chmod mode, path - end - end - - def remove - if directory? - remove_dir1 - else - remove_file - end - end - - def remove_dir1 - platform_support { - Dir.rmdir path().chomp(?/) - } - end - - def remove_file - platform_support { - File.unlink path - } - end - - def platform_support - return yield unless fu_windows? - first_time_p = true - begin - yield - rescue Errno::ENOENT - raise - rescue => err - if first_time_p - first_time_p = false - begin - File.chmod 0700, path() # Windows does not have symlink - retry - rescue SystemCallError - end - end - raise err - end - end - - def preorder_traverse - stack = [self] - while ent = stack.pop - yield ent - stack.concat ent.entries.reverse if ent.directory? - end - end - - alias traverse preorder_traverse - - def postorder_traverse - if directory? - entries().each do |ent| - ent.postorder_traverse do |e| - yield e - end - end - end - ensure - yield self - end - - def wrap_traverse(pre, post) - pre.call self - if directory? - entries.each do |ent| - ent.wrap_traverse pre, post - end - end - post.call self - end - - private - - $fileutils_rb_have_lchmod = nil - - def have_lchmod? - # This is not MT-safe, but it does not matter. - if $fileutils_rb_have_lchmod == nil - $fileutils_rb_have_lchmod = check_have_lchmod? - end - $fileutils_rb_have_lchmod - end - - def check_have_lchmod? - return false unless File.respond_to?(:lchmod) - File.lchmod 0 - return true - rescue NotImplementedError - return false - end - - $fileutils_rb_have_lchown = nil - - def have_lchown? - # This is not MT-safe, but it does not matter. - if $fileutils_rb_have_lchown == nil - $fileutils_rb_have_lchown = check_have_lchown? - end - $fileutils_rb_have_lchown - end - - def check_have_lchown? - return false unless File.respond_to?(:lchown) - File.lchown nil, nil - return true - rescue NotImplementedError - return false - end - - def join(dir, base) - return File.path(dir) if not base or base == '.' - return File.path(base) if not dir or dir == '.' - File.join(dir, base) - end - - if File::ALT_SEPARATOR - DIRECTORY_TERM = "(?=[/#{Regexp.quote(File::ALT_SEPARATOR)}]|\\z)" - else - DIRECTORY_TERM = "(?=/|\\z)" - end - SYSCASE = File::FNM_SYSCASE.nonzero? ? "-i" : "" - - def descendant_directory?(descendant, ascendant) - /\A(?#{SYSCASE}:#{Regexp.quote(ascendant)})#{DIRECTORY_TERM}/ =~ File.dirname(descendant) - end - end # class Entry_ - - def fu_list(arg) #:nodoc: - [arg].flatten.map {|path| File.path(path) } - end - private_module_function :fu_list - - def fu_each_src_dest(src, dest) #:nodoc: - fu_each_src_dest0(src, dest) do |s, d| - raise ArgumentError, "same file: #{s} and #{d}" if fu_same?(s, d) - yield s, d - end - end - private_module_function :fu_each_src_dest - - def fu_each_src_dest0(src, dest) #:nodoc: - if tmp = Array.try_convert(src) - tmp.each do |s| - s = File.path(s) - yield s, File.join(dest, File.basename(s)) - end - else - src = File.path(src) - if File.directory?(dest) - yield src, File.join(dest, File.basename(src)) - else - yield src, File.path(dest) - end - end - end - private_module_function :fu_each_src_dest0 - - def fu_same?(a, b) #:nodoc: - File.identical?(a, b) - end - private_module_function :fu_same? - - @fileutils_output = $stderr - @fileutils_label = '' - - def fu_output_message(msg) #:nodoc: - @fileutils_output ||= $stderr - @fileutils_label ||= '' - @fileutils_output.puts @fileutils_label + msg - end - private_module_function :fu_output_message - - # This hash table holds command options. - OPT_TABLE = {} #:nodoc: internal use only - (private_instance_methods & methods(false)).inject(OPT_TABLE) {|tbl, name| - (tbl[name.to_s] = instance_method(name).parameters).map! {|t, n| n if t == :key}.compact! - tbl - } - - # - # Returns an Array of method names which have any options. - # - # p Bundler::FileUtils.commands #=> ["chmod", "cp", "cp_r", "install", ...] - # - def self.commands - OPT_TABLE.keys - end - - # - # Returns an Array of option names. - # - # p Bundler::FileUtils.options #=> ["noop", "force", "verbose", "preserve", "mode"] - # - def self.options - OPT_TABLE.values.flatten.uniq.map {|sym| sym.to_s } - end - - # - # Returns true if the method +mid+ have an option +opt+. - # - # p Bundler::FileUtils.have_option?(:cp, :noop) #=> true - # p Bundler::FileUtils.have_option?(:rm, :force) #=> true - # p Bundler::FileUtils.have_option?(:rm, :preserve) #=> false - # - def self.have_option?(mid, opt) - li = OPT_TABLE[mid.to_s] or raise ArgumentError, "no such method: #{mid}" - li.include?(opt) - end - - # - # Returns an Array of option names of the method +mid+. - # - # p Bundler::FileUtils.options_of(:rm) #=> ["noop", "verbose", "force"] - # - def self.options_of(mid) - OPT_TABLE[mid.to_s].map {|sym| sym.to_s } - end - - # - # Returns an Array of method names which have the option +opt+. - # - # p Bundler::FileUtils.collect_method(:preserve) #=> ["cp", "cp_r", "copy", "install"] - # - def self.collect_method(opt) - OPT_TABLE.keys.select {|m| OPT_TABLE[m].include?(opt) } - end - - LOW_METHODS = singleton_methods(false) - collect_method(:noop).map(&:intern) - module LowMethods - private - def _do_nothing(*)end - ::Bundler::FileUtils::LOW_METHODS.map {|name| alias_method name, :_do_nothing} - end - - METHODS = singleton_methods() - [:private_module_function, - :commands, :options, :have_option?, :options_of, :collect_method] - - # - # This module has all methods of Bundler::FileUtils module, but it outputs messages - # before acting. This equates to passing the <tt>:verbose</tt> flag to - # methods in Bundler::FileUtils. - # - module Verbose - include Bundler::FileUtils - @fileutils_output = $stderr - @fileutils_label = '' - names = ::Bundler::FileUtils.collect_method(:verbose) - names.each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args, **options) - super(*args, **options, verbose: true) - end - EOS - end - private(*names) - extend self - class << self - public(*::Bundler::FileUtils::METHODS) - end - end - - # - # This module has all methods of Bundler::FileUtils module, but never changes - # files/directories. This equates to passing the <tt>:noop</tt> flag - # to methods in Bundler::FileUtils. - # - module NoWrite - include Bundler::FileUtils - include LowMethods - @fileutils_output = $stderr - @fileutils_label = '' - names = ::Bundler::FileUtils.collect_method(:noop) - names.each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args, **options) - super(*args, **options, noop: true) - end - EOS - end - private(*names) - extend self - class << self - public(*::Bundler::FileUtils::METHODS) - end - end - - # - # This module has all methods of Bundler::FileUtils module, but never changes - # files/directories, with printing message before acting. - # This equates to passing the <tt>:noop</tt> and <tt>:verbose</tt> flag - # to methods in Bundler::FileUtils. - # - module DryRun - include Bundler::FileUtils - include LowMethods - @fileutils_output = $stderr - @fileutils_label = '' - names = ::Bundler::FileUtils.collect_method(:noop) - names.each do |name| - module_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{name}(*args, **options) - super(*args, **options, noop: true, verbose: true) - end - EOS - end - private(*names) - extend self - class << self - public(*::Bundler::FileUtils::METHODS) - end - end - -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo.rb b/lib/bundler/vendor/molinillo/lib/molinillo.rb deleted file mode 100644 index 9e2867144f..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/compatibility' -require 'bundler/vendor/molinillo/lib/molinillo/gem_metadata' -require 'bundler/vendor/molinillo/lib/molinillo/errors' -require 'bundler/vendor/molinillo/lib/molinillo/resolver' -require 'bundler/vendor/molinillo/lib/molinillo/modules/ui' -require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' - -# Bundler::Molinillo is a generic dependency resolution algorithm. -module Bundler::Molinillo -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb b/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb deleted file mode 100644 index 3eba8e4083..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/compatibility.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # Hacks needed for old Ruby versions. - module Compatibility - module_function - - if [].respond_to?(:flat_map) - # Flat map - # @param [Enumerable] enum an enumerable object - # @block the block to flat-map with - # @return The enum, flat-mapped - def flat_map(enum, &blk) - enum.flat_map(&blk) - end - else - # Flat map - # @param [Enumerable] enum an enumerable object - # @block the block to flat-map with - # @return The enum, flat-mapped - def flat_map(enum, &blk) - enum.map(&blk).flatten(1) - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb deleted file mode 100644 index bcacf35243..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # @!visibility private - module Delegates - # Delegates all {Bundler::Molinillo::ResolutionState} methods to a `#state` property. - module ResolutionState - # (see Bundler::Molinillo::ResolutionState#name) - def name - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.name - end - - # (see Bundler::Molinillo::ResolutionState#requirements) - def requirements - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.requirements - end - - # (see Bundler::Molinillo::ResolutionState#activated) - def activated - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.activated - end - - # (see Bundler::Molinillo::ResolutionState#requirement) - def requirement - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.requirement - end - - # (see Bundler::Molinillo::ResolutionState#possibilities) - def possibilities - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.possibilities - end - - # (see Bundler::Molinillo::ResolutionState#depth) - def depth - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.depth - end - - # (see Bundler::Molinillo::ResolutionState#conflicts) - def conflicts - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.conflicts - end - - # (see Bundler::Molinillo::ResolutionState#unused_unwind_options) - def unused_unwind_options - current_state = state || Bundler::Molinillo::ResolutionState.empty - current_state.unused_unwind_options - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb deleted file mode 100644 index ec9c770a28..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider.rb +++ /dev/null @@ -1,81 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - module Delegates - # Delegates all {Bundler::Molinillo::SpecificationProvider} methods to a - # `#specification_provider` property. - module SpecificationProvider - # (see Bundler::Molinillo::SpecificationProvider#search_for) - def search_for(dependency) - with_no_such_dependency_error_handling do - specification_provider.search_for(dependency) - end - end - - # (see Bundler::Molinillo::SpecificationProvider#dependencies_for) - def dependencies_for(specification) - with_no_such_dependency_error_handling do - specification_provider.dependencies_for(specification) - end - end - - # (see Bundler::Molinillo::SpecificationProvider#requirement_satisfied_by?) - def requirement_satisfied_by?(requirement, activated, spec) - with_no_such_dependency_error_handling do - specification_provider.requirement_satisfied_by?(requirement, activated, spec) - end - end - - # (see Bundler::Molinillo::SpecificationProvider#name_for) - def name_for(dependency) - with_no_such_dependency_error_handling do - specification_provider.name_for(dependency) - end - end - - # (see Bundler::Molinillo::SpecificationProvider#name_for_explicit_dependency_source) - def name_for_explicit_dependency_source - with_no_such_dependency_error_handling do - specification_provider.name_for_explicit_dependency_source - end - end - - # (see Bundler::Molinillo::SpecificationProvider#name_for_locking_dependency_source) - def name_for_locking_dependency_source - with_no_such_dependency_error_handling do - specification_provider.name_for_locking_dependency_source - end - end - - # (see Bundler::Molinillo::SpecificationProvider#sort_dependencies) - def sort_dependencies(dependencies, activated, conflicts) - with_no_such_dependency_error_handling do - specification_provider.sort_dependencies(dependencies, activated, conflicts) - end - end - - # (see Bundler::Molinillo::SpecificationProvider#allow_missing?) - def allow_missing?(dependency) - with_no_such_dependency_error_handling do - specification_provider.allow_missing?(dependency) - end - end - - private - - # Ensures any raised {NoSuchDependencyError} has its - # {NoSuchDependencyError#required_by} set. - # @yield - def with_no_such_dependency_error_handling - yield - rescue NoSuchDependencyError => error - if state - vertex = activated.vertex_named(name_for(error.dependency)) - error.required_by += vertex.incoming_edges.map { |e| e.origin.name } - error.required_by << name_for_explicit_dependency_source unless vertex.explicit_requirements.empty? - end - raise - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb deleted file mode 100644 index 677a8bd916..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +++ /dev/null @@ -1,223 +0,0 @@ -# frozen_string_literal: true - -require 'set' -require 'tsort' - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/log' -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex' - -module Bundler::Molinillo - # A directed acyclic graph that is tuned to hold named dependencies - class DependencyGraph - include Enumerable - - # Enumerates through the vertices of the graph. - # @return [Array<Vertex>] The graph's vertices. - def each - return vertices.values.each unless block_given? - vertices.values.each { |v| yield v } - end - - include TSort - - # @!visibility private - alias tsort_each_node each - - # @!visibility private - def tsort_each_child(vertex, &block) - vertex.successors.each(&block) - end - - # Topologically sorts the given vertices. - # @param [Enumerable<Vertex>] vertices the vertices to be sorted, which must - # all belong to the same graph. - # @return [Array<Vertex>] The sorted vertices. - def self.tsort(vertices) - TSort.tsort( - lambda { |b| vertices.each(&b) }, - lambda { |v, &b| (v.successors & vertices).each(&b) } - ) - end - - # A directed edge of a {DependencyGraph} - # @attr [Vertex] origin The origin of the directed edge - # @attr [Vertex] destination The destination of the directed edge - # @attr [Object] requirement The requirement the directed edge represents - Edge = Struct.new(:origin, :destination, :requirement) - - # @return [{String => Vertex}] the vertices of the dependency graph, keyed - # by {Vertex#name} - attr_reader :vertices - - # @return [Log] the op log for this graph - attr_reader :log - - # Initializes an empty dependency graph - def initialize - @vertices = {} - @log = Log.new - end - - # Tags the current state of the dependency as the given tag - # @param [Object] tag an opaque tag for the current state of the graph - # @return [Void] - def tag(tag) - log.tag(self, tag) - end - - # Rewinds the graph to the state tagged as `tag` - # @param [Object] tag the tag to rewind to - # @return [Void] - def rewind_to(tag) - log.rewind_to(self, tag) - end - - # Initializes a copy of a {DependencyGraph}, ensuring that all {#vertices} - # are properly copied. - # @param [DependencyGraph] other the graph to copy. - def initialize_copy(other) - super - @vertices = {} - @log = other.log.dup - traverse = lambda do |new_v, old_v| - return if new_v.outgoing_edges.size == old_v.outgoing_edges.size - old_v.outgoing_edges.each do |edge| - destination = add_vertex(edge.destination.name, edge.destination.payload) - add_edge_no_circular(new_v, destination, edge.requirement) - traverse.call(destination, edge.destination) - end - end - other.vertices.each do |name, vertex| - new_vertex = add_vertex(name, vertex.payload, vertex.root?) - new_vertex.explicit_requirements.replace(vertex.explicit_requirements) - traverse.call(new_vertex, vertex) - end - end - - # @return [String] a string suitable for debugging - def inspect - "#{self.class}:#{vertices.values.inspect}" - end - - # @param [Hash] options options for dot output. - # @return [String] Returns a dot format representation of the graph - def to_dot(options = {}) - edge_label = options.delete(:edge_label) - raise ArgumentError, "Unknown options: #{options.keys}" unless options.empty? - - dot_vertices = [] - dot_edges = [] - vertices.each do |n, v| - dot_vertices << " #{n} [label=\"{#{n}|#{v.payload}}\"]" - v.outgoing_edges.each do |e| - label = edge_label ? edge_label.call(e) : e.requirement - dot_edges << " #{e.origin.name} -> #{e.destination.name} [label=#{label.to_s.dump}]" - end - end - - dot_vertices.uniq! - dot_vertices.sort! - dot_edges.uniq! - dot_edges.sort! - - dot = dot_vertices.unshift('digraph G {').push('') + dot_edges.push('}') - dot.join("\n") - end - - # @return [Boolean] whether the two dependency graphs are equal, determined - # by a recursive traversal of each {#root_vertices} and its - # {Vertex#successors} - def ==(other) - return false unless other - return true if equal?(other) - vertices.each do |name, vertex| - other_vertex = other.vertex_named(name) - return false unless other_vertex - return false unless vertex.payload == other_vertex.payload - return false unless other_vertex.successors.to_set == vertex.successors.to_set - end - end - - # @param [String] name - # @param [Object] payload - # @param [Array<String>] parent_names - # @param [Object] requirement the requirement that is requiring the child - # @return [void] - def add_child_vertex(name, payload, parent_names, requirement) - root = !parent_names.delete(nil) { true } - vertex = add_vertex(name, payload, root) - vertex.explicit_requirements << requirement if root - parent_names.each do |parent_name| - parent_vertex = vertex_named(parent_name) - add_edge(parent_vertex, vertex, requirement) - end - vertex - end - - # Adds a vertex with the given name, or updates the existing one. - # @param [String] name - # @param [Object] payload - # @return [Vertex] the vertex that was added to `self` - def add_vertex(name, payload, root = false) - log.add_vertex(self, name, payload, root) - end - - # Detaches the {#vertex_named} `name` {Vertex} from the graph, recursively - # removing any non-root vertices that were orphaned in the process - # @param [String] name - # @return [Array<Vertex>] the vertices which have been detached - def detach_vertex_named(name) - log.detach_vertex_named(self, name) - end - - # @param [String] name - # @return [Vertex,nil] the vertex with the given name - def vertex_named(name) - vertices[name] - end - - # @param [String] name - # @return [Vertex,nil] the root vertex with the given name - def root_vertex_named(name) - vertex = vertex_named(name) - vertex if vertex && vertex.root? - end - - # Adds a new {Edge} to the dependency graph - # @param [Vertex] origin - # @param [Vertex] destination - # @param [Object] requirement the requirement that this edge represents - # @return [Edge] the added edge - def add_edge(origin, destination, requirement) - if destination.path_to?(origin) - raise CircularDependencyError.new([origin, destination]) - end - add_edge_no_circular(origin, destination, requirement) - end - - # Deletes an {Edge} from the dependency graph - # @param [Edge] edge - # @return [Void] - def delete_edge(edge) - log.delete_edge(self, edge.origin.name, edge.destination.name, edge.requirement) - end - - # Sets the payload of the vertex with the given name - # @param [String] name the name of the vertex - # @param [Object] payload the payload - # @return [Void] - def set_payload(name, payload) - log.set_payload(self, name, payload) - end - - private - - # Adds a new {Edge} to the dependency graph without checking for - # circularity. - # @param (see #add_edge) - # @return (see #add_edge) - def add_edge_no_circular(origin, destination, requirement) - log.add_edge_no_circular(self, origin.name, destination.name, requirement) - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb deleted file mode 100644 index c04c7eec9c..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/action.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - class DependencyGraph - # An action that modifies a {DependencyGraph} that is reversible. - # @abstract - class Action - # rubocop:disable Lint/UnusedMethodArgument - - # @return [Symbol] The name of the action. - def self.action_name - raise 'Abstract' - end - - # Performs the action on the given graph. - # @param [DependencyGraph] graph the graph to perform the action on. - # @return [Void] - def up(graph) - raise 'Abstract' - end - - # Reverses the action on the given graph. - # @param [DependencyGraph] graph the graph to reverse the action on. - # @return [Void] - def down(graph) - raise 'Abstract' - end - - # @return [Action,Nil] The previous action - attr_accessor :previous - - # @return [Action,Nil] The next action - attr_accessor :next - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb deleted file mode 100644 index 9849aea2fe..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb +++ /dev/null @@ -1,66 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # (see DependencyGraph#add_edge_no_circular) - class AddEdgeNoCircular < Action - # @!group Action - - # (see Action.action_name) - def self.action_name - :add_vertex - end - - # (see Action#up) - def up(graph) - edge = make_edge(graph) - edge.origin.outgoing_edges << edge - edge.destination.incoming_edges << edge - edge - end - - # (see Action#down) - def down(graph) - edge = make_edge(graph) - delete_first(edge.origin.outgoing_edges, edge) - delete_first(edge.destination.incoming_edges, edge) - end - - # @!group AddEdgeNoCircular - - # @return [String] the name of the origin of the edge - attr_reader :origin - - # @return [String] the name of the destination of the edge - attr_reader :destination - - # @return [Object] the requirement that the edge represents - attr_reader :requirement - - # @param [DependencyGraph] graph the graph to find vertices from - # @return [Edge] The edge this action adds - def make_edge(graph) - Edge.new(graph.vertex_named(origin), graph.vertex_named(destination), requirement) - end - - # Initialize an action to add an edge to a dependency graph - # @param [String] origin the name of the origin of the edge - # @param [String] destination the name of the destination of the edge - # @param [Object] requirement the requirement that the edge represents - def initialize(origin, destination, requirement) - @origin = origin - @destination = destination - @requirement = requirement - end - - private - - def delete_first(array, item) - return unless index = array.index(item) - array.delete_at(index) - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb deleted file mode 100644 index 0a1e08255b..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/add_vertex.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # (see DependencyGraph#add_vertex) - class AddVertex < Action # :nodoc: - # @!group Action - - # (see Action.action_name) - def self.action_name - :add_vertex - end - - # (see Action#up) - def up(graph) - if existing = graph.vertices[name] - @existing_payload = existing.payload - @existing_root = existing.root - end - vertex = existing || Vertex.new(name, payload) - graph.vertices[vertex.name] = vertex - vertex.payload ||= payload - vertex.root ||= root - vertex - end - - # (see Action#down) - def down(graph) - if defined?(@existing_payload) - vertex = graph.vertices[name] - vertex.payload = @existing_payload - vertex.root = @existing_root - else - graph.vertices.delete(name) - end - end - - # @!group AddVertex - - # @return [String] the name of the vertex - attr_reader :name - - # @return [Object] the payload for the vertex - attr_reader :payload - - # @return [Boolean] whether the vertex is root or not - attr_reader :root - - # Initialize an action to add a vertex to a dependency graph - # @param [String] name the name of the vertex - # @param [Object] payload the payload for the vertex - # @param [Boolean] root whether the vertex is root or not - def initialize(name, payload, root) - @name = name - @payload = payload - @root = root - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb deleted file mode 100644 index 1d9f4b327d..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/delete_edge.rb +++ /dev/null @@ -1,63 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # (see DependencyGraph#delete_edge) - class DeleteEdge < Action - # @!group Action - - # (see Action.action_name) - def self.action_name - :delete_edge - end - - # (see Action#up) - def up(graph) - edge = make_edge(graph) - edge.origin.outgoing_edges.delete(edge) - edge.destination.incoming_edges.delete(edge) - end - - # (see Action#down) - def down(graph) - edge = make_edge(graph) - edge.origin.outgoing_edges << edge - edge.destination.incoming_edges << edge - edge - end - - # @!group DeleteEdge - - # @return [String] the name of the origin of the edge - attr_reader :origin_name - - # @return [String] the name of the destination of the edge - attr_reader :destination_name - - # @return [Object] the requirement that the edge represents - attr_reader :requirement - - # @param [DependencyGraph] graph the graph to find vertices from - # @return [Edge] The edge this action adds - def make_edge(graph) - Edge.new( - graph.vertex_named(origin_name), - graph.vertex_named(destination_name), - requirement - ) - end - - # Initialize an action to add an edge to a dependency graph - # @param [String] origin_name the name of the origin of the edge - # @param [String] destination_name the name of the destination of the edge - # @param [Object] requirement the requirement that the edge represents - def initialize(origin_name, destination_name, requirement) - @origin_name = origin_name - @destination_name = destination_name - @requirement = requirement - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb deleted file mode 100644 index 385dcbdd06..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # @see DependencyGraph#detach_vertex_named - class DetachVertexNamed < Action - # @!group Action - - # (see Action#name) - def self.action_name - :add_vertex - end - - # (see Action#up) - def up(graph) - return [] unless @vertex = graph.vertices.delete(name) - - removed_vertices = [@vertex] - @vertex.outgoing_edges.each do |e| - v = e.destination - v.incoming_edges.delete(e) - if !v.root? && v.incoming_edges.empty? - removed_vertices.concat graph.detach_vertex_named(v.name) - end - end - - @vertex.incoming_edges.each do |e| - v = e.origin - v.outgoing_edges.delete(e) - end - - removed_vertices - end - - # (see Action#down) - def down(graph) - return unless @vertex - graph.vertices[@vertex.name] = @vertex - @vertex.outgoing_edges.each do |e| - e.destination.incoming_edges << e - end - @vertex.incoming_edges.each do |e| - e.origin.outgoing_edges << e - end - end - - # @!group DetachVertexNamed - - # @return [String] the name of the vertex to detach - attr_reader :name - - # Initialize an action to detach a vertex from a dependency graph - # @param [String] name the name of the vertex to detach - def initialize(name) - @name = name - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb deleted file mode 100644 index 8582dd19c1..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/log.rb +++ /dev/null @@ -1,126 +0,0 @@ -# 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' -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/detach_vertex_named' -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload' -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag' - -module Bundler::Molinillo - class DependencyGraph - # A log for dependency graph actions - class Log - # Initializes an empty log - def initialize - @current_action = @first_action = nil - end - - # @!macro [new] action - # {include:DependencyGraph#$0} - # @param [Graph] graph the graph to perform the action on - # @param (see DependencyGraph#$0) - # @return (see DependencyGraph#$0) - - # @macro action - def tag(graph, tag) - push_action(graph, Tag.new(tag)) - end - - # @macro action - def add_vertex(graph, name, payload, root) - push_action(graph, AddVertex.new(name, payload, root)) - end - - # @macro action - def detach_vertex_named(graph, name) - push_action(graph, DetachVertexNamed.new(name)) - end - - # @macro action - def add_edge_no_circular(graph, origin, destination, requirement) - push_action(graph, AddEdgeNoCircular.new(origin, destination, requirement)) - end - - # {include:DependencyGraph#delete_edge} - # @param [Graph] graph the graph to perform the action on - # @param [String] origin_name - # @param [String] destination_name - # @param [Object] requirement - # @return (see DependencyGraph#delete_edge) - def delete_edge(graph, origin_name, destination_name, requirement) - push_action(graph, DeleteEdge.new(origin_name, destination_name, requirement)) - end - - # @macro action - def set_payload(graph, name, payload) - push_action(graph, SetPayload.new(name, payload)) - end - - # Pops the most recent action from the log and undoes the action - # @param [DependencyGraph] graph - # @return [Action] the action that was popped off the log - def pop!(graph) - return unless action = @current_action - unless @current_action = action.previous - @first_action = nil - end - action.down(graph) - action - end - - extend Enumerable - - # @!visibility private - # Enumerates each action in the log - # @yield [Action] - def each - return enum_for unless block_given? - action = @first_action - loop do - break unless action - yield action - action = action.next - end - self - end - - # @!visibility private - # Enumerates each action in the log in reverse order - # @yield [Action] - def reverse_each - return enum_for(:reverse_each) unless block_given? - action = @current_action - loop do - break unless action - yield action - action = action.previous - end - self - end - - # @macro action - def rewind_to(graph, tag) - loop do - action = pop!(graph) - raise "No tag #{tag.inspect} found" unless action - break if action.class.action_name == :tag && action.tag == tag - end - end - - private - - # Adds the given action to the log, running the action - # @param [DependencyGraph] graph - # @param [Action] action - # @return The value returned by `action.up` - def push_action(graph, action) - action.previous = @current_action - @current_action.next = action if @current_action - @current_action = action - @first_action ||= action - action.up(graph) - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb deleted file mode 100644 index 37286d104a..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/set_payload.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # @see DependencyGraph#set_payload - class SetPayload < Action # :nodoc: - # @!group Action - - # (see Action.action_name) - def self.action_name - :set_payload - end - - # (see Action#up) - def up(graph) - vertex = graph.vertex_named(name) - @old_payload = vertex.payload - vertex.payload = payload - end - - # (see Action#down) - def down(graph) - graph.vertex_named(name).payload = @old_payload - end - - # @!group SetPayload - - # @return [String] the name of the vertex - attr_reader :name - - # @return [Object] the payload for the vertex - attr_reader :payload - - # Initialize an action to add set the payload for a vertex in a dependency - # graph - # @param [String] name the name of the vertex - # @param [Object] payload the payload for the vertex - def initialize(name, payload) - @name = name - @payload = payload - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb deleted file mode 100644 index d6ad16e07a..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/tag.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph/action' -module Bundler::Molinillo - class DependencyGraph - # @!visibility private - # @see DependencyGraph#tag - class Tag < Action - # @!group Action - - # (see Action.action_name) - def self.action_name - :tag - end - - # (see Action#up) - def up(_graph) - end - - # (see Action#down) - def down(_graph) - end - - # @!group Tag - - # @return [Object] An opaque tag - attr_reader :tag - - # Initialize an action to tag a state of a dependency graph - # @param [Object] tag an opaque tag - def initialize(tag) - @tag = tag - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb deleted file mode 100644 index e4d016de24..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph/vertex.rb +++ /dev/null @@ -1,126 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - class DependencyGraph - # A vertex in a {DependencyGraph} that encapsulates a {#name} and a - # {#payload} - class Vertex - # @return [String] the name of the vertex - attr_accessor :name - - # @return [Object] the payload the vertex holds - attr_accessor :payload - - # @return [Array<Object>] the explicit requirements that required - # this vertex - attr_reader :explicit_requirements - - # @return [Boolean] whether the vertex is considered a root vertex - attr_accessor :root - alias root? root - - # Initializes a vertex with the given name and payload. - # @param [String] name see {#name} - # @param [Object] payload see {#payload} - def initialize(name, payload) - @name = name.frozen? ? name : name.dup.freeze - @payload = payload - @explicit_requirements = [] - @outgoing_edges = [] - @incoming_edges = [] - end - - # @return [Array<Object>] all of the requirements that required - # this vertex - def requirements - (incoming_edges.map(&:requirement) + explicit_requirements).uniq - end - - # @return [Array<Edge>] the edges of {#graph} that have `self` as their - # {Edge#origin} - attr_accessor :outgoing_edges - - # @return [Array<Edge>] the edges of {#graph} that have `self` as their - # {Edge#destination} - attr_accessor :incoming_edges - - # @return [Array<Vertex>] the vertices of {#graph} that have an edge with - # `self` as their {Edge#destination} - def predecessors - incoming_edges.map(&:origin) - end - - # @return [Array<Vertex>] the vertices of {#graph} where `self` is a - # {#descendent?} - def recursive_predecessors - vertices = predecessors - vertices += Compatibility.flat_map(vertices, &:recursive_predecessors) - vertices.uniq! - vertices - end - - # @return [Array<Vertex>] the vertices of {#graph} that have an edge with - # `self` as their {Edge#origin} - def successors - outgoing_edges.map(&:destination) - end - - # @return [Array<Vertex>] the vertices of {#graph} where `self` is an - # {#ancestor?} - def recursive_successors - vertices = successors - vertices += Compatibility.flat_map(vertices, &:recursive_successors) - vertices.uniq! - vertices - end - - # @return [String] a string suitable for debugging - def inspect - "#{self.class}:#{name}(#{payload.inspect})" - end - - # @return [Boolean] whether the two vertices are equal, determined - # by a recursive traversal of each {Vertex#successors} - def ==(other) - return true if equal?(other) - shallow_eql?(other) && - successors.to_set == other.successors.to_set - end - - # @param [Vertex] other the other vertex to compare to - # @return [Boolean] whether the two vertices are equal, determined - # solely by {#name} and {#payload} equality - def shallow_eql?(other) - return true if equal?(other) - other && - name == other.name && - payload == other.payload - end - - alias eql? == - - # @return [Fixnum] a hash for the vertex based upon its {#name} - def hash - name.hash - end - - # Is there a path from `self` to `other` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def path_to?(other) - equal?(other) || successors.any? { |v| v.path_to?(other) } - end - - alias descendent? path_to? - - # Is there a path from `other` to `self` following edges in the - # dependency graph? - # @return true iff there is a path following edges within this {#graph} - def ancestor?(other) - other.path_to?(self) - end - - alias is_reachable_from? ancestor? - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb b/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb deleted file mode 100644 index fb343250b1..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/errors.rb +++ /dev/null @@ -1,138 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # An error that occurred during the resolution process - class ResolverError < StandardError; end - - # An error caused by searching for a dependency that is completely unknown, - # i.e. has no versions available whatsoever. - class NoSuchDependencyError < ResolverError - # @return [Object] the dependency that could not be found - attr_accessor :dependency - - # @return [Array<Object>] the specifications that depended upon {#dependency} - attr_accessor :required_by - - # Initializes a new error with the given missing dependency. - # @param [Object] dependency @see {#dependency} - # @param [Array<Object>] required_by @see {#required_by} - def initialize(dependency, required_by = []) - @dependency = dependency - @required_by = required_by - super() - end - - # The error message for the missing dependency, including the specifications - # that had this dependency. - def message - sources = required_by.map { |r| "`#{r}`" }.join(' and ') - message = "Unable to find a specification for `#{dependency}`" - message += " depended upon by #{sources}" unless sources.empty? - message - end - end - - # An error caused by attempting to fulfil a dependency that was circular - # - # @note This exception will be thrown iff a {Vertex} is added to a - # {DependencyGraph} that has a {DependencyGraph::Vertex#path_to?} an - # existing {DependencyGraph::Vertex} - class CircularDependencyError < ResolverError - # [Set<Object>] the dependencies responsible for causing the error - attr_reader :dependencies - - # Initializes a new error with the given circular vertices. - # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency - # that caused the error - def initialize(vertices) - super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}" - @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set - end - end - - # An error caused by conflicts in version - class VersionConflict < ResolverError - # @return [{String => Resolution::Conflict}] the conflicts that caused - # resolution to fail - attr_reader :conflicts - - # @return [SpecificationProvider] the specification provider used during - # resolution - attr_reader :specification_provider - - # Initializes a new error with the given version conflicts. - # @param [{String => Resolution::Conflict}] conflicts see {#conflicts} - # @param [SpecificationProvider] specification_provider see {#specification_provider} - def initialize(conflicts, specification_provider) - pairs = [] - Compatibility.flat_map(conflicts.values.flatten, &:requirements).each do |conflicting| - conflicting.each do |source, conflict_requirements| - conflict_requirements.each do |c| - pairs << [c, source] - end - end - end - - super "Unable to satisfy the following requirements:\n\n" \ - "#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}" - - @conflicts = conflicts - @specification_provider = specification_provider - end - - require 'bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider' - include Delegates::SpecificationProvider - - # @return [String] An error message that includes requirement trees, - # which is much more detailed & customizable than the default message - # @param [Hash] opts the options to create a message with. - # @option opts [String] :solver_name The user-facing name of the solver - # @option opts [String] :possibility_type The generic name of a possibility - # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees - # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements - # @option opts [Proc] :additional_message_for_conflict A proc that appends additional - # messages for each conflict - # @option opts [Proc] :version_for_spec A proc that returns the version number for a - # possibility - def message_with_trees(opts = {}) - solver_name = opts.delete(:solver_name) { self.class.name.split('::').first } - possibility_type = opts.delete(:possibility_type) { 'possibility named' } - reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } } - printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } } - additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} } - version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) } - - conflicts.sort.reduce(''.dup) do |o, (name, conflict)| - o << %(\n#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":\n) - if conflict.locked_requirement - o << %( In snapshot (#{name_for_locking_dependency_source}):\n) - o << %( #{printable_requirement.call(conflict.locked_requirement)}\n) - o << %(\n) - end - o << %( In #{name_for_explicit_dependency_source}:\n) - trees = reduce_trees.call(conflict.requirement_trees) - - o << trees.map do |tree| - t = ''.dup - depth = 2 - tree.each do |req| - t << ' ' * depth << req.to_s - unless tree.last == req - if spec = conflict.activated_by_name[name_for(req)] - t << %( was resolved to #{version_for_spec.call(spec)}, which) - end - t << %( depends on) - end - t << %(\n) - depth += 1 - end - t - end.join("\n") - - additional_message_for_conflict.call(o, name, conflict) - - o - end.strip - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb b/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb deleted file mode 100644 index 3feb7be9b5..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/gem_metadata.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # The version of Bundler::Molinillo. - VERSION = '0.6.4'.freeze -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb deleted file mode 100644 index fa094c1981..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +++ /dev/null @@ -1,101 +0,0 @@ -# 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 - # and flexibility. - # - # This module contains the methods that users of Bundler::Molinillo must to implement, - # using knowledge of their own model classes. - module SpecificationProvider - # Search for the specifications that match the given dependency. - # The specifications in the returned array will be considered in reverse - # order, so the latest version ought to be last. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `dependency` parameter. - # - # @param [Object] dependency - # @return [Array<Object>] the specifications that satisfy the given - # `dependency`. - def search_for(dependency) - [] - end - - # Returns the dependencies of `specification`. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `specification` parameter. - # - # @param [Object] specification - # @return [Array<Object>] the dependencies that are required by the given - # `specification`. - def dependencies_for(specification) - [] - end - - # Determines whether the given `requirement` is satisfied by the given - # `spec`, in the context of the current `activated` dependency graph. - # - # @param [Object] requirement - # @param [DependencyGraph] activated the current dependency graph in the - # resolution process. - # @param [Object] spec - # @return [Boolean] whether `requirement` is satisfied by `spec` in the - # context of the current `activated` dependency graph. - def requirement_satisfied_by?(requirement, activated, spec) - true - end - - # Returns the name for the given `dependency`. - # @note This method should be 'pure', i.e. the return value should depend - # only on the `dependency` parameter. - # - # @param [Object] dependency - # @return [String] the name for the given `dependency`. - def name_for(dependency) - dependency.to_s - end - - # @return [String] the name of the source of explicit dependencies, i.e. - # those passed to {Resolver#resolve} directly. - def name_for_explicit_dependency_source - 'user-specified dependency' - end - - # @return [String] the name of the source of 'locked' dependencies, i.e. - # those passed to {Resolver#resolve} directly as the `base` - def name_for_locking_dependency_source - 'Lockfile' - end - - # Sort dependencies so that the ones that are easiest to resolve are first. - # Easiest to resolve is (usually) defined by: - # 1) Is this dependency already activated? - # 2) How relaxed are the requirements? - # 3) Are there any conflicts for this dependency? - # 4) How many possibilities are there to satisfy this dependency? - # - # @param [Array<Object>] dependencies - # @param [DependencyGraph] activated the current dependency graph in the - # resolution process. - # @param [{String => Array<Conflict>}] conflicts - # @return [Array<Object>] a sorted copy of `dependencies`. - def sort_dependencies(dependencies, activated, conflicts) - dependencies.sort_by do |dependency| - name = name_for(dependency) - [ - activated.vertex_named(name).payload ? 0 : 1, - conflicts[name] ? 0 : 1, - ] - end - end - - # Returns whether this dependency, which has no possible matching - # specifications, can safely be ignored. - # - # @param [Object] dependency - # @return [Boolean] whether this dependency can safely be skipped. - def allow_missing?(dependency) - false - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb b/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb deleted file mode 100644 index a166bc6991..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/modules/ui.rb +++ /dev/null @@ -1,67 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # Conveys information about the resolution process to a user. - module UI - # The {IO} object that should be used to print output. `STDOUT`, by default. - # - # @return [IO] - def output - STDOUT - end - - # Called roughly every {#progress_rate}, this method should convey progress - # to the user. - # - # @return [void] - def indicate_progress - output.print '.' unless debug? - end - - # How often progress should be conveyed to the user via - # {#indicate_progress}, in seconds. A third of a second, by default. - # - # @return [Float] - def progress_rate - 0.33 - end - - # Called before resolution begins. - # - # @return [void] - def before_resolution - output.print 'Resolving dependencies...' - end - - # Called after resolution ends (either successfully or with an error). - # By default, prints a newline. - # - # @return [void] - def after_resolution - output.puts - end - - # Conveys debug information to the user. - # - # @param [Integer] depth the current depth of the resolution process. - # @return [void] - def debug(depth = 0) - if debug? - debug_info = yield - debug_info = debug_info.inspect unless debug_info.is_a?(String) - debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" } - output.puts debug_info - end - end - - # Whether or not debug messages should be printed. - # By default, whether or not the `MOLINILLO_DEBUG` environment variable is - # set. - # - # @return [Boolean] - def debug? - return @debug_mode if defined?(@debug_mode) - @debug_mode = ENV['MOLINILLO_DEBUG'] - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb deleted file mode 100644 index 0eb665d17a..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/resolution.rb +++ /dev/null @@ -1,837 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - class Resolver - # A specific resolution from a given {Resolver} - class Resolution - # A conflict that the resolution process encountered - # @attr [Object] requirement the requirement that immediately led to the conflict - # @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_set the set of specs 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. - # @attr [Object] underlying_error an error that has occurred during resolution, and - # will be raised at the end of it if no resolution is found. - Conflict = Struct.new( - :requirement, - :requirements, - :existing, - :possibility_set, - :locked_requirement, - :requirement_trees, - :activated_by_name, - :underlying_error - ) - - class Conflict - # @return [Object] a spec that was unable to be activated due to a conflict - def possibility - possibility_set && possibility_set.latest_version - end - end - - # A collection of possibility states that share the same dependencies - # @attr [Array] dependencies the dependencies for this set of possibilities - # @attr [Array] possibilities the possibilities - PossibilitySet = Struct.new(:dependencies, :possibilities) - - class PossibilitySet - # String representation of the possibility set, for debugging - def to_s - "[#{possibilities.join(', ')}]" - end - - # @return [Object] most up-to-date dependency in the possibility set - def latest_version - possibilities.last - end - end - - # Details of the state to unwind to when a conflict occurs, and the cause of the unwind - # @attr [Integer] state_index the index of the state to unwind to - # @attr [Object] state_requirement the requirement of the state we're unwinding to - # @attr [Array] requirement_tree for the requirement we're relaxing - # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict - # @attr [Array] requirement_trees for the conflict - # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind - UnwindDetails = Struct.new( - :state_index, - :state_requirement, - :requirement_tree, - :conflicting_requirements, - :requirement_trees, - :requirements_unwound_to_instead - ) - - class UnwindDetails - include Comparable - - # We compare UnwindDetails when choosing which state to unwind to. If - # two options have the same state_index we prefer the one most - # removed from a requirement that caused the conflict. Both options - # would unwind to the same state, but a `grandparent` option will - # filter out fewer of its possibilities after doing so - where a state - # is both a `parent` and a `grandparent` to requirements that have - # caused a conflict this is the correct behaviour. - # @param [UnwindDetail] other UnwindDetail to be compared - # @return [Integer] integer specifying ordering - def <=>(other) - if state_index > other.state_index - 1 - elsif state_index == other.state_index - reversed_requirement_tree_index <=> other.reversed_requirement_tree_index - else - -1 - end - end - - # @return [Integer] index of state requirement in reversed requirement tree - # (the conflicting requirement itself will be at position 0) - def reversed_requirement_tree_index - @reversed_requirement_tree_index ||= - if state_requirement - requirement_tree.reverse.index(state_requirement) - else - 999_999 - end - end - - # @return [Boolean] where the requirement of the state we're unwinding - # to directly caused the conflict. Note: in this case, it is - # impossible for the state we're unwinding to to be a parent of - # any of the other conflicting requirements (or we would have - # circularity) - def unwinding_to_primary_requirement? - requirement_tree.last == state_requirement - end - - # @return [Array] array of sub-dependencies to avoid when choosing a - # new possibility for the state we've unwound to. Only relevant for - # non-primary unwinds - def sub_dependencies_to_avoid - @requirements_to_avoid ||= - requirement_trees.map do |tree| - index = tree.index(state_requirement) - tree[index + 1] if index - end.compact - end - - # @return [Array] array of all the requirements that led to the need for - # this unwind - def all_requirements - @all_requirements ||= requirement_trees.flatten(1) - end - end - - # @return [SpecificationProvider] the provider that knows about - # dependencies, requirements, specifications, versions, etc. - attr_reader :specification_provider - - # @return [UI] the UI that knows how to communicate feedback about the - # resolution process back to the user - attr_reader :resolver_ui - - # @return [DependencyGraph] the base dependency graph to which - # dependencies should be 'locked' - attr_reader :base - - # @return [Array] the dependencies that were explicitly required - attr_reader :original_requested - - # Initializes a new resolution. - # @param [SpecificationProvider] specification_provider - # see {#specification_provider} - # @param [UI] resolver_ui see {#resolver_ui} - # @param [Array] requested see {#original_requested} - # @param [DependencyGraph] base see {#base} - def initialize(specification_provider, resolver_ui, requested, base) - @specification_provider = specification_provider - @resolver_ui = resolver_ui - @original_requested = requested - @base = base - @states = [] - @iteration_counter = 0 - @parents_of = Hash.new { |h, k| h[k] = [] } - end - - # Resolves the {#original_requested} dependencies into a full dependency - # graph - # @raise [ResolverError] if successful resolution is impossible - # @return [DependencyGraph] the dependency graph of successfully resolved - # dependencies - def resolve - start_resolution - - while state - break if !state.requirement && state.requirements.empty? - indicate_progress - if state.respond_to?(:pop_possibility_state) # DependencyState - debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" } - state.pop_possibility_state.tap do |s| - if s - states.push(s) - activated.tag(s) - end - end - end - process_topmost_state - end - - resolve_activated_specs - ensure - end_resolution - end - - # @return [Integer] the number of resolver iterations in between calls to - # {#resolver_ui}'s {UI#indicate_progress} method - attr_accessor :iteration_rate - private :iteration_rate - - # @return [Time] the time at which resolution began - attr_accessor :started_at - private :started_at - - # @return [Array<ResolutionState>] the stack of states for the resolution - attr_accessor :states - private :states - - private - - # Sets up the resolution process - # @return [void] - def start_resolution - @started_at = Time.now - - handle_missing_or_push_dependency_state(initial_state) - - debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" } - resolver_ui.before_resolution - end - - def resolve_activated_specs - activated.vertices.each do |_, vertex| - next unless vertex.payload - - latest_version = vertex.payload.possibilities.reverse_each.find do |possibility| - vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) } - end - - activated.set_payload(vertex.name, latest_version) - end - activated.freeze - end - - # Ends the resolution process - # @return [void] - def end_resolution - resolver_ui.after_resolution - debug do - "Finished resolution (#{@iteration_counter} steps) " \ - "(Took #{(ended_at = Time.now) - @started_at} seconds) (#{ended_at})" - end - debug { 'Unactivated: ' + Hash[activated.vertices.reject { |_n, v| v.payload }].keys.join(', ') } if state - debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state - end - - require 'bundler/vendor/molinillo/lib/molinillo/state' - require 'bundler/vendor/molinillo/lib/molinillo/modules/specification_provider' - - require 'bundler/vendor/molinillo/lib/molinillo/delegates/resolution_state' - require 'bundler/vendor/molinillo/lib/molinillo/delegates/specification_provider' - - include Bundler::Molinillo::Delegates::ResolutionState - include Bundler::Molinillo::Delegates::SpecificationProvider - - # Processes the topmost available {RequirementState} on the stack - # @return [void] - def process_topmost_state - if possibility - attempt_to_activate - else - create_conflict - unwind_for_conflict - end - rescue CircularDependencyError => underlying_error - create_conflict(underlying_error) - unwind_for_conflict - end - - # @return [Object] the current possibility that the resolution is trying - # to activate - def possibility - possibilities.last - end - - # @return [RequirementState] the current state the resolution is - # operating upon - def state - states.last - end - - # Creates the initial state for the resolution, based upon the - # {#requested} dependencies - # @return [DependencyState] the initial state for the resolution - def initial_state - graph = DependencyGraph.new.tap do |dg| - original_requested.each do |requested| - vertex = dg.add_vertex(name_for(requested), nil, true) - vertex.explicit_requirements << requested - end - dg.tag(:initial_state) - end - - requirements = sort_dependencies(original_requested, graph, {}) - initial_requirement = requirements.shift - DependencyState.new( - initial_requirement && name_for(initial_requirement), - requirements, - graph, - initial_requirement, - possibilities_for_requirement(initial_requirement, graph), - 0, - {}, - [] - ) - end - - # Unwinds the states stack because a conflict has been encountered - # @return [void] - def unwind_for_conflict - details_for_unwind = build_details_for_unwind - unwind_options = unused_unwind_options - debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" } - conflicts.tap do |c| - sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1) - raise_error_unless_state(c) - activated.rewind_to(sliced_states.first || :initial_state) if sliced_states - state.conflicts = c - state.unused_unwind_options = unwind_options - filter_possibilities_after_unwind(details_for_unwind) - index = states.size - 1 - @parents_of.each { |_, a| a.reject! { |i| i >= index } } - state.unused_unwind_options.reject! { |uw| uw.state_index >= index } - end - end - - # Raises a VersionConflict error, or any underlying error, if there is no - # current state - # @return [void] - def raise_error_unless_state(conflicts) - return if state - - error = conflicts.values.map(&:underlying_error).compact.first - raise error || VersionConflict.new(conflicts, specification_provider) - end - - # @return [UnwindDetails] Details of the nearest index to which we could unwind - def build_details_for_unwind - # Get the possible unwinds for the current conflict - current_conflict = conflicts[name] - binding_requirements = binding_requirements_for_conflict(current_conflict) - unwind_details = unwind_options_for_requirements(binding_requirements) - - last_detail_for_current_unwind = unwind_details.sort.last - current_detail = last_detail_for_current_unwind - - # Look for past conflicts that could be unwound to affect the - # requirement tree for the current conflict - relevant_unused_unwinds = unused_unwind_options.select do |alternative| - intersecting_requirements = - last_detail_for_current_unwind.all_requirements & - alternative.requirements_unwound_to_instead - next if intersecting_requirements.empty? - # Find the highest index unwind whilst looping through - current_detail = alternative if alternative > current_detail - alternative - end - - # Add the current unwind options to the `unused_unwind_options` array. - # The "used" option will be filtered out during `unwind_for_conflict`. - state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 } - - # Update the requirements_unwound_to_instead on any relevant unused unwinds - relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } - unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement } - - current_detail - end - - # @param [Array<Object>] array of requirements that combine to create a conflict - # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance - # of resolving the passed requirements - def unwind_options_for_requirements(binding_requirements) - unwind_details = [] - - trees = [] - binding_requirements.reverse_each do |r| - partial_tree = [r] - trees << partial_tree - unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, []) - - # If this requirement has alternative possibilities, check if any would - # satisfy the other requirements that created this conflict - requirement_state = find_state_for(r) - if conflict_fixing_possibilities?(requirement_state, binding_requirements) - unwind_details << UnwindDetails.new( - states.index(requirement_state), - r, - partial_tree, - binding_requirements, - trees, - [] - ) - end - - # Next, look at the parent of this requirement, and check if the requirement - # could have been avoided if an alternative PossibilitySet had been chosen - parent_r = parent_of(r) - next if parent_r.nil? - partial_tree.unshift(parent_r) - requirement_state = find_state_for(parent_r) - if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) } - unwind_details << UnwindDetails.new( - states.index(requirement_state), - parent_r, - partial_tree, - binding_requirements, - trees, - [] - ) - end - - # Finally, look at the grandparent and up of this requirement, looking - # for any possibilities that wouldn't create their parent requirement - grandparent_r = parent_of(parent_r) - until grandparent_r.nil? - partial_tree.unshift(grandparent_r) - requirement_state = find_state_for(grandparent_r) - if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) } - unwind_details << UnwindDetails.new( - states.index(requirement_state), - grandparent_r, - partial_tree, - binding_requirements, - trees, - [] - ) - end - parent_r = grandparent_r - grandparent_r = parent_of(parent_r) - end - end - - unwind_details - end - - # @param [DependencyState] state - # @param [Array] array of requirements - # @return [Boolean] whether or not the given state has any possibilities - # that could satisfy the given requirements - def conflict_fixing_possibilities?(state, binding_requirements) - return false unless state - - state.possibilities.any? do |possibility_set| - possibility_set.possibilities.any? do |poss| - possibility_satisfies_requirements?(poss, binding_requirements) - end - end - end - - # Filter's a state's possibilities to remove any that would not fix the - # conflict we've just rewound from - # @param [UnwindDetails] details of the conflict just unwound from - # @return [void] - def filter_possibilities_after_unwind(unwind_details) - return unless state && !state.possibilities.empty? - - if unwind_details.unwinding_to_primary_requirement? - filter_possibilities_for_primary_unwind(unwind_details) - else - filter_possibilities_for_parent_unwind(unwind_details) - end - end - - # Filter's a state's possibilities to remove any that would not satisfy - # the requirements in the conflict we've just rewound from - # @param [UnwindDetails] details of the conflict just unwound from - # @return [void] - def filter_possibilities_for_primary_unwind(unwind_details) - unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index } - unwinds_to_state << unwind_details - unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements) - - state.possibilities.reject! do |possibility_set| - possibility_set.possibilities.none? do |poss| - unwind_requirement_sets.any? do |requirements| - possibility_satisfies_requirements?(poss, requirements) - end - end - end - end - - # @param [Object] possibility a single possibility - # @param [Array] requirements an array of requirements - # @return [Boolean] whether the possibility satisfies all of the - # given requirements - def possibility_satisfies_requirements?(possibility, requirements) - name = name_for(possibility) - - activated.tag(:swap) - activated.set_payload(name, possibility) if activated.vertex_named(name) - satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) } - activated.rewind_to(:swap) - - satisfied - end - - # Filter's a state's possibilities to remove any that would (eventually) - # create a requirement in the conflict we've just rewound from - # @param [UnwindDetails] details of the conflict just unwound from - # @return [void] - def filter_possibilities_for_parent_unwind(unwind_details) - unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index } - unwinds_to_state << unwind_details - - primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq - parent_unwinds = unwinds_to_state.uniq - primary_unwinds - - allowed_possibility_sets = Compatibility.flat_map(primary_unwinds) do |unwind| - states[unwind.state_index].possibilities.select do |possibility_set| - possibility_set.possibilities.any? do |poss| - possibility_satisfies_requirements?(poss, unwind.conflicting_requirements) - end - end - end - - requirements_to_avoid = Compatibility.flat_map(parent_unwinds, &:sub_dependencies_to_avoid) - - state.possibilities.reject! do |possibility_set| - !allowed_possibility_sets.include?(possibility_set) && - (requirements_to_avoid - possibility_set.dependencies).empty? - end - end - - # @param [Conflict] conflict - # @return [Array] minimal array of requirements that would cause the passed - # conflict to occur. - def binding_requirements_for_conflict(conflict) - return [conflict.requirement] if conflict.possibility.nil? - - possible_binding_requirements = conflict.requirements.values.flatten(1).uniq - - # When there’s a `CircularDependency` error the conflicting requirement - # (the one causing the circular) won’t be `conflict.requirement` - # (which won’t be for the right state, because we won’t have created it, - # because it’s circular). - # We need to make sure we have that requirement in the conflict’s list, - # otherwise we won’t be able to unwind properly, so we just return all - # the requirements for the conflict. - return possible_binding_requirements if conflict.underlying_error - - possibilities = search_for(conflict.requirement) - - # If all the requirements together don't filter out all possibilities, - # then the only two requirements we need to consider are the initial one - # (where the dependency's version was first chosen) and the last - if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities) - return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact - end - - # Loop through the possible binding requirements, removing each one - # that doesn't bind. Use a `reverse_each` as we want the earliest set of - # binding requirements, and don't use `reject!` as we wish to refine the - # array *on each iteration*. - binding_requirements = possible_binding_requirements.dup - possible_binding_requirements.reverse_each do |req| - next if req == conflict.requirement - unless binding_requirement_in_set?(req, binding_requirements, possibilities) - binding_requirements -= [req] - end - end - - binding_requirements - end - - # @param [Object] requirement we wish to check - # @param [Array] array of requirements - # @param [Array] array of possibilities the requirements will be used to filter - # @return [Boolean] whether or not the given requirement is required to filter - # out all elements of the array of possibilities. - def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities) - possibilities.any? do |poss| - possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement]) - end - end - - # @return [Object] the requirement that led to `requirement` being added - # to the list of requirements. - def parent_of(requirement) - return unless requirement - return unless index = @parents_of[requirement].last - return unless parent_state = @states[index] - parent_state.requirement - end - - # @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 vertex = activated.vertex_named(name) - return nil unless vertex.payload - states.find { |s| s.name == name }.requirement - end - - # @return [ResolutionState] the state whose `requirement` is the given - # `requirement`. - def find_state_for(requirement) - return nil unless requirement - states.find { |i| requirement == i.requirement } - end - - # @return [Conflict] a {Conflict} that reflects the failure to activate - # the {#possibility} in conjunction with the current {#state} - def create_conflict(underlying_error = nil) - vertex = activated.vertex_named(name) - locked_requirement = locked_requirement_named(name) - - requirements = {} - unless vertex.explicit_requirements.empty? - 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 do |edge| - (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement) - end - - activated_by_name = {} - activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload } - conflicts[name] = Conflict.new( - requirement, - requirements, - vertex.payload && vertex.payload.latest_version, - possibility, - locked_requirement, - requirement_trees, - activated_by_name, - underlying_error - ) - end - - # @return [Array<Array<Object>>] The different requirement - # trees that led to every requirement for the current spec. - def requirement_trees - vertex = activated.vertex_named(name) - vertex.requirements.map { |r| requirement_tree_for(r) } - end - - # @return [Array<Object>] the list of requirements that led to - # `requirement` being required. - def requirement_tree_for(requirement) - tree = [] - while requirement - tree.unshift(requirement) - requirement = parent_of(requirement) - end - tree - end - - # Indicates progress roughly once every second - # @return [void] - def indicate_progress - @iteration_counter += 1 - @progress_rate ||= resolver_ui.progress_rate - if iteration_rate.nil? - if Time.now - started_at >= @progress_rate - self.iteration_rate = @iteration_counter - end - end - - if iteration_rate && (@iteration_counter % iteration_rate) == 0 - resolver_ui.indicate_progress - end - end - - # Calls the {#resolver_ui}'s {UI#debug} method - # @param [Integer] depth the depth of the {#states} stack - # @param [Proc] block a block that yields a {#to_s} - # @return [void] - def debug(depth = 0, &block) - resolver_ui.debug(depth, &block) - end - - # Attempts to activate the current {#possibility} - # @return [void] - def attempt_to_activate - debug(depth) { 'Attempting to activate ' + possibility.to_s } - existing_vertex = activated.vertex_named(name) - if existing_vertex.payload - debug(depth) { "Found existing spec (#{existing_vertex.payload})" } - attempt_to_filter_existing_spec(existing_vertex) - else - latest = possibility.latest_version - # use reject!(!satisfied) for 1.8.7 compatibility - possibility.possibilities.reject! do |possibility| - !requirement_satisfied_by?(requirement, activated, possibility) - end - if possibility.latest_version.nil? - # ensure there's a possibility for better error messages - possibility.possibilities << latest if latest - create_conflict - unwind_for_conflict - else - activate_new_spec - end - end - end - - # Attempts to update the existing vertex's `PossibilitySet` with a filtered version - # @return [void] - def attempt_to_filter_existing_spec(vertex) - filtered_set = filtered_possibility_set(vertex) - if !filtered_set.possibilities.empty? - activated.set_payload(name, filtered_set) - new_requirements = requirements.dup - push_state_for_requirements(new_requirements, false) - else - create_conflict - debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" } - unwind_for_conflict - end - end - - # Generates a filtered version of the existing vertex's `PossibilitySet` using the - # current state's `requirement` - # @param [Object] existing vertex - # @return [PossibilitySet] filtered possibility set - def filtered_possibility_set(vertex) - PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities) - end - - # @param [String] requirement_name the spec name to search for - # @return [Object] the locked spec named `requirement_name`, if one - # is found on {#base} - def locked_requirement_named(requirement_name) - vertex = base.vertex_named(requirement_name) - vertex && vertex.payload - end - - # Add the current {#possibility} to the dependency graph of the current - # {#state} - # @return [void] - def activate_new_spec - conflicts.delete(name) - debug(depth) { "Activated #{name} at #{possibility}" } - activated.set_payload(name, possibility) - require_nested_dependencies_for(possibility) - end - - # Requires the dependencies that the recently activated spec has - # @param [Object] activated_possibility the PossibilitySet that has just been - # activated - # @return [void] - def require_nested_dependencies_for(possibility_set) - nested_dependencies = dependencies_for(possibility_set.latest_version) - debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" } - nested_dependencies.each do |d| - activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d) - parent_index = states.size - 1 - parents = @parents_of[d] - parents << parent_index if parents.empty? - end - - push_state_for_requirements(requirements + nested_dependencies, !nested_dependencies.empty?) - end - - # Pushes a new {DependencyState} that encapsulates both existing and new - # requirements - # @param [Array] new_requirements - # @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 = nil - loop do - new_requirement = new_requirements.shift - break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement } - end - new_name = new_requirement ? name_for(new_requirement) : ''.freeze - possibilities = possibilities_for_requirement(new_requirement) - handle_missing_or_push_dependency_state DependencyState.new( - new_name, new_requirements, new_activated, - new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup - ) - end - - # Checks a proposed requirement with any existing locked requirement - # before generating an array of possibilities for it. - # @param [Object] the proposed requirement - # @return [Array] possibilities - def possibilities_for_requirement(requirement, activated = self.activated) - return [] unless requirement - if locked_requirement_named(name_for(requirement)) - return locked_requirement_possibility_set(requirement, activated) - end - - group_possibilities(search_for(requirement)) - end - - # @param [Object] the proposed requirement - # @return [Array] possibility set containing only the locked requirement, if any - def locked_requirement_possibility_set(requirement, activated = self.activated) - all_possibilities = search_for(requirement) - locked_requirement = locked_requirement_named(name_for(requirement)) - - # Longwinded way to build a possibilities array with either the locked - # requirement or nothing in it. Required, since the API for - # locked_requirement isn't guaranteed. - locked_possibilities = all_possibilities.select do |possibility| - requirement_satisfied_by?(locked_requirement, activated, possibility) - end - - group_possibilities(locked_possibilities) - end - - # Build an array of PossibilitySets, with each element representing a group of - # dependency versions that all have the same sub-dependency version constraints - # and are contiguous. - # @param [Array] an array of possibilities - # @return [Array] an array of possibility sets - def group_possibilities(possibilities) - possibility_sets = [] - current_possibility_set = nil - - possibilities.reverse_each do |possibility| - dependencies = dependencies_for(possibility) - if current_possibility_set && current_possibility_set.dependencies == dependencies - current_possibility_set.possibilities.unshift(possibility) - else - possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility])) - current_possibility_set = possibility_sets.first - end - end - - possibility_sets - 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 vertex in {#activated} is removed, and we continue - # resolving the remaining requirements. - # @param [DependencyState] state - # @return [void] - def handle_missing_or_push_dependency_state(state) - if state.requirement && state.possibilities.empty? && allow_missing?(state.requirement) - state.activated.detach_vertex_named(state.name) - push_state_for_requirements(state.requirements.dup, false, state.activated) - else - states.push(state).tap { activated.tag(state) } - end - end - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb b/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb deleted file mode 100644 index 7d36858778..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/resolver.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'bundler/vendor/molinillo/lib/molinillo/dependency_graph' - -module Bundler::Molinillo - # This class encapsulates a dependency resolver. - # The resolver is responsible for determining which set of dependencies to - # activate, with feedback from the {#specification_provider} - # - # - class Resolver - require 'bundler/vendor/molinillo/lib/molinillo/resolution' - - # @return [SpecificationProvider] the specification provider used - # in the resolution process - attr_reader :specification_provider - - # @return [UI] the UI module used to communicate back to the user - # during the resolution process - attr_reader :resolver_ui - - # Initializes a new resolver. - # @param [SpecificationProvider] specification_provider - # see {#specification_provider} - # @param [UI] resolver_ui - # see {#resolver_ui} - def initialize(specification_provider, resolver_ui) - @specification_provider = specification_provider - @resolver_ui = resolver_ui - end - - # Resolves the requested dependencies into a {DependencyGraph}, - # locking to the base dependency graph (if specified) - # @param [Array] requested an array of 'requested' dependencies that the - # {#specification_provider} can understand - # @param [DependencyGraph,nil] base the base dependency graph to which - # dependencies should be 'locked' - def resolve(requested, base = DependencyGraph.new) - Resolution.new(specification_provider, - resolver_ui, - requested, - base). - resolve - end - end -end diff --git a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb b/lib/bundler/vendor/molinillo/lib/molinillo/state.rb deleted file mode 100644 index 68fa1f54e3..0000000000 --- a/lib/bundler/vendor/molinillo/lib/molinillo/state.rb +++ /dev/null @@ -1,58 +0,0 @@ -# frozen_string_literal: true - -module Bundler::Molinillo - # A state that a {Resolution} can be in - # @attr [String] name the name of the current requirement - # @attr [Array<Object>] requirements currently unsatisfied requirements - # @attr [DependencyGraph] activated the graph of activated dependencies - # @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 [Hash] conflicts unresolved conflicts, indexed by dependency name - # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored - ResolutionState = Struct.new( - :name, - :requirements, - :activated, - :requirement, - :possibilities, - :depth, - :conflicts, - :unused_unwind_options - ) - - class ResolutionState - # Returns an empty resolution state - # @return [ResolutionState] an empty state - def self.empty - new(nil, [], DependencyGraph.new, nil, nil, 0, {}, []) - end - end - - # A state that encapsulates a set of {#requirements} with an {Array} of - # possibilities - class DependencyState < ResolutionState - # Removes a possibility from `self` - # @return [PossibilityState] a state with a single possibility, - # the possibility that was removed from `self` - def pop_possibility_state - PossibilityState.new( - name, - requirements.dup, - activated, - requirement, - [possibilities.pop], - depth + 1, - conflicts.dup, - unused_unwind_options.dup - ).tap do |state| - state.activated.tag(state) - end - end - end - - # A state that encapsulates a single possibility to fulfill the given - # {#requirement} - class PossibilityState < ResolutionState - end -end diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb deleted file mode 100644 index e5e09080c2..0000000000 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/faster.rb +++ /dev/null @@ -1,27 +0,0 @@ -require 'net/protocol' - -## -# Aaron Patterson's monkeypatch (accepted into 1.9.1) to fix Net::HTTP's speed -# problems. -# -# http://gist.github.com/251244 - -class Net::BufferedIO #:nodoc: - alias :old_rbuf_fill :rbuf_fill - - def rbuf_fill - if @io.respond_to? :read_nonblock then - begin - @rbuf << @io.read_nonblock(65536) - rescue Errno::EWOULDBLOCK, Errno::EAGAIN => e - retry if IO.select [@io], nil, nil, @read_timeout - raise Timeout::Error, e.message - end - else # SSL sockets do not have read_nonblock - timeout @read_timeout do - @rbuf << @io.sysread(65536) - end - end - end -end if RUBY_VERSION < '1.9' - diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb deleted file mode 100644 index 7cbca5bc06..0000000000 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +++ /dev/null @@ -1,1233 +0,0 @@ -require 'net/http' -begin - require 'net/https' -rescue LoadError - # net/https or openssl -end if RUBY_VERSION < '1.9' # but only for 1.8 -require 'bundler/vendor/net-http-persistent/lib/net/http/faster' -require 'uri' -require 'cgi' # for escaping - -begin - require 'net/http/pipeline' -rescue LoadError -end - -autoload :OpenSSL, 'openssl' - -## -# Persistent connections for Net::HTTP -# -# Bundler::Persistent::Net::HTTP::Persistent maintains persistent connections across all the -# servers you wish to talk to. For each host:port you communicate with a -# single persistent connection is created. -# -# Multiple Bundler::Persistent::Net::HTTP::Persistent objects will share the same set of -# connections. -# -# For each thread you start a new connection will be created. A -# Bundler::Persistent::Net::HTTP::Persistent connection will not be shared across threads. -# -# You can shut down the HTTP connections when done by calling #shutdown. You -# should name your Bundler::Persistent::Net::HTTP::Persistent object if you intend to call this -# method. -# -# Example: -# -# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent' -# -# uri = URI 'http://example.com/awesome/web/service' -# -# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name' -# -# # perform a GET -# response = http.request uri -# -# # or -# -# get = Net::HTTP::Get.new uri.request_uri -# response = http.request get -# -# # create a POST -# post_uri = uri + 'create' -# post = Net::HTTP::Post.new post_uri.path -# post.set_form_data 'some' => 'cool data' -# -# # perform the POST, the URI is always required -# response http.request post_uri, post -# -# Note that for GET, HEAD and other requests that do not have a body you want -# to use URI#request_uri not URI#path. The request_uri contains the query -# params which are sent in the body for other requests. -# -# == SSL -# -# SSL connections are automatically created depending upon the scheme of the -# URI. SSL connections are automatically verified against the default -# certificate store for your computer. You can override this by changing -# verify_mode or by specifying an alternate cert_store. -# -# Here are the SSL settings, see the individual methods for documentation: -# -# #certificate :: This client's certificate -# #ca_file :: The certificate-authority -# #cert_store :: An SSL certificate store -# #private_key :: The client's SSL private key -# #reuse_ssl_sessions :: Reuse a previously opened SSL session for a new -# connection -# #ssl_version :: Which specific SSL version to use -# #verify_callback :: For server certificate verification -# #verify_mode :: How connections should be verified -# -# == Proxies -# -# A proxy can be set through #proxy= or at initialization time by providing a -# second argument to ::new. The proxy may be the URI of the proxy server or -# <code>:ENV</code> which will consult environment variables. -# -# See #proxy= and #proxy_from_env for details. -# -# == Headers -# -# Headers may be specified for use in every request. #headers are appended to -# any headers on the request. #override_headers replace existing headers on -# the request. -# -# The difference between the two can be seen in setting the User-Agent. Using -# <code>http.headers['User-Agent'] = 'MyUserAgent'</code> will send "Ruby, -# MyUserAgent" while <code>http.override_headers['User-Agent'] = -# 'MyUserAgent'</code> will send "MyUserAgent". -# -# == Tuning -# -# === Segregation -# -# By providing an application name to ::new you can separate your connections -# from the connections of other applications. -# -# === Idle Timeout -# -# If a connection hasn't been used for this number of seconds it will automatically be -# reset upon the next use to avoid attempting to send to a closed connection. -# The default value is 5 seconds. nil means no timeout. Set through #idle_timeout. -# -# Reducing this value may help avoid the "too many connection resets" error -# when sending non-idempotent requests while increasing this value will cause -# fewer round-trips. -# -# === Read Timeout -# -# The amount of time allowed between reading two chunks from the socket. Set -# through #read_timeout -# -# === Max Requests -# -# The number of requests that should be made before opening a new connection. -# Typically many keep-alive capable servers tune this to 100 or less, so the -# 101st request will fail with ECONNRESET. If unset (default), this value has no -# effect, if set, connections will be reset on the request after max_requests. -# -# === Open Timeout -# -# The amount of time to wait for a connection to be opened. Set through -# #open_timeout. -# -# === Socket Options -# -# Socket options may be set on newly-created connections. See #socket_options -# for details. -# -# === Non-Idempotent Requests -# -# By default non-idempotent requests will not be retried per RFC 2616. By -# setting retry_change_requests to true requests will automatically be retried -# once. -# -# Only do this when you know that retrying a POST or other non-idempotent -# request is safe for your application and will not create duplicate -# resources. -# -# The recommended way to handle non-idempotent requests is the following: -# -# require 'bundler/vendor/net-http-persistent/lib/net/http/persistent' -# -# uri = URI 'http://example.com/awesome/web/service' -# post_uri = uri + 'create' -# -# http = Bundler::Persistent::Net::HTTP::Persistent.new 'my_app_name' -# -# post = Net::HTTP::Post.new post_uri.path -# # ... fill in POST request -# -# begin -# response = http.request post_uri, post -# rescue Bundler::Persistent::Net::HTTP::Persistent::Error -# -# # POST failed, make a new request to verify the server did not process -# # the request -# exists_uri = uri + '...' -# response = http.get exists_uri -# -# # Retry if it failed -# retry if response.code == '404' -# end -# -# The method of determining if the resource was created or not is unique to -# the particular service you are using. Of course, you will want to add -# protection from infinite looping. -# -# === Connection Termination -# -# If you are done using the Bundler::Persistent::Net::HTTP::Persistent instance you may shut down -# all the connections in the current thread with #shutdown. This is not -# recommended for normal use, it should only be used when it will be several -# minutes before you make another HTTP request. -# -# If you are using multiple threads, call #shutdown in each thread when the -# thread is done making requests. If you don't call shutdown, that's OK. -# Ruby will automatically garbage collect and shutdown your HTTP connections -# when the thread terminates. - -class Bundler::Persistent::Net::HTTP::Persistent - - ## - # The beginning of Time - - EPOCH = Time.at 0 # :nodoc: - - ## - # Is OpenSSL available? This test works with autoload - - HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc: - - ## - # The version of Bundler::Persistent::Net::HTTP::Persistent you are using - - VERSION = '2.9.4' - - ## - # Exceptions rescued for automatic retry on ruby 2.0.0. This overlaps with - # the exception list for ruby 1.x. - - RETRIED_EXCEPTIONS = [ # :nodoc: - (Net::ReadTimeout if Net.const_defined? :ReadTimeout), - IOError, - EOFError, - Errno::ECONNRESET, - Errno::ECONNABORTED, - Errno::EPIPE, - (OpenSSL::SSL::SSLError if HAVE_OPENSSL), - Timeout::Error, - ].compact - - ## - # Error class for errors raised by Bundler::Persistent::Net::HTTP::Persistent. Various - # SystemCallErrors are re-raised with a human-readable message under this - # class. - - class Error < StandardError; end - - ## - # Use this method to detect the idle timeout of the host at +uri+. The - # value returned can be used to configure #idle_timeout. +max+ controls the - # maximum idle timeout to detect. - # - # After - # - # Idle timeout detection is performed by creating a connection then - # performing a HEAD request in a loop until the connection terminates - # waiting one additional second per loop. - # - # NOTE: This may not work on ruby > 1.9. - - def self.detect_idle_timeout uri, max = 10 - uri = URI uri unless URI::Generic === uri - uri += '/' - - req = Net::HTTP::Head.new uri.request_uri - - http = new 'net-http-persistent detect_idle_timeout' - - connection = http.connection_for uri - - sleep_time = 0 - - loop do - response = connection.request req - - $stderr.puts "HEAD #{uri} => #{response.code}" if $DEBUG - - unless Net::HTTPOK === response then - raise Error, "bad response code #{response.code} detecting idle timeout" - end - - break if sleep_time >= max - - sleep_time += 1 - - $stderr.puts "sleeping #{sleep_time}" if $DEBUG - sleep sleep_time - end - rescue - # ignore StandardErrors, we've probably found the idle timeout. - ensure - http.shutdown - - return sleep_time unless $! - end - - ## - # This client's OpenSSL::X509::Certificate - - attr_reader :certificate - - # For Net::HTTP parity - alias cert certificate - - ## - # An SSL certificate authority. Setting this will set verify_mode to - # VERIFY_PEER. - - attr_reader :ca_file - - ## - # An SSL certificate store. Setting this will override the default - # certificate store. See verify_mode for more information. - - attr_reader :cert_store - - ## - # Sends debug_output to this IO via Net::HTTP#set_debug_output. - # - # Never use this method in production code, it causes a serious security - # hole. - - attr_accessor :debug_output - - ## - # Current connection generation - - attr_reader :generation # :nodoc: - - ## - # Where this instance's connections live in the thread local variables - - attr_reader :generation_key # :nodoc: - - ## - # Headers that are added to every request using Net::HTTP#add_field - - attr_reader :headers - - ## - # Maps host:port to an HTTP version. This allows us to enable version - # specific features. - - attr_reader :http_versions - - ## - # Maximum time an unused connection can remain idle before being - # automatically closed. - - attr_accessor :idle_timeout - - ## - # Maximum number of requests on a connection before it is considered expired - # and automatically closed. - - attr_accessor :max_requests - - ## - # The value sent in the Keep-Alive header. Defaults to 30. Not needed for - # HTTP/1.1 servers. - # - # This may not work correctly for HTTP/1.0 servers - # - # This method may be removed in a future version as RFC 2616 does not - # require this header. - - attr_accessor :keep_alive - - ## - # A name for this connection. Allows you to keep your connections apart - # from everybody else's. - - attr_reader :name - - ## - # Seconds to wait until a connection is opened. See Net::HTTP#open_timeout - - attr_accessor :open_timeout - - ## - # Headers that are added to every request using Net::HTTP#[]= - - attr_reader :override_headers - - ## - # This client's SSL private key - - attr_reader :private_key - - # For Net::HTTP parity - alias key private_key - - ## - # The URL through which requests will be proxied - - attr_reader :proxy_uri - - ## - # List of host suffixes which will not be proxied - - attr_reader :no_proxy - - ## - # Seconds to wait until reading one block. See Net::HTTP#read_timeout - - attr_accessor :read_timeout - - ## - # Where this instance's request counts live in the thread local variables - - attr_reader :request_key # :nodoc: - - ## - # By default SSL sessions are reused to avoid extra SSL handshakes. Set - # this to false if you have problems communicating with an HTTPS server - # like: - # - # SSL_connect [...] read finished A: unexpected message (OpenSSL::SSL::SSLError) - - attr_accessor :reuse_ssl_sessions - - ## - # An array of options for Socket#setsockopt. - # - # By default the TCP_NODELAY option is set on sockets. - # - # To set additional options append them to this array: - # - # http.socket_options << [Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1] - - attr_reader :socket_options - - ## - # Current SSL connection generation - - attr_reader :ssl_generation # :nodoc: - - ## - # Where this instance's SSL connections live in the thread local variables - - attr_reader :ssl_generation_key # :nodoc: - - ## - # SSL version to use. - # - # By default, the version will be negotiated automatically between client - # and server. Ruby 1.9 and newer only. - - attr_reader :ssl_version if RUBY_VERSION > '1.9' - - ## - # Where this instance's last-use times live in the thread local variables - - attr_reader :timeout_key # :nodoc: - - ## - # SSL verification callback. Used when ca_file is set. - - attr_reader :verify_callback - - ## - # HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER which verifies - # the server certificate. - # - # If no ca_file or cert_store is set the default system certificate store is - # used. - # - # You can use +verify_mode+ to override any default values. - - attr_reader :verify_mode - - ## - # Enable retries of non-idempotent requests that change data (e.g. POST - # requests) when the server has disconnected. - # - # This will in the worst case lead to multiple requests with the same data, - # but it may be useful for some applications. Take care when enabling - # this option to ensure it is safe to POST or perform other non-idempotent - # requests to the server. - - attr_accessor :retry_change_requests - - ## - # Creates a new Bundler::Persistent::Net::HTTP::Persistent. - # - # Set +name+ to keep your connections apart from everybody else's. Not - # required currently, but highly recommended. Your library name should be - # good enough. This parameter will be required in a future version. - # - # +proxy+ may be set to a URI::HTTP or :ENV to pick up proxy options from - # the environment. See proxy_from_env for details. - # - # In order to use a URI for the proxy you may need to do some extra work - # beyond URI parsing if the proxy requires a password: - # - # proxy = URI 'http://proxy.example' - # proxy.user = 'AzureDiamond' - # proxy.password = 'hunter2' - - def initialize name = nil, proxy = nil - @name = name - - @debug_output = nil - @proxy_uri = nil - @no_proxy = [] - @headers = {} - @override_headers = {} - @http_versions = {} - @keep_alive = 30 - @open_timeout = nil - @read_timeout = nil - @idle_timeout = 5 - @max_requests = nil - @socket_options = [] - - @socket_options << [Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1] if - Socket.const_defined? :TCP_NODELAY - - key = ['net_http_persistent', name].compact - @generation_key = [key, 'generations' ].join('_').intern - @ssl_generation_key = [key, 'ssl_generations'].join('_').intern - @request_key = [key, 'requests' ].join('_').intern - @timeout_key = [key, 'timeouts' ].join('_').intern - - @certificate = nil - @ca_file = nil - @private_key = nil - @ssl_version = nil - @verify_callback = nil - @verify_mode = nil - @cert_store = nil - - @generation = 0 # incremented when proxy URI changes - @ssl_generation = 0 # incremented when SSL session variables change - - if HAVE_OPENSSL then - @verify_mode = OpenSSL::SSL::VERIFY_PEER - @reuse_ssl_sessions = OpenSSL::SSL.const_defined? :Session - end - - @retry_change_requests = false - - @ruby_1 = RUBY_VERSION < '2' - @retried_on_ruby_2 = !@ruby_1 - - self.proxy = proxy if proxy - end - - ## - # Sets this client's OpenSSL::X509::Certificate - - def certificate= certificate - @certificate = certificate - - reconnect_ssl - end - - # For Net::HTTP parity - alias cert= certificate= - - ## - # Sets the SSL certificate authority file. - - def ca_file= file - @ca_file = file - - reconnect_ssl - end - - ## - # Overrides the default SSL certificate store used for verifying - # connections. - - def cert_store= store - @cert_store = store - - reconnect_ssl - end - - ## - # Finishes all connections on the given +thread+ that were created before - # the given +generation+ in the threads +generation_key+ list. - # - # See #shutdown for a bunch of scary warning about misusing this method. - - def cleanup(generation, thread = Thread.current, - generation_key = @generation_key) # :nodoc: - timeouts = thread[@timeout_key] - - (0...generation).each do |old_generation| - next unless thread[generation_key] - - conns = thread[generation_key].delete old_generation - - conns.each_value do |conn| - finish conn, thread - - timeouts.delete conn.object_id if timeouts - end if conns - end - end - - ## - # Creates a new connection for +uri+ - - def connection_for uri - Thread.current[@generation_key] ||= Hash.new { |h,k| h[k] = {} } - Thread.current[@ssl_generation_key] ||= Hash.new { |h,k| h[k] = {} } - Thread.current[@request_key] ||= Hash.new 0 - Thread.current[@timeout_key] ||= Hash.new EPOCH - - use_ssl = uri.scheme.downcase == 'https' - - if use_ssl then - raise Bundler::Persistent::Net::HTTP::Persistent::Error, 'OpenSSL is not available' unless - HAVE_OPENSSL - - ssl_generation = @ssl_generation - - ssl_cleanup ssl_generation - - connections = Thread.current[@ssl_generation_key][ssl_generation] - else - generation = @generation - - cleanup generation - - connections = Thread.current[@generation_key][generation] - end - - net_http_args = [uri.host, uri.port] - connection_id = net_http_args.join ':' - - if @proxy_uri and not proxy_bypass? uri.host, uri.port then - connection_id << @proxy_connection_id - net_http_args.concat @proxy_args - else - net_http_args.concat [nil, nil, nil, nil] - end - - connection = connections[connection_id] - - unless connection = connections[connection_id] then - connections[connection_id] = http_class.new(*net_http_args) - connection = connections[connection_id] - ssl connection if use_ssl - else - reset connection if expired? connection - end - - start connection unless connection.started? - - connection.read_timeout = @read_timeout if @read_timeout - connection.keep_alive_timeout = @idle_timeout if @idle_timeout && connection.respond_to?(:keep_alive_timeout=) - - connection - rescue Errno::ECONNREFUSED - address = connection.proxy_address || connection.address - port = connection.proxy_port || connection.port - - raise Error, "connection refused: #{address}:#{port}" - rescue Errno::EHOSTDOWN - address = connection.proxy_address || connection.address - port = connection.proxy_port || connection.port - - raise Error, "host down: #{address}:#{port}" - end - - ## - # Returns an error message containing the number of requests performed on - # this connection - - def error_message connection - requests = Thread.current[@request_key][connection.object_id] - 1 # fixup - last_use = Thread.current[@timeout_key][connection.object_id] - - age = Time.now - last_use - - "after #{requests} requests on #{connection.object_id}, " \ - "last used #{age} seconds ago" - end - - ## - # URI::escape wrapper - - def escape str - CGI.escape str if str - end - - ## - # URI::unescape wrapper - - def unescape str - CGI.unescape str if str - end - - - ## - # Returns true if the connection should be reset due to an idle timeout, or - # maximum request count, false otherwise. - - def expired? connection - requests = Thread.current[@request_key][connection.object_id] - return true if @max_requests && requests >= @max_requests - return false unless @idle_timeout - return true if @idle_timeout.zero? - - last_used = Thread.current[@timeout_key][connection.object_id] - - Time.now - last_used > @idle_timeout - end - - ## - # Starts the Net::HTTP +connection+ - - def start connection - connection.set_debug_output @debug_output if @debug_output - connection.open_timeout = @open_timeout if @open_timeout - - connection.start - - socket = connection.instance_variable_get :@socket - - if socket then # for fakeweb - @socket_options.each do |option| - socket.io.setsockopt(*option) - end - end - end - - ## - # Finishes the Net::HTTP +connection+ - - def finish connection, thread = Thread.current - if requests = thread[@request_key] then - requests.delete connection.object_id - end - - connection.finish - rescue IOError - end - - def http_class # :nodoc: - if RUBY_VERSION > '2.0' then - Net::HTTP - elsif [:Artifice, :FakeWeb, :WebMock].any? { |klass| - Object.const_defined?(klass) - } or not @reuse_ssl_sessions then - Net::HTTP - else - Bundler::Persistent::Net::HTTP::Persistent::SSLReuse - end - end - - ## - # Returns the HTTP protocol version for +uri+ - - def http_version uri - @http_versions["#{uri.host}:#{uri.port}"] - end - - ## - # Is +req+ idempotent according to RFC 2616? - - def idempotent? req - case req - when Net::HTTP::Delete, Net::HTTP::Get, Net::HTTP::Head, - Net::HTTP::Options, Net::HTTP::Put, Net::HTTP::Trace then - true - end - end - - ## - # Is the request +req+ idempotent or is retry_change_requests allowed. - # - # If +retried_on_ruby_2+ is true, true will be returned if we are on ruby, - # retry_change_requests is allowed and the request is not idempotent. - - def can_retry? req, retried_on_ruby_2 = false - return @retry_change_requests && !idempotent?(req) if retried_on_ruby_2 - - @retry_change_requests || idempotent?(req) - end - - if RUBY_VERSION > '1.9' then - ## - # Workaround for missing Net::HTTPHeader#connection_close? on Ruby 1.8 - - def connection_close? header - header.connection_close? - end - - ## - # Workaround for missing Net::HTTPHeader#connection_keep_alive? on Ruby 1.8 - - def connection_keep_alive? header - header.connection_keep_alive? - end - else - ## - # Workaround for missing Net::HTTPRequest#connection_close? on Ruby 1.8 - - def connection_close? header - header['connection'] =~ /close/ or header['proxy-connection'] =~ /close/ - end - - ## - # Workaround for missing Net::HTTPRequest#connection_keep_alive? on Ruby - # 1.8 - - def connection_keep_alive? header - header['connection'] =~ /keep-alive/ or - header['proxy-connection'] =~ /keep-alive/ - end - end - - ## - # Deprecated in favor of #expired? - - def max_age # :nodoc: - return Time.now + 1 unless @idle_timeout - - Time.now - @idle_timeout - end - - ## - # Adds "http://" to the String +uri+ if it is missing. - - def normalize_uri uri - (uri =~ /^https?:/) ? uri : "http://#{uri}" - end - - ## - # Pipelines +requests+ to the HTTP server at +uri+ yielding responses if a - # block is given. Returns all responses received. - # - # See - # Net::HTTP::Pipeline[http://docs.seattlerb.org/net-http-pipeline/Net/HTTP/Pipeline.html] - # for further details. - # - # Only if <tt>net-http-pipeline</tt> was required before - # <tt>net-http-persistent</tt> #pipeline will be present. - - def pipeline uri, requests, &block # :yields: responses - connection = connection_for uri - - connection.pipeline requests, &block - end - - ## - # Sets this client's SSL private key - - def private_key= key - @private_key = key - - reconnect_ssl - end - - # For Net::HTTP parity - alias key= private_key= - - ## - # Sets the proxy server. The +proxy+ may be the URI of the proxy server, - # the symbol +:ENV+ which will read the proxy from the environment or nil to - # disable use of a proxy. See #proxy_from_env for details on setting the - # proxy from the environment. - # - # If the proxy URI is set after requests have been made, the next request - # will shut-down and re-open all connections. - # - # The +no_proxy+ query parameter can be used to specify hosts which shouldn't - # be reached via proxy; if set it should be a comma separated list of - # hostname suffixes, optionally with +:port+ appended, for example - # <tt>example.com,some.host:8080</tt>. - - def proxy= proxy - @proxy_uri = case proxy - when :ENV then proxy_from_env - when URI::HTTP then proxy - when nil then # ignore - else raise ArgumentError, 'proxy must be :ENV or a URI::HTTP' - end - - @no_proxy.clear - - if @proxy_uri then - @proxy_args = [ - @proxy_uri.host, - @proxy_uri.port, - unescape(@proxy_uri.user), - unescape(@proxy_uri.password), - ] - - @proxy_connection_id = [nil, *@proxy_args].join ':' - - if @proxy_uri.query then - @no_proxy = CGI.parse(@proxy_uri.query)['no_proxy'].join(',').downcase.split(',').map { |x| x.strip }.reject { |x| x.empty? } - end - end - - reconnect - reconnect_ssl - end - - ## - # Creates a URI for an HTTP proxy server from ENV variables. - # - # If +HTTP_PROXY+ is set a proxy will be returned. - # - # If +HTTP_PROXY_USER+ or +HTTP_PROXY_PASS+ are set the URI is given the - # indicated user and password unless HTTP_PROXY contains either of these in - # the URI. - # - # The +NO_PROXY+ ENV variable can be used to specify hosts which shouldn't - # be reached via proxy; if set it should be a comma separated list of - # hostname suffixes, optionally with +:port+ appended, for example - # <tt>example.com,some.host:8080</tt>. When set to <tt>*</tt> no proxy will - # be returned. - # - # For Windows users, lowercase ENV variables are preferred over uppercase ENV - # variables. - - def proxy_from_env - env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY'] - - return nil if env_proxy.nil? or env_proxy.empty? - - uri = URI normalize_uri env_proxy - - env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY'] - - # '*' is special case for always bypass - return nil if env_no_proxy == '*' - - if env_no_proxy then - uri.query = "no_proxy=#{escape(env_no_proxy)}" - end - - unless uri.user or uri.password then - uri.user = escape ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER'] - uri.password = escape ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS'] - end - - uri - end - - ## - # Returns true when proxy should by bypassed for host. - - def proxy_bypass? host, port - host = host.downcase - host_port = [host, port].join ':' - - @no_proxy.each do |name| - return true if host[-name.length, name.length] == name or - host_port[-name.length, name.length] == name - end - - false - end - - ## - # Forces reconnection of HTTP connections. - - def reconnect - @generation += 1 - end - - ## - # Forces reconnection of SSL connections. - - def reconnect_ssl - @ssl_generation += 1 - end - - ## - # Finishes then restarts the Net::HTTP +connection+ - - def reset connection - Thread.current[@request_key].delete connection.object_id - Thread.current[@timeout_key].delete connection.object_id - - finish connection - - start connection - rescue Errno::ECONNREFUSED - e = Error.new "connection refused: #{connection.address}:#{connection.port}" - e.set_backtrace $@ - raise e - rescue Errno::EHOSTDOWN - e = Error.new "host down: #{connection.address}:#{connection.port}" - e.set_backtrace $@ - raise e - end - - ## - # Makes a request on +uri+. If +req+ is nil a Net::HTTP::Get is performed - # against +uri+. - # - # If a block is passed #request behaves like Net::HTTP#request (the body of - # the response will not have been read). - # - # +req+ must be a Net::HTTPRequest subclass (see Net::HTTP for a list). - # - # If there is an error and the request is idempotent according to RFC 2616 - # it will be retried automatically. - - def request uri, req = nil, &block - retried = false - bad_response = false - - req = request_setup req || uri - - connection = connection_for uri - connection_id = connection.object_id - - begin - Thread.current[@request_key][connection_id] += 1 - response = connection.request req, &block - - if connection_close?(req) or - (response.http_version <= '1.0' and - not connection_keep_alive?(response)) or - connection_close?(response) then - connection.finish - end - rescue Net::HTTPBadResponse => e - message = error_message connection - - finish connection - - raise Error, "too many bad responses #{message}" if - bad_response or not can_retry? req - - bad_response = true - retry - rescue *RETRIED_EXCEPTIONS => e # retried on ruby 2 - request_failed e, req, connection if - retried or not can_retry? req, @retried_on_ruby_2 - - reset connection - - retried = true - retry - rescue Errno::EINVAL, Errno::ETIMEDOUT => e # not retried on ruby 2 - request_failed e, req, connection if retried or not can_retry? req - - reset connection - - retried = true - retry - rescue Exception => e - finish connection - - raise - ensure - Thread.current[@timeout_key][connection_id] = Time.now - end - - @http_versions["#{uri.host}:#{uri.port}"] ||= response.http_version - - response - end - - ## - # Raises an Error for +exception+ which resulted from attempting the request - # +req+ on the +connection+. - # - # Finishes the +connection+. - - def request_failed exception, req, connection # :nodoc: - due_to = "(due to #{exception.message} - #{exception.class})" - message = "too many connection resets #{due_to} #{error_message connection}" - - finish connection - - - raise Error, message, exception.backtrace - end - - ## - # Creates a GET request if +req_or_uri+ is a URI and adds headers to the - # request. - # - # Returns the request. - - def request_setup req_or_uri # :nodoc: - req = if URI === req_or_uri then - Net::HTTP::Get.new req_or_uri.request_uri - else - req_or_uri - end - - @headers.each do |pair| - req.add_field(*pair) - end - - @override_headers.each do |name, value| - req[name] = value - end - - unless req['Connection'] then - req.add_field 'Connection', 'keep-alive' - req.add_field 'Keep-Alive', @keep_alive - end - - req - end - - ## - # Shuts down all connections for +thread+. - # - # Uses the current thread by default. - # - # If you've used Bundler::Persistent::Net::HTTP::Persistent across multiple threads you should - # call this in each thread when you're done making HTTP requests. - # - # *NOTE*: Calling shutdown for another thread can be dangerous! - # - # If the thread is still using the connection it may cause an error! It is - # best to call #shutdown in the thread at the appropriate time instead! - - def shutdown thread = Thread.current - generation = reconnect - cleanup generation, thread, @generation_key - - ssl_generation = reconnect_ssl - cleanup ssl_generation, thread, @ssl_generation_key - - thread[@request_key] = nil - thread[@timeout_key] = nil - end - - ## - # Shuts down all connections in all threads - # - # *NOTE*: THIS METHOD IS VERY DANGEROUS! - # - # Do not call this method if other threads are still using their - # connections! Call #shutdown at the appropriate time instead! - # - # Use this method only as a last resort! - - def shutdown_in_all_threads - Thread.list.each do |thread| - shutdown thread - end - - nil - end - - ## - # Enables SSL on +connection+ - - def ssl connection - connection.use_ssl = true - - connection.ssl_version = @ssl_version if @ssl_version - - connection.verify_mode = @verify_mode - - if OpenSSL::SSL::VERIFY_PEER == OpenSSL::SSL::VERIFY_NONE and - not Object.const_defined?(:I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG) then - warn <<-WARNING - !!!SECURITY WARNING!!! - -The SSL HTTP connection to: - - #{connection.address}:#{connection.port} - - !!!MAY NOT BE VERIFIED!!! - -On your platform your OpenSSL implementation is broken. - -There is no difference between the values of VERIFY_NONE and VERIFY_PEER. - -This means that attempting to verify the security of SSL connections may not -work. This exposes you to man-in-the-middle exploits, snooping on the -contents of your connection and other dangers to the security of your data. - -To disable this warning define the following constant at top-level in your -application: - - I_KNOW_THAT_OPENSSL_VERIFY_PEER_EQUALS_VERIFY_NONE_IS_WRONG = nil - - WARNING - end - - if @ca_file then - connection.ca_file = @ca_file - connection.verify_mode = OpenSSL::SSL::VERIFY_PEER - connection.verify_callback = @verify_callback if @verify_callback - end - - if @certificate and @private_key then - connection.cert = @certificate - connection.key = @private_key - end - - connection.cert_store = if @cert_store then - @cert_store - else - store = OpenSSL::X509::Store.new - store.set_default_paths - store - end - end - - ## - # Finishes all connections that existed before the given SSL parameter - # +generation+. - - def ssl_cleanup generation # :nodoc: - cleanup generation, Thread.current, @ssl_generation_key - end - - ## - # SSL version to use - - def ssl_version= ssl_version - @ssl_version = ssl_version - - reconnect_ssl - end if RUBY_VERSION > '1.9' - - ## - # Sets the HTTPS verify mode. Defaults to OpenSSL::SSL::VERIFY_PEER. - # - # Setting this to VERIFY_NONE is a VERY BAD IDEA and should NEVER be used. - # Securely transfer the correct certificate and update the default - # certificate store or set the ca file instead. - - def verify_mode= verify_mode - @verify_mode = verify_mode - - reconnect_ssl - end - - ## - # SSL verification callback. - - def verify_callback= callback - @verify_callback = callback - - reconnect_ssl - end - -end - -require 'bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse' - diff --git a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb b/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb deleted file mode 100644 index 1b6b789f6d..0000000000 --- a/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/ssl_reuse.rb +++ /dev/null @@ -1,129 +0,0 @@ -## -# This Net::HTTP subclass adds SSL session reuse and Server Name Indication -# (SNI) RFC 3546. -# -# DO NOT DEPEND UPON THIS CLASS -# -# This class is an implementation detail and is subject to change or removal -# at any time. - -class Bundler::Persistent::Net::HTTP::Persistent::SSLReuse < Net::HTTP - - @is_proxy_class = false - @proxy_addr = nil - @proxy_port = nil - @proxy_user = nil - @proxy_pass = nil - - def initialize address, port = nil # :nodoc: - super - - @ssl_session = nil - end - - ## - # From ruby trunk r33086 including http://redmine.ruby-lang.org/issues/5341 - - def connect # :nodoc: - D "opening connection to #{conn_address()}..." - s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) } - D "opened" - if use_ssl? - ssl_parameters = Hash.new - iv_list = instance_variables - SSL_ATTRIBUTES.each do |name| - ivname = "@#{name}".intern - if iv_list.include?(ivname) and - value = instance_variable_get(ivname) - ssl_parameters[name] = value - end - end - unless @ssl_context then - @ssl_context = OpenSSL::SSL::SSLContext.new - @ssl_context.set_params(ssl_parameters) - end - s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) - s.sync_close = true - end - @socket = Net::BufferedIO.new(s) - @socket.read_timeout = @read_timeout - @socket.continue_timeout = @continue_timeout if - @socket.respond_to? :continue_timeout - @socket.debug_output = @debug_output - if use_ssl? - begin - if proxy? - @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', - @address, @port, HTTPVersion) - @socket.writeline "Host: #{@address}:#{@port}" - if proxy_user - credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') - credential.delete!("\r\n") - @socket.writeline "Proxy-Authorization: Basic #{credential}" - end - @socket.writeline '' - Net::HTTPResponse.read_new(@socket).value - end - s.session = @ssl_session if @ssl_session - # Server Name Indication (SNI) RFC 3546 - s.hostname = @address if s.respond_to? :hostname= - timeout(@open_timeout) { s.connect } - if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE - s.post_connection_check(@address) - end - @ssl_session = s.session - rescue => exception - D "Conn close because of connect error #{exception}" - @socket.close if @socket and not @socket.closed? - raise exception - end - end - on_connect - end if RUBY_VERSION > '1.9' - - ## - # From ruby_1_8_7 branch r29865 including a modified - # http://redmine.ruby-lang.org/issues/5341 - - def connect # :nodoc: - D "opening connection to #{conn_address()}..." - s = timeout(@open_timeout) { TCPSocket.open(conn_address(), conn_port()) } - D "opened" - if use_ssl? - unless @ssl_context.verify_mode - warn "warning: peer certificate won't be verified in this SSL session" - @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE - end - s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) - s.sync_close = true - end - @socket = Net::BufferedIO.new(s) - @socket.read_timeout = @read_timeout - @socket.debug_output = @debug_output - if use_ssl? - if proxy? - @socket.writeline sprintf('CONNECT %s:%s HTTP/%s', - @address, @port, HTTPVersion) - @socket.writeline "Host: #{@address}:#{@port}" - if proxy_user - credential = ["#{proxy_user}:#{proxy_pass}"].pack('m') - credential.delete!("\r\n") - @socket.writeline "Proxy-Authorization: Basic #{credential}" - end - @socket.writeline '' - Net::HTTPResponse.read_new(@socket).value - end - s.session = @ssl_session if @ssl_session - s.connect - if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE - s.post_connection_check(@address) - end - @ssl_session = s.session - end - on_connect - end if RUBY_VERSION < '1.9' - - private :connect - -end - diff --git a/lib/bundler/vendor/thor/lib/thor.rb b/lib/bundler/vendor/thor/lib/thor.rb deleted file mode 100644 index 999e8b7e61..0000000000 --- a/lib/bundler/vendor/thor/lib/thor.rb +++ /dev/null @@ -1,509 +0,0 @@ -require "set" -require "bundler/vendor/thor/lib/thor/base" - -class Bundler::Thor - class << self - # Allows for custom "Command" package naming. - # - # === Parameters - # name<String> - # options<Hash> - # - def package_name(name, _ = {}) - @package_name = name.nil? || name == "" ? nil : name - end - - # Sets the default command when thor is executed without an explicit command to be called. - # - # ==== Parameters - # meth<Symbol>:: name of the default command - # - def default_command(meth = nil) - if meth - @default_command = meth == :none ? "help" : meth.to_s - else - @default_command ||= from_superclass(:default_command, "help") - end - end - alias_method :default_task, :default_command - - # Registers another Bundler::Thor subclass as a command. - # - # ==== Parameters - # klass<Class>:: Bundler::Thor subclass to register - # command<String>:: Subcommand name to use - # usage<String>:: Short usage for the subcommand - # description<String>:: Description for the subcommand - def register(klass, subcommand_name, usage, description, options = {}) - if klass <= Bundler::Thor::Group - desc usage, description, options - define_method(subcommand_name) { |*args| invoke(klass, args) } - else - desc usage, description, options - subcommand subcommand_name, klass - end - end - - # Defines the usage and the description of the next command. - # - # ==== Parameters - # usage<String> - # description<String> - # options<String> - # - def desc(usage, description, options = {}) - if options[:for] - command = find_and_refresh_command(options[:for]) - command.usage = usage if usage - command.description = description if description - else - @usage = usage - @desc = description - @hide = options[:hide] || false - end - end - - # Defines the long description of the next command. - # - # ==== Parameters - # long description<String> - # - def long_desc(long_description, options = {}) - if options[:for] - command = find_and_refresh_command(options[:for]) - command.long_description = long_description if long_description - else - @long_desc = long_description - end - end - - # Maps an input to a command. If you define: - # - # map "-T" => "list" - # - # Running: - # - # thor -T - # - # Will invoke the list command. - # - # ==== Parameters - # Hash[String|Array => Symbol]:: Maps the string or the strings in the array to the given command. - # - def map(mappings = nil) - @map ||= from_superclass(:map, {}) - - if mappings - mappings.each do |key, value| - if key.respond_to?(:each) - key.each { |subkey| @map[subkey] = value } - else - @map[key] = value - end - end - end - - @map - end - - # Declares the options for the next command to be declared. - # - # ==== Parameters - # Hash[Symbol => Object]:: The hash key is the name of the option and the value - # is the type of the option. Can be :string, :array, :hash, :boolean, :numeric - # or :required (string). If you give a value, the type of the value is used. - # - def method_options(options = nil) - @method_options ||= {} - build_options(options, @method_options) if options - @method_options - end - - alias_method :options, :method_options - - # Adds an option to the set of method options. If :for is given as option, - # it allows you to change the options from a previous defined command. - # - # def previous_command - # # magic - # end - # - # method_option :foo => :bar, :for => :previous_command - # - # def next_command - # # magic - # end - # - # ==== Parameters - # name<Symbol>:: The name of the argument. - # options<Hash>:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :default - Default value for this argument. It cannot be required and have default values. - # :aliases - Aliases for this option. - # :type - The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :banner - String to show on usage notes. - # :hide - If you want to hide this option from the help. - # - def method_option(name, options = {}) - scope = if options[:for] - find_and_refresh_command(options[:for]).options - else - method_options - end - - build_option(name, options, scope) - end - alias_method :option, :method_option - - # Prints help information for the given command. - # - # ==== Parameters - # shell<Bundler::Thor::Shell> - # command_name<String> - # - def command_help(shell, command_name) - meth = normalize_command_name(command_name) - command = all_commands[meth] - handle_no_command_error(meth) unless command - - shell.say "Usage:" - shell.say " #{banner(command)}" - shell.say - class_options_help(shell, nil => command.options.values) - if command.long_description - shell.say "Description:" - shell.print_wrapped(command.long_description, :indent => 2) - else - shell.say command.description - end - end - alias_method :task_help, :command_help - - # Prints help information for this class. - # - # ==== Parameters - # shell<Bundler::Thor::Shell> - # - def help(shell, subcommand = false) - list = printable_commands(true, subcommand) - Bundler::Thor::Util.thor_classes_in(self).each do |klass| - list += klass.printable_commands(false) - end - list.sort! { |a, b| a[0] <=> b[0] } - - if defined?(@package_name) && @package_name - shell.say "#{@package_name} commands:" - else - shell.say "Commands:" - end - - shell.print_table(list, :indent => 2, :truncate => true) - shell.say - class_options_help(shell) - end - - # Returns commands ready to be printed. - def printable_commands(all = true, subcommand = false) - (all ? all_commands : commands).map do |_, command| - next if command.hidden? - item = [] - item << banner(command, false, subcommand) - item << (command.description ? "# #{command.description.gsub(/\s+/m, ' ')}" : "") - item - end.compact - end - alias_method :printable_tasks, :printable_commands - - def subcommands - @subcommands ||= from_superclass(:subcommands, []) - end - alias_method :subtasks, :subcommands - - def subcommand_classes - @subcommand_classes ||= {} - end - - def subcommand(subcommand, subcommand_class) - subcommands << subcommand.to_s - subcommand_class.subcommand_help subcommand - subcommand_classes[subcommand.to_s] = subcommand_class - - define_method(subcommand) do |*args| - args, opts = Bundler::Thor::Arguments.split(args) - invoke_args = [args, opts, {:invoked_via_subcommand => true, :class_options => options}] - invoke_args.unshift "help" if opts.delete("--help") || opts.delete("-h") - invoke subcommand_class, *invoke_args - end - subcommand_class.commands.each do |_meth, command| - command.ancestor_name = subcommand - end - end - alias_method :subtask, :subcommand - - # Extend check unknown options to accept a hash of conditions. - # - # === Parameters - # options<Hash>: A hash containing :only and/or :except keys - def check_unknown_options!(options = {}) - @check_unknown_options ||= {} - options.each do |key, value| - if value - @check_unknown_options[key] = Array(value) - else - @check_unknown_options.delete(key) - end - end - @check_unknown_options - end - - # Overwrite check_unknown_options? to take subcommands and options into account. - def check_unknown_options?(config) #:nodoc: - options = check_unknown_options - return false unless options - - command = config[:current_command] - return true unless command - - name = command.name - - if subcommands.include?(name) - false - elsif options[:except] - !options[:except].include?(name.to_sym) - elsif options[:only] - options[:only].include?(name.to_sym) - else - true - end - end - - # Stop parsing of options as soon as an unknown option or a regular - # argument is encountered. All remaining arguments are passed to the command. - # This is useful if you have a command that can receive arbitrary additional - # options, and where those additional options should not be handled by - # Bundler::Thor. - # - # ==== Example - # - # To better understand how this is useful, let's consider a command that calls - # an external command. A user may want to pass arbitrary options and - # arguments to that command. The command itself also accepts some options, - # which should be handled by Bundler::Thor. - # - # class_option "verbose", :type => :boolean - # stop_on_unknown_option! :exec - # check_unknown_options! :except => :exec - # - # desc "exec", "Run a shell command" - # def exec(*args) - # puts "diagnostic output" if options[:verbose] - # Kernel.exec(*args) - # end - # - # Here +exec+ can be called with +--verbose+ to get diagnostic output, - # e.g.: - # - # $ thor exec --verbose echo foo - # diagnostic output - # foo - # - # But if +--verbose+ is given after +echo+, it is passed to +echo+ instead: - # - # $ thor exec echo --verbose foo - # --verbose foo - # - # ==== Parameters - # Symbol ...:: A list of commands that should be affected. - def stop_on_unknown_option!(*command_names) - stop_on_unknown_option.merge(command_names) - end - - def stop_on_unknown_option?(command) #:nodoc: - command && stop_on_unknown_option.include?(command.name.to_sym) - end - - # Disable the check for required options for the given commands. - # This is useful if you have a command that does not need the required options - # to work, like help. - # - # ==== Parameters - # Symbol ...:: A list of commands that should be affected. - def disable_required_check!(*command_names) - disable_required_check.merge(command_names) - end - - def disable_required_check?(command) #:nodoc: - command && disable_required_check.include?(command.name.to_sym) - end - - protected - - def stop_on_unknown_option #:nodoc: - @stop_on_unknown_option ||= Set.new - end - - # help command has the required check disabled by default. - def disable_required_check #:nodoc: - @disable_required_check ||= Set.new([:help]) - end - - # The method responsible for dispatching given the args. - def dispatch(meth, given_args, given_opts, config) #:nodoc: # rubocop:disable MethodLength - meth ||= retrieve_command_name(given_args) - command = all_commands[normalize_command_name(meth)] - - if !command && config[:invoked_via_subcommand] - # We're a subcommand and our first argument didn't match any of our - # commands. So we put it back and call our default command. - given_args.unshift(meth) - command = all_commands[normalize_command_name(default_command)] - end - - if command - args, opts = Bundler::Thor::Options.split(given_args) - if stop_on_unknown_option?(command) && !args.empty? - # given_args starts with a non-option, so we treat everything as - # ordinary arguments - args.concat opts - opts.clear - end - else - args = given_args - opts = nil - command = dynamic_command_class.new(meth) - end - - opts = given_opts || opts || [] - config[:current_command] = command - config[:command_options] = command.options - - instance = new(args, opts, config) - yield instance if block_given? - args = instance.args - trailing = args[Range.new(arguments.size, -1)] - instance.invoke_command(command, trailing || []) - end - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Bundler::Thor::Runner. It receives - # the command that is going to be invoked and a boolean which indicates if - # the namespace should be displayed as arguments. - # - def banner(command, namespace = nil, subcommand = false) - "#{basename} #{command.formatted_usage(self, $thor_runner, subcommand)}" - end - - def baseclass #:nodoc: - Bundler::Thor - end - - def dynamic_command_class #:nodoc: - Bundler::Thor::DynamicCommand - end - - def create_command(meth) #:nodoc: - @usage ||= nil - @desc ||= nil - @long_desc ||= nil - @hide ||= nil - - if @usage && @desc - base_class = @hide ? Bundler::Thor::HiddenCommand : Bundler::Thor::Command - commands[meth] = base_class.new(meth, @desc, @long_desc, @usage, method_options) - @usage, @desc, @long_desc, @method_options, @hide = nil - true - elsif all_commands[meth] || meth == "method_missing" - true - else - puts "[WARNING] Attempted to create command #{meth.inspect} without usage or description. " \ - "Call desc if you want this method to be available as command or declare it inside a " \ - "no_commands{} block. Invoked from #{caller[1].inspect}." - false - end - end - alias_method :create_task, :create_command - - def initialize_added #:nodoc: - class_options.merge!(method_options) - @method_options = nil - end - - # Retrieve the command name from given args. - def retrieve_command_name(args) #:nodoc: - meth = args.first.to_s unless args.empty? - args.shift if meth && (map[meth] || meth !~ /^\-/) - end - alias_method :retrieve_task_name, :retrieve_command_name - - # receives a (possibly nil) command name and returns a name that is in - # the commands hash. In addition to normalizing aliases, this logic - # will determine if a shortened command is an unambiguous substring of - # a command or alias. - # - # +normalize_command_name+ also converts names like +animal-prison+ - # into +animal_prison+. - def normalize_command_name(meth) #:nodoc: - return default_command.to_s.tr("-", "_") unless meth - - possibilities = find_command_possibilities(meth) - raise AmbiguousTaskError, "Ambiguous command #{meth} matches [#{possibilities.join(', ')}]" if possibilities.size > 1 - - if possibilities.empty? - meth ||= default_command - elsif map[meth] - meth = map[meth] - else - meth = possibilities.first - end - - meth.to_s.tr("-", "_") # treat foo-bar as foo_bar - end - alias_method :normalize_task_name, :normalize_command_name - - # this is the logic that takes the command name passed in by the user - # and determines whether it is an unambiguous substrings of a command or - # alias name. - def find_command_possibilities(meth) - len = meth.to_s.length - possibilities = all_commands.merge(map).keys.select { |n| meth == n[0, len] }.sort - unique_possibilities = possibilities.map { |k| map[k] || k }.uniq - - if possibilities.include?(meth) - [meth] - elsif unique_possibilities.size == 1 - unique_possibilities - else - possibilities - end - end - alias_method :find_task_possibilities, :find_command_possibilities - - def subcommand_help(cmd) - desc "help [COMMAND]", "Describe subcommands or one specific subcommand" - class_eval " - def help(command = nil, subcommand = true); super; end -" - end - alias_method :subtask_help, :subcommand_help - end - - include Bundler::Thor::Base - - map HELP_MAPPINGS => :help - - desc "help [COMMAND]", "Describe available commands or one specific command" - def help(command = nil, subcommand = false) - if command - if self.class.subcommands.include? command - self.class.subcommand_classes[command].help(shell, true) - else - self.class.command_help(shell, command) - end - else - self.class.help(shell, subcommand) - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions.rb b/lib/bundler/vendor/thor/lib/thor/actions.rb deleted file mode 100644 index e6698572a9..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions.rb +++ /dev/null @@ -1,321 +0,0 @@ -require "uri" -require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" -require "bundler/vendor/thor/lib/thor/actions/create_file" -require "bundler/vendor/thor/lib/thor/actions/create_link" -require "bundler/vendor/thor/lib/thor/actions/directory" -require "bundler/vendor/thor/lib/thor/actions/empty_directory" -require "bundler/vendor/thor/lib/thor/actions/file_manipulation" -require "bundler/vendor/thor/lib/thor/actions/inject_into_file" - -class Bundler::Thor - module Actions - attr_accessor :behavior - - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # Hold source paths for one Bundler::Thor instance. source_paths_for_search is the - # method responsible to gather source_paths from this current class, - # inherited paths and the source root. - # - def source_paths - @_source_paths ||= [] - end - - # Stores and return the source root for this class - def source_root(path = nil) - @_source_root = path if path - @_source_root ||= nil - end - - # Returns the source paths in the following order: - # - # 1) This class source paths - # 2) Source root - # 3) Parents source paths - # - def source_paths_for_search - paths = [] - paths += source_paths - paths << source_root if source_root - paths += from_superclass(:source_paths, []) - paths - end - - # Add runtime options that help actions execution. - # - def add_runtime_options! - class_option :force, :type => :boolean, :aliases => "-f", :group => :runtime, - :desc => "Overwrite files that already exist" - - class_option :pretend, :type => :boolean, :aliases => "-p", :group => :runtime, - :desc => "Run but do not make any changes" - - class_option :quiet, :type => :boolean, :aliases => "-q", :group => :runtime, - :desc => "Suppress status output" - - class_option :skip, :type => :boolean, :aliases => "-s", :group => :runtime, - :desc => "Skip files that already exist" - end - end - - # Extends initializer to add more configuration options. - # - # ==== Configuration - # behavior<Symbol>:: The actions default behavior. Can be :invoke or :revoke. - # It also accepts :force, :skip and :pretend to set the behavior - # and the respective option. - # - # destination_root<String>:: The root directory needed for some actions. - # - def initialize(args = [], options = {}, config = {}) - self.behavior = case config[:behavior].to_s - when "force", "skip" - _cleanup_options_and_set(options, config[:behavior]) - :invoke - when "revoke" - :revoke - else - :invoke - end - - super - self.destination_root = config[:destination_root] - end - - # Wraps an action object and call it accordingly to the thor class behavior. - # - def action(instance) #:nodoc: - if behavior == :revoke - instance.revoke! - else - instance.invoke! - end - end - - # Returns the root for this thor class (also aliased as destination root). - # - def destination_root - @destination_stack.last - end - - # Sets the root for this thor class. Relatives path are added to the - # directory where the script was invoked and expanded. - # - def destination_root=(root) - @destination_stack ||= [] - @destination_stack[0] = File.expand_path(root || "") - end - - # Returns the given path relative to the absolute root (ie, root where - # the script started). - # - def relative_to_original_destination_root(path, remove_dot = true) - path = path.dup - if path.gsub!(@destination_stack[0], ".") - remove_dot ? (path[2..-1] || "") : path - else - path - end - end - - # Holds source paths in instance so they can be manipulated. - # - def source_paths - @source_paths ||= self.class.source_paths_for_search - end - - # Receives a file or directory and search for it in the source paths. - # - def find_in_source_paths(file) - possible_files = [file, file + TEMPLATE_EXTNAME] - relative_root = relative_to_original_destination_root(destination_root, false) - - source_paths.each do |source| - possible_files.each do |f| - source_file = File.expand_path(f, File.join(source, relative_root)) - return source_file if File.exist?(source_file) - end - end - - message = "Could not find #{file.inspect} in any of your source paths. ".dup - - unless self.class.source_root - message << "Please invoke #{self.class.name}.source_root(PATH) with the PATH containing your templates. " - end - - message << if source_paths.empty? - "Currently you have no source paths." - else - "Your current source paths are: \n#{source_paths.join("\n")}" - end - - raise Error, message - end - - # Do something in the root or on a provided subfolder. If a relative path - # is given it's referenced from the current root. The full path is yielded - # to the block you provide. The path is set back to the previous path when - # the method exits. - # - # ==== Parameters - # dir<String>:: the directory to move to. - # config<Hash>:: give :verbose => true to log and use padding. - # - def inside(dir = "", config = {}, &block) - verbose = config.fetch(:verbose, false) - pretend = options[:pretend] - - say_status :inside, dir, verbose - shell.padding += 1 if verbose - @destination_stack.push File.expand_path(dir, destination_root) - - # If the directory doesnt exist and we're not pretending - if !File.exist?(destination_root) && !pretend - require "fileutils" - FileUtils.mkdir_p(destination_root) - end - - if pretend - # In pretend mode, just yield down to the block - block.arity == 1 ? yield(destination_root) : yield - else - require "fileutils" - FileUtils.cd(destination_root) { block.arity == 1 ? yield(destination_root) : yield } - end - - @destination_stack.pop - shell.padding -= 1 if verbose - end - - # Goes to the root and execute the given block. - # - def in_root - inside(@destination_stack.first) { yield } - end - - # Loads an external file and execute it in the instance binding. - # - # ==== Parameters - # path<String>:: The path to the file to execute. Can be a web address or - # a relative path from the source root. - # - # ==== Examples - # - # apply "http://gist.github.com/103208" - # - # apply "recipes/jquery.rb" - # - def apply(path, config = {}) - verbose = config.fetch(:verbose, true) - is_uri = path =~ %r{^https?\://} - path = find_in_source_paths(path) unless is_uri - - say_status :apply, path, verbose - shell.padding += 1 if verbose - - contents = if is_uri - open(path, "Accept" => "application/x-thor-template", &:read) - else - open(path, &:read) - end - - instance_eval(contents, path) - shell.padding -= 1 if verbose - end - - # Executes a command returning the contents of the command. - # - # ==== Parameters - # command<String>:: the command to be executed. - # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. Specify :with - # to append an executable to command execution. - # - # ==== Example - # - # inside('vendor') do - # run('ln -s ~/edge rails') - # end - # - def run(command, config = {}) - return unless behavior == :invoke - - destination = relative_to_original_destination_root(destination_root, false) - desc = "#{command} from #{destination.inspect}" - - if config[:with] - desc = "#{File.basename(config[:with].to_s)} #{desc}" - command = "#{config[:with]} #{command}" - end - - say_status :run, desc, config.fetch(:verbose, true) - - unless options[:pretend] - config[:capture] ? `#{command}` : system(command.to_s) - end - end - - # Executes a ruby script (taking into account WIN32 platform quirks). - # - # ==== Parameters - # command<String>:: the command to be executed. - # config<Hash>:: give :verbose => false to not log the status. - # - def run_ruby_script(command, config = {}) - return unless behavior == :invoke - run command, config.merge(:with => Bundler::Thor::Util.ruby_command) - end - - # Run a thor command. A hash of options can be given and it's converted to - # switches. - # - # ==== Parameters - # command<String>:: the command to be invoked - # args<Array>:: arguments to the command - # config<Hash>:: give :verbose => false to not log the status, :capture => true to hide to output. - # Other options are given as parameter to Bundler::Thor. - # - # - # ==== Examples - # - # thor :install, "http://gist.github.com/103208" - # #=> thor install http://gist.github.com/103208 - # - # thor :list, :all => true, :substring => 'rails' - # #=> thor list --all --substring=rails - # - def thor(command, *args) - config = args.last.is_a?(Hash) ? args.pop : {} - verbose = config.key?(:verbose) ? config.delete(:verbose) : true - pretend = config.key?(:pretend) ? config.delete(:pretend) : false - capture = config.key?(:capture) ? config.delete(:capture) : false - - args.unshift(command) - args.push Bundler::Thor::Options.to_switches(config) - command = args.join(" ").strip - - run command, :with => :thor, :verbose => verbose, :pretend => pretend, :capture => capture - end - - protected - - # Allow current root to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:destination_root => destination_root) - end - - def _cleanup_options_and_set(options, key) #:nodoc: - case options - when Array - %w(--force -f --skip -s).each { |i| options.delete(i) } - options << "--#{key}" - when Hash - [:force, :skip, "force", "skip"].each { |i| options.delete(i) } - options.merge!(key => true) - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb deleted file mode 100644 index 97d22d9bbd..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/create_file.rb +++ /dev/null @@ -1,104 +0,0 @@ -require "bundler/vendor/thor/lib/thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # Create a new file relative to the destination root with the given data, - # which is the return value of a block or a data string. - # - # ==== Parameters - # destination<String>:: the relative path to the destination root. - # data<String|NilClass>:: the data to append to the file. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # create_file "lib/fun_party.rb" do - # hostname = ask("What is the virtual hostname I should use?") - # "vhost.name = #{hostname}" - # end - # - # create_file "config/apache.conf", "your apache config" - # - def create_file(destination, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - data = args.first - action CreateFile.new(self, destination, block || data.to_s, config) - end - alias_method :add_file, :create_file - - # CreateFile is a subset of Template, which instead of rendering a file with - # ERB, it gets the content from the user. - # - class CreateFile < EmptyDirectory #:nodoc: - attr_reader :data - - def initialize(base, destination, data, config = {}) - @data = data - super(base, destination, config) - end - - # Checks if the content of the file at the destination is identical to the rendered result. - # - # ==== Returns - # Boolean:: true if it is identical, false otherwise. - # - def identical? - exists? && File.binread(destination) == render - end - - # Holds the content to be added to the file. - # - def render - @render ||= if data.is_a?(Proc) - data.call - else - data - end - end - - def invoke! - invoke_with_conflict_check do - require "fileutils" - FileUtils.mkdir_p(File.dirname(destination)) - File.open(destination, "wb") { |f| f.write render } - end - given_destination - end - - protected - - # Now on conflict we check if the file is identical or not. - # - def on_conflict_behavior(&block) - if identical? - say_status :identical, :blue - else - options = base.options.merge(config) - force_or_skip_or_conflict(options[:force], options[:skip], &block) - end - end - - # If force is true, run the action, otherwise check if it's not being - # skipped. If both are false, show the file_collision menu, if the menu - # returns true, force it, otherwise skip. - # - def force_or_skip_or_conflict(force, skip, &block) - if force - say_status :force, :yellow - yield unless pretend? - elsif skip - say_status :skip, :yellow - else - say_status :conflict, :red - force_or_skip_or_conflict(force_on_collision?, true, &block) - end - end - - # Shows the file collision menu to the user and gets the result. - # - def force_on_collision? - base.shell.file_collision(destination) { render } - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb b/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb deleted file mode 100644 index 3a664401b4..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/create_link.rb +++ /dev/null @@ -1,60 +0,0 @@ -require "bundler/vendor/thor/lib/thor/actions/create_file" - -class Bundler::Thor - module Actions - # Create a new file relative to the destination root from the given source. - # - # ==== Parameters - # destination<String>:: the relative path to the destination root. - # source<String|NilClass>:: the relative path to the source root. - # config<Hash>:: give :verbose => false to not log the status. - # :: give :symbolic => false for hard link. - # - # ==== Examples - # - # create_link "config/apache.conf", "/etc/apache.conf" - # - def create_link(destination, *args) - config = args.last.is_a?(Hash) ? args.pop : {} - source = args.first - action CreateLink.new(self, destination, source, config) - end - alias_method :add_link, :create_link - - # CreateLink is a subset of CreateFile, which instead of taking a block of - # data, just takes a source string from the user. - # - class CreateLink < CreateFile #:nodoc: - attr_reader :data - - # Checks if the content of the file at the destination is identical to the rendered result. - # - # ==== Returns - # Boolean:: true if it is identical, false otherwise. - # - def identical? - exists? && File.identical?(render, destination) - end - - def invoke! - invoke_with_conflict_check do - require "fileutils" - FileUtils.mkdir_p(File.dirname(destination)) - # Create a symlink by default - config[:symbolic] = true if config[:symbolic].nil? - File.unlink(destination) if exists? - if config[:symbolic] - File.symlink(render, destination) - else - File.link(render, destination) - end - end - given_destination - end - - def exists? - super || File.symlink?(destination) - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/directory.rb deleted file mode 100644 index f555f7b7e0..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/directory.rb +++ /dev/null @@ -1,118 +0,0 @@ -require "bundler/vendor/thor/lib/thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # Copies recursively the files from source directory to root directory. - # If any of the files finishes with .tt, it's considered to be a template - # and is placed in the destination without the extension .tt. If any - # empty directory is found, it's copied and all .empty_directory files are - # ignored. If any file name is wrapped within % signs, the text within - # the % signs will be executed as a method and replaced with the returned - # value. Let's suppose a doc directory with the following files: - # - # doc/ - # components/.empty_directory - # README - # rdoc.rb.tt - # %app_name%.rb - # - # When invoked as: - # - # directory "doc" - # - # It will create a doc directory in the destination with the following - # files (assuming that the `app_name` method returns the value "blog"): - # - # doc/ - # components/ - # README - # rdoc.rb - # blog.rb - # - # <b>Encoded path note:</b> Since Bundler::Thor internals use Object#respond_to? to check if it can - # expand %something%, this `something` should be a public method in the class calling - # #directory. If a method is private, Bundler::Thor stack raises PrivateMethodEncodedError. - # - # ==== Parameters - # source<String>:: the relative path to the source root. - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status. - # If :recursive => false, does not look for paths recursively. - # If :mode => :preserve, preserve the file mode from the source. - # If :exclude_pattern => /regexp/, prevents copying files that match that regexp. - # - # ==== Examples - # - # directory "doc" - # directory "doc", "docs", :recursive => false - # - def directory(source, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first || source - action Directory.new(self, source, destination || source, config, &block) - end - - class Directory < EmptyDirectory #:nodoc: - attr_reader :source - - def initialize(base, source, destination = nil, config = {}, &block) - @source = File.expand_path(base.find_in_source_paths(source.to_s)) - @block = block - super(base, destination, {:recursive => true}.merge(config)) - end - - def invoke! - base.empty_directory given_destination, config - execute! - end - - def revoke! - execute! - end - - protected - - def execute! - lookup = Util.escape_globs(source) - lookup = config[:recursive] ? File.join(lookup, "**") : lookup - lookup = file_level_lookup(lookup) - - files(lookup).sort.each do |file_source| - next if File.directory?(file_source) - next if config[:exclude_pattern] && file_source.match(config[:exclude_pattern]) - file_destination = File.join(given_destination, file_source.gsub(source, ".")) - file_destination.gsub!("/./", "/") - - case file_source - when /\.empty_directory$/ - dirname = File.dirname(file_destination).gsub(%r{/\.$}, "") - next if dirname == given_destination - base.empty_directory(dirname, config) - when /#{TEMPLATE_EXTNAME}$/ - base.template(file_source, file_destination[0..-4], config, &@block) - else - base.copy_file(file_source, file_destination, config, &@block) - end - end - end - - if RUBY_VERSION < "2.0" - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "{*,.[a-z]*}") - end - - def files(lookup) - Dir[lookup] - end - else - def file_level_lookup(previous_lookup) - File.join(previous_lookup, "*") - end - - def files(lookup) - Dir.glob(lookup, File::FNM_DOTMATCH) - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb b/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb deleted file mode 100644 index 284d92c19a..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/empty_directory.rb +++ /dev/null @@ -1,143 +0,0 @@ -class Bundler::Thor - module Actions - # Creates an empty directory. - # - # ==== Parameters - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # empty_directory "doc" - # - def empty_directory(destination, config = {}) - action EmptyDirectory.new(self, destination, config) - end - - # Class which holds create directory logic. This is the base class for - # other actions like create_file and directory. - # - # This implementation is based in Templater actions, created by Jonas Nicklas - # and Michael S. Klishin under MIT LICENSE. - # - class EmptyDirectory #:nodoc: - attr_reader :base, :destination, :given_destination, :relative_destination, :config - - # Initializes given the source and destination. - # - # ==== Parameters - # base<Bundler::Thor::Base>:: A Bundler::Thor::Base instance - # source<String>:: Relative path to the source of this file - # destination<String>:: Relative path to the destination of this file - # config<Hash>:: give :verbose => false to not log the status. - # - def initialize(base, destination, config = {}) - @base = base - @config = {:verbose => true}.merge(config) - self.destination = destination - end - - # Checks if the destination file already exists. - # - # ==== Returns - # Boolean:: true if the file exists, false otherwise. - # - def exists? - ::File.exist?(destination) - end - - def invoke! - invoke_with_conflict_check do - require "fileutils" - ::FileUtils.mkdir_p(destination) - end - end - - def revoke! - say_status :remove, :red - require "fileutils" - ::FileUtils.rm_rf(destination) if !pretend? && exists? - given_destination - end - - protected - - # Shortcut for pretend. - # - def pretend? - base.options[:pretend] - end - - # Sets the absolute destination value from a relative destination value. - # It also stores the given and relative destination. Let's suppose our - # script is being executed on "dest", it sets the destination root to - # "dest". The destination, given_destination and relative_destination - # are related in the following way: - # - # inside "bar" do - # empty_directory "baz" - # end - # - # destination #=> dest/bar/baz - # relative_destination #=> bar/baz - # given_destination #=> baz - # - def destination=(destination) - return unless destination - @given_destination = convert_encoded_instructions(destination.to_s) - @destination = ::File.expand_path(@given_destination, base.destination_root) - @relative_destination = base.relative_to_original_destination_root(@destination) - end - - # Filenames in the encoded form are converted. If you have a file: - # - # %file_name%.rb - # - # It calls #file_name from the base and replaces %-string with the - # return value (should be String) of #file_name: - # - # user.rb - # - # The method referenced can be either public or private. - # - def convert_encoded_instructions(filename) - filename.gsub(/%(.*?)%/) do |initial_string| - method = $1.strip - base.respond_to?(method, true) ? base.send(method) : initial_string - end - end - - # Receives a hash of options and just execute the block if some - # conditions are met. - # - def invoke_with_conflict_check(&block) - if exists? - on_conflict_behavior(&block) - else - yield unless pretend? - say_status :create, :green - end - - destination - rescue Errno::EISDIR, Errno::EEXIST - on_file_clash_behavior - end - - def on_file_clash_behavior - say_status :file_clash, :red - end - - # What to do when the destination file already exists. - # - def on_conflict_behavior - say_status :exist, :blue - end - - # Shortcut to say_status shell method. - # - def say_status(status, color) - base.shell.say_status status, relative_destination, color if config[:verbose] - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb b/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb deleted file mode 100644 index 4c83bebc86..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +++ /dev/null @@ -1,364 +0,0 @@ -require "erb" - -class Bundler::Thor - module Actions - # Copies the file from the relative source to the relative destination. If - # the destination is not given it's assumed to be equal to the source. - # - # ==== Parameters - # source<String>:: the relative path to the source root. - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status, and - # :mode => :preserve, to preserve the file mode from the source. - - # - # ==== Examples - # - # copy_file "README", "doc/README" - # - # copy_file "doc/README" - # - def copy_file(source, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first || source - source = File.expand_path(find_in_source_paths(source.to_s)) - - create_file destination, nil, config do - content = File.binread(source) - content = yield(content) if block - content - end - if config[:mode] == :preserve - mode = File.stat(source).mode - chmod(destination, mode, config) - end - end - - # Links the file from the relative source to the relative destination. If - # the destination is not given it's assumed to be equal to the source. - # - # ==== Parameters - # source<String>:: the relative path to the source root. - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # link_file "README", "doc/README" - # - # link_file "doc/README" - # - def link_file(source, *args) - config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first || source - source = File.expand_path(find_in_source_paths(source.to_s)) - - create_link destination, source, config - end - - # Gets the content at the given address and places it at the given relative - # destination. If a block is given instead of destination, the content of - # the url is yielded and used as location. - # - # ==== Parameters - # source<String>:: the address of the given content. - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # get "http://gist.github.com/103208", "doc/README" - # - # get "http://gist.github.com/103208" do |content| - # content.split("\n").first - # end - # - def get(source, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first - - if source =~ %r{^https?\://} - require "open-uri" - else - source = File.expand_path(find_in_source_paths(source.to_s)) - end - - render = open(source) { |input| input.binmode.read } - - destination ||= if block_given? - block.arity == 1 ? yield(render) : yield - else - File.basename(source) - end - - create_file destination, render, config - end - - # Gets an ERB template at the relative source, executes it and makes a copy - # at the relative destination. If the destination is not given it's assumed - # to be equal to the source removing .tt from the filename. - # - # ==== Parameters - # source<String>:: the relative path to the source root. - # destination<String>:: the relative path to the destination root. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # template "README", "doc/README" - # - # template "doc/README" - # - def template(source, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - destination = args.first || source.sub(/#{TEMPLATE_EXTNAME}$/, "") - - source = File.expand_path(find_in_source_paths(source.to_s)) - context = config.delete(:context) || instance_eval("binding") - - create_file destination, nil, config do - content = CapturableERB.new(::File.binread(source), nil, "-", "@output_buffer").tap do |erb| - erb.filename = source - end.result(context) - content = yield(content) if block - content - end - end - - # Changes the mode of the given file or directory. - # - # ==== Parameters - # mode<Integer>:: the file mode - # path<String>:: the name of the file to change mode - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # chmod "script/server", 0755 - # - def chmod(path, mode, config = {}) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - say_status :chmod, relative_to_original_destination_root(path), config.fetch(:verbose, true) - unless options[:pretend] - require "fileutils" - FileUtils.chmod_R(mode, path) - end - end - - # Prepend text to a file. Since it depends on insert_into_file, it's reversible. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # data<String>:: the data to prepend to the file, can be also given as a block. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # prepend_to_file 'config/environments/test.rb', 'config.gem "rspec"' - # - # prepend_to_file 'config/environments/test.rb' do - # 'config.gem "rspec"' - # end - # - def prepend_to_file(path, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config[:after] = /\A/ - insert_into_file(path, *(args << config), &block) - end - alias_method :prepend_file, :prepend_to_file - - # Append text to a file. Since it depends on insert_into_file, it's reversible. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # data<String>:: the data to append to the file, can be also given as a block. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # append_to_file 'config/environments/test.rb', 'config.gem "rspec"' - # - # append_to_file 'config/environments/test.rb' do - # 'config.gem "rspec"' - # end - # - def append_to_file(path, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config[:before] = /\z/ - insert_into_file(path, *(args << config), &block) - end - alias_method :append_file, :append_to_file - - # Injects text right after the class definition. Since it depends on - # insert_into_file, it's reversible. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # klass<String|Class>:: the class to be manipulated - # data<String>:: the data to append to the class, can be also given as a block. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # inject_into_class "app/controllers/application_controller.rb", ApplicationController, " filter_parameter :password\n" - # - # inject_into_class "app/controllers/application_controller.rb", ApplicationController do - # " filter_parameter :password\n" - # end - # - def inject_into_class(path, klass, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config[:after] = /class #{klass}\n|class #{klass} .*\n/ - insert_into_file(path, *(args << config), &block) - end - - # Injects text right after the module definition. Since it depends on - # insert_into_file, it's reversible. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # module_name<String|Class>:: the module to be manipulated - # data<String>:: the data to append to the class, can be also given as a block. - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Examples - # - # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper, " def help; 'help'; end\n" - # - # inject_into_module "app/helpers/application_helper.rb", ApplicationHelper do - # " def help; 'help'; end\n" - # end - # - def inject_into_module(path, module_name, *args, &block) - config = args.last.is_a?(Hash) ? args.pop : {} - config[:after] = /module #{module_name}\n|module #{module_name} .*\n/ - insert_into_file(path, *(args << config), &block) - end - - # Run a regular expression replacement on a file. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # flag<Regexp|String>:: the regexp or string to be replaced - # replacement<String>:: the replacement, can be also given as a block - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # gsub_file 'app/controllers/application_controller.rb', /#\s*(filter_parameter_logging :password)/, '\1' - # - # gsub_file 'README', /rake/, :green do |match| - # match << " no more. Use thor!" - # end - # - def gsub_file(path, flag, *args, &block) - return unless behavior == :invoke - config = args.last.is_a?(Hash) ? args.pop : {} - - path = File.expand_path(path, destination_root) - say_status :gsub, relative_to_original_destination_root(path), config.fetch(:verbose, true) - - unless options[:pretend] - content = File.binread(path) - content.gsub!(flag, *args, &block) - File.open(path, "wb") { |file| file.write(content) } - end - end - - # Uncomment all lines matching a given regex. It will leave the space - # which existed before the comment hash in tact but will remove any spacing - # between the comment hash and the beginning of the line. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # flag<Regexp|String>:: the regexp or string used to decide which lines to uncomment - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # uncomment_lines 'config/initializers/session_store.rb', /active_record/ - # - def uncomment_lines(path, flag, *args) - flag = flag.respond_to?(:source) ? flag.source : flag - - gsub_file(path, /^(\s*)#[[:blank:]]*(.*#{flag})/, '\1\2', *args) - end - - # Comment all lines matching a given regex. It will leave the space - # which existed before the beginning of the line in tact and will insert - # a single space after the comment hash. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # flag<Regexp|String>:: the regexp or string used to decide which lines to comment - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # comment_lines 'config/initializers/session_store.rb', /cookie_store/ - # - def comment_lines(path, flag, *args) - flag = flag.respond_to?(:source) ? flag.source : flag - - gsub_file(path, /^(\s*)([^#|\n]*#{flag})/, '\1# \2', *args) - end - - # Removes a file at the given location. - # - # ==== Parameters - # path<String>:: path of the file to be changed - # config<Hash>:: give :verbose => false to not log the status. - # - # ==== Example - # - # remove_file 'README' - # remove_file 'app/controllers/application_controller.rb' - # - def remove_file(path, config = {}) - return unless behavior == :invoke - path = File.expand_path(path, destination_root) - - say_status :remove, relative_to_original_destination_root(path), config.fetch(:verbose, true) - if !options[:pretend] && File.exist?(path) - require "fileutils" - ::FileUtils.rm_rf(path) - end - end - alias_method :remove_dir, :remove_file - - attr_accessor :output_buffer - private :output_buffer, :output_buffer= - - private - - def concat(string) - @output_buffer.concat(string) - end - - def capture(*args) - with_output_buffer { yield(*args) } - end - - def with_output_buffer(buf = "".dup) #:nodoc: - raise ArgumentError, "Buffer can not be a frozen object" if buf.frozen? - old_buffer = output_buffer - self.output_buffer = buf - yield - output_buffer - ensure - self.output_buffer = old_buffer - end - - # Bundler::Thor::Actions#capture depends on what kind of buffer is used in ERB. - # Thus CapturableERB fixes ERB to use String buffer. - class CapturableERB < ERB - def set_eoutvar(compiler, eoutvar = "_erbout") - compiler.put_cmd = "#{eoutvar}.concat" - compiler.insert_cmd = "#{eoutvar}.concat" - compiler.pre_cmd = ["#{eoutvar} = ''.dup"] - compiler.post_cmd = [eoutvar] - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb b/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb deleted file mode 100644 index 349b26ff65..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +++ /dev/null @@ -1,109 +0,0 @@ -require "bundler/vendor/thor/lib/thor/actions/empty_directory" - -class Bundler::Thor - module Actions - # Injects the given content into a file. Different from gsub_file, this - # method is reversible. - # - # ==== Parameters - # destination<String>:: Relative path to the destination root - # data<String>:: Data to add to the file. Can be given as a block. - # config<Hash>:: give :verbose => false to not log the status and the flag - # for injection (:after or :before) or :force => true for - # insert two or more times the same content. - # - # ==== Examples - # - # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n" - # - # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do - # gems = ask "Which gems would you like to add?" - # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n") - # end - # - def insert_into_file(destination, *args, &block) - data = block_given? ? block : args.shift - config = args.shift - action InjectIntoFile.new(self, destination, data, config) - end - alias_method :inject_into_file, :insert_into_file - - class InjectIntoFile < EmptyDirectory #:nodoc: - attr_reader :replacement, :flag, :behavior - - def initialize(base, destination, data, config) - super(base, destination, {:verbose => true}.merge(config)) - - @behavior, @flag = if @config.key?(:after) - [:after, @config.delete(:after)] - else - [:before, @config.delete(:before)] - end - - @replacement = data.is_a?(Proc) ? data.call : data - @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp) - end - - def invoke! - say_status :invoke - - content = if @behavior == :after - '\0' + replacement - else - replacement + '\0' - end - - if exists? - replace!(/#{flag}/, content, config[:force]) - else - unless pretend? - raise Bundler::Thor::Error, "The file #{ destination } does not appear to exist" - end - end - end - - def revoke! - say_status :revoke - - regexp = if @behavior == :after - content = '\1\2' - /(#{flag})(.*)(#{Regexp.escape(replacement)})/m - else - content = '\2\3' - /(#{Regexp.escape(replacement)})(.*)(#{flag})/m - end - - replace!(regexp, content, true) - end - - protected - - def say_status(behavior) - status = if behavior == :invoke - if flag == /\A/ - :prepend - elsif flag == /\z/ - :append - else - :insert - end - else - :subtract - end - - super(status, config[:verbose]) - end - - # Adds the content to the file. - # - def replace!(regexp, string, force) - return if pretend? - content = File.read(destination) - if force || !content.include?(replacement) - content.gsub!(regexp, string) - File.open(destination, "wb") { |file| file.write(content) } - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb deleted file mode 100644 index 9bd1077170..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/base.rb +++ /dev/null @@ -1,679 +0,0 @@ -require "bundler/vendor/thor/lib/thor/command" -require "bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access" -require "bundler/vendor/thor/lib/thor/core_ext/ordered_hash" -require "bundler/vendor/thor/lib/thor/error" -require "bundler/vendor/thor/lib/thor/invocation" -require "bundler/vendor/thor/lib/thor/parser" -require "bundler/vendor/thor/lib/thor/shell" -require "bundler/vendor/thor/lib/thor/line_editor" -require "bundler/vendor/thor/lib/thor/util" - -class Bundler::Thor - autoload :Actions, "bundler/vendor/thor/lib/thor/actions" - autoload :RakeCompat, "bundler/vendor/thor/lib/thor/rake_compat" - autoload :Group, "bundler/vendor/thor/lib/thor/group" - - # Shortcuts for help. - HELP_MAPPINGS = %w(-h -? --help -D) - - # Bundler::Thor methods that should not be overwritten by the user. - THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root - action add_file create_file in_root inside run run_ruby_script) - - TEMPLATE_EXTNAME = ".tt" - - module Base - attr_accessor :options, :parent_options, :args - - # It receives arguments in an Array and two hashes, one for options and - # other for configuration. - # - # Notice that it does not check if all required arguments were supplied. - # It should be done by the parser. - # - # ==== Parameters - # args<Array[Object]>:: An array of objects. The objects are applied to their - # respective accessors declared with <tt>argument</tt>. - # - # options<Hash>:: An options hash that will be available as self.options. - # The hash given is converted to a hash with indifferent - # access, magic predicates (options.skip?) and then frozen. - # - # config<Hash>:: Configuration for this Bundler::Thor class. - # - def initialize(args = [], local_options = {}, config = {}) - parse_options = self.class.class_options - - # The start method splits inbound arguments at the first argument - # that looks like an option (starts with - or --). It then calls - # new, passing in the two halves of the arguments Array as the - # first two parameters. - - command_options = config.delete(:command_options) # hook for start - parse_options = parse_options.merge(command_options) if command_options - if local_options.is_a?(Array) - array_options = local_options - hash_options = {} - else - # Handle the case where the class was explicitly instantiated - # with pre-parsed options. - array_options = [] - hash_options = local_options - end - - # Let Bundler::Thor::Options parse the options first, so it can remove - # declared options from the array. This will leave us with - # a list of arguments that weren't declared. - stop_on_unknown = self.class.stop_on_unknown_option? config[:current_command] - disable_required_check = self.class.disable_required_check? config[:current_command] - opts = Bundler::Thor::Options.new(parse_options, hash_options, stop_on_unknown, disable_required_check) - self.options = opts.parse(array_options) - self.options = config[:class_options].merge(options) if config[:class_options] - - # If unknown options are disallowed, make sure that none of the - # remaining arguments looks like an option. - opts.check_unknown! if self.class.check_unknown_options?(config) - - # Add the remaining arguments from the options parser to the - # arguments passed in to initialize. Then remove any positional - # arguments declared using #argument (this is primarily used - # by Bundler::Thor::Group). Tis will leave us with the remaining - # positional arguments. - to_parse = args - to_parse += opts.remaining unless self.class.strict_args_position?(config) - - thor_args = Bundler::Thor::Arguments.new(self.class.arguments) - thor_args.parse(to_parse).each { |k, v| __send__("#{k}=", v) } - @args = thor_args.remaining - end - - class << self - def included(base) #:nodoc: - base.extend ClassMethods - base.send :include, Invocation - base.send :include, Shell - end - - # Returns the classes that inherits from Bundler::Thor or Bundler::Thor::Group. - # - # ==== Returns - # Array[Class] - # - def subclasses - @subclasses ||= [] - end - - # Returns the files where the subclasses are kept. - # - # ==== Returns - # Hash[path<String> => Class] - # - def subclass_files - @subclass_files ||= Hash.new { |h, k| h[k] = [] } - end - - # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the - # class and the file on Bundler::Thor::Base. This is the method responsable for it. - # - def register_klass_file(klass) #:nodoc: - file = caller[1].match(/(.*):\d+/)[1] - Bundler::Thor::Base.subclasses << klass unless Bundler::Thor::Base.subclasses.include?(klass) - - file_subclasses = Bundler::Thor::Base.subclass_files[File.expand_path(file)] - file_subclasses << klass unless file_subclasses.include?(klass) - end - end - - module ClassMethods - def attr_reader(*) #:nodoc: - no_commands { super } - end - - def attr_writer(*) #:nodoc: - no_commands { super } - end - - def attr_accessor(*) #:nodoc: - no_commands { super } - end - - # If you want to raise an error for unknown options, call check_unknown_options! - # This is disabled by default to allow dynamic invocations. - def check_unknown_options! - @check_unknown_options = true - end - - def check_unknown_options #:nodoc: - @check_unknown_options ||= from_superclass(:check_unknown_options, false) - end - - def check_unknown_options?(config) #:nodoc: - !!check_unknown_options - end - - # If you want to raise an error when the default value of an option does not match - # the type call check_default_type! - # This is disabled by default for compatibility. - def check_default_type! - @check_default_type = true - end - - def check_default_type #:nodoc: - @check_default_type ||= from_superclass(:check_default_type, false) - end - - def check_default_type? #:nodoc: - !!check_default_type - end - - # If true, option parsing is suspended as soon as an unknown option or a - # regular argument is encountered. All remaining arguments are passed to - # the command as regular arguments. - def stop_on_unknown_option?(command_name) #:nodoc: - false - end - - # If true, option set will not suspend the execution of the command when - # a required option is not provided. - def disable_required_check?(command_name) #:nodoc: - false - end - - # If you want only strict string args (useful when cascading thor classes), - # call strict_args_position! This is disabled by default to allow dynamic - # invocations. - def strict_args_position! - @strict_args_position = true - end - - def strict_args_position #:nodoc: - @strict_args_position ||= from_superclass(:strict_args_position, false) - end - - def strict_args_position?(config) #:nodoc: - !!strict_args_position - end - - # Adds an argument to the class and creates an attr_accessor for it. - # - # Arguments are different from options in several aspects. The first one - # is how they are parsed from the command line, arguments are retrieved - # from position: - # - # thor command NAME - # - # Instead of: - # - # thor command --name=NAME - # - # Besides, arguments are used inside your code as an accessor (self.argument), - # while options are all kept in a hash (self.options). - # - # Finally, arguments cannot have type :default or :boolean but can be - # optional (supplying :optional => :true or :required => false), although - # you cannot have a required argument after a non-required argument. If you - # try it, an error is raised. - # - # ==== Parameters - # name<Symbol>:: The name of the argument. - # options<Hash>:: Described below. - # - # ==== Options - # :desc - Description for the argument. - # :required - If the argument is required or not. - # :optional - If the argument is optional or not. - # :type - The type of the argument, can be :string, :hash, :array, :numeric. - # :default - Default value for this argument. It cannot be required and have default values. - # :banner - String to show on usage notes. - # - # ==== Errors - # ArgumentError:: Raised if you supply a required argument after a non required one. - # - def argument(name, options = {}) - is_thor_reserved_word?(name, :argument) - no_commands { attr_accessor name } - - required = if options.key?(:optional) - !options[:optional] - elsif options.key?(:required) - options[:required] - else - options[:default].nil? - end - - remove_argument name - - if required - arguments.each do |argument| - next if argument.required? - raise ArgumentError, "You cannot have #{name.to_s.inspect} as required argument after " \ - "the non-required argument #{argument.human_name.inspect}." - end - end - - options[:required] = required - - arguments << Bundler::Thor::Argument.new(name, options) - end - - # Returns this class arguments, looking up in the ancestors chain. - # - # ==== Returns - # Array[Bundler::Thor::Argument] - # - def arguments - @arguments ||= from_superclass(:arguments, []) - end - - # Adds a bunch of options to the set of class options. - # - # class_options :foo => false, :bar => :required, :baz => :string - # - # If you prefer more detailed declaration, check class_option. - # - # ==== Parameters - # Hash[Symbol => Object] - # - def class_options(options = nil) - @class_options ||= from_superclass(:class_options, {}) - build_options(options, @class_options) if options - @class_options - end - - # Adds an option to the set of class options - # - # ==== Parameters - # name<Symbol>:: The name of the argument. - # options<Hash>:: Described below. - # - # ==== Options - # :desc:: -- Description for the argument. - # :required:: -- If the argument is required or not. - # :default:: -- Default value for this argument. - # :group:: -- The group for this options. Use by class options to output options in different levels. - # :aliases:: -- Aliases for this option. <b>Note:</b> Bundler::Thor follows a convention of one-dash-one-letter options. Thus aliases like "-something" wouldn't be parsed; use either "\--something" or "-s" instead. - # :type:: -- The type of the argument, can be :string, :hash, :array, :numeric or :boolean. - # :banner:: -- String to show on usage notes. - # :hide:: -- If you want to hide this option from the help. - # - def class_option(name, options = {}) - build_option(name, options, class_options) - end - - # Removes a previous defined argument. If :undefine is given, undefine - # accessors as well. - # - # ==== Parameters - # names<Array>:: Arguments to be removed - # - # ==== Examples - # - # remove_argument :foo - # remove_argument :foo, :bar, :baz, :undefine => true - # - def remove_argument(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - arguments.delete_if { |a| a.name == name.to_s } - undef_method name, "#{name}=" if options[:undefine] - end - end - - # Removes a previous defined class option. - # - # ==== Parameters - # names<Array>:: Class options to be removed - # - # ==== Examples - # - # remove_class_option :foo - # remove_class_option :foo, :bar, :baz - # - def remove_class_option(*names) - names.each do |name| - class_options.delete(name) - end - end - - # Defines the group. This is used when thor list is invoked so you can specify - # that only commands from a pre-defined group will be shown. Defaults to standard. - # - # ==== Parameters - # name<String|Symbol> - # - def group(name = nil) - if name - @group = name.to_s - else - @group ||= from_superclass(:group, "standard") - end - end - - # Returns the commands for this Bundler::Thor class. - # - # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command - # objects as values. - # - def commands - @commands ||= Bundler::Thor::CoreExt::OrderedHash.new - end - alias_method :tasks, :commands - - # Returns the commands for this Bundler::Thor class and all subclasses. - # - # ==== Returns - # OrderedHash:: An ordered hash with commands names as keys and Bundler::Thor::Command - # objects as values. - # - def all_commands - @all_commands ||= from_superclass(:all_commands, Bundler::Thor::CoreExt::OrderedHash.new) - @all_commands.merge!(commands) - end - alias_method :all_tasks, :all_commands - - # Removes a given command from this Bundler::Thor class. This is usually done if you - # are inheriting from another class and don't want it to be available - # anymore. - # - # By default it only remove the mapping to the command. But you can supply - # :undefine => true to undefine the method from the class as well. - # - # ==== Parameters - # name<Symbol|String>:: The name of the command to be removed - # options<Hash>:: You can give :undefine => true if you want commands the method - # to be undefined from the class as well. - # - def remove_command(*names) - options = names.last.is_a?(Hash) ? names.pop : {} - - names.each do |name| - commands.delete(name.to_s) - all_commands.delete(name.to_s) - undef_method name if options[:undefine] - end - end - alias_method :remove_task, :remove_command - - # All methods defined inside the given block are not added as commands. - # - # So you can do: - # - # class MyScript < Bundler::Thor - # no_commands do - # def this_is_not_a_command - # end - # end - # end - # - # You can also add the method and remove it from the command list: - # - # class MyScript < Bundler::Thor - # def this_is_not_a_command - # end - # remove_command :this_is_not_a_command - # end - # - def no_commands - @no_commands = true - yield - ensure - @no_commands = false - end - alias_method :no_tasks, :no_commands - - # Sets the namespace for the Bundler::Thor or Bundler::Thor::Group class. By default the - # namespace is retrieved from the class name. If your Bundler::Thor class is named - # Scripts::MyScript, the help method, for example, will be called as: - # - # thor scripts:my_script -h - # - # If you change the namespace: - # - # namespace :my_scripts - # - # You change how your commands are invoked: - # - # thor my_scripts -h - # - # Finally, if you change your namespace to default: - # - # namespace :default - # - # Your commands can be invoked with a shortcut. Instead of: - # - # thor :my_command - # - def namespace(name = nil) - if name - @namespace = name.to_s - else - @namespace ||= Bundler::Thor::Util.namespace_from_thor_class(self) - end - end - - # Parses the command and options from the given args, instantiate the class - # and invoke the command. This method is used when the arguments must be parsed - # from an array. If you are inside Ruby and want to use a Bundler::Thor class, you - # can simply initialize it: - # - # script = MyScript.new(args, options, config) - # script.invoke(:command, first_arg, second_arg, third_arg) - # - def start(given_args = ARGV, config = {}) - config[:shell] ||= Bundler::Thor::Base.shell.new - dispatch(nil, given_args.dup, nil, config) - rescue Bundler::Thor::Error => e - config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) - exit(1) if exit_on_failure? - rescue Errno::EPIPE - # This happens if a thor command is piped to something like `head`, - # which closes the pipe when it's done reading. This will also - # mean that if the pipe is closed, further unnecessary - # computation will not occur. - exit(0) - end - - # Allows to use private methods from parent in child classes as commands. - # - # ==== Parameters - # names<Array>:: Method names to be used as commands - # - # ==== Examples - # - # public_command :foo - # public_command :foo, :bar, :baz - # - def public_command(*names) - names.each do |name| - class_eval "def #{name}(*); super end" - end - end - alias_method :public_task, :public_command - - def handle_no_command_error(command, has_namespace = $thor_runner) #:nodoc: - raise UndefinedCommandError, "Could not find command #{command.inspect} in #{namespace.inspect} namespace." if has_namespace - raise UndefinedCommandError, "Could not find command #{command.inspect}." - end - alias_method :handle_no_task_error, :handle_no_command_error - - def handle_argument_error(command, error, args, arity) #:nodoc: - name = [command.ancestor_name, command.name].compact.join(" ") - msg = "ERROR: \"#{basename} #{name}\" was called with ".dup - msg << "no arguments" if args.empty? - msg << "arguments " << args.inspect unless args.empty? - msg << "\nUsage: #{banner(command).inspect}" - raise InvocationError, msg - end - - protected - - # Prints the class options per group. If an option does not belong to - # any group, it's printed as Class option. - # - def class_options_help(shell, groups = {}) #:nodoc: - # Group options by group - class_options.each do |_, value| - groups[value.group] ||= [] - groups[value.group] << value - end - - # Deal with default group - global_options = groups.delete(nil) || [] - print_options(shell, global_options) - - # Print all others - groups.each do |group_name, options| - print_options(shell, options, group_name) - end - end - - # Receives a set of options and print them. - def print_options(shell, options, group_name = nil) - return if options.empty? - - list = [] - padding = options.map { |o| o.aliases.size }.max.to_i * 4 - - options.each do |option| - next if option.hide - item = [option.usage(padding)] - item.push(option.description ? "# #{option.description}" : "") - - list << item - list << ["", "# Default: #{option.default}"] if option.show_default? - list << ["", "# Possible values: #{option.enum.join(', ')}"] if option.enum - end - - shell.say(group_name ? "#{group_name} options:" : "Options:") - shell.print_table(list, :indent => 2) - shell.say "" - end - - # Raises an error if the word given is a Bundler::Thor reserved word. - def is_thor_reserved_word?(word, type) #:nodoc: - return false unless THOR_RESERVED_WORDS.include?(word.to_s) - raise "#{word.inspect} is a Bundler::Thor reserved word and cannot be defined as #{type}" - end - - # Build an option and adds it to the given scope. - # - # ==== Parameters - # name<Symbol>:: The name of the argument. - # options<Hash>:: Described in both class_option and method_option. - # scope<Hash>:: Options hash that is being built up - def build_option(name, options, scope) #:nodoc: - scope[name] = Bundler::Thor::Option.new(name, options.merge(:check_default_type => check_default_type?)) - end - - # Receives a hash of options, parse them and add to the scope. This is a - # fast way to set a bunch of options: - # - # build_options :foo => true, :bar => :required, :baz => :string - # - # ==== Parameters - # Hash[Symbol => Object] - def build_options(options, scope) #:nodoc: - options.each do |key, value| - scope[key] = Bundler::Thor::Option.parse(key, value) - end - end - - # Finds a command with the given name. If the command belongs to the current - # class, just return it, otherwise dup it and add the fresh copy to the - # current command hash. - def find_and_refresh_command(name) #:nodoc: - if commands[name.to_s] - commands[name.to_s] - elsif command = all_commands[name.to_s] # rubocop:disable AssignmentInCondition - commands[name.to_s] = command.clone - else - raise ArgumentError, "You supplied :for => #{name.inspect}, but the command #{name.inspect} could not be found." - end - end - alias_method :find_and_refresh_task, :find_and_refresh_command - - # Everytime someone inherits from a Bundler::Thor class, register the klass - # and file into baseclass. - def inherited(klass) - Bundler::Thor::Base.register_klass_file(klass) - klass.instance_variable_set(:@no_commands, false) - end - - # Fire this callback whenever a method is added. Added methods are - # tracked as commands by invoking the create_command method. - def method_added(meth) - meth = meth.to_s - - if meth == "initialize" - initialize_added - return - end - - # Return if it's not a public instance method - return unless public_method_defined?(meth.to_sym) - - @no_commands ||= false - return if @no_commands || !create_command(meth) - - is_thor_reserved_word?(meth, :command) - Bundler::Thor::Base.register_klass_file(self) - end - - # Retrieves a value from superclass. If it reaches the baseclass, - # returns default. - def from_superclass(method, default = nil) - if self == baseclass || !superclass.respond_to?(method, true) - default - else - value = superclass.send(method) - - # Ruby implements `dup` on Object, but raises a `TypeError` - # if the method is called on immediates. As a result, we - # don't have a good way to check whether dup will succeed - # without calling it and rescuing the TypeError. - begin - value.dup - rescue TypeError - value - end - - end - end - - # A flag that makes the process exit with status 1 if any error happens. - def exit_on_failure? - false - end - - # - # The basename of the program invoking the thor class. - # - def basename - File.basename($PROGRAM_NAME).split(" ").first - end - - # SIGNATURE: Sets the baseclass. This is where the superclass lookup - # finishes. - def baseclass #:nodoc: - end - - # SIGNATURE: Creates a new command if valid_command? is true. This method is - # called when a new method is added to the class. - def create_command(meth) #:nodoc: - end - alias_method :create_task, :create_command - - # SIGNATURE: Defines behavior when the initialize method is added to the - # class. - def initialize_added #:nodoc: - end - - # SIGNATURE: The hook invoked by start. - def dispatch(command, given_args, given_opts, config) #:nodoc: - raise NotImplementedError - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/command.rb b/lib/bundler/vendor/thor/lib/thor/command.rb deleted file mode 100644 index c636948e5d..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/command.rb +++ /dev/null @@ -1,135 +0,0 @@ -class Bundler::Thor - class Command < Struct.new(:name, :description, :long_description, :usage, :options, :ancestor_name) - FILE_REGEXP = /^#{Regexp.escape(File.dirname(__FILE__))}/ - - def initialize(name, description, long_description, usage, options = nil) - super(name.to_s, description, long_description, usage, options || {}) - end - - def initialize_copy(other) #:nodoc: - super(other) - self.options = other.options.dup if other.options - end - - def hidden? - false - end - - # By default, a command invokes a method in the thor class. You can change this - # implementation to create custom commands. - def run(instance, args = []) - arity = nil - - if private_method?(instance) - instance.class.handle_no_command_error(name) - elsif public_method?(instance) - arity = instance.method(name).arity - instance.__send__(name, *args) - elsif local_method?(instance, :method_missing) - instance.__send__(:method_missing, name.to_sym, *args) - else - instance.class.handle_no_command_error(name) - end - rescue ArgumentError => e - handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) - rescue NoMethodError => e - handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e) - end - - # Returns the formatted usage by injecting given required arguments - # and required options into the given usage. - def formatted_usage(klass, namespace = true, subcommand = false) - if ancestor_name - formatted = "#{ancestor_name} ".dup # add space - elsif namespace - namespace = klass.namespace - formatted = "#{namespace.gsub(/^(default)/, '')}:".dup - end - formatted ||= "#{klass.namespace.split(':').last} ".dup if subcommand - - formatted ||= "".dup - - # Add usage with required arguments - formatted << if klass && !klass.arguments.empty? - usage.to_s.gsub(/^#{name}/) do |match| - match << " " << klass.arguments.map(&:usage).compact.join(" ") - end - else - usage.to_s - end - - # Add required options - formatted << " #{required_options}" - - # Strip and go! - formatted.strip - end - - protected - - def not_debugging?(instance) - !(instance.class.respond_to?(:debugging) && instance.class.debugging) - end - - def required_options - @required_options ||= options.map { |_, o| o.usage if o.required? }.compact.sort.join(" ") - end - - # Given a target, checks if this class name is a public method. - def public_method?(instance) #:nodoc: - !(instance.public_methods & [name.to_s, name.to_sym]).empty? - end - - def private_method?(instance) - !(instance.private_methods & [name.to_s, name.to_sym]).empty? - end - - def local_method?(instance, name) - methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) - !(methods & [name.to_s, name.to_sym]).empty? - end - - def sans_backtrace(backtrace, caller) #:nodoc: - saned = backtrace.reject { |frame| frame =~ FILE_REGEXP || (frame =~ /\.java:/ && RUBY_PLATFORM =~ /java/) || (frame =~ %r{^kernel/} && RUBY_ENGINE =~ /rbx/) } - saned - caller - end - - def handle_argument_error?(instance, error, caller) - not_debugging?(instance) && (error.message =~ /wrong number of arguments/ || error.message =~ /given \d*, expected \d*/) && begin - saned = sans_backtrace(error.backtrace, caller) - # Ruby 1.9 always include the called method in the backtrace - saned.empty? || (saned.size == 1 && RUBY_VERSION >= "1.9") - end - end - - def handle_no_method_error?(instance, error, caller) - not_debugging?(instance) && - error.message =~ /^undefined method `#{name}' for #{Regexp.escape(instance.to_s)}$/ - end - end - Task = Command - - # A command that is hidden in help messages but still invocable. - class HiddenCommand < Command - def hidden? - true - end - end - HiddenTask = HiddenCommand - - # A dynamic command that handles method missing scenarios. - class DynamicCommand < Command - def initialize(name, options = nil) - super(name.to_s, "A dynamically-generated command", name.to_s, name.to_s, options) - end - - def run(instance, args = []) - if (instance.methods & [name.to_s, name.to_sym]).empty? - super - else - instance.class.handle_no_command_error(name) - end - end - end - DynamicTask = DynamicCommand -end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb deleted file mode 100644 index c167aa33b8..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +++ /dev/null @@ -1,97 +0,0 @@ -class Bundler::Thor - module CoreExt #:nodoc: - # A hash with indifferent access and magic predicates. - # - # hash = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true - # - # hash[:foo] #=> 'bar' - # hash['foo'] #=> 'bar' - # hash.foo? #=> true - # - class HashWithIndifferentAccess < ::Hash #:nodoc: - def initialize(hash = {}) - super() - hash.each do |key, value| - self[convert_key(key)] = value - end - end - - def [](key) - super(convert_key(key)) - end - - def []=(key, value) - super(convert_key(key), value) - end - - def delete(key) - super(convert_key(key)) - end - - def fetch(key, *args) - super(convert_key(key), *args) - end - - def key?(key) - super(convert_key(key)) - end - - def values_at(*indices) - indices.map { |key| self[convert_key(key)] } - end - - def merge(other) - dup.merge!(other) - end - - def merge!(other) - other.each do |key, value| - self[convert_key(key)] = value - end - self - end - - def reverse_merge(other) - self.class.new(other).merge(self) - end - - def reverse_merge!(other_hash) - replace(reverse_merge(other_hash)) - end - - def replace(other_hash) - super(other_hash) - end - - # Convert to a Hash with String keys. - def to_hash - Hash.new(default).merge!(self) - end - - protected - - def convert_key(key) - key.is_a?(Symbol) ? key.to_s : key - end - - # Magic predicates. For instance: - # - # options.force? # => !!options['force'] - # options.shebang # => "/usr/lib/local/ruby" - # options.test_framework?(:rspec) # => options[:test_framework] == :rspec - # - def method_missing(method, *args) - method = method.to_s - if method =~ /^(\w+)\?$/ - if args.empty? - !!self[$1] - else - self[$1] == args.first - end - else - self[method] - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb deleted file mode 100644 index 0f6e2e0af2..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/core_ext/io_binary_read.rb +++ /dev/null @@ -1,12 +0,0 @@ -class IO #:nodoc: - class << self - unless method_defined? :binread - def binread(file, *args) - raise ArgumentError, "wrong number of arguments (#{1 + args.size} for 1..3)" unless args.size < 3 - File.open(file, "rb") do |f| - f.read(*args) - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb b/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb deleted file mode 100644 index 76f1e43c65..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/core_ext/ordered_hash.rb +++ /dev/null @@ -1,129 +0,0 @@ -class Bundler::Thor - module CoreExt - class OrderedHash < ::Hash - if RUBY_VERSION < "1.9" - def initialize(*args, &block) - super - @keys = [] - end - - def initialize_copy(other) - super - # make a deep copy of keys - @keys = other.keys - end - - def []=(key, value) - @keys << key unless key?(key) - super - end - - def delete(key) - if key? key - index = @keys.index(key) - @keys.delete_at index - end - super - end - - def delete_if - super - sync_keys! - self - end - - alias_method :reject!, :delete_if - - def reject(&block) - dup.reject!(&block) - end - - def keys - @keys.dup - end - - def values - @keys.map { |key| self[key] } - end - - def to_hash - self - end - - def to_a - @keys.map { |key| [key, self[key]] } - end - - def each_key - return to_enum(:each_key) unless block_given? - @keys.each { |key| yield(key) } - self - end - - def each_value - return to_enum(:each_value) unless block_given? - @keys.each { |key| yield(self[key]) } - self - end - - def each - return to_enum(:each) unless block_given? - @keys.each { |key| yield([key, self[key]]) } - self - end - - def each_pair - return to_enum(:each_pair) unless block_given? - @keys.each { |key| yield(key, self[key]) } - self - end - - alias_method :select, :find_all - - def clear - super - @keys.clear - self - end - - def shift - k = @keys.first - v = delete(k) - [k, v] - end - - def merge!(other_hash) - if block_given? - other_hash.each { |k, v| self[k] = key?(k) ? yield(k, self[k], v) : v } - else - other_hash.each { |k, v| self[k] = v } - end - self - end - - alias_method :update, :merge! - - def merge(other_hash, &block) - dup.merge!(other_hash, &block) - end - - # When replacing with another hash, the initial order of our keys must come from the other hash -ordered or not. - def replace(other) - super - @keys = other.keys - self - end - - def inspect - "#<#{self.class} #{super}>" - end - - private - - def sync_keys! - @keys.delete_if { |k| !key?(k) } - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/error.rb b/lib/bundler/vendor/thor/lib/thor/error.rb deleted file mode 100644 index 2f816081f3..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/error.rb +++ /dev/null @@ -1,32 +0,0 @@ -class Bundler::Thor - # Bundler::Thor::Error is raised when it's caused by wrong usage of thor classes. Those - # errors have their backtrace suppressed and are nicely shown to the user. - # - # Errors that are caused by the developer, like declaring a method which - # overwrites a thor keyword, SHOULD NOT raise a Bundler::Thor::Error. This way, we - # ensure that developer errors are shown with full backtrace. - class Error < StandardError - end - - # Raised when a command was not found. - class UndefinedCommandError < Error - end - UndefinedTaskError = UndefinedCommandError - - class AmbiguousCommandError < Error - end - AmbiguousTaskError = AmbiguousCommandError - - # Raised when a command was found, but not invoked properly. - class InvocationError < Error - end - - class UnknownArgumentError < Error - end - - class RequiredArgumentMissingError < InvocationError - end - - class MalformattedArgumentError < InvocationError - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/group.rb b/lib/bundler/vendor/thor/lib/thor/group.rb deleted file mode 100644 index 05ddc10cd3..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/group.rb +++ /dev/null @@ -1,281 +0,0 @@ -require "bundler/vendor/thor/lib/thor/base" - -# Bundler::Thor has a special class called Bundler::Thor::Group. The main difference to Bundler::Thor class -# is that it invokes all commands at once. It also include some methods that allows -# invocations to be done at the class method, which are not available to Bundler::Thor -# commands. -class Bundler::Thor::Group - class << self - # The description for this Bundler::Thor::Group. If none is provided, but a source root - # exists, tries to find the USAGE one folder above it, otherwise searches - # in the superclass. - # - # ==== Parameters - # description<String>:: The description for this Bundler::Thor::Group. - # - def desc(description = nil) - if description - @desc = description - else - @desc ||= from_superclass(:desc, nil) - end - end - - # Prints help information. - # - # ==== Options - # short:: When true, shows only usage. - # - def help(shell) - shell.say "Usage:" - shell.say " #{banner}\n" - shell.say - class_options_help(shell) - shell.say desc if desc - end - - # Stores invocations for this class merging with superclass values. - # - def invocations #:nodoc: - @invocations ||= from_superclass(:invocations, {}) - end - - # Stores invocation blocks used on invoke_from_option. - # - def invocation_blocks #:nodoc: - @invocation_blocks ||= from_superclass(:invocation_blocks, {}) - end - - # Invoke the given namespace or class given. It adds an instance - # method that will invoke the klass and command. You can give a block to - # configure how it will be invoked. - # - # The namespace/class given will have its options showed on the help - # usage. Check invoke_from_option for more information. - # - def invoke(*names, &block) - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, true) - - names.each do |name| - invocations[name] = false - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_#{name.to_s.gsub(/\W/, '_')} - klass, command = self.class.prepare_for_invocation(nil, #{name.inspect}) - - if klass - say_status :invoke, #{name.inspect}, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, command, &block - else - say_status :error, %(#{name.inspect} [not found]), :red - end - end - METHOD - end - end - - # Invoke a thor class based on the value supplied by the user to the - # given option named "name". A class option must be created before this - # method is invoked for each name given. - # - # ==== Examples - # - # class GemGenerator < Bundler::Thor::Group - # class_option :test_framework, :type => :string - # invoke_from_option :test_framework - # end - # - # ==== Boolean options - # - # In some cases, you want to invoke a thor class if some option is true or - # false. This is automatically handled by invoke_from_option. Then the - # option name is used to invoke the generator. - # - # ==== Preparing for invocation - # - # In some cases you want to customize how a specified hook is going to be - # invoked. You can do that by overwriting the class method - # prepare_for_invocation. The class method must necessarily return a klass - # and an optional command. - # - # ==== Custom invocations - # - # You can also supply a block to customize how the option is going to be - # invoked. The block receives two parameters, an instance of the current - # class and the klass to be invoked. - # - def invoke_from_option(*names, &block) - options = names.last.is_a?(Hash) ? names.pop : {} - verbose = options.fetch(:verbose, :white) - - names.each do |name| - unless class_options.key?(name) - raise ArgumentError, "You have to define the option #{name.inspect} " \ - "before setting invoke_from_option." - end - - invocations[name] = true - invocation_blocks[name] = block if block_given? - - class_eval <<-METHOD, __FILE__, __LINE__ - def _invoke_from_option_#{name.to_s.gsub(/\W/, '_')} - return unless options[#{name.inspect}] - - value = options[#{name.inspect}] - value = #{name.inspect} if TrueClass === value - klass, command = self.class.prepare_for_invocation(#{name.inspect}, value) - - if klass - say_status :invoke, value, #{verbose.inspect} - block = self.class.invocation_blocks[#{name.inspect}] - _invoke_for_class_method klass, command, &block - else - say_status :error, %(\#{value} [not found]), :red - end - end - METHOD - end - end - - # Remove a previously added invocation. - # - # ==== Examples - # - # remove_invocation :test_framework - # - def remove_invocation(*names) - names.each do |name| - remove_command(name) - remove_class_option(name) - invocations.delete(name) - invocation_blocks.delete(name) - end - end - - # Overwrite class options help to allow invoked generators options to be - # shown recursively when invoking a generator. - # - def class_options_help(shell, groups = {}) #:nodoc: - get_options_from_invocations(groups, class_options) do |klass| - klass.send(:get_options_from_invocations, groups, class_options) - end - super(shell, groups) - end - - # Get invocations array and merge options from invocations. Those - # options are added to group_options hash. Options that already exists - # in base_options are not added twice. - # - def get_options_from_invocations(group_options, base_options) #:nodoc: # rubocop:disable MethodLength - invocations.each do |name, from_option| - value = if from_option - option = class_options[name] - option.type == :boolean ? name : option.default - else - name - end - next unless value - - klass, _ = prepare_for_invocation(name, value) - next unless klass && klass.respond_to?(:class_options) - - value = value.to_s - human_name = value.respond_to?(:classify) ? value.classify : value - - group_options[human_name] ||= [] - group_options[human_name] += klass.class_options.values.select do |class_option| - base_options[class_option.name.to_sym].nil? && class_option.group.nil? && - !group_options.values.flatten.any? { |i| i.name == class_option.name } - end - - yield klass if block_given? - end - end - - # Returns commands ready to be printed. - def printable_commands(*) - item = [] - item << banner - item << (desc ? "# #{desc.gsub(/\s+/m, ' ')}" : "") - [item] - end - alias_method :printable_tasks, :printable_commands - - def handle_argument_error(command, error, _args, arity) #:nodoc: - msg = "#{basename} #{command.name} takes #{arity} argument".dup - msg << "s" if arity > 1 - msg << ", but it should not." - raise error, msg - end - - protected - - # The method responsible for dispatching given the args. - def dispatch(command, given_args, given_opts, config) #:nodoc: - if Bundler::Thor::HELP_MAPPINGS.include?(given_args.first) - help(config[:shell]) - return - end - - args, opts = Bundler::Thor::Options.split(given_args) - opts = given_opts || opts - - instance = new(args, opts, config) - yield instance if block_given? - - if command - instance.invoke_command(all_commands[command]) - else - instance.invoke_all - end - end - - # The banner for this class. You can customize it if you are invoking the - # thor class by another ways which is not the Bundler::Thor::Runner. - def banner - "#{basename} #{self_command.formatted_usage(self, false)}" - end - - # Represents the whole class as a command. - def self_command #:nodoc: - Bundler::Thor::DynamicCommand.new(namespace, class_options) - end - alias_method :self_task, :self_command - - def baseclass #:nodoc: - Bundler::Thor::Group - end - - def create_command(meth) #:nodoc: - commands[meth.to_s] = Bundler::Thor::Command.new(meth, nil, nil, nil, nil) - true - end - alias_method :create_task, :create_command - end - - include Bundler::Thor::Base - -protected - - # Shortcut to invoke with padding and block handling. Use internally by - # invoke and invoke_from_option class methods. - def _invoke_for_class_method(klass, command = nil, *args, &block) #:nodoc: - with_padding do - if block - case block.arity - when 3 - yield(self, klass, command) - when 2 - yield(self, klass) - when 1 - instance_exec(klass, &block) - end - else - invoke klass, command, *args - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/invocation.rb b/lib/bundler/vendor/thor/lib/thor/invocation.rb deleted file mode 100644 index 866d2212a7..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/invocation.rb +++ /dev/null @@ -1,177 +0,0 @@ -class Bundler::Thor - module Invocation - def self.included(base) #:nodoc: - base.extend ClassMethods - end - - module ClassMethods - # This method is responsible for receiving a name and find the proper - # class and command for it. The key is an optional parameter which is - # available only in class methods invocations (i.e. in Bundler::Thor::Group). - def prepare_for_invocation(key, name) #:nodoc: - case name - when Symbol, String - Bundler::Thor::Util.find_class_and_command_by_namespace(name.to_s, !key) - else - name - end - end - end - - # Make initializer aware of invocations and the initialization args. - def initialize(args = [], options = {}, config = {}, &block) #:nodoc: - @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] } - @_initializer = [args, options, config] - super - end - - # Make the current command chain accessible with in a Bundler::Thor-(sub)command - def current_command_chain - @_invocations.values.flatten.map(&:to_sym) - end - - # Receives a name and invokes it. The name can be a string (either "command" or - # "namespace:command"), a Bundler::Thor::Command, a Class or a Bundler::Thor instance. If the - # command cannot be guessed by name, it can also be supplied as second argument. - # - # You can also supply the arguments, options and configuration values for - # the command to be invoked, if none is given, the same values used to - # initialize the invoker are used to initialize the invoked. - # - # When no name is given, it will invoke the default command of the current class. - # - # ==== Examples - # - # class A < Bundler::Thor - # def foo - # invoke :bar - # invoke "b:hello", ["Erik"] - # end - # - # def bar - # invoke "b:hello", ["Erik"] - # end - # end - # - # class B < Bundler::Thor - # def hello(name) - # puts "hello #{name}" - # end - # end - # - # You can notice that the method "foo" above invokes two commands: "bar", - # which belongs to the same class and "hello" which belongs to the class B. - # - # By using an invocation system you ensure that a command is invoked only once. - # In the example above, invoking "foo" will invoke "b:hello" just once, even - # if it's invoked later by "bar" method. - # - # When class A invokes class B, all arguments used on A initialization are - # supplied to B. This allows lazy parse of options. Let's suppose you have - # some rspec commands: - # - # class Rspec < Bundler::Thor::Group - # class_option :mock_framework, :type => :string, :default => :rr - # - # def invoke_mock_framework - # invoke "rspec:#{options[:mock_framework]}" - # end - # end - # - # As you noticed, it invokes the given mock framework, which might have its - # own options: - # - # class Rspec::RR < Bundler::Thor::Group - # class_option :style, :type => :string, :default => :mock - # end - # - # Since it's not rspec concern to parse mock framework options, when RR - # is invoked all options are parsed again, so RR can extract only the options - # that it's going to use. - # - # If you want Rspec::RR to be initialized with its own set of options, you - # have to do that explicitly: - # - # invoke "rspec:rr", [], :style => :foo - # - # Besides giving an instance, you can also give a class to invoke: - # - # invoke Rspec::RR, [], :style => :foo - # - def invoke(name = nil, *args) - if name.nil? - warn "[Bundler::Thor] Calling invoke() without argument is deprecated. Please use invoke_all instead.\n#{caller.join("\n")}" - return invoke_all - end - - args.unshift(nil) if args.first.is_a?(Array) || args.first.nil? - command, args, opts, config = args - - klass, command = _retrieve_class_and_command(name, command) - raise "Missing Bundler::Thor class for invoke #{name}" unless klass - raise "Expected Bundler::Thor class, got #{klass}" unless klass <= Bundler::Thor::Base - - args, opts, config = _parse_initialization_options(args, opts, config) - klass.send(:dispatch, command, args, opts, config) do |instance| - instance.parent_options = options - end - end - - # Invoke the given command if the given args. - def invoke_command(command, *args) #:nodoc: - current = @_invocations[self.class] - - unless current.include?(command.name) - current << command.name - command.run(self, *args) - end - end - alias_method :invoke_task, :invoke_command - - # Invoke all commands for the current instance. - def invoke_all #:nodoc: - self.class.all_commands.map { |_, command| invoke_command(command) } - end - - # Invokes using shell padding. - def invoke_with_padding(*args) - with_padding { invoke(*args) } - end - - protected - - # Configuration values that are shared between invocations. - def _shared_configuration #:nodoc: - {:invocations => @_invocations} - end - - # This method simply retrieves the class and command to be invoked. - # If the name is nil or the given name is a command in the current class, - # use the given name and return self as class. Otherwise, call - # prepare_for_invocation in the current class. - def _retrieve_class_and_command(name, sent_command = nil) #:nodoc: - if name.nil? - [self.class, nil] - elsif self.class.all_commands[name.to_s] - [self.class, name.to_s] - else - klass, command = self.class.prepare_for_invocation(nil, name) - [klass, command || sent_command] - end - end - alias_method :_retrieve_class_and_task, :_retrieve_class_and_command - - # Initialize klass using values stored in the @_initializer. - def _parse_initialization_options(args, opts, config) #:nodoc: - stored_args, stored_opts, stored_config = @_initializer - - args ||= stored_args.dup - opts ||= stored_opts.dup - - config ||= {} - config = stored_config.merge(_shared_configuration).merge!(config) - - [args, opts, config] - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor.rb b/lib/bundler/vendor/thor/lib/thor/line_editor.rb deleted file mode 100644 index ce81a17484..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/line_editor.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "bundler/vendor/thor/lib/thor/line_editor/basic" -require "bundler/vendor/thor/lib/thor/line_editor/readline" - -class Bundler::Thor - module LineEditor - def self.readline(prompt, options = {}) - best_available.new(prompt, options).readline - end - - def self.best_available - [ - Bundler::Thor::LineEditor::Readline, - Bundler::Thor::LineEditor::Basic - ].detect(&:available?) - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb deleted file mode 100644 index 0adb2b3137..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/line_editor/basic.rb +++ /dev/null @@ -1,37 +0,0 @@ -class Bundler::Thor - module LineEditor - class Basic - attr_reader :prompt, :options - - def self.available? - true - end - - def initialize(prompt, options) - @prompt = prompt - @options = options - end - - def readline - $stdout.print(prompt) - get_input - end - - private - - def get_input - if echo? - $stdin.gets - else - # Lazy-load io/console since it is gem-ified as of 2.3 - require "io/console" if RUBY_VERSION > "1.9.2" - $stdin.noecho(&:gets) - end - end - - def echo? - options.fetch(:echo, true) - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb b/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb deleted file mode 100644 index dd39cff35d..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/line_editor/readline.rb +++ /dev/null @@ -1,88 +0,0 @@ -begin - require "readline" -rescue LoadError -end - -class Bundler::Thor - module LineEditor - class Readline < Basic - def self.available? - Object.const_defined?(:Readline) - end - - def readline - if echo? - ::Readline.completion_append_character = nil - # Ruby 1.8.7 does not allow Readline.completion_proc= to receive nil. - if complete = completion_proc - ::Readline.completion_proc = complete - end - ::Readline.readline(prompt, add_to_history?) - else - super - end - end - - private - - def add_to_history? - options.fetch(:add_to_history, true) - end - - def completion_proc - if use_path_completion? - proc { |text| PathCompletion.new(text).matches } - elsif completion_options.any? - proc do |text| - completion_options.select { |option| option.start_with?(text) } - end - end - end - - def completion_options - options.fetch(:limited_to, []) - end - - def use_path_completion? - options.fetch(:path, false) - end - - class PathCompletion - attr_reader :text - private :text - - def initialize(text) - @text = text - end - - def matches - relative_matches - end - - private - - def relative_matches - absolute_matches.map { |path| path.sub(base_path, "") } - end - - def absolute_matches - Dir[glob_pattern].map do |path| - if File.directory?(path) - "#{path}/" - else - path - end - end - end - - def glob_pattern - "#{base_path}#{text}*" - end - - def base_path - "#{Dir.pwd}/" - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/parser.rb b/lib/bundler/vendor/thor/lib/thor/parser.rb deleted file mode 100644 index 08f80e565d..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/parser.rb +++ /dev/null @@ -1,4 +0,0 @@ -require "bundler/vendor/thor/lib/thor/parser/argument" -require "bundler/vendor/thor/lib/thor/parser/arguments" -require "bundler/vendor/thor/lib/thor/parser/option" -require "bundler/vendor/thor/lib/thor/parser/options" diff --git a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb b/lib/bundler/vendor/thor/lib/thor/parser/argument.rb deleted file mode 100644 index dfe7398583..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/parser/argument.rb +++ /dev/null @@ -1,70 +0,0 @@ -class Bundler::Thor - class Argument #:nodoc: - VALID_TYPES = [:numeric, :hash, :array, :string] - - attr_reader :name, :description, :enum, :required, :type, :default, :banner - alias_method :human_name, :name - - def initialize(name, options = {}) - class_name = self.class.name.split("::").last - - type = options[:type] - - raise ArgumentError, "#{class_name} name can't be nil." if name.nil? - raise ArgumentError, "Type :#{type} is not valid for #{class_name.downcase}s." if type && !valid_type?(type) - - @name = name.to_s - @description = options[:desc] - @required = options.key?(:required) ? options[:required] : true - @type = (type || :string).to_sym - @default = options[:default] - @banner = options[:banner] || default_banner - @enum = options[:enum] - - validate! # Trigger specific validations - end - - def usage - required? ? banner : "[#{banner}]" - end - - def required? - required - end - - def show_default? - case default - when Array, String, Hash - !default.empty? - else - default - end - end - - protected - - def validate! - raise ArgumentError, "An argument cannot be required and have default value." if required? && !default.nil? - raise ArgumentError, "An argument cannot have an enum other than an array." if @enum && !@enum.is_a?(Array) - end - - def valid_type?(type) - self.class::VALID_TYPES.include?(type.to_sym) - end - - def default_banner - case type - when :boolean - nil - when :string, :default - human_name.upcase - when :numeric - "N" - when :hash - "key:value" - when :array - "one two three" - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb b/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb deleted file mode 100644 index 1fd790f4b7..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/parser/arguments.rb +++ /dev/null @@ -1,175 +0,0 @@ -class Bundler::Thor - class Arguments #:nodoc: # rubocop:disable ClassLength - NUMERIC = /[-+]?(\d*\.\d+|\d+)/ - - # Receives an array of args and returns two arrays, one with arguments - # and one with switches. - # - def self.split(args) - arguments = [] - - args.each do |item| - break if item =~ /^-/ - arguments << item - end - - [arguments, args[Range.new(arguments.size, -1)]] - end - - def self.parse(*args) - to_parse = args.pop - new(*args).parse(to_parse) - end - - # Takes an array of Bundler::Thor::Argument objects. - # - def initialize(arguments = []) - @assigns = {} - @non_assigned_required = [] - @switches = arguments - - arguments.each do |argument| - if !argument.default.nil? - @assigns[argument.human_name] = argument.default - elsif argument.required? - @non_assigned_required << argument - end - end - end - - def parse(args) - @pile = args.dup - - @switches.each do |argument| - break unless peek - @non_assigned_required.delete(argument) - @assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name) - end - - check_requirement! - @assigns - end - - def remaining - @pile - end - - private - - def no_or_skip?(arg) - arg =~ /^--(no|skip)-([-\w]+)$/ - $2 - end - - def last? - @pile.empty? - end - - def peek - @pile.first - end - - def shift - @pile.shift - end - - def unshift(arg) - if arg.is_a?(Array) - @pile = arg + @pile - else - @pile.unshift(arg) - end - end - - def current_is_value? - peek && peek.to_s !~ /^-/ - end - - # Runs through the argument array getting strings that contains ":" and - # mark it as a hash: - # - # [ "name:string", "age:integer" ] - # - # Becomes: - # - # { "name" => "string", "age" => "integer" } - # - def parse_hash(name) - return shift if peek.is_a?(Hash) - hash = {} - - while current_is_value? && peek.include?(":") - key, value = shift.split(":", 2) - raise MalformattedArgumentError, "You can't specify '#{key}' more than once in option '#{name}'; got #{key}:#{hash[key]} and #{key}:#{value}" if hash.include? key - hash[key] = value - end - hash - end - - # Runs through the argument array getting all strings until no string is - # found or a switch is found. - # - # ["a", "b", "c"] - # - # And returns it as an array: - # - # ["a", "b", "c"] - # - def parse_array(name) - return shift if peek.is_a?(Array) - array = [] - array << shift while current_is_value? - array - end - - # Check if the peek is numeric format and return a Float or Integer. - # Check if the peek is included in enum if enum is provided. - # Otherwise raises an error. - # - def parse_numeric(name) - return shift if peek.is_a?(Numeric) - - unless peek =~ NUMERIC && $& == peek - raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}" - end - - value = $&.index(".") ? shift.to_f : shift.to_i - if @switches.is_a?(Hash) && switch = @switches[name] - if switch.enum && !switch.enum.include?(value) - raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" - end - end - value - end - - # Parse string: - # for --string-arg, just return the current value in the pile - # for --no-string-arg, nil - # Check if the peek is included in enum if enum is provided. Otherwise raises an error. - # - def parse_string(name) - if no_or_skip?(name) - nil - else - value = shift - if @switches.is_a?(Hash) && switch = @switches[name] - if switch.enum && !switch.enum.include?(value) - raise MalformattedArgumentError, "Expected '#{name}' to be one of #{switch.enum.join(', ')}; got #{value}" - end - end - value - end - end - - # Raises an error if @non_assigned_required array is not empty. - # - def check_requirement! - return if @non_assigned_required.empty? - names = @non_assigned_required.map do |o| - o.respond_to?(:switch_name) ? o.switch_name : o.human_name - end.join("', '") - class_name = self.class.name.split("::").last.downcase - raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'" - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/option.rb b/lib/bundler/vendor/thor/lib/thor/parser/option.rb deleted file mode 100644 index 85169b56c8..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/parser/option.rb +++ /dev/null @@ -1,146 +0,0 @@ -class Bundler::Thor - class Option < Argument #:nodoc: - attr_reader :aliases, :group, :lazy_default, :hide - - VALID_TYPES = [:boolean, :numeric, :hash, :array, :string] - - def initialize(name, options = {}) - @check_default_type = options[:check_default_type] - options[:required] = false unless options.key?(:required) - super - @lazy_default = options[:lazy_default] - @group = options[:group].to_s.capitalize if options[:group] - @aliases = Array(options[:aliases]) - @hide = options[:hide] - end - - # This parse quick options given as method_options. It makes several - # assumptions, but you can be more specific using the option method. - # - # parse :foo => "bar" - # #=> Option foo with default value bar - # - # parse [:foo, :baz] => "bar" - # #=> Option foo with default value bar and alias :baz - # - # parse :foo => :required - # #=> Required option foo without default value - # - # parse :foo => 2 - # #=> Option foo with default value 2 and type numeric - # - # parse :foo => :numeric - # #=> Option foo without default value and type numeric - # - # parse :foo => true - # #=> Option foo with default value true and type boolean - # - # The valid types are :boolean, :numeric, :hash, :array and :string. If none - # is given a default type is assumed. This default type accepts arguments as - # string (--foo=value) or booleans (just --foo). - # - # By default all options are optional, unless :required is given. - # - def self.parse(key, value) - if key.is_a?(Array) - name, *aliases = key - else - name = key - aliases = [] - end - - name = name.to_s - default = value - - type = case value - when Symbol - default = nil - if VALID_TYPES.include?(value) - value - elsif required = (value == :required) # rubocop:disable AssignmentInCondition - :string - end - when TrueClass, FalseClass - :boolean - when Numeric - :numeric - when Hash, Array, String - value.class.name.downcase.to_sym - end - - new(name.to_s, :required => required, :type => type, :default => default, :aliases => aliases) - end - - def switch_name - @switch_name ||= dasherized? ? name : dasherize(name) - end - - def human_name - @human_name ||= dasherized? ? undasherize(name) : name - end - - def usage(padding = 0) - sample = if banner && !banner.to_s.empty? - "#{switch_name}=#{banner}".dup - else - switch_name - end - - sample = "[#{sample}]".dup unless required? - - if boolean? - sample << ", [#{dasherize('no-' + human_name)}]" unless (name == "force") || name.start_with?("no-") - end - - if aliases.empty? - (" " * padding) << sample - else - "#{aliases.join(', ')}, #{sample}" - end - end - - VALID_TYPES.each do |type| - class_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{type}? - self.type == #{type.inspect} - end - RUBY - end - - protected - - def validate! - raise ArgumentError, "An option cannot be boolean and required." if boolean? && required? - validate_default_type! if @check_default_type - end - - def validate_default_type! - default_type = case @default - when nil - return - when TrueClass, FalseClass - required? ? :string : :boolean - when Numeric - :numeric - when Symbol - :string - when Hash, Array, String - @default.class.name.downcase.to_sym - end - - raise ArgumentError, "Expected #{@type} default value for '#{switch_name}'; got #{@default.inspect} (#{default_type})" unless default_type == @type - end - - def dasherized? - name.index("-") == 0 - end - - def undasherize(str) - str.sub(/^-{1,2}/, "") - end - - def dasherize(str) - (str.length > 1 ? "--" : "-") + str.tr("_", "-") - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/parser/options.rb b/lib/bundler/vendor/thor/lib/thor/parser/options.rb deleted file mode 100644 index 70f6366842..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/parser/options.rb +++ /dev/null @@ -1,221 +0,0 @@ -class Bundler::Thor - class Options < Arguments #:nodoc: # rubocop:disable ClassLength - LONG_RE = /^(--\w+(?:-\w+)*)$/ - SHORT_RE = /^(-[a-z])$/i - EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i - SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args - SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i - OPTS_END = "--".freeze - - # Receives a hash and makes it switches. - def self.to_switches(options) - options.map do |key, value| - case value - when true - "--#{key}" - when Array - "--#{key} #{value.map(&:inspect).join(' ')}" - when Hash - "--#{key} #{value.map { |k, v| "#{k}:#{v}" }.join(' ')}" - when nil, false - nil - else - "--#{key} #{value.inspect}" - end - end.compact.join(" ") - end - - # Takes a hash of Bundler::Thor::Option and a hash with defaults. - # - # If +stop_on_unknown+ is true, #parse will stop as soon as it encounters - # an unknown option or a regular argument. - def initialize(hash_options = {}, defaults = {}, stop_on_unknown = false, disable_required_check = false) - @stop_on_unknown = stop_on_unknown - @disable_required_check = disable_required_check - options = hash_options.values - super(options) - - # Add defaults - defaults.each do |key, value| - @assigns[key.to_s] = value - @non_assigned_required.delete(hash_options[key]) - end - - @shorts = {} - @switches = {} - @extra = [] - - options.each do |option| - @switches[option.switch_name] = option - - option.aliases.each do |short| - name = short.to_s.sub(/^(?!\-)/, "-") - @shorts[name] ||= option.switch_name - end - end - end - - def remaining - @extra - end - - def peek - return super unless @parsing_options - - result = super - if result == OPTS_END - shift - @parsing_options = false - super - else - result - end - end - - def parse(args) # rubocop:disable MethodLength - @pile = args.dup - @parsing_options = true - - while peek - if parsing_options? - match, is_switch = current_is_switch? - shifted = shift - - if is_switch - case shifted - when SHORT_SQ_RE - unshift($1.split("").map { |f| "-#{f}" }) - next - when EQ_RE, SHORT_NUM - unshift($2) - switch = $1 - when LONG_RE, SHORT_RE - switch = $1 - end - - switch = normalize_switch(switch) - option = switch_option(switch) - @assigns[option.human_name] = parse_peek(switch, option) - elsif @stop_on_unknown - @parsing_options = false - @extra << shifted - @extra << shift while peek - break - elsif match - @extra << shifted - @extra << shift while peek && peek !~ /^-/ - else - @extra << shifted - end - else - @extra << shift - end - end - - check_requirement! unless @disable_required_check - - assigns = Bundler::Thor::CoreExt::HashWithIndifferentAccess.new(@assigns) - assigns.freeze - assigns - end - - def check_unknown! - # an unknown option starts with - or -- and has no more --'s afterward. - unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ } - raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty? - end - - protected - - # Check if the current value in peek is a registered switch. - # - # Two booleans are returned. The first is true if the current value - # starts with a hyphen; the second is true if it is a registered switch. - def current_is_switch? - case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM - [true, switch?($1)] - when SHORT_SQ_RE - [true, $1.split("").any? { |f| switch?("-#{f}") }] - else - [false, false] - end - end - - def current_is_switch_formatted? - case peek - when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE - true - else - false - end - end - - def current_is_value? - peek && (!parsing_options? || super) - end - - def switch?(arg) - switch_option(normalize_switch(arg)) - end - - def switch_option(arg) - if match = no_or_skip?(arg) # rubocop:disable AssignmentInCondition - @switches[arg] || @switches["--#{match}"] - else - @switches[arg] - end - end - - # Check if the given argument is actually a shortcut. - # - def normalize_switch(arg) - (@shorts[arg] || arg).tr("_", "-") - end - - def parsing_options? - peek - @parsing_options - end - - # Parse boolean values which can be given as --foo=true, --foo or --no-foo. - # - def parse_boolean(switch) - if current_is_value? - if ["true", "TRUE", "t", "T", true].include?(peek) - shift - true - elsif ["false", "FALSE", "f", "F", false].include?(peek) - shift - false - else - !no_or_skip?(switch) - end - else - @switches.key?(switch) || !no_or_skip?(switch) - end - end - - # Parse the value at the peek analyzing if it requires an input or not. - # - def parse_peek(switch, option) - if parsing_options? && (current_is_switch_formatted? || last?) - if option.boolean? - # No problem for boolean types - elsif no_or_skip?(switch) - return nil # User set value to nil - elsif option.string? && !option.required? - # Return the default if there is one, else the human name - return option.lazy_default || option.default || option.human_name - elsif option.lazy_default - return option.lazy_default - else - raise MalformattedArgumentError, "No value provided for option '#{switch}'" - end - end - - @non_assigned_required.delete(option) - send(:"parse_#{option.type}", switch) - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/rake_compat.rb b/lib/bundler/vendor/thor/lib/thor/rake_compat.rb deleted file mode 100644 index 60282e2991..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/rake_compat.rb +++ /dev/null @@ -1,71 +0,0 @@ -require "rake" -require "rake/dsl_definition" - -class Bundler::Thor - # Adds a compatibility layer to your Bundler::Thor classes which allows you to use - # rake package tasks. For example, to use rspec rake tasks, one can do: - # - # require 'bundler/vendor/thor/lib/thor/rake_compat' - # require 'rspec/core/rake_task' - # - # class Default < Bundler::Thor - # include Bundler::Thor::RakeCompat - # - # RSpec::Core::RakeTask.new(:spec) do |t| - # t.spec_opts = ['--options', './.rspec'] - # t.spec_files = FileList['spec/**/*_spec.rb'] - # end - # end - # - module RakeCompat - include Rake::DSL if defined?(Rake::DSL) - - def self.rake_classes - @rake_classes ||= [] - end - - def self.included(base) - # Hack. Make rakefile point to invoker, so rdoc task is generated properly. - rakefile = File.basename(caller[0].match(/(.*):\d+/)[1]) - Rake.application.instance_variable_set(:@rakefile, rakefile) - rake_classes << base - end - end -end - -# override task on (main), for compatibility with Rake 0.9 -instance_eval do - alias rake_namespace namespace - - def task(*) - task = super - - if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition - non_namespaced_name = task.name.split(":").last - - description = non_namespaced_name - description << task.arg_names.map { |n| n.to_s.upcase }.join(" ") - description.strip! - - klass.desc description, Rake.application.last_description || non_namespaced_name - Rake.application.last_description = nil - klass.send :define_method, non_namespaced_name do |*args| - Rake::Task[task.name.to_sym].invoke(*args) - end - end - - task - end - - def namespace(name) - if klass = Bundler::Thor::RakeCompat.rake_classes.last # rubocop:disable AssignmentInCondition - const_name = Bundler::Thor::Util.camel_case(name.to_s).to_sym - klass.const_set(const_name, Class.new(Bundler::Thor)) - new_klass = klass.const_get(const_name) - Bundler::Thor::RakeCompat.rake_classes << new_klass - end - - super - Bundler::Thor::RakeCompat.rake_classes.pop - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/runner.rb b/lib/bundler/vendor/thor/lib/thor/runner.rb deleted file mode 100644 index b110b8d478..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/runner.rb +++ /dev/null @@ -1,324 +0,0 @@ -require "bundler/vendor/thor/lib/thor" -require "bundler/vendor/thor/lib/thor/group" -require "bundler/vendor/thor/lib/thor/core_ext/io_binary_read" - -require "yaml" -require "digest" -require "pathname" - -class Bundler::Thor::Runner < Bundler::Thor #:nodoc: # rubocop:disable ClassLength - map "-T" => :list, "-i" => :install, "-u" => :update, "-v" => :version - - def self.banner(command, all = false, subcommand = false) - "thor " + command.formatted_usage(self, all, subcommand) - end - - def self.exit_on_failure? - true - end - - # Override Bundler::Thor#help so it can give information about any class and any method. - # - def help(meth = nil) - if meth && !respond_to?(meth) - initialize_thorfiles(meth) - klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) - self.class.handle_no_command_error(command, false) if klass.nil? - klass.start(["-h", command].compact, :shell => shell) - else - super - end - end - - # If a command is not found on Bundler::Thor::Runner, method missing is invoked and - # Bundler::Thor::Runner is then responsible for finding the command in all classes. - # - def method_missing(meth, *args) - meth = meth.to_s - initialize_thorfiles(meth) - klass, command = Bundler::Thor::Util.find_class_and_command_by_namespace(meth) - self.class.handle_no_command_error(command, false) if klass.nil? - args.unshift(command) if command - klass.start(args, :shell => shell) - end - - desc "install NAME", "Install an optionally named Bundler::Thor file into your system commands" - method_options :as => :string, :relative => :boolean, :force => :boolean - def install(name) # rubocop:disable MethodLength - initialize_thorfiles - - # If a directory name is provided as the argument, look for a 'main.thor' - # command in said directory. - begin - if File.directory?(File.expand_path(name)) - base = File.join(name, "main.thor") - package = :directory - contents = open(base, &:read) - else - base = name - package = :file - contents = open(name, &:read) - end - rescue OpenURI::HTTPError - raise Error, "Error opening URI '#{name}'" - rescue Errno::ENOENT - raise Error, "Error opening file '#{name}'" - end - - say "Your Bundler::Thorfile contains:" - say contents - - unless options["force"] - return false if no?("Do you wish to continue [y/N]?") - end - - as = options["as"] || begin - first_line = contents.split("\n")[0] - (match = first_line.match(/\s*#\s*module:\s*([^\n]*)/)) ? match[1].strip : nil - end - - unless as - basename = File.basename(name) - as = ask("Please specify a name for #{name} in the system repository [#{basename}]:") - as = basename if as.empty? - end - - location = if options[:relative] || name =~ %r{^https?://} - name - else - File.expand_path(name) - end - - thor_yaml[as] = { - :filename => Digest(:MD5).hexdigest(name + as), - :location => location, - :namespaces => Bundler::Thor::Util.namespaces_in_content(contents, base) - } - - save_yaml(thor_yaml) - say "Storing thor file in your system repository" - destination = File.join(thor_root, thor_yaml[as][:filename]) - - if package == :file - File.open(destination, "w") { |f| f.puts contents } - else - require "fileutils" - FileUtils.cp_r(name, destination) - end - - thor_yaml[as][:filename] # Indicate success - end - - desc "version", "Show Bundler::Thor version" - def version - require "bundler/vendor/thor/lib/thor/version" - say "Bundler::Thor #{Bundler::Thor::VERSION}" - end - - desc "uninstall NAME", "Uninstall a named Bundler::Thor module" - def uninstall(name) - raise Error, "Can't find module '#{name}'" unless thor_yaml[name] - say "Uninstalling #{name}." - require "fileutils" - FileUtils.rm_rf(File.join(thor_root, (thor_yaml[name][:filename]).to_s)) - - thor_yaml.delete(name) - save_yaml(thor_yaml) - - puts "Done." - end - - desc "update NAME", "Update a Bundler::Thor file from its original location" - def update(name) - raise Error, "Can't find module '#{name}'" if !thor_yaml[name] || !thor_yaml[name][:location] - - say "Updating '#{name}' from #{thor_yaml[name][:location]}" - - old_filename = thor_yaml[name][:filename] - self.options = options.merge("as" => name) - - if File.directory? File.expand_path(name) - require "fileutils" - FileUtils.rm_rf(File.join(thor_root, old_filename)) - - thor_yaml.delete(old_filename) - save_yaml(thor_yaml) - - filename = install(name) - else - filename = install(thor_yaml[name][:location]) - end - - File.delete(File.join(thor_root, old_filename)) unless filename == old_filename - end - - desc "installed", "List the installed Bundler::Thor modules and commands" - method_options :internal => :boolean - def installed - initialize_thorfiles(nil, true) - display_klasses(true, options["internal"]) - end - - desc "list [SEARCH]", "List the available thor commands (--substring means .*SEARCH)" - method_options :substring => :boolean, :group => :string, :all => :boolean, :debug => :boolean - def list(search = "") - initialize_thorfiles - - search = ".*#{search}" if options["substring"] - search = /^#{search}.*/i - group = options[:group] || "standard" - - klasses = Bundler::Thor::Base.subclasses.select do |k| - (options[:all] || k.group == group) && k.namespace =~ search - end - - display_klasses(false, false, klasses) - end - -private - - def thor_root - Bundler::Thor::Util.thor_root - end - - def thor_yaml - @thor_yaml ||= begin - yaml_file = File.join(thor_root, "thor.yml") - yaml = YAML.load_file(yaml_file) if File.exist?(yaml_file) - yaml || {} - end - end - - # Save the yaml file. If none exists in thor root, creates one. - # - def save_yaml(yaml) - yaml_file = File.join(thor_root, "thor.yml") - - unless File.exist?(yaml_file) - require "fileutils" - FileUtils.mkdir_p(thor_root) - yaml_file = File.join(thor_root, "thor.yml") - FileUtils.touch(yaml_file) - end - - File.open(yaml_file, "w") { |f| f.puts yaml.to_yaml } - end - - # Load the Bundler::Thorfiles. If relevant_to is supplied, looks for specific files - # in the thor_root instead of loading them all. - # - # By default, it also traverses the current path until find Bundler::Thor files, as - # described in thorfiles. This look up can be skipped by supplying - # skip_lookup true. - # - def initialize_thorfiles(relevant_to = nil, skip_lookup = false) - thorfiles(relevant_to, skip_lookup).each do |f| - Bundler::Thor::Util.load_thorfile(f, nil, options[:debug]) unless Bundler::Thor::Base.subclass_files.keys.include?(File.expand_path(f)) - end - end - - # Finds Bundler::Thorfiles by traversing from your current directory down to the root - # directory of your system. If at any time we find a Bundler::Thor file, we stop. - # - # We also ensure that system-wide Bundler::Thorfiles are loaded first, so local - # Bundler::Thorfiles can override them. - # - # ==== Example - # - # If we start at /Users/wycats/dev/thor ... - # - # 1. /Users/wycats/dev/thor - # 2. /Users/wycats/dev - # 3. /Users/wycats <-- we find a Bundler::Thorfile here, so we stop - # - # Suppose we start at c:\Documents and Settings\james\dev\thor ... - # - # 1. c:\Documents and Settings\james\dev\thor - # 2. c:\Documents and Settings\james\dev - # 3. c:\Documents and Settings\james - # 4. c:\Documents and Settings - # 5. c:\ <-- no Bundler::Thorfiles found! - # - def thorfiles(relevant_to = nil, skip_lookup = false) - thorfiles = [] - - unless skip_lookup - Pathname.pwd.ascend do |path| - thorfiles = Bundler::Thor::Util.globs_for(path).map { |g| Dir[g] }.flatten - break unless thorfiles.empty? - end - end - - files = (relevant_to ? thorfiles_relevant_to(relevant_to) : Bundler::Thor::Util.thor_root_glob) - files += thorfiles - files -= ["#{thor_root}/thor.yml"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Load Bundler::Thorfiles relevant to the given method. If you provide "foo:bar" it - # will load all thor files in the thor.yaml that has "foo" e "foo:bar" - # namespaces registered. - # - def thorfiles_relevant_to(meth) - lookup = [meth, meth.split(":")[0...-1].join(":")] - - files = thor_yaml.select do |_, v| - v[:namespaces] && !(v[:namespaces] & lookup).empty? - end - - files.map { |_, v| File.join(thor_root, (v[:filename]).to_s) } - end - - # Display information about the given klasses. If with_module is given, - # it shows a table with information extracted from the yaml file. - # - def display_klasses(with_modules = false, show_internal = false, klasses = Bundler::Thor::Base.subclasses) - klasses -= [Bundler::Thor, Bundler::Thor::Runner, Bundler::Thor::Group] unless show_internal - - raise Error, "No Bundler::Thor commands available" if klasses.empty? - show_modules if with_modules && !thor_yaml.empty? - - list = Hash.new { |h, k| h[k] = [] } - groups = klasses.select { |k| k.ancestors.include?(Bundler::Thor::Group) } - - # Get classes which inherit from Bundler::Thor - (klasses - groups).each { |k| list[k.namespace.split(":").first] += k.printable_commands(false) } - - # Get classes which inherit from Bundler::Thor::Base - groups.map! { |k| k.printable_commands(false).first } - list["root"] = groups - - # Order namespaces with default coming first - list = list.sort { |a, b| a[0].sub(/^default/, "") <=> b[0].sub(/^default/, "") } - list.each { |n, commands| display_commands(n, commands) unless commands.empty? } - end - - def display_commands(namespace, list) #:nodoc: - list.sort! { |a, b| a[0] <=> b[0] } - - say shell.set_color(namespace, :blue, true) - say "-" * namespace.size - - print_table(list, :truncate => true) - say - end - alias_method :display_tasks, :display_commands - - def show_modules #:nodoc: - info = [] - labels = %w(Modules Namespaces) - - info << labels - info << ["-" * labels[0].size, "-" * labels[1].size] - - thor_yaml.each do |name, hash| - info << [name, hash[:namespaces].join(", ")] - end - - print_table info - say "" - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/shell.rb b/lib/bundler/vendor/thor/lib/thor/shell.rb deleted file mode 100644 index e945549324..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/shell.rb +++ /dev/null @@ -1,81 +0,0 @@ -require "rbconfig" - -class Bundler::Thor - module Base - class << self - attr_writer :shell - - # Returns the shell used in all Bundler::Thor classes. If you are in a Unix platform - # it will use a colored log, otherwise it will use a basic one without color. - # - def shell - @shell ||= if ENV["THOR_SHELL"] && !ENV["THOR_SHELL"].empty? - Bundler::Thor::Shell.const_get(ENV["THOR_SHELL"]) - elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"] - Bundler::Thor::Shell::Basic - else - Bundler::Thor::Shell::Color - end - end - end - end - - module Shell - SHELL_DELEGATED_METHODS = [:ask, :error, :set_color, :yes?, :no?, :say, :say_status, :print_in_columns, :print_table, :print_wrapped, :file_collision, :terminal_width] - attr_writer :shell - - autoload :Basic, "bundler/vendor/thor/lib/thor/shell/basic" - autoload :Color, "bundler/vendor/thor/lib/thor/shell/color" - autoload :HTML, "bundler/vendor/thor/lib/thor/shell/html" - - # Add shell to initialize config values. - # - # ==== Configuration - # shell<Object>:: An instance of the shell to be used. - # - # ==== Examples - # - # class MyScript < Bundler::Thor - # argument :first, :type => :numeric - # end - # - # MyScript.new [1.0], { :foo => :bar }, :shell => Bundler::Thor::Shell::Basic.new - # - def initialize(args = [], options = {}, config = {}) - super - self.shell = config[:shell] - shell.base ||= self if shell.respond_to?(:base) - end - - # Holds the shell for the given Bundler::Thor instance. If no shell is given, - # it gets a default shell from Bundler::Thor::Base.shell. - def shell - @shell ||= Bundler::Thor::Base.shell.new - end - - # Common methods that are delegated to the shell. - SHELL_DELEGATED_METHODS.each do |method| - module_eval <<-METHOD, __FILE__, __LINE__ - def #{method}(*args,&block) - shell.#{method}(*args,&block) - end - METHOD - end - - # Yields the given block with padding. - def with_padding - shell.padding += 1 - yield - ensure - shell.padding -= 1 - end - - protected - - # Allow shell to be shared between invocations. - # - def _shared_configuration #:nodoc: - super.merge!(:shell => shell) - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb b/lib/bundler/vendor/thor/lib/thor/shell/basic.rb deleted file mode 100644 index 5162390efd..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +++ /dev/null @@ -1,437 +0,0 @@ -class Bundler::Thor - module Shell - class Basic - attr_accessor :base - attr_reader :padding - - # Initialize base, mute and padding to nil. - # - def initialize #:nodoc: - @base = nil - @mute = false - @padding = 0 - @always_force = false - end - - # Mute everything that's inside given block - # - def mute - @mute = true - yield - ensure - @mute = false - end - - # Check if base is muted - # - def mute? - @mute - end - - # Sets the output padding, not allowing less than zero values. - # - def padding=(value) - @padding = [0, value].max - end - - # Sets the output padding while executing a block and resets it. - # - def indent(count = 1) - orig_padding = padding - self.padding = padding + count - yield - self.padding = orig_padding - end - - # Asks something to the user and receives a response. - # - # If asked to limit the correct responses, you can pass in an - # array of acceptable answers. If one of those is not supplied, - # they will be shown a message stating that one of those answers - # must be given and re-asked the question. - # - # If asking for sensitive information, the :echo option can be set - # to false to mask user input from $stdin. - # - # If the required input is a path, then set the path option to - # true. This will enable tab completion for file paths relative - # to the current working directory on systems that support - # Readline. - # - # ==== Example - # ask("What is your name?") - # - # ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"]) - # - # ask("What is your password?", :echo => false) - # - # ask("Where should the file be saved?", :path => true) - # - def ask(statement, *args) - options = args.last.is_a?(Hash) ? args.pop : {} - color = args.first - - if options[:limited_to] - ask_filtered(statement, color, options) - else - ask_simply(statement, color, options) - end - end - - # Say (print) something to the user. If the sentence ends with a whitespace - # or tab character, a new line is not appended (print + flush). Otherwise - # are passed straight to puts (behavior got from Highline). - # - # ==== Example - # say("I know you knew that.") - # - def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/)) - buffer = prepare_message(message, *color) - buffer << "\n" if force_new_line && !message.to_s.end_with?("\n") - - stdout.print(buffer) - stdout.flush - end - - # Say a status with the given color and appends the message. Since this - # method is used frequently by actions, it allows nil or false to be given - # in log_status, avoiding the message from being shown. If a Symbol is - # given in log_status, it's used as the color. - # - def say_status(status, message, log_status = true) - return if quiet? || log_status == false - spaces = " " * (padding + 1) - color = log_status.is_a?(Symbol) ? log_status : :green - - status = status.to_s.rjust(12) - status = set_color status, color, true if color - - buffer = "#{status}#{spaces}#{message}" - buffer = "#{buffer}\n" unless buffer.end_with?("\n") - - stdout.print(buffer) - stdout.flush - end - - # Make a question the to user and returns true if the user replies "y" or - # "yes". - # - def yes?(statement, color = nil) - !!(ask(statement, color, :add_to_history => false) =~ is?(:yes)) - end - - # Make a question the to user and returns true if the user replies "n" or - # "no". - # - def no?(statement, color = nil) - !!(ask(statement, color, :add_to_history => false) =~ is?(:no)) - end - - # Prints values in columns - # - # ==== Parameters - # Array[String, String, ...] - # - def print_in_columns(array) - return if array.empty? - colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2 - array.each_with_index do |value, index| - # Don't output trailing spaces when printing the last column - if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length - stdout.puts value - else - stdout.printf("%-#{colwidth}s", value) - end - end - end - - # Prints a table. - # - # ==== Parameters - # Array[Array[String, String, ...]] - # - # ==== Options - # indent<Integer>:: Indent the first column by indent value. - # colwidth<Integer>:: Force the first column to colwidth spaces wide. - # - def print_table(array, options = {}) # rubocop:disable MethodLength - return if array.empty? - - formats = [] - indent = options[:indent].to_i - colwidth = options[:colwidth] - options[:truncate] = terminal_width if options[:truncate] == true - - formats << "%-#{colwidth + 2}s".dup if colwidth - start = colwidth ? 1 : 0 - - colcount = array.max { |a, b| a.size <=> b.size }.size - - maximas = [] - - start.upto(colcount - 1) do |index| - maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max - maximas << maxima - formats << if index == colcount - 1 - # Don't output 2 trailing spaces when printing the last column - "%-s".dup - else - "%-#{maxima + 2}s".dup - end - end - - formats[0] = formats[0].insert(0, " " * indent) - formats << "%s" - - array.each do |row| - sentence = "".dup - - row.each_with_index do |column, index| - maxima = maximas[index] - - f = if column.is_a?(Numeric) - if index == row.size - 1 - # Don't output 2 trailing spaces when printing the last column - "%#{maxima}s" - else - "%#{maxima}s " - end - else - formats[index] - end - sentence << f % column.to_s - end - - sentence = truncate(sentence, options[:truncate]) if options[:truncate] - stdout.puts sentence - end - end - - # Prints a long string, word-wrapping the text to the current width of the - # terminal display. Ideal for printing heredocs. - # - # ==== Parameters - # String - # - # ==== Options - # indent<Integer>:: Indent each line of the printed paragraph by indent value. - # - def print_wrapped(message, options = {}) - indent = options[:indent] || 0 - width = terminal_width - indent - paras = message.split("\n\n") - - paras.map! do |unwrapped| - unwrapped.strip.tr("\n", " ").squeeze(" ").gsub(/.{1,#{width}}(?:\s|\Z)/) { ($& + 5.chr).gsub(/\n\005/, "\n").gsub(/\005/, "\n") } - end - - paras.each do |para| - para.split("\n").each do |line| - stdout.puts line.insert(0, " " * indent) - end - stdout.puts unless para == paras.last - end - end - - # Deals with file collision and returns true if the file should be - # overwritten and false otherwise. If a block is given, it uses the block - # response as the content for the diff. - # - # ==== Parameters - # destination<String>:: the destination file to solve conflicts - # block<Proc>:: an optional block that returns the value to be used in diff - # - def file_collision(destination) - return true if @always_force - options = block_given? ? "[Ynaqdh]" : "[Ynaqh]" - - loop do - answer = ask( - %[Overwrite #{destination}? (enter "h" for help) #{options}], - :add_to_history => false - ) - - case answer - when nil - say "" - return true - when is?(:yes), is?(:force), "" - return true - when is?(:no), is?(:skip) - return false - when is?(:always) - return @always_force = true - when is?(:quit) - say "Aborting..." - raise SystemExit - when is?(:diff) - show_diff(destination, yield) if block_given? - say "Retrying..." - else - say file_collision_help - end - end - end - - # This code was copied from Rake, available under MIT-LICENSE - # Copyright (c) 2003, 2004 Jim Weirich - def terminal_width - result = if ENV["THOR_COLUMNS"] - ENV["THOR_COLUMNS"].to_i - else - unix? ? dynamic_width : 80 - end - result < 10 ? 80 : result - rescue - 80 - end - - # Called if something goes wrong during the execution. This is used by Bundler::Thor - # internally and should not be used inside your scripts. If something went - # wrong, you can always raise an exception. If you raise a Bundler::Thor::Error, it - # will be rescued and wrapped in the method below. - # - def error(statement) - stderr.puts statement - end - - # Apply color to the given string with optional bold. Disabled in the - # Bundler::Thor::Shell::Basic class. - # - def set_color(string, *) #:nodoc: - string - end - - protected - - def prepare_message(message, *color) - spaces = " " * padding - spaces + set_color(message.to_s, *color) - end - - def can_display_colors? - false - end - - def lookup_color(color) - return color unless color.is_a?(Symbol) - self.class.const_get(color.to_s.upcase) - end - - def stdout - $stdout - end - - def stderr - $stderr - end - - def is?(value) #:nodoc: - value = value.to_s - - if value.size == 1 - /\A#{value}\z/i - else - /\A(#{value}|#{value[0, 1]})\z/i - end - end - - def file_collision_help #:nodoc: - <<-HELP - Y - yes, overwrite - n - no, do not overwrite - a - all, overwrite this and all others - q - quit, abort - d - diff, show the differences between the old and the new - h - help, show this help - HELP - end - - def show_diff(destination, content) #:nodoc: - diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u" - - require "tempfile" - Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp| - temp.write content - temp.rewind - system %(#{diff_cmd} "#{destination}" "#{temp.path}") - end - end - - def quiet? #:nodoc: - mute? || (base && base.options[:quiet]) - end - - # Calculate the dynamic width of the terminal - def dynamic_width - @dynamic_width ||= (dynamic_width_stty.nonzero? || dynamic_width_tput) - end - - def dynamic_width_stty - `stty size 2>/dev/null`.split[1].to_i - end - - def dynamic_width_tput - `tput cols 2>/dev/null`.to_i - end - - def unix? - RUBY_PLATFORM =~ /(aix|darwin|linux|(net|free|open)bsd|cygwin|solaris|irix|hpux)/i - end - - def truncate(string, width) - as_unicode do - chars = string.chars.to_a - if chars.length <= width - chars.join - else - chars[0, width - 3].join + "..." - end - end - end - - if "".respond_to?(:encode) - def as_unicode - yield - end - else - def as_unicode - old = $KCODE - $KCODE = "U" - yield - ensure - $KCODE = old - end - end - - def ask_simply(statement, color, options) - default = options[:default] - message = [statement, ("(#{default})" if default), nil].uniq.join(" ") - message = prepare_message(message, *color) - result = Bundler::Thor::LineEditor.readline(message, options) - - return unless result - - result = result.strip - - if default && result == "" - default - else - result - end - end - - def ask_filtered(statement, color, options) - answer_set = options[:limited_to] - correct_answer = nil - until correct_answer - answers = answer_set.join(", ") - answer = ask_simply("#{statement} [#{answers}]", color, options) - correct_answer = answer_set.include?(answer) ? answer : nil - say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer - end - correct_answer - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/color.rb b/lib/bundler/vendor/thor/lib/thor/shell/color.rb deleted file mode 100644 index da289cb50c..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/shell/color.rb +++ /dev/null @@ -1,149 +0,0 @@ -require "bundler/vendor/thor/lib/thor/shell/basic" - -class Bundler::Thor - module Shell - # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check - # Bundler::Thor::Shell::Basic to see all available methods. - # - class Color < Basic - # Embed in a String to clear all previous ANSI sequences. - CLEAR = "\e[0m" - # The start of an ANSI bold sequence. - BOLD = "\e[1m" - - # Set the terminal's foreground ANSI color to black. - BLACK = "\e[30m" - # Set the terminal's foreground ANSI color to red. - RED = "\e[31m" - # Set the terminal's foreground ANSI color to green. - GREEN = "\e[32m" - # Set the terminal's foreground ANSI color to yellow. - YELLOW = "\e[33m" - # Set the terminal's foreground ANSI color to blue. - BLUE = "\e[34m" - # Set the terminal's foreground ANSI color to magenta. - MAGENTA = "\e[35m" - # Set the terminal's foreground ANSI color to cyan. - CYAN = "\e[36m" - # Set the terminal's foreground ANSI color to white. - WHITE = "\e[37m" - - # Set the terminal's background ANSI color to black. - ON_BLACK = "\e[40m" - # Set the terminal's background ANSI color to red. - ON_RED = "\e[41m" - # Set the terminal's background ANSI color to green. - ON_GREEN = "\e[42m" - # Set the terminal's background ANSI color to yellow. - ON_YELLOW = "\e[43m" - # Set the terminal's background ANSI color to blue. - ON_BLUE = "\e[44m" - # Set the terminal's background ANSI color to magenta. - ON_MAGENTA = "\e[45m" - # Set the terminal's background ANSI color to cyan. - ON_CYAN = "\e[46m" - # Set the terminal's background ANSI color to white. - ON_WHITE = "\e[47m" - - # Set color by using a string or one of the defined constants. If a third - # option is set to true, it also adds bold to the string. This is based - # on Highline implementation and it automatically appends CLEAR to the end - # of the returned String. - # - # Pass foreground, background and bold options to this method as - # symbols. - # - # Example: - # - # set_color "Hi!", :red, :on_white, :bold - # - # The available colors are: - # - # :bold - # :black - # :red - # :green - # :yellow - # :blue - # :magenta - # :cyan - # :white - # :on_black - # :on_red - # :on_green - # :on_yellow - # :on_blue - # :on_magenta - # :on_cyan - # :on_white - def set_color(string, *colors) - if colors.compact.empty? || !can_display_colors? - string - elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } - ansi_colors = colors.map { |color| lookup_color(color) } - "#{ansi_colors.join}#{string}#{CLEAR}" - else - # The old API was `set_color(color, bold=boolean)`. We - # continue to support the old API because you should never - # break old APIs unnecessarily :P - foreground, bold = colors - foreground = self.class.const_get(foreground.to_s.upcase) if foreground.is_a?(Symbol) - - bold = bold ? BOLD : "" - "#{bold}#{foreground}#{string}#{CLEAR}" - end - end - - protected - - def can_display_colors? - stdout.tty? - end - - # Overwrite show_diff to show diff with colors if Diff::LCS is - # available. - # - def show_diff(destination, content) #:nodoc: - if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? - actual = File.binread(destination).to_s.split("\n") - content = content.to_s.split("\n") - - Diff::LCS.sdiff(actual, content).each do |diff| - output_diff_line(diff) - end - else - super - end - end - - def output_diff_line(diff) #:nodoc: - case diff.action - when "-" - say "- #{diff.old_element.chomp}", :red, true - when "+" - say "+ #{diff.new_element.chomp}", :green, true - when "!" - say "- #{diff.old_element.chomp}", :red, true - say "+ #{diff.new_element.chomp}", :green, true - else - say " #{diff.old_element.chomp}", nil, true - end - end - - # Check if Diff::LCS is loaded. If it is, use it to create pretty output - # for diff. - # - def diff_lcs_loaded? #:nodoc: - return true if defined?(Diff::LCS) - return @diff_lcs_loaded unless @diff_lcs_loaded.nil? - - @diff_lcs_loaded = begin - require "diff/lcs" - true - rescue LoadError - false - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/shell/html.rb b/lib/bundler/vendor/thor/lib/thor/shell/html.rb deleted file mode 100644 index 83d2054988..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/shell/html.rb +++ /dev/null @@ -1,126 +0,0 @@ -require "bundler/vendor/thor/lib/thor/shell/basic" - -class Bundler::Thor - module Shell - # Inherit from Bundler::Thor::Shell::Basic and add set_color behavior. Check - # Bundler::Thor::Shell::Basic to see all available methods. - # - class HTML < Basic - # The start of an HTML bold sequence. - BOLD = "font-weight: bold" - - # Set the terminal's foreground HTML color to black. - BLACK = "color: black" - # Set the terminal's foreground HTML color to red. - RED = "color: red" - # Set the terminal's foreground HTML color to green. - GREEN = "color: green" - # Set the terminal's foreground HTML color to yellow. - YELLOW = "color: yellow" - # Set the terminal's foreground HTML color to blue. - BLUE = "color: blue" - # Set the terminal's foreground HTML color to magenta. - MAGENTA = "color: magenta" - # Set the terminal's foreground HTML color to cyan. - CYAN = "color: cyan" - # Set the terminal's foreground HTML color to white. - WHITE = "color: white" - - # Set the terminal's background HTML color to black. - ON_BLACK = "background-color: black" - # Set the terminal's background HTML color to red. - ON_RED = "background-color: red" - # Set the terminal's background HTML color to green. - ON_GREEN = "background-color: green" - # Set the terminal's background HTML color to yellow. - ON_YELLOW = "background-color: yellow" - # Set the terminal's background HTML color to blue. - ON_BLUE = "background-color: blue" - # Set the terminal's background HTML color to magenta. - ON_MAGENTA = "background-color: magenta" - # Set the terminal's background HTML color to cyan. - ON_CYAN = "background-color: cyan" - # Set the terminal's background HTML color to white. - ON_WHITE = "background-color: white" - - # Set color by using a string or one of the defined constants. If a third - # option is set to true, it also adds bold to the string. This is based - # on Highline implementation and it automatically appends CLEAR to the end - # of the returned String. - # - def set_color(string, *colors) - if colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) } - html_colors = colors.map { |color| lookup_color(color) } - "<span style=\"#{html_colors.join('; ')};\">#{string}</span>" - else - color, bold = colors - html_color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol) - styles = [html_color] - styles << BOLD if bold - "<span style=\"#{styles.join('; ')};\">#{string}</span>" - end - end - - # Ask something to the user and receives a response. - # - # ==== Example - # ask("What is your name?") - # - # TODO: Implement #ask for Bundler::Thor::Shell::HTML - def ask(statement, color = nil) - raise NotImplementedError, "Implement #ask for Bundler::Thor::Shell::HTML" - end - - protected - - def can_display_colors? - true - end - - # Overwrite show_diff to show diff with colors if Diff::LCS is - # available. - # - def show_diff(destination, content) #:nodoc: - if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil? - actual = File.binread(destination).to_s.split("\n") - content = content.to_s.split("\n") - - Diff::LCS.sdiff(actual, content).each do |diff| - output_diff_line(diff) - end - else - super - end - end - - def output_diff_line(diff) #:nodoc: - case diff.action - when "-" - say "- #{diff.old_element.chomp}", :red, true - when "+" - say "+ #{diff.new_element.chomp}", :green, true - when "!" - say "- #{diff.old_element.chomp}", :red, true - say "+ #{diff.new_element.chomp}", :green, true - else - say " #{diff.old_element.chomp}", nil, true - end - end - - # Check if Diff::LCS is loaded. If it is, use it to create pretty output - # for diff. - # - def diff_lcs_loaded? #:nodoc: - return true if defined?(Diff::LCS) - return @diff_lcs_loaded unless @diff_lcs_loaded.nil? - - @diff_lcs_loaded = begin - require "diff/lcs" - true - rescue LoadError - false - end - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb deleted file mode 100644 index 5d03177a28..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/util.rb +++ /dev/null @@ -1,268 +0,0 @@ -require "rbconfig" - -class Bundler::Thor - module Sandbox #:nodoc: - end - - # This module holds several utilities: - # - # 1) Methods to convert thor namespaces to constants and vice-versa. - # - # Bundler::Thor::Util.namespace_from_thor_class(Foo::Bar::Baz) #=> "foo:bar:baz" - # - # 2) Loading thor files and sandboxing: - # - # Bundler::Thor::Util.load_thorfile("~/.thor/foo") - # - module Util - class << self - # Receives a namespace and search for it in the Bundler::Thor::Base subclasses. - # - # ==== Parameters - # namespace<String>:: The namespace to search for. - # - def find_by_namespace(namespace) - namespace = "default#{namespace}" if namespace.empty? || namespace =~ /^:/ - Bundler::Thor::Base.subclasses.detect { |klass| klass.namespace == namespace } - end - - # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor - # commands can be added to a sandbox, this method is also responsable for - # removing the sandbox namespace. - # - # This method should not be used in general because it's used to deal with - # older versions of Bundler::Thor. On current versions, if you need to get the - # namespace from a class, just call namespace on it. - # - # ==== Parameters - # constant<Object>:: The constant to be converted to the thor path. - # - # ==== Returns - # String:: If we receive Foo::Bar::Baz it returns "foo:bar:baz" - # - def namespace_from_thor_class(constant) - constant = constant.to_s.gsub(/^Bundler::Thor::Sandbox::/, "") - constant = snake_case(constant).squeeze(":") - constant - end - - # Given the contents, evaluate it inside the sandbox and returns the - # namespaces defined in the sandbox. - # - # ==== Parameters - # contents<String> - # - # ==== Returns - # Array[Object] - # - def namespaces_in_content(contents, file = __FILE__) - old_constants = Bundler::Thor::Base.subclasses.dup - Bundler::Thor::Base.subclasses.clear - - load_thorfile(file, contents) - - new_constants = Bundler::Thor::Base.subclasses.dup - Bundler::Thor::Base.subclasses.replace(old_constants) - - new_constants.map!(&:namespace) - new_constants.compact! - new_constants - end - - # Returns the thor classes declared inside the given class. - # - def thor_classes_in(klass) - stringfied_constants = klass.constants.map(&:to_s) - Bundler::Thor::Base.subclasses.select do |subclass| - next unless subclass.name - stringfied_constants.include?(subclass.name.gsub("#{klass.name}::", "")) - end - end - - # Receives a string and convert it to snake case. SnakeCase returns snake_case. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def snake_case(str) - return str.downcase if str =~ /^[A-Z_]+$/ - str.gsub(/\B[A-Z]/, '_\&').squeeze("_") =~ /_*(.*)/ - $+.downcase - end - - # Receives a string and convert it to camel case. camel_case returns CamelCase. - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def camel_case(str) - return str if str !~ /_/ && str =~ /[A-Z]+.*/ - str.split("_").map(&:capitalize).join - end - - # Receives a namespace and tries to retrieve a Bundler::Thor or Bundler::Thor::Group class - # from it. It first searches for a class using the all the given namespace, - # if it's not found, removes the highest entry and searches for the class - # again. If found, returns the highest entry as the class name. - # - # ==== Examples - # - # class Foo::Bar < Bundler::Thor - # def baz - # end - # end - # - # class Baz::Foo < Bundler::Thor::Group - # end - # - # Bundler::Thor::Util.namespace_to_thor_class("foo:bar") #=> Foo::Bar, nil # will invoke default command - # Bundler::Thor::Util.namespace_to_thor_class("baz:foo") #=> Baz::Foo, nil - # Bundler::Thor::Util.namespace_to_thor_class("foo:bar:baz") #=> Foo::Bar, "baz" - # - # ==== Parameters - # namespace<String> - # - def find_class_and_command_by_namespace(namespace, fallback = true) - if namespace.include?(":") # look for a namespaced command - pieces = namespace.split(":") - command = pieces.pop - klass = Bundler::Thor::Util.find_by_namespace(pieces.join(":")) - end - unless klass # look for a Bundler::Thor::Group with the right name - klass = Bundler::Thor::Util.find_by_namespace(namespace) - command = nil - end - if !klass && fallback # try a command in the default namespace - command = namespace - klass = Bundler::Thor::Util.find_by_namespace("") - end - [klass, command] - end - alias_method :find_class_and_task_by_namespace, :find_class_and_command_by_namespace - - # Receives a path and load the thor file in the path. The file is evaluated - # inside the sandbox to avoid namespacing conflicts. - # - def load_thorfile(path, content = nil, debug = false) - content ||= File.binread(path) - - begin - Bundler::Thor::Sandbox.class_eval(content, path) - rescue StandardError => e - $stderr.puts("WARNING: unable to load thorfile #{path.inspect}: #{e.message}") - if debug - $stderr.puts(*e.backtrace) - else - $stderr.puts(e.backtrace.first) - end - end - end - - def user_home - @@user_home ||= if ENV["HOME"] - ENV["HOME"] - elsif ENV["USERPROFILE"] - ENV["USERPROFILE"] - elsif ENV["HOMEDRIVE"] && ENV["HOMEPATH"] - File.join(ENV["HOMEDRIVE"], ENV["HOMEPATH"]) - elsif ENV["APPDATA"] - ENV["APPDATA"] - else - begin - File.expand_path("~") - rescue - if File::ALT_SEPARATOR - "C:/" - else - "/" - end - end - end - end - - # Returns the root where thor files are located, depending on the OS. - # - def thor_root - File.join(user_home, ".thor").tr('\\', "/") - end - - # Returns the files in the thor root. On Windows thor_root will be something - # like this: - # - # C:\Documents and Settings\james\.thor - # - # If we don't #gsub the \ character, Dir.glob will fail. - # - def thor_root_glob - files = Dir["#{escape_globs(thor_root)}/*"] - - files.map! do |file| - File.directory?(file) ? File.join(file, "main.thor") : file - end - end - - # Where to look for Bundler::Thor files. - # - def globs_for(path) - path = escape_globs(path) - ["#{path}/Bundler::Thorfile", "#{path}/*.thor", "#{path}/tasks/*.thor", "#{path}/lib/tasks/*.thor"] - end - - # Return the path to the ruby interpreter taking into account multiple - # installations and windows extensions. - # - def ruby_command - @ruby_command ||= begin - ruby_name = RbConfig::CONFIG["ruby_install_name"] - ruby = File.join(RbConfig::CONFIG["bindir"], ruby_name) - ruby << RbConfig::CONFIG["EXEEXT"] - - # avoid using different name than ruby (on platforms supporting links) - if ruby_name != "ruby" && File.respond_to?(:readlink) - begin - alternate_ruby = File.join(RbConfig::CONFIG["bindir"], "ruby") - alternate_ruby << RbConfig::CONFIG["EXEEXT"] - - # ruby is a symlink - if File.symlink? alternate_ruby - linked_ruby = File.readlink alternate_ruby - - # symlink points to 'ruby_install_name' - ruby = alternate_ruby if linked_ruby == ruby_name || linked_ruby == ruby - end - rescue NotImplementedError # rubocop:disable HandleExceptions - # just ignore on windows - end - end - - # escape string in case path to ruby executable contain spaces. - ruby.sub!(/.*\s.*/m, '"\&"') - ruby - end - end - - # Returns a string that has had any glob characters escaped. - # The glob characters are `* ? { } [ ]`. - # - # ==== Examples - # - # Bundler::Thor::Util.escape_globs('[apps]') # => '\[apps\]' - # - # ==== Parameters - # String - # - # ==== Returns - # String - # - def escape_globs(path) - path.to_s.gsub(/[*?{}\[\]]/, '\\\\\\&') - end - end - end -end diff --git a/lib/bundler/vendor/thor/lib/thor/version.rb b/lib/bundler/vendor/thor/lib/thor/version.rb deleted file mode 100644 index df8f18821a..0000000000 --- a/lib/bundler/vendor/thor/lib/thor/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Bundler::Thor - VERSION = "0.20.0" -end diff --git a/lib/bundler/vendored_fileutils.rb b/lib/bundler/vendored_fileutils.rb deleted file mode 100644 index d14e98baf7..0000000000 --- a/lib/bundler/vendored_fileutils.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -module Bundler; end -if RUBY_VERSION >= "2.4" - require "bundler/vendor/fileutils/lib/fileutils" -else - # the version we vendor is 2.4+ - require "fileutils" -end diff --git a/lib/bundler/vendored_molinillo.rb b/lib/bundler/vendored_molinillo.rb deleted file mode 100644 index 061b634f72..0000000000 --- a/lib/bundler/vendored_molinillo.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -module Bundler; end -require "bundler/vendor/molinillo/lib/molinillo" diff --git a/lib/bundler/vendored_persistent.rb b/lib/bundler/vendored_persistent.rb deleted file mode 100644 index de9c42fcc1..0000000000 --- a/lib/bundler/vendored_persistent.rb +++ /dev/null @@ -1,52 +0,0 @@ -# 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 - require "openssl" -rescue LoadError - # some Ruby builds don't have OpenSSL -end -module Bundler - module Persistent - module Net - module HTTP - end - end - end -end -require "bundler/vendor/net-http-persistent/lib/net/http/persistent" - -module Bundler - class PersistentHTTP < Persistent::Net::HTTP::Persistent - def connection_for(uri) - connection = super - warn_old_tls_version_rubygems_connection(uri, connection) - connection - end - - def warn_old_tls_version_rubygems_connection(uri, connection) - return unless connection.use_ssl? - return unless (uri.host || "").end_with?("rubygems.org") - - socket = connection.instance_variable_get(:@socket) - return unless socket - socket_io = socket.io - return unless socket_io.respond_to?(:ssl_version) - ssl_version = socket_io.ssl_version - - case ssl_version - when /TLSv([\d\.]+)/ - version = Gem::Version.new($1) - if version < Gem::Version.new("1.2") - Bundler.ui.warn \ - "Warning: Your Ruby version is compiled against a copy of OpenSSL that is very old. " \ - "Starting in January 2018, RubyGems.org will refuse connection requests from these " \ - "very old versions of OpenSSL. If you will need to continue installing gems after " \ - "January 2018, please follow this guide to upgrade: http://ruby.to/tls-outdated.", - :wrap => true - end - end - end - end -end diff --git a/lib/bundler/vendored_thor.rb b/lib/bundler/vendored_thor.rb deleted file mode 100644 index 8cca090f55..0000000000 --- a/lib/bundler/vendored_thor.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -module Bundler - def self.require_thor_actions - Kernel.send(:require, "bundler/vendor/thor/lib/thor/actions") - end -end -require "bundler/vendor/thor/lib/thor" diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb deleted file mode 100644 index 87b648681a..0000000000 --- a/lib/bundler/version.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: false - -# Ruby 1.9.3 and old RubyGems don't play nice with frozen version strings -# rubocop:disable MutableConstant - -module Bundler - # We're doing this because we might write tests that deal - # with other versions of bundler and we are unsure how to - # handle this better. - VERSION = "1.16.1" unless defined?(::Bundler::VERSION) - - def self.overwrite_loaded_gem_version - begin - require "rubygems" - rescue LoadError - return - end - return unless bundler_spec = Gem.loaded_specs["bundler"] - return if bundler_spec.version == VERSION - bundler_spec.version = Bundler::VERSION - end - private_class_method :overwrite_loaded_gem_version - overwrite_loaded_gem_version - - def self.bundler_major_version - @bundler_major_version ||= VERSION.split(".").first.to_i - end -end diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb deleted file mode 100644 index ec25716cde..0000000000 --- a/lib/bundler/version_ranges.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -module Bundler - module VersionRanges - NEq = Struct.new(:version) - ReqR = Struct.new(:left, :right) - class ReqR - Endpoint = Struct.new(:version, :inclusive) - def to_s - "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}" - end - INFINITY = Object.new.freeze - ZERO = Gem::Version.new("0.a") - - def cover?(v) - return false if left.inclusive && left.version > v - return false if !left.inclusive && left.version >= v - - if right.version != INFINITY - return false if right.inclusive && right.version < v - return false if !right.inclusive && right.version <= v - end - - true - end - - def empty? - left.version == right.version && !(left.inclusive && right.inclusive) - end - - def single? - left.version == right.version - end - - UNIVERSAL = ReqR.new(ReqR::Endpoint.new(Gem::Version.new("0.a"), true), ReqR::Endpoint.new(ReqR::INFINITY, false)).freeze - end - - def self.for_many(requirements) - requirements = requirements.map(&:requirements).flatten(1).map {|r| r.join(" ") } - requirements << ">= 0.a" if requirements.empty? - requirement = Gem::Requirement.new(requirements) - self.for(requirement) - end - - def self.for(requirement) - ranges = requirement.requirements.map do |op, v| - case op - when "=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v, true)) - when "!=" then NEq.new(v) - when ">=" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(ReqR::INFINITY, false)) - when ">" then ReqR.new(ReqR::Endpoint.new(v, false), ReqR::Endpoint.new(ReqR::INFINITY, false)) - when "<" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, false)) - when "<=" then ReqR.new(ReqR::Endpoint.new(ReqR::ZERO, true), ReqR::Endpoint.new(v, true)) - when "~>" then ReqR.new(ReqR::Endpoint.new(v, true), ReqR::Endpoint.new(v.bump, false)) - else raise "unknown version op #{op} in requirement #{requirement}" - end - end.uniq - ranges, neqs = ranges.partition {|r| !r.is_a?(NEq) } - - [ranges.sort_by {|range| [range.left.version, range.left.inclusive ? 0 : 1] }, neqs.map(&:version)] - end - - def self.empty?(ranges, neqs) - !ranges.reduce(ReqR::UNIVERSAL) do |last_range, curr_range| - next false unless last_range - next false if curr_range.single? && neqs.include?(curr_range.left.version) - next curr_range if last_range.right.version == ReqR::INFINITY - case last_range.right.version <=> curr_range.left.version - when 1 then next curr_range - when 0 then next(last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) && curr_range) - when -1 then next false - end - end - end - end -end diff --git a/lib/bundler/vlad.rb b/lib/bundler/vlad.rb deleted file mode 100644 index 68181e7db8..0000000000 --- a/lib/bundler/vlad.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true - -require "bundler/shared_helpers" -Bundler::SharedHelpers.major_deprecation 2, - "The Bundler task for Vlad" - -# Vlad task for Bundler. -# -# Add "require 'bundler/vlad'" in your Vlad deploy.rb, and -# include the vlad:bundle:install task in your vlad:deploy task. -require "bundler/deployment" - -include Rake::DSL if defined? Rake::DSL - -namespace :vlad do - Bundler::Deployment.define_task(Rake::RemoteTask, :remote_task, :roles => :app) -end diff --git a/lib/bundler/worker.rb b/lib/bundler/worker.rb deleted file mode 100644 index e91cfa7805..0000000000 --- a/lib/bundler/worker.rb +++ /dev/null @@ -1,106 +0,0 @@ -# frozen_string_literal: true - -require "thread" - -module Bundler - class Worker - POISON = Object.new - - class WrappedException < StandardError - attr_reader :exception - def initialize(exn) - @exception = exn - end - end - - # @return [String] the name of the worker - attr_reader :name - - # Creates a worker pool of specified size - # - # @param size [Integer] Size of pool - # @param name [String] name the name of the worker - # @param func [Proc] job to run in inside the worker pool - def initialize(size, name, func) - @name = name - @request_queue = Queue.new - @response_queue = Queue.new - @func = func - @size = size - @threads = nil - SharedHelpers.trap("INT") { abort_threads } - end - - # Enqueue a request to be executed in the worker pool - # - # @param obj [String] mostly it is name of spec that should be downloaded - def enq(obj) - create_threads unless @threads - @request_queue.enq obj - end - - # Retrieves results of job function being executed in worker pool - def deq - result = @response_queue.deq - raise result.exception if result.is_a?(WrappedException) - result - end - - def stop - stop_threads - end - - private - - def process_queue(i) - loop do - obj = @request_queue.deq - break if obj.equal? POISON - @response_queue.enq apply_func(obj, i) - end - end - - def apply_func(obj, i) - @func.call(obj, i) - rescue Exception => e - WrappedException.new(e) - end - - # Stop the worker threads by sending a poison object down the request queue - # so as worker threads after retrieving it, shut themselves down - def stop_threads - return unless @threads - @threads.each { @request_queue.enq POISON } - @threads.each(&:join) - @threads = nil - end - - def abort_threads - return unless @threads - Bundler.ui.debug("\n#{caller.join("\n")}") - @threads.each(&:exit) - exit 1 - end - - def create_threads - creation_errors = [] - - @threads = Array.new(@size) do |i| - begin - Thread.start { process_queue(i) }.tap do |thread| - thread.name = "#{name} Worker ##{i}" if thread.respond_to?(:name=) - end - rescue ThreadError => e - creation_errors << e - nil - end - end.compact - - return if creation_errors.empty? - - message = "Failed to create threads for the #{name} worker: #{creation_errors.map(&:to_s).uniq.join(", ")}" - raise ThreadCreationError, message if @threads.empty? - Bundler.ui.info message - end - end -end diff --git a/lib/bundler/yaml_serializer.rb b/lib/bundler/yaml_serializer.rb deleted file mode 100644 index 0fd81c40ef..0000000000 --- a/lib/bundler/yaml_serializer.rb +++ /dev/null @@ -1,90 +0,0 @@ -# frozen_string_literal: true - -module Bundler - # A stub yaml serializer that can handle only hashes and strings (as of now). - module YAMLSerializer - module_function - - def dump(hash) - yaml = String.new("---") - yaml << dump_hash(hash) - end - - def dump_hash(hash) - yaml = String.new("\n") - hash.each do |k, v| - yaml << k << ":" - if v.is_a?(Hash) - yaml << dump_hash(v).gsub(/^(?!$)/, " ") # indent all non-empty lines - elsif v.is_a?(Array) # Expected to be array of strings - yaml << "\n- " << v.map {|s| s.to_s.gsub(/\s+/, " ").inspect }.join("\n- ") << "\n" - else - yaml << " " << v.to_s.gsub(/\s+/, " ").inspect << "\n" - end - end - yaml - end - - ARRAY_REGEX = / - ^ - (?:[ ]*-[ ]) # '- ' before array items - (['"]?) # optional opening quote - (.*) # value - \1 # matching closing quote - $ - /xo - - 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 - (['"]?) # optional opening quote - (.*) # value - \3 # matching closing quote - $ - /xo - - def load(str) - res = {} - stack = [res] - last_hash = nil - last_empty_key = nil - str.split(/\r?\n/).each do |line| - if match = HASH_REGEX.match(line) - indent, key, quote, val = match.captures - key = convert_to_backward_compatible_key(key) - depth = indent.scan(/ /).length - if quote.empty? && val.empty? - new_hash = {} - stack[depth][key] = new_hash - stack[depth + 1] = new_hash - last_empty_key = key - last_hash = stack[depth] - else - stack[depth][key] = val - end - elsif match = ARRAY_REGEX.match(line) - _, val = match.captures - last_hash[last_empty_key] = [] unless last_hash[last_empty_key].is_a?(Array) - - last_hash[last_empty_key].push(val) - end - end - res - end - - # for settings' keys - def convert_to_backward_compatible_key(key) - key = "#{key}/" if key =~ /https?:/i && key !~ %r{/\Z} - key = key.gsub(".", "__") if key.include?(".") - key - end - - class << self - private :dump_hash, :convert_to_backward_compatible_key - end - end -end diff --git a/lib/rubygems.rb b/lib/rubygems.rb index b7097f22cd..0475ced164 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -176,7 +176,7 @@ module Gem write_binary_errors end.freeze - USE_BUNDLER_FOR_GEMDEPS = true # :nodoc: + USE_BUNDLER_FOR_GEMDEPS = false # :nodoc: @@win_platform = nil diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb index 82afb8a068..f7f216e5e3 100644 --- a/lib/rubygems/test_case.rb +++ b/lib/rubygems/test_case.rb @@ -25,7 +25,9 @@ unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty? gem 'json' end -require 'bundler' +if Gem::USE_BUNDLER_FOR_GEMDEPS + require 'bundler' +end require 'minitest/autorun' require 'rubygems/deprecate' @@ -235,7 +237,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase @current_dir = Dir.pwd @fetcher = nil - Bundler.ui = Bundler::UI::Silent.new + if Gem::USE_BUNDLER_FOR_GEMDEPS + Bundler.ui = Bundler::UI::Silent.new + end @back_ui = Gem::DefaultUserInteraction.ui @ui = Gem::MockGemUi.new # This needs to be a new instance since we call use_ui(@ui) when we want to @@ -331,7 +335,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase Gem.loaded_specs.clear Gem.clear_default_specs Gem::Specification.unresolved_deps.clear - Bundler.reset! + if Gem::USE_BUNDLER_FOR_GEMDEPS + Bundler.reset! + end Gem.configuration.verbose = true Gem.configuration.update_sources = true |