diff options
Diffstat (limited to 'lib/bundler/source')
| -rw-r--r-- | lib/bundler/source/gemspec.rb | 5 | ||||
| -rw-r--r-- | lib/bundler/source/git.rb | 14 | ||||
| -rw-r--r-- | lib/bundler/source/git/git_proxy.rb | 98 | ||||
| -rw-r--r-- | lib/bundler/source/metadata.rb | 4 | ||||
| -rw-r--r-- | lib/bundler/source/path.rb | 28 | ||||
| -rw-r--r-- | lib/bundler/source/path/installer.rb | 2 | ||||
| -rw-r--r-- | lib/bundler/source/rubygems.rb | 170 | ||||
| -rw-r--r-- | lib/bundler/source/rubygems/remote.rb | 28 | ||||
| -rw-r--r-- | lib/bundler/source/rubygems_aggregate.rb | 5 |
9 files changed, 251 insertions, 103 deletions
diff --git a/lib/bundler/source/gemspec.rb b/lib/bundler/source/gemspec.rb index 7e3447e776..ed766dbe74 100644 --- a/lib/bundler/source/gemspec.rb +++ b/lib/bundler/source/gemspec.rb @@ -4,14 +4,15 @@ module Bundler class Source class Gemspec < Path attr_reader :gemspec + attr_writer :checksum_store def initialize(options) super @gemspec = options["gemspec"] end - def as_path_source - Path.new(options) + def to_s + "gemspec at `#{@path}`" end end end diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index d57944ee12..a002a2570a 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -191,8 +191,13 @@ module Bundler set_cache_path!(app_cache_path) if use_app_cache? if requires_checkout? && !@copied - fetch unless use_app_cache? - checkout + Plugin.hook(Plugin::Events::GIT_BEFORE_FETCH, self) + begin + fetch unless use_app_cache? + checkout + ensure + Plugin.hook(Plugin::Events::GIT_AFTER_FETCH, self) + end end local_specs @@ -238,7 +243,7 @@ module Bundler # 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 ||= if Bundler.feature_flag.global_gem_cache? + @cache_path ||= if Bundler.settings[:global_gem_cache] Bundler.user_cache else Bundler.bundle_path.join("cache", "bundler") @@ -268,7 +273,7 @@ module Bundler private def cache_to(custom_path, try_migrate: false) - return unless Bundler.feature_flag.cache_all? + return unless Bundler.settings[:cache_all] app_cache_path = app_cache_path(custom_path) @@ -416,7 +421,6 @@ module Bundler 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 diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index e01c5876de..8094dcaa9d 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -16,7 +16,7 @@ module Bundler def initialize(command) msg = String.new msg << "Bundler is trying to run `#{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 https://github.com/rubygems/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md " + msg << "this error message could probably be more useful. Please submit a ticket at https://github.com/ruby/rubygems/issues/new?labels=Bundler&template=bundler-related-issue.md " msg << "with steps to reproduce as well as the following\n\nCALLER: #{caller.join("\n")}" super msg end @@ -57,6 +57,29 @@ module Bundler attr_accessor :path, :uri, :branch, :tag, :ref, :explicit_ref attr_writer :revision + def self.version + @version ||= full_version[/((\.?\d+)+).*/, 1] + end + + def self.full_version + @full_version ||= begin + raise GitNotInstalledError.new unless Bundler.git_present? + + require "open3" + out, err, status = Open3.capture3("git", "--version") + + raise GitCommandError.new("--version", SharedHelpers.pwd, err) unless status.success? + Bundler.ui.warn err unless err.empty? + + out.sub(/git version\s*/, "").strip + end + end + + def self.reset + @version = nil + @full_version = nil + end + def initialize(path, uri, options = {}, revision = nil, git = nil) @path = path @uri = uri @@ -92,11 +115,11 @@ module Bundler end def version - @version ||= full_version.match(/((\.?\d+)+).*/)[1] + self.class.version end def full_version - @full_version ||= git_local("--version").sub(/git version\s*/, "").strip + self.class.full_version end def checkout @@ -121,7 +144,7 @@ module Bundler FileUtils.rm_rf(p) end git "clone", "--no-checkout", "--quiet", path.to_s, destination.to_s - File.chmod(((File.stat(destination).mode | 0o777) & ~File.umask), destination) + File.chmod((File.stat(destination).mode | 0o777) & ~File.umask, destination) rescue Errno::EEXIST => e file_path = e.message[%r{.*?((?:[a-zA-Z]:)?/.*)}, 1] raise GitError, "Bundler could not install a gem because it needs to " \ @@ -137,7 +160,7 @@ module Bundler git "fetch", "--force", "--quiet", *extra_fetch_args(ref), dir: destination end - git "reset", "--hard", @revision, dir: destination + git "reset", "--hard", revision, dir: destination if submodules git_retry "submodule", "update", "--init", "--recursive", dir: destination @@ -156,7 +179,7 @@ module Bundler private def git_remote_fetch(args) - command = ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact + command = fetch_command(args) command_with_no_credentials = check_allowed(command) Bundler::Retry.new("`#{command_with_no_credentials}` at #{path}", [MissingGitRevisionError]).attempts do @@ -166,6 +189,11 @@ module Bundler if err.include?("couldn't find remote ref") || err.include?("not our ref") raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri) else + if shallow? + args -= depth_args + command = fetch_command(args) + command_with_no_credentials = check_allowed(command) + end raise GitCommandError.new(command_with_no_credentials, path, err) end end @@ -178,23 +206,22 @@ module Bundler FileUtils.mkdir_p(p) end - command = ["clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s] + clone_args = extra_clone_args + command = clone_command(clone_args) command_with_no_credentials = check_allowed(command) Bundler::Retry.new("`#{command_with_no_credentials}`", [MissingGitRevisionError]).attempts do _, err, status = capture(command, nil) return extra_ref if status.success? - if err.include?("Could not find remote branch") + if err.include?("Could not find remote branch") || # git up to 2.49 + err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri) else - idx = command.index("--depth") - if idx - command.delete_at(idx) - command.delete_at(idx) + if shallow? + clone_args -= depth_args + command = clone_command(clone_args) command_with_no_credentials = check_allowed(command) - - err += "Retrying without --depth argument." end raise GitCommandError.new(command_with_no_credentials, path, err) end @@ -203,14 +230,14 @@ module Bundler def clone_needs_unshallow? return false unless path.join("shallow").exist? - return true if full_clone? + return true unless shallow? @revision && @revision != head_revision end def extra_ref return false if not_pinned? - return true unless full_clone? + return true if shallow? ref.start_with?("refs/") end @@ -262,7 +289,7 @@ module Bundler end def not_pinned? - branch || tag || ref.nil? + branch_option || ref.nil? end def pinned_to_full_sha? @@ -304,8 +331,8 @@ module Bundler end def has_revision_cached? - return unless @revision && path.exist? - git("cat-file", "-e", @revision, dir: path) + return unless commit && path.exist? + git("cat-file", "-e", commit, dir: path) true rescue GitError false @@ -405,13 +432,14 @@ module Bundler end def capture3_args_for(cmd, dir) - return ["git", *cmd] unless dir + # Disable automatic maintenance so a background commit-graph write in + # the source repo can't race the hardlinking local clone and fail with + # "hardlink different from source". + opts = ["-c", "gc.auto=0", "-c", "maintenance.auto=false"] - if Bundler.feature_flag.bundler_3_mode? || supports_minus_c? - ["git", "-C", dir.to_s, *cmd] - else - ["git", *cmd, { chdir: dir.to_s }] - end + return ["git", *opts, *cmd] unless dir + + ["git", "-C", dir.to_s, *opts, *cmd] end def extra_clone_args @@ -426,12 +454,20 @@ module Bundler # anyways. return args if @revision - args += ["--branch", branch || tag] if branch || tag + args += ["--branch", branch_option] if branch_option args end + def fetch_command(args) + ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact + end + + def clone_command(args) + ["clone", "--bare", "--no-hardlinks", "--quiet", *args, "--", configured_uri, path.to_s] + end + def depth_args - return [] if full_clone? + return [] unless shallow? ["--depth", depth.to_s] end @@ -442,12 +478,12 @@ module Bundler extra_args end - def full_clone? - depth.nil? + def branch_option + branch || tag end - def supports_minus_c? - @supports_minus_c ||= Gem::Version.new(version) >= Gem::Version.new("1.8.5") + def shallow? + !depth.nil? end def needs_allow_any_sha1_in_want? diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index fd959cd64e..ecf8895187 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -58,6 +58,10 @@ module Bundler def version_message(spec) "#{spec.name} #{spec.version}" end + + def checksum_store + @checksum_store ||= Checksum::Store.new + end end end end diff --git a/lib/bundler/source/path.rb b/lib/bundler/source/path.rb index a24cd8159e..366a23aea7 100644 --- a/lib/bundler/source/path.rb +++ b/lib/bundler/source/path.rb @@ -24,7 +24,7 @@ module Bundler @path = Pathname.new(options["path"]) expanded_path = expand(@path) @path = if @path.relative? - expanded_path.relative_path_from(root_path.expand_path) + expanded_path.relative_path_from(File.expand_path(root_path)) else expanded_path end @@ -53,6 +53,8 @@ module Bundler "source at `#{@path}`" end + alias_method :identifier, :to_s + alias_method :to_gemfile, :path def hash @@ -60,8 +62,8 @@ module Bundler end def eql?(other) - return unless other.class == self.class - expanded_original_path == other.expanded_original_path && + [Gemspec, Path].include?(other.class) && + expanded_original_path == other.expanded_original_path && version == other.version end @@ -81,7 +83,7 @@ module Bundler def cache(spec, custom_path = nil) app_cache_path = app_cache_path(custom_path) - return unless Bundler.feature_flag.cache_all? + return unless Bundler.settings[:cache_all] return if expand(@original_path).to_s.index(root_path.to_s + "/") == 0 unless @original_path.exist? @@ -124,11 +126,7 @@ module Bundler end def expand(somepath) - if Bundler.current_ruby.jruby? # TODO: Unify when https://github.com/rubygems/bundler/issues/7598 fixed upstream and all supported jrubies include the fix - somepath.expand_path(root_path).expand_path - else - somepath.expand_path(root_path) - end + somepath.expand_path(root_path) rescue ArgumentError => e Bundler.ui.debug(e) raise PathError, "There was an error while trying to use the path " \ @@ -167,6 +165,13 @@ module Bundler next unless spec = load_gemspec(file) spec.source = self + # The ignore attribute is for ignoring installed gems that don't + # have extensions correctly compiled for activation. In the case of + # path sources, there's a single version of each gem in the path + # source available to Bundler, so we always certainly want to + # consider that for activation and never makes sense to ignore it. + spec.ignored = false + # Validation causes extension_dir to be calculated, which depends # on #source, so we validate here instead of load_gemspec validate_spec(spec) @@ -215,10 +220,11 @@ module Bundler # Some gem authors put absolute paths in their gemspec # and we have to save them from themselves spec.files = spec.files.filter_map do |path| - next path unless /\A#{Pathname::SEPARATOR_PAT}/o.match?(path) + pathname = Pathname.new(path) + next path unless pathname.absolute? next if File.directory?(path) begin - Pathname.new(path).relative_path_from(gem_dir).to_s + pathname.relative_path_from(gem_dir).to_s rescue ArgumentError path end diff --git a/lib/bundler/source/path/installer.rb b/lib/bundler/source/path/installer.rb index 0af28fe770..39765e5da2 100644 --- a/lib/bundler/source/path/installer.rb +++ b/lib/bundler/source/path/installer.rb @@ -24,7 +24,7 @@ module Bundler def post_install run_hooks(:pre_install) - unless @disable_extensions + unless @disable_extensions || Bundler.settings[:no_build_extension] build_extensions run_hooks(:post_build) end diff --git a/lib/bundler/source/rubygems.rb b/lib/bundler/source/rubygems.rb index 19800e9c58..ed864604fe 100644 --- a/lib/bundler/source/rubygems.rb +++ b/lib/bundler/source/rubygems.rb @@ -8,21 +8,26 @@ module Bundler autoload :Remote, File.expand_path("rubygems/remote", __dir__) # Ask for X gems per API request - API_REQUEST_SIZE = 50 + API_REQUEST_SIZE = 100 + REQUIRE_MUTEX = Mutex.new attr_accessor :remotes def initialize(options = {}) @options = options @remotes = [] + @remote_cooldowns = {} @dependency_names = [] @allow_remote = false @allow_cached = false @allow_local = options["allow_local"] || false @prefer_local = false @checksum_store = Checksum::Store.new + @gem_installers = {} + @gem_installers_mutex = Mutex.new - Array(options["remotes"]).reverse_each {|r| add_remote(r) } + cooldown = options["cooldown"] + Array(options["remotes"]).reverse_each {|r| add_remote(r, cooldown: cooldown) } @lockfile_remotes = @remotes if options["from_lockfile"] end @@ -145,6 +150,13 @@ module Bundler # sources, and large_idx.merge! small_idx is way faster than # small_idx.merge! large_idx. index = @allow_remote ? remote_specs.dup : Index.new + + # Snapshot per-version `created_at` from the remote info before installed + # / cached specs overwrite the EndpointSpecification objects that carry + # it. The cooldown filter consults `created_at` on every candidate, so + # local stubs need the published date back-filled to participate. + remote_created_at = collect_remote_created_at(index) + index.merge!(cached_specs) if @allow_cached index.merge!(installed_specs) if @allow_local @@ -158,66 +170,57 @@ module Bundler end end + backfill_created_at(index, remote_created_at) unless remote_created_at.empty? + index end end - def install(spec, options = {}) + def download(spec, options = {}) if (spec.default_gem? && !cached_built_in_gem(spec, local: options[:local])) || (installed?(spec) && !options[:force]) - print_using_message "Using #{version_message(spec, options[:previous_spec])}" - return nil # no post-install message + return true end - if spec.remote - # Check for this spec from other sources - uris = [spec.remote, *remotes_for_spec(spec)].map(&:anonymized_uri).uniq - Installer.ambiguous_gems << [spec.name, *uris] if uris.length > 1 - end - - path = fetch_gem_if_possible(spec, options[:previous_spec]) - raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path - - return if Bundler.settings[:no_install] - - install_path = rubygems_dir - bin_path = Bundler.system_bindir - - require_relative "../rubygems_gem_installer" - - installer = Bundler::RubyGemsGemInstaller.at( - path, - security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], - install_dir: install_path.to_s, - bin_dir: bin_path.to_s, - ignore_dependencies: true, - wrappers: true, - env_shebang: true, - build_args: options[:build_args], - bundler_extension_cache_path: extension_cache_path(spec) - ) + installer = rubygems_gem_installer(spec, options) if spec.remote s = begin installer.spec rescue Gem::Package::FormatError - Bundler.rm_rf(path) + Bundler.rm_rf(installer.gem) raise rescue Gem::Security::Exception => e raise SecurityError, - "The gem #{File.basename(path, ".gem")} can't be installed because " \ + "The gem #{installer.gem} can't be installed because " \ "the security policy didn't allow it, with the message: #{e.message}" end spec.__swap__(s) end + spec + end + + def install(spec, options = {}) + if (spec.default_gem? && !cached_built_in_gem(spec, local: options[:local])) || (installed?(spec) && !options[:force]) + print_using_message "Using #{version_message(spec, options[:previous_spec])}" + return nil # no post-install message + end + + return if Bundler.settings[:no_install] + + installer = rubygems_gem_installer(spec, options) spec.source.checksum_store.register(spec, installer.gem_checksum) message = "Installing #{version_message(spec, options[:previous_spec])}" message += " with native extensions" if spec.extensions.any? Bundler.ui.confirm message - installed_spec = installer.install + installed_spec = nil + + Gem.time("Installed #{spec.name} in", 0, true) do + installed_spec = installer.install + end spec.full_gem_path = installed_spec.full_gem_path spec.loaded_from = installed_spec.loaded_from @@ -251,9 +254,14 @@ module Bundler cached_path end - def add_remote(source) + def add_remote(source, cooldown: nil) uri = normalize_uri(source) @remotes.unshift(uri) unless @remotes.include?(uri) + @remote_cooldowns[uri] = cooldown if cooldown + end + + def cooldown_for(uri) + @remote_cooldowns[uri] end def spec_names @@ -274,7 +282,7 @@ module Bundler def remote_fetchers @remote_fetchers ||= remotes.to_h do |uri| - remote = Source::Rubygems::Remote.new(uri) + remote = Source::Rubygems::Remote.new(uri, cooldown: cooldown_for(uri)) [remote, Bundler::Fetcher.new(remote)] end.freeze end @@ -322,6 +330,13 @@ module Bundler @allow_remote && api_fetchers.any? end + def clear_cache + @specs = nil + @installed_specs = nil + @default_specs = nil + @cached_specs = nil + end + protected def remote_names @@ -332,13 +347,6 @@ module Bundler remotes.map(&method(:remove_auth)) 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 cached_gem(spec) global_cache_path = download_cache_path(spec) caches << global_cache_path if global_cache_path @@ -471,6 +479,31 @@ module Bundler private + def collect_remote_created_at(index) + return {} unless @allow_remote + + snapshot = {} + index.each do |spec| + next unless spec.respond_to?(:created_at) && spec.created_at + # Remember the remote that supplied the date too: when a source has + # several remotes with different per-URI cooldown settings we must + # restore the same one during backfill so `effective_cooldown` agrees. + snapshot[[spec.name, spec.version]] = [spec.created_at, spec.remote] + end + snapshot + end + + def backfill_created_at(index, snapshot) + index.each do |spec| + next unless spec.respond_to?(:created_at=) + next if spec.created_at + remote_created_at, remote = snapshot[[spec.name, spec.version]] + next unless remote_created_at + spec.created_at = remote_created_at + spec.remote ||= remote if remote && spec.respond_to?(:remote=) + end + end + def lockfile_remotes @lockfile_remotes || credless_remotes end @@ -491,7 +524,15 @@ module Bundler uri = spec.remote.uri Bundler.ui.confirm("Fetching #{version_message(spec, previous_spec)}") gem_remote_fetcher = remote_fetchers.fetch(spec.remote).gem_remote_fetcher - Bundler.rubygems.download_gem(spec, uri, download_cache_path, gem_remote_fetcher) + + Plugin.hook(Plugin::Events::GEM_BEFORE_FETCH, spec) + begin + Gem.time("Downloaded #{spec.name} in", 0, true) do + Bundler.rubygems.download_gem(spec, uri, download_cache_path, gem_remote_fetcher) + end + ensure + Plugin.hook(Plugin::Events::GEM_AFTER_FETCH, spec) + end end # Returns the global cache path of the calling Rubygems::Source object. @@ -506,17 +547,52 @@ module Bundler # @return [Pathname] The global cache path. # def download_cache_path(spec) - return unless Bundler.feature_flag.global_gem_cache? + return unless Bundler.settings[:global_gem_cache] return unless remote = spec.remote return unless cache_slug = remote.cache_slug - Bundler.user_cache.join("gems", cache_slug) + if Gem.respond_to?(:global_gem_cache_path) + Pathname.new(Gem.global_gem_cache_path).join(cache_slug) + else + # Fall back to old location for older RubyGems versions + Bundler.user_cache.join("gems", cache_slug) + end end def extension_cache_slug(spec) return unless remote = spec.remote remote.cache_slug end + + # We are using a mutex to read and write from/to the hash. + # The reason this double synchronization was added is for performance + # and to lock the mutex for the shortest possible amount of time. Otherwise, + # all threads are fighting over this mutex and when it gets acquired it gets locked + # until a thread finishes downloading a gem, leaving the other threads waiting + # doing nothing. + def rubygems_gem_installer(spec, options) + @gem_installers_mutex.synchronize { @gem_installers[spec.name] } || begin + path = fetch_gem_if_possible(spec, options[:previous_spec]) + raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path + + REQUIRE_MUTEX.synchronize { require_relative "../rubygems_gem_installer" } + + installer = Bundler::RubyGemsGemInstaller.at( + path, + security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]], + install_dir: rubygems_dir.to_s, + bin_dir: Bundler.system_bindir.to_s, + ignore_dependencies: true, + wrappers: true, + env_shebang: true, + build_args: options[:build_args], + bundler_extension_cache_path: extension_cache_path(spec), + build_extension: Bundler.settings[:no_build_extension] ? false : nil, + install_plugin: Bundler.settings[:no_install_plugin] ? false : nil + ) + @gem_installers_mutex.synchronize { @gem_installers[spec.name] ||= installer } + end + end end end end diff --git a/lib/bundler/source/rubygems/remote.rb b/lib/bundler/source/rubygems/remote.rb index 9c5c06de24..3d847424b7 100644 --- a/lib/bundler/source/rubygems/remote.rb +++ b/lib/bundler/source/rubygems/remote.rb @@ -4,9 +4,9 @@ module Bundler class Source class Rubygems class Remote - attr_reader :uri, :anonymized_uri, :original_uri + attr_reader :uri, :anonymized_uri, :original_uri, :cooldown - def initialize(uri) + def initialize(uri, cooldown: nil) orig_uri = uri uri = Bundler.settings.mirror_for(uri) @original_uri = orig_uri if orig_uri != uri @@ -14,8 +14,21 @@ module Bundler @uri = apply_auth(uri, fallback_auth).freeze @anonymized_uri = remove_auth(@uri).freeze + @cooldown = cooldown end + # Returns the cooldown days that apply to this remote, resolving the + # precedence CLI > config > Gemfile per-source. Returns nil if no + # cooldown applies. + def effective_cooldown + override = Bundler.settings[:cooldown] + return override if override + @cooldown + end + + MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length + private_constant :MAX_CACHE_SLUG_HOST_SIZE + # @return [String] A slug suitable for use as a cache key for this # remote. # @@ -28,10 +41,15 @@ module Bundler host = cache_uri.to_s.start_with?("file://") ? nil : cache_uri.host uri_parts = [host, cache_uri.user, cache_uri.port, cache_uri.path] - uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.compact.join(".")) + uri_parts.compact! + uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.join(".")) + + uri_parts.pop + host_parts = uri_parts.join(".") + return uri_digest if host_parts.empty? - uri_parts[-1] = uri_digest - uri_parts.compact.join(".") + shortened_host_parts = host_parts[0...MAX_CACHE_SLUG_HOST_SIZE] + [shortened_host_parts, uri_digest].join(".") end end diff --git a/lib/bundler/source/rubygems_aggregate.rb b/lib/bundler/source/rubygems_aggregate.rb index 99ef81ad54..8aeaa375fa 100644 --- a/lib/bundler/source/rubygems_aggregate.rb +++ b/lib/bundler/source/rubygems_aggregate.rb @@ -5,9 +5,10 @@ module Bundler class RubygemsAggregate attr_reader :source_map, :sources - def initialize(sources, source_map) + def initialize(sources, source_map, excluded_sources = []) @sources = sources @source_map = source_map + @excluded_sources = excluded_sources @index = build_index end @@ -31,6 +32,8 @@ module Bundler dependency_names = source_map.pinned_spec_names sources.all_sources.each do |source| + next if @excluded_sources.include?(source) + source.dependency_names = dependency_names - source_map.pinned_spec_names(source) idx.add_source source.specs dependency_names.concat(source.unmet_deps).uniq! |
