diff options
59 files changed, 667 insertions, 496 deletions
diff --git a/lib/bundler/fetcher/downloader.rb b/lib/bundler/fetcher/downloader.rb index 2f691886ad..28d33f1aed 100644 --- a/lib/bundler/fetcher/downloader.rb +++ b/lib/bundler/fetcher/downloader.rb @@ -61,9 +61,6 @@ module Bundler 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 diff --git a/lib/bundler/friendly_errors.rb b/lib/bundler/friendly_errors.rb index 8277784a6a..71faeaf282 100644 --- a/lib/bundler/friendly_errors.rb +++ b/lib/bundler/friendly_errors.rb @@ -36,9 +36,6 @@ module Bundler end when Thor::Error Bundler.ui.error error.message - when LoadError - raise error unless /cannot load such file -- openssl|openssl.so|libcrypto.so/.match?(error.message) - Bundler.ui.error "\nCould not load OpenSSL. #{error.class}: #{error}\n#{error.backtrace.join("\n ")}" when Interrupt Bundler.ui.error "\nQuitting..." Bundler.ui.trace error diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb index dc89dedeb2..b8ee4029b4 100644 --- a/lib/bundler/source/git.rb +++ b/lib/bundler/source/git.rb @@ -148,7 +148,7 @@ module Bundler "#{current_branch} but Gemfile specifies #{branch}" end - changed = cached_revision && cached_revision != git_proxy.revision + changed = cached_revision && cached_revision != revision if !Bundler.settings[:disable_local_revision_check] && changed && !@unlocked && !git_proxy.contains?(cached_revision) raise GitError, "The Gemfile lock is pointing to revision #{shortref_for_display(cached_revision)} " \ diff --git a/lib/bundler/source/git/git_proxy.rb b/lib/bundler/source/git/git_proxy.rb index e379c84741..e6c138c9aa 100644 --- a/lib/bundler/source/git/git_proxy.rb +++ b/lib/bundler/source/git/git_proxy.rb @@ -61,7 +61,7 @@ module Bundler end def revision - @revision ||= find_local_revision + @revision ||= allowed_with_path { find_local_revision } end def current_branch @@ -90,16 +90,11 @@ module Bundler Bundler.ui.info "Fetching #{credential_filtered_uri}" - unless path.exist? - SharedHelpers.filesystem_access(path.dirname) do |p| - FileUtils.mkdir_p(p) - end - git_retry "clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s - return unless extra_ref - end + extra_fetch_needed = clone_needs_extra_fetch? + unshallow_needed = clone_needs_unshallow? + return unless extra_fetch_needed || unshallow_needed - fetch_args = extra_fetch_args - fetch_args.unshift("--unshallow") if path.join("shallow").exist? && full_clone? + fetch_args = unshallow_needed ? ["--unshallow"] : depth_args git_retry(*["fetch", "--force", "--quiet", "--no-tags", *fetch_args, "--", configured_uri, refspec].compact, :dir => path) end @@ -123,7 +118,7 @@ module Bundler end end - git(*["fetch", "--force", "--quiet", *extra_fetch_args, path.to_s, revision_refspec].compact, :dir => destination) + git "fetch", "--force", "--quiet", *extra_fetch_args, :dir => destination git "reset", "--hard", @revision, :dir => destination @@ -137,6 +132,24 @@ module Bundler private + def clone_needs_extra_fetch? + return true if path.exist? + + SharedHelpers.filesystem_access(path.dirname) do |p| + FileUtils.mkdir_p(p) + end + git_retry "clone", "--bare", "--no-hardlinks", "--quiet", *extra_clone_args, "--", configured_uri, path.to_s + + extra_ref + end + + def clone_needs_unshallow? + return false unless path.join("shallow").exist? + return true if full_clone? + + @revision && @revision != head_revision + end + def extra_ref return false if not_pinned? return true unless full_clone? @@ -147,7 +160,7 @@ module Bundler def depth return @depth if defined?(@depth) - @depth = if legacy_locked_revision? || !supports_fetching_unreachable_refs? + @depth = if !supports_fetching_unreachable_refs? nil elsif not_pinned? 1 @@ -241,11 +254,24 @@ module Bundler end def find_local_revision - allowed_with_path do - git("rev-parse", "--verify", branch || tag || ref || "HEAD", :dir => path).strip - end + options_ref = branch || tag || ref + return head_revision if options_ref.nil? + + find_revision_for(options_ref) + end + + def head_revision + verify("HEAD") + end + + def find_revision_for(reference) + verify(reference) rescue GitCommandError => e - raise MissingGitRevisionError.new(e.command, path, branch || tag || ref, credential_filtered_uri) + raise MissingGitRevisionError.new(e.command, path, reference, credential_filtered_uri) + end + + def verify(reference) + git("rev-parse", "--verify", reference, :dir => path).strip end # Adds credentials to the URI @@ -325,16 +351,16 @@ module Bundler args end - def extra_fetch_args + def depth_args return [] if full_clone? ["--depth", depth.to_s] end - def revision_refspec - return if legacy_locked_revision? - - revision + def extra_fetch_args + extra_args = [path.to_s, *depth_args] + extra_args.push(revision) unless legacy_locked_revision? + extra_args end def full_clone? diff --git a/spec/bundler/bundler/fetcher/downloader_spec.rb b/spec/bundler/bundler/fetcher/downloader_spec.rb index 94a0993a54..0412ddb83a 100644 --- a/spec/bundler/bundler/fetcher/downloader_spec.rb +++ b/spec/bundler/bundler/fetcher/downloader_spec.rb @@ -178,26 +178,6 @@ RSpec.describe Bundler::Fetcher::Downloader do end end - context "when the request response causes a NoMethodError" do - before { allow(connection).to receive(:request).with(uri, net_http_get) { raise NoMethodError.new(message) } } - - context "and the error message is about use_ssl=" do - let(:message) { "undefined method 'use_ssl='" } - - it "should raise a LoadError about openssl" do - expect { subject.request(uri, options) }.to raise_error(LoadError, "cannot load such file -- openssl") - end - end - - context "and the error message is not about use_ssl=" do - let(:message) { "undefined method 'undefined_method_call'" } - - it "should raise the original NoMethodError" do - expect { subject.request(uri, options) }.to raise_error(NoMethodError, /undefined method 'undefined_method_call'/) - end - end - end - context "when the request response causes a OpenSSL::SSL::SSLError" do before { allow(connection).to receive(:request).with(uri, net_http_get) { raise OpenSSL::SSL::SSLError.new } } diff --git a/spec/bundler/bundler/friendly_errors_spec.rb b/spec/bundler/bundler/friendly_errors_spec.rb index 69fba7b826..37afe488f3 100644 --- a/spec/bundler/bundler/friendly_errors_spec.rb +++ b/spec/bundler/bundler/friendly_errors_spec.rb @@ -110,19 +110,6 @@ RSpec.describe Bundler, "friendly errors" do it_behaves_like "Bundler.ui receive error", Bundler::Thor::Error.new end - context "LoadError" do - let(:error) { LoadError.new("cannot load such file -- openssl") } - - before do - allow(error).to receive(:backtrace).and_return(["backtrace"]) - end - - it "Bundler.ui receive error" do - expect(Bundler.ui).to receive(:error).with("\nCould not load OpenSSL. LoadError: cannot load such file -- openssl\nbacktrace") - Bundler::FriendlyErrors.log_error(error) - end - end - context "Interrupt" do it "Bundler.ui receive error" do expect(Bundler.ui).to receive(:error).with("\nQuitting...") diff --git a/spec/bundler/lock/git_spec.rb b/spec/bundler/lock/git_spec.rb index df1564d614..28d167b773 100644 --- a/spec/bundler/lock/git_spec.rb +++ b/spec/bundler/lock/git_spec.rb @@ -34,6 +34,13 @@ RSpec.describe "bundle lock with git gems" do expect(out).to eq("WIN") end + it "properly clones a git source locked to an out of date ref" do + update_git "foo" + + bundle :install, :env => { "BUNDLE_PATH" => "foo" } + expect(err).to be_empty + end + it "provides correct #full_gem_path" do run <<-RUBY puts Bundler.rubygems.find_name('foo').first.full_gem_path diff --git a/spec/bundler/support/artifice/compact_index.rb b/spec/bundler/support/artifice/compact_index.rb index fb068fa9b5..ebc4d0ae5b 100644 --- a/spec/bundler/support/artifice/compact_index.rb +++ b/spec/bundler/support/artifice/compact_index.rb @@ -1,120 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s -require "compact_index" - -class CompactIndexAPI < Endpoint - helpers do - include Spec::Path - - def load_spec(name, version, platform, gem_repo) - full_name = "#{name}-#{version}" - full_name += "-#{platform}" if platform != "ruby" - Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")))) - end - - def etag_response - response_body = yield - checksum = Digest(:MD5).hexdigest(response_body) - return if not_modified?(checksum) - headers "ETag" => quote(checksum) - headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60" - content_type "text/plain" - requested_range_for(response_body) - rescue StandardError => e - puts e - puts e.backtrace - raise - end - - def not_modified?(checksum) - etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"]) - - return unless etags.include?(checksum) - headers "ETag" => quote(checksum) - status 304 - body "" - end - - def requested_range_for(response_body) - ranges = Rack::Utils.byte_ranges(env, response_body.bytesize) - - if ranges - status 206 - body ranges.map! {|range| slice_body(response_body, range) }.join - else - status 200 - body response_body - end - end - - def quote(string) - %("#{string}") - end - - def parse_etags(value) - value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : [] - end - - def slice_body(body, range) - body.byteslice(range) - end - - def gems(gem_repo = default_gem_repo) - @gems ||= {} - @gems[gem_repo] ||= begin - specs = Bundler::Deprecate.skip_during do - %w[specs.4.8 prerelease_specs.4.8].map do |filename| - Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform| - load_spec(name, version, platform, gem_repo) - end - end.flatten - end - - specs.group_by(&:name).map do |name, versions| - gem_versions = versions.map do |spec| - deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d| - reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ") - CompactIndex::Dependency.new(d.name, reqs) - end - checksum = begin - Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest - rescue StandardError - nil - end - CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil, - deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s) - end - CompactIndex::Gem.new(name, gem_versions) - end - end - end - end - - get "/names" do - etag_response do - CompactIndex.names(gems.map(&:name)) - end - end - - get "/versions" do - etag_response do - file = tmp("versions.list") - FileUtils.rm_f(file) - file = CompactIndex::VersionsFile.new(file.to_s) - file.create(gems) - file.contents - end - end - - get "/info/:name" do - etag_response do - gem = gems.find {|g| g.name == params[:name] } - CompactIndex.info(gem ? gem.versions : []) - end - end -end +require_relative "helpers/compact_index" +require_relative "helpers/artifice" Artifice.activate_with(CompactIndexAPI) diff --git a/spec/bundler/support/artifice/compact_index_api_missing.rb b/spec/bundler/support/artifice/compact_index_api_missing.rb index 2fd8b6d2e9..f771f7d1f0 100644 --- a/spec/bundler/support/artifice/compact_index_api_missing.rb +++ b/spec/bundler/support/artifice/compact_index_api_missing.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexApiMissing < CompactIndexAPI get "/fetch/actual/gem/:id" do @@ -10,4 +8,6 @@ class CompactIndexApiMissing < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexApiMissing) diff --git a/spec/bundler/support/artifice/compact_index_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_basic_authentication.rb index 775f1a3977..b9115cdd86 100644 --- a/spec/bundler/support/artifice/compact_index_basic_authentication.rb +++ b/spec/bundler/support/artifice/compact_index_basic_authentication.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexBasicAuthentication < CompactIndexAPI before do @@ -12,4 +10,6 @@ class CompactIndexBasicAuthentication < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexBasicAuthentication) diff --git a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb index 1abe64236c..a6545b9ee4 100644 --- a/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb +++ b/spec/bundler/support/artifice/compact_index_checksum_mismatch.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexChecksumMismatch < CompactIndexAPI get "/versions" do @@ -13,4 +11,6 @@ class CompactIndexChecksumMismatch < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexChecksumMismatch) diff --git a/spec/bundler/support/artifice/compact_index_concurrent_download.rb b/spec/bundler/support/artifice/compact_index_concurrent_download.rb index 14c31f35a4..35548f278c 100644 --- a/spec/bundler/support/artifice/compact_index_concurrent_download.rb +++ b/spec/bundler/support/artifice/compact_index_concurrent_download.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexConcurrentDownload < CompactIndexAPI get "/versions" do @@ -29,4 +27,6 @@ class CompactIndexConcurrentDownload < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexConcurrentDownload) diff --git a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb index cfe22c7f51..401e8a98d8 100644 --- a/spec/bundler/support/artifice/compact_index_creds_diff_host.rb +++ b/spec/bundler/support/artifice/compact_index_creds_diff_host.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexCredsDiffHost < CompactIndexAPI helpers do @@ -36,4 +34,6 @@ class CompactIndexCredsDiffHost < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexCredsDiffHost) diff --git a/spec/bundler/support/artifice/compact_index_extra.rb b/spec/bundler/support/artifice/compact_index_extra.rb index cec368276a..cd41b3ecca 100644 --- a/spec/bundler/support/artifice/compact_index_extra.rb +++ b/spec/bundler/support/artifice/compact_index_extra.rb @@ -1,37 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate - -class CompactIndexExtra < CompactIndexAPI - get "/extra/versions" do - halt 404 - end - - get "/extra/api/v1/dependencies" do - halt 404 - end - - get "/extra/specs.4.8.gz" do - File.binread("#{gem_repo2}/specs.4.8.gz") - end - - get "/extra/prerelease_specs.4.8.gz" do - File.binread("#{gem_repo2}/prerelease_specs.4.8.gz") - end - - get "/extra/quick/Marshal.4.8/:id" do - redirect "/extra/fetch/actual/gem/#{params[:id]}" - end - - get "/extra/fetch/actual/gem/:id" do - File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") - end - - get "/extra/gems/:id" do - File.binread("#{gem_repo2}/gems/#{params[:id]}") - end -end +require_relative "helpers/compact_index_extra" +require_relative "helpers/artifice" Artifice.activate_with(CompactIndexExtra) diff --git a/spec/bundler/support/artifice/compact_index_extra_api.rb b/spec/bundler/support/artifice/compact_index_extra_api.rb index 5cc13421a8..8b9d304ab4 100644 --- a/spec/bundler/support/artifice/compact_index_extra_api.rb +++ b/spec/bundler/support/artifice/compact_index_extra_api.rb @@ -1,52 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate - -class CompactIndexExtraApi < CompactIndexAPI - get "/extra/names" do - etag_response do - CompactIndex.names(gems(gem_repo4).map(&:name)) - end - end - - get "/extra/versions" do - etag_response do - file = tmp("versions.list") - FileUtils.rm_f(file) - file = CompactIndex::VersionsFile.new(file.to_s) - file.create(gems(gem_repo4)) - file.contents - end - end - - get "/extra/info/:name" do - etag_response do - gem = gems(gem_repo4).find {|g| g.name == params[:name] } - CompactIndex.info(gem ? gem.versions : []) - end - end - - get "/extra/specs.4.8.gz" do - File.binread("#{gem_repo4}/specs.4.8.gz") - end - - get "/extra/prerelease_specs.4.8.gz" do - File.binread("#{gem_repo4}/prerelease_specs.4.8.gz") - end - - get "/extra/quick/Marshal.4.8/:id" do - redirect "/extra/fetch/actual/gem/#{params[:id]}" - end - - get "/extra/fetch/actual/gem/:id" do - File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}") - end - - get "/extra/gems/:id" do - File.binread("#{gem_repo4}/gems/#{params[:id]}") - end -end +require_relative "helpers/compact_index_extra_api" +require_relative "helpers/artifice" Artifice.activate_with(CompactIndexExtraApi) diff --git a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb index b9d757c266..df6ede584c 100644 --- a/spec/bundler/support/artifice/compact_index_extra_api_missing.rb +++ b/spec/bundler/support/artifice/compact_index_extra_api_missing.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index_extra_api" - -Artifice.deactivate +require_relative "helpers/compact_index_extra_api" class CompactIndexExtraAPIMissing < CompactIndexExtraApi get "/extra/fetch/actual/gem/:id" do @@ -14,4 +12,6 @@ class CompactIndexExtraAPIMissing < CompactIndexExtraApi end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexExtraAPIMissing) diff --git a/spec/bundler/support/artifice/compact_index_extra_missing.rb b/spec/bundler/support/artifice/compact_index_extra_missing.rb index ff1e47a1bb..255c89afdb 100644 --- a/spec/bundler/support/artifice/compact_index_extra_missing.rb +++ b/spec/bundler/support/artifice/compact_index_extra_missing.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index_extra" - -Artifice.deactivate +require_relative "helpers/compact_index_extra" class CompactIndexExtraMissing < CompactIndexExtra get "/extra/fetch/actual/gem/:id" do @@ -14,4 +12,6 @@ class CompactIndexExtraMissing < CompactIndexExtra end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexExtraMissing) diff --git a/spec/bundler/support/artifice/compact_index_forbidden.rb b/spec/bundler/support/artifice/compact_index_forbidden.rb index 3eebe0fbd8..18c30ed9a2 100644 --- a/spec/bundler/support/artifice/compact_index_forbidden.rb +++ b/spec/bundler/support/artifice/compact_index_forbidden.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexForbidden < CompactIndexAPI get "/versions" do @@ -10,4 +8,6 @@ class CompactIndexForbidden < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexForbidden) diff --git a/spec/bundler/support/artifice/compact_index_host_redirect.rb b/spec/bundler/support/artifice/compact_index_host_redirect.rb index 304c897d68..9a711186db 100644 --- a/spec/bundler/support/artifice/compact_index_host_redirect.rb +++ b/spec/bundler/support/artifice/compact_index_host_redirect.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexHostRedirect < CompactIndexAPI get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do @@ -18,4 +16,6 @@ class CompactIndexHostRedirect < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexHostRedirect) diff --git a/spec/bundler/support/artifice/compact_index_no_gem.rb b/spec/bundler/support/artifice/compact_index_no_gem.rb index 0a4be08a46..71f6629688 100644 --- a/spec/bundler/support/artifice/compact_index_no_gem.rb +++ b/spec/bundler/support/artifice/compact_index_no_gem.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexNoGem < CompactIndexAPI get "/gems/:id" do @@ -10,4 +8,6 @@ class CompactIndexNoGem < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexNoGem) diff --git a/spec/bundler/support/artifice/compact_index_partial_update.rb b/spec/bundler/support/artifice/compact_index_partial_update.rb index cb1c7b9481..8c73011346 100644 --- a/spec/bundler/support/artifice/compact_index_partial_update.rb +++ b/spec/bundler/support/artifice/compact_index_partial_update.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexPartialUpdate < CompactIndexAPI # Stub the server to never return 304s. This simulates the behaviour of @@ -35,4 +33,6 @@ class CompactIndexPartialUpdate < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexPartialUpdate) diff --git a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb index acf76dfbf0..20546ba4c3 100644 --- a/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb +++ b/spec/bundler/support/artifice/compact_index_partial_update_no_etag_not_incremental.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI def partial_update_no_etag @@ -37,4 +35,6 @@ class CompactIndexPartialUpdateNoEtagNotIncremental < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexPartialUpdateNoEtagNotIncremental) diff --git a/spec/bundler/support/artifice/compact_index_precompiled_before.rb b/spec/bundler/support/artifice/compact_index_precompiled_before.rb index 9f310e653b..b5f72f546a 100644 --- a/spec/bundler/support/artifice/compact_index_precompiled_before.rb +++ b/spec/bundler/support/artifice/compact_index_precompiled_before.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexPrecompiledBefore < CompactIndexAPI get "/info/:name" do @@ -22,4 +20,6 @@ class CompactIndexPrecompiledBefore < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexPrecompiledBefore) diff --git a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb index bb616125bb..8a7c4b79b0 100644 --- a/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb +++ b/spec/bundler/support/artifice/compact_index_range_not_satisfiable.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexRangeNotSatisfiable < CompactIndexAPI get "/versions" do @@ -31,4 +29,6 @@ class CompactIndexRangeNotSatisfiable < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexRangeNotSatisfiable) diff --git a/spec/bundler/support/artifice/compact_index_rate_limited.rb b/spec/bundler/support/artifice/compact_index_rate_limited.rb index 570105e2a0..4495491635 100644 --- a/spec/bundler/support/artifice/compact_index_rate_limited.rb +++ b/spec/bundler/support/artifice/compact_index_rate_limited.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexRateLimited < CompactIndexAPI class RequestCounter @@ -45,4 +43,6 @@ class CompactIndexRateLimited < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexRateLimited) diff --git a/spec/bundler/support/artifice/compact_index_redirects.rb b/spec/bundler/support/artifice/compact_index_redirects.rb index 99adc797bf..f7ba393239 100644 --- a/spec/bundler/support/artifice/compact_index_redirects.rb +++ b/spec/bundler/support/artifice/compact_index_redirects.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexRedirect < CompactIndexAPI get "/fetch/actual/gem/:id" do @@ -18,4 +16,6 @@ class CompactIndexRedirect < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexRedirect) diff --git a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb index 7d427b5382..fa25c4eca1 100644 --- a/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb +++ b/spec/bundler/support/artifice/compact_index_strict_basic_authentication.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexStrictBasicAuthentication < CompactIndexAPI before do @@ -17,4 +15,6 @@ class CompactIndexStrictBasicAuthentication < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexStrictBasicAuthentication) diff --git a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb index 036fac70b3..15850599b6 100644 --- a/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb +++ b/spec/bundler/support/artifice/compact_index_wrong_dependencies.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexWrongDependencies < CompactIndexAPI get "/info/:name" do @@ -14,4 +12,6 @@ class CompactIndexWrongDependencies < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexWrongDependencies) diff --git a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb index 8add32b88f..acc13a56ff 100644 --- a/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb +++ b/spec/bundler/support/artifice/compact_index_wrong_gem_checksum.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "compact_index" - -Artifice.deactivate +require_relative "helpers/compact_index" class CompactIndexWrongGemChecksum < CompactIndexAPI get "/info/:name" do @@ -17,4 +15,6 @@ class CompactIndexWrongGemChecksum < CompactIndexAPI end end +require_relative "helpers/artifice" + Artifice.activate_with(CompactIndexWrongGemChecksum) diff --git a/spec/bundler/support/artifice/endpoint.rb b/spec/bundler/support/artifice/endpoint.rb index b0e3f750ea..15242a7942 100644 --- a/spec/bundler/support/artifice/endpoint.rb +++ b/spec/bundler/support/artifice/endpoint.rb @@ -1,115 +1,6 @@ # frozen_string_literal: true -require_relative "../path" - -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) - -require "artifice" -require "sinatra/base" - -ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant -ALL_REQUESTS_MUTEX = Thread::Mutex.new - -at_exit do - if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"] - expected = expected.split("\n").sort - actual = ALL_REQUESTS.sort - - unless expected == actual - raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}" - end - end -end - -class Endpoint < Sinatra::Base - def self.all_requests - @all_requests ||= [] - end - - set :raise_errors, true - set :show_exceptions, false - - def call!(*) - super.tap do - ALL_REQUESTS_MUTEX.synchronize do - ALL_REQUESTS << @request.url - end - end - end - - helpers do - include Spec::Path - - def default_gem_repo - if ENV["BUNDLER_SPEC_GEM_REPO"] - Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"]) - else - case request.host - when "gem.repo1" - Spec::Path.gem_repo1 - when "gem.repo2" - Spec::Path.gem_repo2 - when "gem.repo3" - Spec::Path.gem_repo3 - when "gem.repo4" - Spec::Path.gem_repo4 - else - Spec::Path.gem_repo1 - end - end - end - - def dependencies_for(gem_names, gem_repo = default_gem_repo) - return [] if gem_names.nil? || gem_names.empty? - - all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename| - Marshal.load(File.open(gem_repo.join(filename)).read) - end.inject(:+) - - all_specs.map do |name, version, platform| - spec = load_spec(name, version, platform, gem_repo) - next unless gem_names.include?(spec.name) - { - :name => spec.name, - :number => spec.version.version, - :platform => spec.platform.to_s, - :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| - [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] - end, - } - end.compact - end - - def load_spec(name, version, platform, gem_repo) - full_name = "#{name}-#{version}" - full_name += "-#{platform}" if platform != "ruby" - Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")))) - end - end - - get "/quick/Marshal.4.8/:id" do - redirect "/fetch/actual/gem/#{params[:id]}" - end - - get "/fetch/actual/gem/:id" do - File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}") - end - - get "/gems/:id" do - File.binread("#{default_gem_repo}/gems/#{params[:id]}") - end - - get "/api/v1/dependencies" do - Marshal.dump(dependencies_for(params[:gems])) - end - - get "/specs.4.8.gz" do - File.binread("#{default_gem_repo}/specs.4.8.gz") - end - - get "/prerelease_specs.4.8.gz" do - File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz") - end -end +require_relative "helpers/endpoint" +require_relative "helpers/artifice" Artifice.activate_with(Endpoint) diff --git a/spec/bundler/support/artifice/endpoint_500.rb b/spec/bundler/support/artifice/endpoint_500.rb index a0d850a44d..d8ab6b65bc 100644 --- a/spec/bundler/support/artifice/endpoint_500.rb +++ b/spec/bundler/support/artifice/endpoint_500.rb @@ -2,17 +2,16 @@ require_relative "../path" -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) -require "artifice" require "sinatra/base" -Artifice.deactivate - class Endpoint500 < Sinatra::Base before do halt 500 end end +require_relative "helpers/artifice" + Artifice.activate_with(Endpoint500) diff --git a/spec/bundler/support/artifice/endpoint_api_forbidden.rb b/spec/bundler/support/artifice/endpoint_api_forbidden.rb index edc2463424..6bdc5896d6 100644 --- a/spec/bundler/support/artifice/endpoint_api_forbidden.rb +++ b/spec/bundler/support/artifice/endpoint_api_forbidden.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointApiForbidden < Endpoint get "/api/v1/dependencies" do @@ -10,4 +8,6 @@ class EndpointApiForbidden < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointApiForbidden) diff --git a/spec/bundler/support/artifice/endpoint_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_basic_authentication.rb index ff3d1493d6..e8e3569e63 100644 --- a/spec/bundler/support/artifice/endpoint_basic_authentication.rb +++ b/spec/bundler/support/artifice/endpoint_basic_authentication.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointBasicAuthentication < Endpoint before do @@ -12,4 +10,6 @@ class EndpointBasicAuthentication < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb index 8b8972cedd..ce30de0a68 100644 --- a/spec/bundler/support/artifice/endpoint_creds_diff_host.rb +++ b/spec/bundler/support/artifice/endpoint_creds_diff_host.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointCredsDiffHost < Endpoint helpers do @@ -36,4 +34,6 @@ class EndpointCredsDiffHost < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointCredsDiffHost) diff --git a/spec/bundler/support/artifice/endpoint_extra.rb b/spec/bundler/support/artifice/endpoint_extra.rb index 942c4352b7..021fd435fe 100644 --- a/spec/bundler/support/artifice/endpoint_extra.rb +++ b/spec/bundler/support/artifice/endpoint_extra.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointExtra < Endpoint get "/extra/api/v1/dependencies" do @@ -30,4 +28,6 @@ class EndpointExtra < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointExtra) diff --git a/spec/bundler/support/artifice/endpoint_extra_api.rb b/spec/bundler/support/artifice/endpoint_extra_api.rb index 1cfef7a7fc..a965af6e73 100644 --- a/spec/bundler/support/artifice/endpoint_extra_api.rb +++ b/spec/bundler/support/artifice/endpoint_extra_api.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointExtraApi < Endpoint get "/extra/api/v1/dependencies" do @@ -31,4 +29,6 @@ class EndpointExtraApi < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointExtraApi) diff --git a/spec/bundler/support/artifice/endpoint_extra_missing.rb b/spec/bundler/support/artifice/endpoint_extra_missing.rb index 5fd9238207..73e2defb32 100644 --- a/spec/bundler/support/artifice/endpoint_extra_missing.rb +++ b/spec/bundler/support/artifice/endpoint_extra_missing.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint_extra" - -Artifice.deactivate +require_relative "helpers/endpoint_extra" class EndpointExtraMissing < EndpointExtra get "/extra/fetch/actual/gem/:id" do @@ -14,4 +12,6 @@ class EndpointExtraMissing < EndpointExtra end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointExtraMissing) diff --git a/spec/bundler/support/artifice/endpoint_fallback.rb b/spec/bundler/support/artifice/endpoint_fallback.rb index 08edf232e3..742e563f07 100644 --- a/spec/bundler/support/artifice/endpoint_fallback.rb +++ b/spec/bundler/support/artifice/endpoint_fallback.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointFallback < Endpoint DEPENDENCY_LIMIT = 60 @@ -16,4 +14,6 @@ class EndpointFallback < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointFallback) diff --git a/spec/bundler/support/artifice/endpoint_host_redirect.rb b/spec/bundler/support/artifice/endpoint_host_redirect.rb index 338cbcad00..0efb6cda02 100644 --- a/spec/bundler/support/artifice/endpoint_host_redirect.rb +++ b/spec/bundler/support/artifice/endpoint_host_redirect.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointHostRedirect < Endpoint get "/fetch/actual/gem/:id", :host_name => "localgemserver.test" do @@ -14,4 +12,6 @@ class EndpointHostRedirect < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointHostRedirect) diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/endpoint_marshal_fail.rb index 22c13e3e17..74ce321de6 100644 --- a/spec/bundler/support/artifice/endpoint_marshal_fail.rb +++ b/spec/bundler/support/artifice/endpoint_marshal_fail.rb @@ -1,13 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint_fallback" - -Artifice.deactivate - -class EndpointMarshalFail < EndpointFallback - get "/api/v1/dependencies" do - "f0283y01hasf" - end -end +require_relative "helpers/endpoint_marshal_fail" +require_relative "helpers/artifice" Artifice.activate_with(EndpointMarshalFail) diff --git a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb index c341c3993f..ea4cfbe965 100644 --- a/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb +++ b/spec/bundler/support/artifice/endpoint_marshal_fail_basic_authentication.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint_marshal_fail" - -Artifice.deactivate +require_relative "helpers/endpoint_marshal_fail" class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail before do @@ -12,4 +10,6 @@ class EndpointMarshalFailBasicAuthentication < EndpointMarshalFail end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointMarshalFailBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint_mirror_source.rb b/spec/bundler/support/artifice/endpoint_mirror_source.rb index 788a9027f3..6ea1a77eca 100644 --- a/spec/bundler/support/artifice/endpoint_mirror_source.rb +++ b/spec/bundler/support/artifice/endpoint_mirror_source.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" +require_relative "helpers/endpoint" class EndpointMirrorSource < Endpoint get "/gems/:id" do @@ -12,4 +12,6 @@ class EndpointMirrorSource < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointMirrorSource) diff --git a/spec/bundler/support/artifice/endpoint_redirect.rb b/spec/bundler/support/artifice/endpoint_redirect.rb index ee97fccf64..84f546ba9d 100644 --- a/spec/bundler/support/artifice/endpoint_redirect.rb +++ b/spec/bundler/support/artifice/endpoint_redirect.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointRedirect < Endpoint get "/fetch/actual/gem/:id" do @@ -14,4 +12,6 @@ class EndpointRedirect < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointRedirect) diff --git a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb index 4d4da08770..8ce1bdd4ad 100644 --- a/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb +++ b/spec/bundler/support/artifice/endpoint_strict_basic_authentication.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint" - -Artifice.deactivate +require_relative "helpers/endpoint" class EndpointStrictBasicAuthentication < Endpoint before do @@ -17,4 +15,6 @@ class EndpointStrictBasicAuthentication < Endpoint end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointStrictBasicAuthentication) diff --git a/spec/bundler/support/artifice/endpoint_timeout.rb b/spec/bundler/support/artifice/endpoint_timeout.rb index c118da1893..86b793e499 100644 --- a/spec/bundler/support/artifice/endpoint_timeout.rb +++ b/spec/bundler/support/artifice/endpoint_timeout.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "endpoint_fallback" - -Artifice.deactivate +require_relative "helpers/endpoint_fallback" class EndpointTimeout < EndpointFallback SLEEP_TIMEOUT = 3 @@ -12,4 +10,6 @@ class EndpointTimeout < EndpointFallback end end +require_relative "helpers/artifice" + Artifice.activate_with(EndpointTimeout) diff --git a/spec/bundler/support/artifice/fail.rb b/spec/bundler/support/artifice/fail.rb index f69f2eccc6..6286e43fbd 100644 --- a/spec/bundler/support/artifice/fail.rb +++ b/spec/bundler/support/artifice/fail.rb @@ -2,10 +2,6 @@ require "net/http" -# We can't use artifice here because it uses rack - -module Artifice; end # for < 2.0, Net::HTTP::Persistent::SSLReuse - class Fail < Net::HTTP # Net::HTTP uses a @newimpl instance variable to decide whether # to use a legacy implementation. Since we are subclassing @@ -27,8 +23,7 @@ class Fail < Net::HTTP end end +require_relative "helpers/artifice" + # Replace Net::HTTP with our failing subclass -::Net.class_eval do - remove_const(:HTTP) - const_set(:HTTP, ::Fail) -end +Artifice.replace_net_http(::Fail) diff --git a/spec/bundler/support/artifice/helpers/artifice.rb b/spec/bundler/support/artifice/helpers/artifice.rb new file mode 100644 index 0000000000..b8c78614fb --- /dev/null +++ b/spec/bundler/support/artifice/helpers/artifice.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +# This module was initially borrowed from https://github.com/wycats/artifice +module Artifice + # Activate Artifice with a particular Rack endpoint. + # + # Calling this method will replace the Net::HTTP system + # with a replacement that routes all requests to the + # Rack endpoint. + # + # @param [#call] endpoint A valid Rack endpoint + def self.activate_with(endpoint) + require_relative "rack_request" + + Net::HTTP.endpoint = endpoint + replace_net_http(Artifice::Net::HTTP) + end + + # Deactivate the Artifice replacement. + def self.deactivate + replace_net_http(::Net::HTTP) + end + + def self.replace_net_http(value) + ::Net.class_eval do + remove_const(:HTTP) + const_set(:HTTP, value) + end + end +end diff --git a/spec/bundler/support/artifice/helpers/compact_index.rb b/spec/bundler/support/artifice/helpers/compact_index.rb new file mode 100644 index 0000000000..4df47a9659 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/compact_index.rb @@ -0,0 +1,118 @@ +# frozen_string_literal: true + +require_relative "endpoint" + +$LOAD_PATH.unshift Dir[Spec::Path.base_system_gem_path.join("gems/compact_index*/lib")].first.to_s +require "compact_index" + +class CompactIndexAPI < Endpoint + helpers do + include Spec::Path + + def load_spec(name, version, platform, gem_repo) + full_name = "#{name}-#{version}" + full_name += "-#{platform}" if platform != "ruby" + Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")))) + end + + def etag_response + response_body = yield + checksum = Digest(:MD5).hexdigest(response_body) + return if not_modified?(checksum) + headers "ETag" => quote(checksum) + headers "Surrogate-Control" => "max-age=2592000, stale-while-revalidate=60" + content_type "text/plain" + requested_range_for(response_body) + rescue StandardError => e + puts e + puts e.backtrace + raise + end + + def not_modified?(checksum) + etags = parse_etags(request.env["HTTP_IF_NONE_MATCH"]) + + return unless etags.include?(checksum) + headers "ETag" => quote(checksum) + status 304 + body "" + end + + def requested_range_for(response_body) + ranges = Rack::Utils.byte_ranges(env, response_body.bytesize) + + if ranges + status 206 + body ranges.map! {|range| slice_body(response_body, range) }.join + else + status 200 + body response_body + end + end + + def quote(string) + %("#{string}") + end + + def parse_etags(value) + value ? value.split(/, ?/).select {|s| s.sub!(/"(.*)"/, '\1') } : [] + end + + def slice_body(body, range) + body.byteslice(range) + end + + def gems(gem_repo = default_gem_repo) + @gems ||= {} + @gems[gem_repo] ||= begin + specs = Bundler::Deprecate.skip_during do + %w[specs.4.8 prerelease_specs.4.8].map do |filename| + Marshal.load(File.open(gem_repo.join(filename)).read).map do |name, version, platform| + load_spec(name, version, platform, gem_repo) + end + end.flatten + end + + specs.group_by(&:name).map do |name, versions| + gem_versions = versions.map do |spec| + deps = spec.dependencies.select {|d| d.type == :runtime }.map do |d| + reqs = d.requirement.requirements.map {|r| r.join(" ") }.join(", ") + CompactIndex::Dependency.new(d.name, reqs) + end + checksum = begin + Digest(:SHA256).file("#{gem_repo}/gems/#{spec.original_name}.gem").base64digest + rescue StandardError + nil + end + CompactIndex::GemVersion.new(spec.version.version, spec.platform.to_s, checksum, nil, + deps, spec.required_ruby_version.to_s, spec.required_rubygems_version.to_s) + end + CompactIndex::Gem.new(name, gem_versions) + end + end + end + end + + get "/names" do + etag_response do + CompactIndex.names(gems.map(&:name)) + end + end + + get "/versions" do + etag_response do + file = tmp("versions.list") + FileUtils.rm_f(file) + file = CompactIndex::VersionsFile.new(file.to_s) + file.create(gems) + file.contents + end + end + + get "/info/:name" do + etag_response do + gem = gems.find {|g| g.name == params[:name] } + CompactIndex.info(gem ? gem.versions : []) + end + end +end diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra.rb b/spec/bundler/support/artifice/helpers/compact_index_extra.rb new file mode 100644 index 0000000000..9e742630dd --- /dev/null +++ b/spec/bundler/support/artifice/helpers/compact_index_extra.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require_relative "compact_index" + +class CompactIndexExtra < CompactIndexAPI + get "/extra/versions" do + halt 404 + end + + get "/extra/api/v1/dependencies" do + halt 404 + end + + get "/extra/specs.4.8.gz" do + File.binread("#{gem_repo2}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.binread("#{gem_repo2}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.binread("#{gem_repo2}/gems/#{params[:id]}") + end +end diff --git a/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb new file mode 100644 index 0000000000..d9a7d83d23 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/compact_index_extra_api.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require_relative "compact_index" + +class CompactIndexExtraApi < CompactIndexAPI + get "/extra/names" do + etag_response do + CompactIndex.names(gems(gem_repo4).map(&:name)) + end + end + + get "/extra/versions" do + etag_response do + file = tmp("versions.list") + FileUtils.rm_f(file) + file = CompactIndex::VersionsFile.new(file.to_s) + file.create(gems(gem_repo4)) + file.contents + end + end + + get "/extra/info/:name" do + etag_response do + gem = gems(gem_repo4).find {|g| g.name == params[:name] } + CompactIndex.info(gem ? gem.versions : []) + end + end + + get "/extra/specs.4.8.gz" do + File.binread("#{gem_repo4}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.binread("#{gem_repo4}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.binread("#{gem_repo4}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.binread("#{gem_repo4}/gems/#{params[:id]}") + end +end diff --git a/spec/bundler/support/artifice/helpers/endpoint.rb b/spec/bundler/support/artifice/helpers/endpoint.rb new file mode 100644 index 0000000000..fc0381dc38 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/endpoint.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require_relative "../../path" + +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) + +require "sinatra/base" + +ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant +ALL_REQUESTS_MUTEX = Thread::Mutex.new + +at_exit do + if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"] + expected = expected.split("\n").sort + actual = ALL_REQUESTS.sort + + unless expected == actual + raise "Unexpected requests!\nExpected:\n\t#{expected.join("\n\t")}\n\nActual:\n\t#{actual.join("\n\t")}" + end + end +end + +class Endpoint < Sinatra::Base + def self.all_requests + @all_requests ||= [] + end + + set :raise_errors, true + set :show_exceptions, false + + def call!(*) + super.tap do + ALL_REQUESTS_MUTEX.synchronize do + ALL_REQUESTS << @request.url + end + end + end + + helpers do + include Spec::Path + + def default_gem_repo + if ENV["BUNDLER_SPEC_GEM_REPO"] + Pathname.new(ENV["BUNDLER_SPEC_GEM_REPO"]) + else + case request.host + when "gem.repo1" + Spec::Path.gem_repo1 + when "gem.repo2" + Spec::Path.gem_repo2 + when "gem.repo3" + Spec::Path.gem_repo3 + when "gem.repo4" + Spec::Path.gem_repo4 + else + Spec::Path.gem_repo1 + end + end + end + + def dependencies_for(gem_names, gem_repo = default_gem_repo) + return [] if gem_names.nil? || gem_names.empty? + + all_specs = %w[specs.4.8 prerelease_specs.4.8].map do |filename| + Marshal.load(File.open(gem_repo.join(filename)).read) + end.inject(:+) + + all_specs.map do |name, version, platform| + spec = load_spec(name, version, platform, gem_repo) + next unless gem_names.include?(spec.name) + { + :name => spec.name, + :number => spec.version.version, + :platform => spec.platform.to_s, + :dependencies => spec.dependencies.select {|dep| dep.type == :runtime }.map do |dep| + [dep.name, dep.requirement.requirements.map {|a| a.join(" ") }.join(", ")] + end, + } + end.compact + end + + def load_spec(name, version, platform, gem_repo) + full_name = "#{name}-#{version}" + full_name += "-#{platform}" if platform != "ruby" + Marshal.load(Bundler.rubygems.inflate(File.binread(gem_repo.join("quick/Marshal.4.8/#{full_name}.gemspec.rz")))) + end + end + + get "/quick/Marshal.4.8/:id" do + redirect "/fetch/actual/gem/#{params[:id]}" + end + + get "/fetch/actual/gem/:id" do + File.binread("#{default_gem_repo}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/gems/:id" do + File.binread("#{default_gem_repo}/gems/#{params[:id]}") + end + + get "/api/v1/dependencies" do + Marshal.dump(dependencies_for(params[:gems])) + end + + get "/specs.4.8.gz" do + File.binread("#{default_gem_repo}/specs.4.8.gz") + end + + get "/prerelease_specs.4.8.gz" do + File.binread("#{default_gem_repo}/prerelease_specs.4.8.gz") + end +end diff --git a/spec/bundler/support/artifice/helpers/endpoint_extra.rb b/spec/bundler/support/artifice/helpers/endpoint_extra.rb new file mode 100644 index 0000000000..ad08495b50 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/endpoint_extra.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require_relative "endpoint" + +class EndpointExtra < Endpoint + get "/extra/api/v1/dependencies" do + halt 404 + end + + get "/extra/specs.4.8.gz" do + File.binread("#{gem_repo2}/specs.4.8.gz") + end + + get "/extra/prerelease_specs.4.8.gz" do + File.binread("#{gem_repo2}/prerelease_specs.4.8.gz") + end + + get "/extra/quick/Marshal.4.8/:id" do + redirect "/extra/fetch/actual/gem/#{params[:id]}" + end + + get "/extra/fetch/actual/gem/:id" do + File.binread("#{gem_repo2}/quick/Marshal.4.8/#{params[:id]}") + end + + get "/extra/gems/:id" do + File.binread("#{gem_repo2}/gems/#{params[:id]}") + end +end diff --git a/spec/bundler/support/artifice/helpers/endpoint_fallback.rb b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb new file mode 100644 index 0000000000..a232930b67 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/endpoint_fallback.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require_relative "endpoint" + +class EndpointFallback < Endpoint + DEPENDENCY_LIMIT = 60 + + get "/api/v1/dependencies" do + if params[:gems] && params[:gems].size <= DEPENDENCY_LIMIT + Marshal.dump(dependencies_for(params[:gems])) + else + halt 413, "Too many gems to resolve, please request less than #{DEPENDENCY_LIMIT} gems" + end + end +end diff --git a/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb new file mode 100644 index 0000000000..c409d39d99 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/endpoint_marshal_fail.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require_relative "endpoint_fallback" + +class EndpointMarshalFail < EndpointFallback + get "/api/v1/dependencies" do + "f0283y01hasf" + end +end diff --git a/spec/bundler/support/artifice/helpers/rack_request.rb b/spec/bundler/support/artifice/helpers/rack_request.rb new file mode 100644 index 0000000000..c4a07812a6 --- /dev/null +++ b/spec/bundler/support/artifice/helpers/rack_request.rb @@ -0,0 +1,100 @@ +# frozen_string_literal: true + +require "rack/test" +require "net/http" + +module Artifice + module Net + # This is an internal object that can receive Rack requests + # to the application using the Rack::Test API + class RackRequest + include Rack::Test::Methods + attr_reader :app + + def initialize(app) + @app = app + end + end + + class HTTP < ::Net::HTTP + class << self + attr_accessor :endpoint + end + + # Net::HTTP uses a @newimpl instance variable to decide whether + # to use a legacy implementation. Since we are subclassing + # Net::HTTP, we must set it + @newimpl = true + + # We don't need to connect, so blank out this method + def connect + end + + # Replace the Net::HTTP request method with a method + # that converts the request into a Rack request and + # dispatches it to the Rack endpoint. + # + # @param [Net::HTTPRequest] req A Net::HTTPRequest + # object, or one if its subclasses + # @param [optional, String, #read] body This should + # be sent as "rack.input". If it's a String, it will + # be converted to a StringIO. + # @return [Net::HTTPResponse] + # + # @yield [Net::HTTPResponse] If a block is provided, + # this method will yield the Net::HTTPResponse to + # it after the body is read. + def request(req, body = nil, &block) + rack_request = RackRequest.new(self.class.endpoint) + + req.each_header do |header, value| + rack_request.header(header, value) + end + + scheme = use_ssl? ? "https" : "http" + prefix = "#{scheme}://#{addr_port}" + body_stream_contents = req.body_stream.read if req.body_stream + + response = rack_request.request("#{prefix}#{req.path}", + { :method => req.method, :input => body || req.body || body_stream_contents }) + + make_net_http_response(response, &block) + end + + private + + # This method takes a Rack response and creates a Net::HTTPResponse + # Instead of trying to mock HTTPResponse directly, we just convert + # the Rack response into a String that looks like a normal HTTP + # response and call Net::HTTPResponse.read_new + # + # @param [Array(#to_i, Hash, #each)] response a Rack response + # @return [Net::HTTPResponse] + # @yield [Net::HTTPResponse] If a block is provided, yield the + # response to it after the body is read + def make_net_http_response(response) + status = response.status + headers = response.headers + body = response.body + + response_string = [] + response_string << "HTTP/1.1 #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}" + + headers.each do |header, value| + response_string << "#{header}: #{value}" + end + + response_string << "" << body + + response_io = ::Net::BufferedIO.new(StringIO.new(response_string.join("\n"))) + res = ::Net::HTTPResponse.read_new(response_io) + + res.reading_body(response_io, true) do + yield res if block_given? + end + + res + end + end + end +end diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb index ceb133346f..e5be1e3d70 100644 --- a/spec/bundler/support/artifice/vcr.rb +++ b/spec/bundler/support/artifice/vcr.rb @@ -158,8 +158,7 @@ class BundlerVCRHTTP < Net::HTTP alias_method :request, :request_with_vcr end +require_relative "helpers/artifice" + # Replace Net::HTTP with our VCR subclass -::Net.class_eval do - remove_const(:HTTP) - const_set(:HTTP, BundlerVCRHTTP) -end +Artifice.replace_net_http(BundlerVCRHTTP) diff --git a/spec/bundler/support/artifice/windows.rb b/spec/bundler/support/artifice/windows.rb index 674470688d..4d90e0a426 100644 --- a/spec/bundler/support/artifice/windows.rb +++ b/spec/bundler/support/artifice/windows.rb @@ -2,13 +2,10 @@ require_relative "../path" -$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{artifice,mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) +$LOAD_PATH.unshift(*Dir[Spec::Path.base_system_gem_path.join("gems/{mustermann,rack,tilt,sinatra,ruby2_keywords}-*/lib")].map(&:to_s)) -require "artifice" require "sinatra/base" -Artifice.deactivate - class Windows < Sinatra::Base set :raise_errors, true set :show_exceptions, false @@ -43,4 +40,6 @@ class Windows < Sinatra::Base end end +require_relative "helpers/artifice" + Artifice.activate_with(Windows) diff --git a/tool/bundler/test_gems.rb b/tool/bundler/test_gems.rb index 215d23183e..3fb58b9388 100644 --- a/tool/bundler/test_gems.rb +++ b/tool/bundler/test_gems.rb @@ -5,7 +5,6 @@ source "https://rubygems.org" gem "rack", "2.0.8" gem "webrick", "1.7.0" gem "rack-test", "~> 1.1" -gem "artifice", "~> 0.6.0" gem "compact_index", "~> 0.13.0" gem "sinatra", "~> 2.0" gem "rake", "13.0.1" diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index 62b65c007f..554fd3ed9e 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -1,8 +1,6 @@ GEM remote: https://rubygems.org/ specs: - artifice (0.6) - rack-test builder (3.2.4) compact_index (0.13.0) mustermann (1.1.2) @@ -33,7 +31,6 @@ PLATFORMS x86_64-linux DEPENDENCIES - artifice (~> 0.6.0) builder (~> 3.2) compact_index (~> 0.13.0) rack (= 2.0.8) |