parent
2581de112c
commit
49b0f3b024
Notes:
git
2022-12-15 10:07:00 +00:00
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)} " \
|
||||
|
@ -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?
|
||||
|
@ -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 } }
|
||||
|
||||
|
@ -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...")
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
30
spec/bundler/support/artifice/helpers/artifice.rb
Normal file
30
spec/bundler/support/artifice/helpers/artifice.rb
Normal file
@ -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
|
118
spec/bundler/support/artifice/helpers/compact_index.rb
Normal file
118
spec/bundler/support/artifice/helpers/compact_index.rb
Normal file
@ -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
|
33
spec/bundler/support/artifice/helpers/compact_index_extra.rb
Normal file
33
spec/bundler/support/artifice/helpers/compact_index_extra.rb
Normal file
@ -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
|
@ -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
|
112
spec/bundler/support/artifice/helpers/endpoint.rb
Normal file
112
spec/bundler/support/artifice/helpers/endpoint.rb
Normal file
@ -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
|
29
spec/bundler/support/artifice/helpers/endpoint_extra.rb
Normal file
29
spec/bundler/support/artifice/helpers/endpoint_extra.rb
Normal file
@ -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
|
15
spec/bundler/support/artifice/helpers/endpoint_fallback.rb
Normal file
15
spec/bundler/support/artifice/helpers/endpoint_fallback.rb
Normal file
@ -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
|
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative "endpoint_fallback"
|
||||
|
||||
class EndpointMarshalFail < EndpointFallback
|
||||
get "/api/v1/dependencies" do
|
||||
"f0283y01hasf"
|
||||
end
|
||||
end
|
100
spec/bundler/support/artifice/helpers/rack_request.rb
Normal file
100
spec/bundler/support/artifice/helpers/rack_request.rb
Normal file
@ -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
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user