Class: Bundler::Fetcher
- Inherits:
-
Object
- Object
- Bundler::Fetcher
- Defined in:
- lib/bundler/fetcher.rb
Overview
Handles all the fetching with the rubygems server
Defined Under Namespace
Classes: AuthenticationRequiredError, BadAuthenticationError, CertificateFailureError, FallbackError, NetworkDownError, SSLError
Constant Summary collapse
- AUTH_ERRORS =
Exceptions classes that should bypass retry attempts. If your password didn’t work the first time, it’s not going to the third time.
[AuthenticationRequiredError, BadAuthenticationError]
Class Attribute Summary collapse
-
.api_timeout ⇒ Object
Returns the value of attribute api_timeout.
-
.disable_endpoint ⇒ Object
Returns the value of attribute disable_endpoint.
-
.max_retries ⇒ Object
Returns the value of attribute max_retries.
-
.redirect_limit ⇒ Object
Returns the value of attribute redirect_limit.
Class Method Summary collapse
Instance Method Summary collapse
- #connection ⇒ Object
-
#fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = []) ⇒ Object
fetch index.
-
#fetch_spec(spec) ⇒ Object
fetch a gem specification.
-
#gemspec_cached_path(spec_file_name) ⇒ Object
cached gem specification path, if one exists.
-
#initialize(remote_uri) ⇒ Fetcher
constructor
A new instance of Fetcher.
- #inspect ⇒ Object
-
#specs(gem_names, source) ⇒ Object
return the specs in the bundler format as an index.
- #uri ⇒ Object
- #use_api ⇒ Object
Constructor Details
#initialize(remote_uri) ⇒ Fetcher
Returns a new instance of Fetcher.
104 105 106 107 108 109 110 111 112 113 |
# File 'lib/bundler/fetcher.rb', line 104 def initialize(remote_uri) @redirect_limit = 5 # How many redirects to allow in one request @api_timeout = 10 # How long to wait for each API call @max_retries = 3 # How many retries for the API call @anonymizable_uri = configured_uri_for(remote_uri) Socket.do_not_reverse_lookup = true connection # create persistent connection end |
Class Attribute Details
.api_timeout ⇒ Object
Returns the value of attribute api_timeout.
54 55 56 |
# File 'lib/bundler/fetcher.rb', line 54 def api_timeout @api_timeout end |
.disable_endpoint ⇒ Object
Returns the value of attribute disable_endpoint.
54 55 56 |
# File 'lib/bundler/fetcher.rb', line 54 def disable_endpoint @disable_endpoint end |
.max_retries ⇒ Object
Returns the value of attribute max_retries.
54 55 56 |
# File 'lib/bundler/fetcher.rb', line 54 def max_retries @max_retries end |
.redirect_limit ⇒ Object
Returns the value of attribute redirect_limit.
54 55 56 |
# File 'lib/bundler/fetcher.rb', line 54 def redirect_limit @redirect_limit end |
Class Method Details
.download_gem_from_uri(spec, uri) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/bundler/fetcher.rb', line 56 def download_gem_from_uri(spec, uri) spec.fetch_platform download_path = Bundler.requires_sudo? ? Bundler.tmp(spec.full_name) : Bundler.rubygems.gem_dir gem_path = "#{Bundler.rubygems.gem_dir}/cache/#{spec.full_name}.gem" FileUtils.mkdir_p("#{download_path}/cache") Bundler.rubygems.download_gem(spec, uri, download_path) if Bundler.requires_sudo? Bundler.mkdir_p "#{Bundler.rubygems.gem_dir}/cache" Bundler.sudo "mv #{Bundler.tmp(spec.full_name)}/cache/#{spec.full_name}.gem #{gem_path}" end gem_path end |
.user_agent ⇒ Object
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/bundler/fetcher.rb', line 73 def user_agent @user_agent ||= begin ruby = Bundler.ruby_version agent = "bundler/#{Bundler::VERSION}" agent << " rubygems/#{Gem::VERSION}" agent << " ruby/#{ruby.version}" agent << " (#{ruby.host})" agent << " command/#{ARGV.first}" if ruby.engine != "ruby" # engine_version raises on unknown engines engine_version = ruby.engine_version rescue "???" agent << " #{ruby.engine}/#{engine_version}" end agent << " options/#{Bundler.settings.all.join(",")}" # 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 |
Instance Method Details
#connection ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'lib/bundler/fetcher.rb', line 115 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 = Net::HTTP::Persistent.new 'bundler', :ENV if remote_uri.scheme == "https" con.verify_mode = (Bundler.settings[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER) con.cert_store = bundler_cert_store end if Bundler.settings[:ssl_client_cert] pem = File.read(Bundler.settings[:ssl_client_cert]) con.cert = OpenSSL::X509::Certificate.new(pem) con.key = OpenSSL::PKey::RSA.new(pem) end con.read_timeout = @api_timeout con.override_headers["User-Agent"] = self.class.user_agent con end end |
#fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = []) ⇒ Object
fetch index
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/bundler/fetcher.rb', line 211 def fetch_remote_specs(gem_names, full_dependency_list = [], last_spec_list = []) query_list = gem_names - full_dependency_list # only display the message on the first run if Bundler.ui.debug? Bundler.ui.debug "Query List: #{query_list.inspect}" else Bundler.ui.info ".", false end return {remote_uri => last_spec_list} if query_list.empty? remote_specs = Bundler::Retry.new("dependency api", AUTH_ERRORS).attempts do fetch_dependency_remote_specs(query_list) end spec_list, deps_list = remote_specs returned_gems = spec_list.map {|spec| spec.first }.uniq fetch_remote_specs(deps_list, full_dependency_list + returned_gems, spec_list + last_spec_list) rescue HTTPError, MarshalError, 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, trying the full index" @use_api = false return nil end |
#fetch_spec(spec) ⇒ Object
fetch a gem specification
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/bundler/fetcher.rb', line 147 def fetch_spec(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(fetch(uri)) end rescue MarshalError raise HTTPError, "Gemspec #{spec} contained invalid data.\n" \ "Your network or your gem server is probably having issues right now." end |
#gemspec_cached_path(spec_file_name) ⇒ Object
cached gem specification path, if one exists
165 166 167 168 169 |
# File 'lib/bundler/fetcher.rb', line 165 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 |
#inspect ⇒ Object
254 255 256 |
# File 'lib/bundler/fetcher.rb', line 254 def inspect "#<#{self.class}:0x#{object_id} uri=#{uri}>" end |
#specs(gem_names, source) ⇒ Object
return the specs in the bundler format as an index
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/bundler/fetcher.rb', line 172 def specs(gem_names, source) old = Bundler.rubygems.sources index = Index.new if gem_names && use_api specs = fetch_remote_specs(gem_names) end if specs.nil? # API errors mean we should treat this as a non-API source @use_api = false specs = Bundler::Retry.new("source fetch", AUTH_ERRORS).attempts do fetch_all_remote_specs end end specs[remote_uri].each do |name, version, platform, dependencies| next if name == 'bundler' spec = nil if dependencies spec = EndpointSpecification.new(name, version, platform, dependencies) else spec = RemoteSpecification.new(name, version, platform, self) end spec.source = source spec.source_uri = @anonymizable_uri index << spec end index rescue CertificateFailureError => e Bundler.ui.info "" if gem_names && use_api # newline after dots raise e ensure Bundler.rubygems.sources = old end |
#uri ⇒ Object
142 143 144 |
# File 'lib/bundler/fetcher.rb', line 142 def uri @anonymizable_uri.without_credentials end |
#use_api ⇒ Object
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/bundler/fetcher.rb', line 237 def use_api return @use_api if defined?(@use_api) if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint @use_api = false elsif fetch(dependency_api_uri) @use_api = true end rescue NetworkDownError => e raise HTTPError, e. rescue AuthenticationRequiredError # We got a 401 from the server. Don't fall back to the full index, just fail. raise rescue HTTPError @use_api = false end |