mirror of
https://github.com/ruby/ruby.git
synced 2025-08-24 21:44:30 +02:00

We now run specs against a single version, so I prefer to keep a single
branch. Once we bump the major version, this will need very little
updates, and that seems fine.
3866d25a00
1095 lines
34 KiB
Ruby
1095 lines
34 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe "compact index api" do
|
|
let(:source_hostname) { "localgemserver.test" }
|
|
let(:source_uri) { "http://#{source_hostname}" }
|
|
|
|
it "should use the API" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "has a debug mode" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index", env: { "DEBUG_COMPACT_INDEX" => "true" }
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(err).to include("[Bundler::CompactIndexClient] available?")
|
|
expect(err).to include("[Bundler::CompactIndexClient] fetching versions")
|
|
expect(err).to include("[Bundler::CompactIndexClient] info(myrack)")
|
|
expect(err).to include("[Bundler::CompactIndexClient] fetching info/myrack")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "should URI encode gem names" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem " sinatra"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index", raise_on_error: false
|
|
expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.")
|
|
end
|
|
|
|
it "should handle nested dependencies" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "rails"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems(
|
|
"rails 2.3.2",
|
|
"actionpack 2.3.2",
|
|
"activerecord 2.3.2",
|
|
"actionmailer 2.3.2",
|
|
"activeresource 2.3.2",
|
|
"activesupport 2.3.2"
|
|
)
|
|
end
|
|
|
|
it "should handle case sensitivity conflicts" do
|
|
build_repo4(build_compact_index: false) do
|
|
build_gem "myrack", "1.0" do |s|
|
|
s.add_dependency("Myrack", "0.1")
|
|
end
|
|
build_gem "Myrack", "0.1"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem "myrack", "1.0"
|
|
gem "Myrack", "0.1"
|
|
G
|
|
|
|
# can't use `include_gems` here since the `require` will conflict on a
|
|
# case-insensitive FS
|
|
run "Bundler.require; puts Gem.loaded_specs.values_at('myrack', 'Myrack').map(&:full_name)"
|
|
expect(out).to eq("myrack-1.0\nMyrack-0.1")
|
|
end
|
|
|
|
it "should handle multiple gem dependencies on the same gem" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "net-sftp"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(the_bundle).to include_gems "net-sftp 1.1.1"
|
|
end
|
|
|
|
it "should use the endpoint when using deployment mode" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
bundle "config set --local deployment true"
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "handles git dependencies that are in rubygems" do
|
|
build_git "foo" do |s|
|
|
s.executables = "foobar"
|
|
s.add_dependency "rails", "2.3.2"
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
git "#{lib_path("foo-1.0")}" do
|
|
gem 'foo'
|
|
end
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
expect(the_bundle).to include_gems("rails 2.3.2")
|
|
end
|
|
|
|
it "handles git dependencies that are in rubygems using deployment mode" do
|
|
build_git "foo" do |s|
|
|
s.executables = "foobar"
|
|
s.add_dependency "rails", "2.3.2"
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'foo', :git => "#{lib_path("foo-1.0")}"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
bundle "config set --local deployment true"
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
expect(the_bundle).to include_gems("rails 2.3.2")
|
|
end
|
|
|
|
it "doesn't fail if you only have a git gem with no deps when using deployment mode" do
|
|
build_git "foo"
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'foo', :git => "#{lib_path("foo-1.0")}"
|
|
G
|
|
|
|
bundle "install", artifice: "compact_index"
|
|
bundle "config set --local deployment true"
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
expect(the_bundle).to include_gems("foo 1.0")
|
|
end
|
|
|
|
it "falls back when the API URL returns 403 Forbidden" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, verbose: true, artifice: "compact_index_forbidden"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "falls back when the versions endpoint has a checksum mismatch" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, verbose: true, artifice: "compact_index_checksum_mismatch"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(out).to include("The checksum of /versions does not match the checksum provided by the server!")
|
|
expect(out).to include("Calculated checksums #{{ "sha-256" => "8KfZiM/fszVkqhP/m5s9lvE6M9xKu4I1bU4Izddp5Ms=" }.inspect} did not match expected #{{ "sha-256" => "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" }.inspect}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "shows proper path when permission errors happen", :permissions do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
versions = compact_index_cache_path.join(
|
|
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
|
|
)
|
|
versions.dirname.mkpath
|
|
versions.write("created_at")
|
|
FileUtils.chmod("-r", versions)
|
|
|
|
bundle :install, artifice: "compact_index", raise_on_error: false
|
|
|
|
expect(err).to include(
|
|
"There was an error while trying to read from `#{versions}`. It is likely that you need to grant read permissions for that path."
|
|
)
|
|
end
|
|
|
|
it "falls back when the user's home directory does not exist or is not writable" do
|
|
ENV["HOME"] = tmp("missing_home").to_s
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "handles host redirects" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_host_redirect"
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "handles host redirects without Gem::Net::HTTP::Persistent" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
FileUtils.mkdir_p lib_path
|
|
File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
|
|
h.write <<-H
|
|
module Kernel
|
|
alias require_without_disabled_net_http require
|
|
def require(*args)
|
|
raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
|
|
require_without_disabled_net_http(*args)
|
|
end
|
|
end
|
|
H
|
|
end
|
|
|
|
bundle :install, artifice: "compact_index_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")]
|
|
expect(out).to_not match(/Too many redirects/)
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "times out when Bundler::Fetcher redirects too much" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_redirects", raise_on_error: false
|
|
expect(err).to match(/Too many redirects/)
|
|
end
|
|
|
|
context "when --full-index is specified" do
|
|
it "should use the modern index for install" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "install --full-index", artifice: "compact_index"
|
|
expect(out).to include("Fetching source index from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "should use the modern index for update" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "update --full-index", artifice: "compact_index", all: true
|
|
expect(out).to include("Fetching source index from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
end
|
|
|
|
it "does not double check for gems that are only installed locally" do
|
|
build_repo2 do
|
|
build_gem "net_a" do |s|
|
|
s.add_dependency "net_b"
|
|
s.add_dependency "net_build_extensions"
|
|
end
|
|
|
|
build_gem "net_b"
|
|
|
|
build_gem "net_build_extensions" do |s|
|
|
s.add_dependency "rake"
|
|
s.extensions << "Rakefile"
|
|
s.write "Rakefile", <<-RUBY
|
|
task :default do
|
|
path = File.expand_path("lib", __dir__)
|
|
FileUtils.mkdir_p(path)
|
|
File.open("\#{path}/net_build_extensions.rb", "w") do |f|
|
|
f.puts "NET_BUILD_EXTENSIONS = 'YES'"
|
|
end
|
|
end
|
|
RUBY
|
|
end
|
|
end
|
|
|
|
system_gems %w[myrack-1.0.0 thin-1.0 net_a-1.0], gem_repo: gem_repo2
|
|
bundle "config set --local path.system true"
|
|
ENV["BUNDLER_SPEC_ALL_REQUESTS"] = <<~EOS.strip
|
|
#{source_uri}/versions
|
|
#{source_uri}/info/myrack
|
|
EOS
|
|
|
|
install_gemfile <<-G, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
expect(stdboth).not_to include "Double checking"
|
|
end
|
|
|
|
it "fetches again when more dependencies are found in subsequent sources" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra"
|
|
gem "back_deps"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_extra"
|
|
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
|
|
end
|
|
|
|
it "fetches again when more dependencies are found in subsequent sources with source blocks" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_extra", verbose: true
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra" do
|
|
gem "back_deps"
|
|
end
|
|
G
|
|
|
|
expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0"
|
|
end
|
|
|
|
it "fetches gem versions even when those gems are already installed" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack", "1.0.0"
|
|
G
|
|
bundle :install, artifice: "compact_index_extra_api"
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
|
|
build_repo4 do
|
|
build_gem "myrack", "1.2" do |s|
|
|
s.executables = "myrackup"
|
|
end
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}" do; end
|
|
source "#{source_uri}/extra"
|
|
gem "myrack", "1.2"
|
|
G
|
|
bundle :install, artifice: "compact_index_extra_api"
|
|
expect(the_bundle).to include_gems "myrack 1.2"
|
|
end
|
|
|
|
it "considers all possible versions of dependencies from all api gem sources" do
|
|
# In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
|
|
# exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
|
|
# of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
|
|
# repo and installs it.
|
|
build_repo4 do
|
|
build_gem "activesupport", "1.2.0"
|
|
build_gem "somegem", "1.0.0" do |s|
|
|
s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
|
|
end
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra"
|
|
gem 'somegem', '1.0.0'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_extra_api"
|
|
|
|
expect(the_bundle).to include_gems "somegem 1.0.0"
|
|
expect(the_bundle).to include_gems "activesupport 1.2.3"
|
|
end
|
|
|
|
it "prints API output properly with back deps" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra" do
|
|
gem "back_deps"
|
|
end
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_extra"
|
|
|
|
expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
|
|
expect(out).to include("Fetching source index from http://localgemserver.test/extra")
|
|
end
|
|
|
|
it "does not fetch every spec when doing back deps" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
build_gem "missing"
|
|
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_extra_missing"
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra" do
|
|
gem "back_deps"
|
|
end
|
|
G
|
|
|
|
expect(the_bundle).to include_gems "back_deps 1.0"
|
|
end
|
|
|
|
it "does not fetch every spec when doing back deps & everything is the compact index" do
|
|
build_repo4 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
build_gem "missing"
|
|
|
|
FileUtils.rm_r Dir[gem_repo4("gems/foo-*.gem")]
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_extra_api_missing"
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra" do
|
|
gem "back_deps"
|
|
end
|
|
G
|
|
|
|
expect(the_bundle).to include_gem "back_deps 1.0"
|
|
end
|
|
|
|
it "uses the endpoint if all sources support it" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
|
|
gem 'foo'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_api_missing"
|
|
expect(the_bundle).to include_gems "foo 1.0"
|
|
end
|
|
|
|
it "fetches again when more dependencies are found in subsequent sources using deployment mode" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra"
|
|
gem "back_deps"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_extra"
|
|
bundle "config --set local deployment true"
|
|
bundle :install, artifice: "compact_index_extra"
|
|
expect(the_bundle).to include_gems "back_deps 1.0"
|
|
end
|
|
|
|
it "fetches again when more dependencies are found in subsequent sources using deployment mode with blocks" do
|
|
build_repo2 do
|
|
build_gem "back_deps" do |s|
|
|
s.add_dependency "foo"
|
|
end
|
|
FileUtils.rm_r Dir[gem_repo2("gems/foo-*.gem")]
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
source "#{source_uri}/extra" do
|
|
gem "back_deps"
|
|
end
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_extra"
|
|
bundle "config set --local deployment true"
|
|
bundle :install, artifice: "compact_index_extra"
|
|
expect(the_bundle).to include_gems "back_deps 1.0"
|
|
end
|
|
|
|
it "does not refetch if the only unmet dependency is bundler" do
|
|
build_repo2 do
|
|
build_gem "bundler_dep" do |s|
|
|
s.add_dependency "bundler"
|
|
end
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
|
|
gem "bundler_dep"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
end
|
|
|
|
it "installs the binstubs" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "install --binstubs", artifice: "compact_index"
|
|
|
|
gembin "myrackup"
|
|
expect(out).to eq("1.0.0")
|
|
end
|
|
|
|
it "installs the bins when using --path and uses autoclean" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "install --path vendor/bundle", artifice: "compact_index"
|
|
|
|
expect(vendored_gems("bin/myrackup")).to exist
|
|
end
|
|
|
|
it "installs the bins when using --path and uses bundle clean" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "install --path vendor/bundle --no-clean", artifice: "compact_index"
|
|
|
|
expect(vendored_gems("bin/myrackup")).to exist
|
|
end
|
|
|
|
it "prints post_install_messages" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack-obama'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Post-install message from myrack:")
|
|
end
|
|
|
|
it "should display the post install message for a dependency" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack_middleware'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
expect(out).to include("Post-install message from myrack:")
|
|
expect(out).to include("Myrack's post install message")
|
|
end
|
|
|
|
context "when using basic authentication" do
|
|
let(:user) { "user" }
|
|
let(:password) { "pass" }
|
|
let(:basic_auth_source_uri) do
|
|
uri = Gem::URI.parse(source_uri)
|
|
uri.user = user
|
|
uri.password = password
|
|
|
|
uri
|
|
end
|
|
|
|
it "passes basic authentication details and strips out creds" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_basic_authentication"
|
|
expect(out).not_to include("#{user}:#{password}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "passes basic authentication details and strips out creds also in verbose mode" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, verbose: true, artifice: "compact_index_basic_authentication"
|
|
expect(out).not_to include("#{user}:#{password}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "strips http basic auth creds when warning about ambiguous sources" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_basic_authentication"
|
|
expect(err).to include("Warning: the gem 'myrack' was found in multiple sources.")
|
|
expect(err).not_to include("#{user}:#{password}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "does not pass the user / password to different hosts on redirect" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_creds_diff_host"
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
describe "with authentication details in bundle config" do
|
|
before do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
end
|
|
|
|
it "reads authentication details by host name from bundle config" do
|
|
bundle "config set #{source_hostname} #{user}:#{password}"
|
|
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication"
|
|
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "reads authentication details by full url from bundle config" do
|
|
# The trailing slash is necessary here; Fetcher canonicalizes the URI.
|
|
bundle "config set #{source_uri}/ #{user}:#{password}"
|
|
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication"
|
|
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "should use the API" do
|
|
bundle "config set #{source_hostname} #{user}:#{password}"
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication"
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "prefers auth supplied in the source uri" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle "config set #{source_hostname} otheruser:wrong"
|
|
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication"
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "shows instructions if auth is not provided for the source" do
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
|
|
expect(err).to include("bundle config set --global #{source_hostname} username:password")
|
|
end
|
|
|
|
it "fails if authentication has already been provided, but failed" do
|
|
bundle "config set #{source_hostname} #{user}:wrong"
|
|
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false
|
|
expect(err).to include("Bad username or password")
|
|
end
|
|
|
|
it "does not fallback to old dependency API if bad authentication is provided" do
|
|
bundle "config set #{source_hostname} #{user}:wrong"
|
|
|
|
bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false, verbose: true
|
|
expect(err).to include("Bad username or password")
|
|
expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions")
|
|
expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies")
|
|
end
|
|
end
|
|
|
|
describe "with no password" do
|
|
let(:password) { nil }
|
|
|
|
it "passes basic authentication details" do
|
|
gemfile <<-G
|
|
source "#{basic_auth_source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_basic_authentication"
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when ruby is compiled without openssl" do
|
|
before do
|
|
# Install a monkeypatch that reproduces the effects of openssl being
|
|
# missing when the fetcher runs, as happens in real life. The reason
|
|
# we can't just overwrite openssl.rb is that Artifice uses it.
|
|
bundled_app("broken_ssl").mkpath
|
|
bundled_app("broken_ssl/openssl.rb").open("w") do |f|
|
|
f.write <<-RUBY
|
|
raise LoadError, "cannot load such file -- openssl"
|
|
RUBY
|
|
end
|
|
end
|
|
|
|
it "explains what to do to get it, and includes original error" do
|
|
gemfile <<-G
|
|
source "#{source_uri.gsub(/http/, "https")}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, env: { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }, raise_on_error: false, artifice: nil
|
|
expect(err).to include("recompile Ruby").and include("cannot load such file")
|
|
end
|
|
end
|
|
|
|
context "when SSL certificate verification fails" do
|
|
it "explains what happened" do
|
|
# Install a monkeypatch that reproduces the effects of openssl raising
|
|
# a certificate validation error when RubyGems tries to connect.
|
|
gemfile <<-G
|
|
class Gem::Net::HTTP
|
|
def start
|
|
raise OpenSSL::SSL::SSLError, "certificate verify failed"
|
|
end
|
|
end
|
|
|
|
source "#{source_uri.gsub(/http/, "https")}"
|
|
gem "myrack"
|
|
G
|
|
|
|
bundle :install, raise_on_error: false
|
|
expect(err).to match(/could not verify the SSL certificate/i)
|
|
end
|
|
end
|
|
|
|
context ".gemrc with sources is present" do
|
|
it "uses other sources declared in the Gemfile" do
|
|
File.open(home(".gemrc"), "w") do |file|
|
|
file.puts({ sources: ["https://rubygems.org"] }.to_yaml)
|
|
end
|
|
|
|
begin
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index_forbidden"
|
|
ensure
|
|
home(".gemrc").rmtree
|
|
end
|
|
end
|
|
end
|
|
|
|
it "performs update with etag not-modified" do
|
|
versions_etag = compact_index_cache_path.join(
|
|
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions.etag"
|
|
)
|
|
expect(versions_etag.file?).to eq(false)
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
# Initial install creates the cached versions file and etag file
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
expect(versions_etag.file?).to eq(true)
|
|
previous_content = versions_etag.binread
|
|
|
|
# Update the Gemfile so we can check subsequent install was successful
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
# Second install should match etag
|
|
bundle :install, artifice: "compact_index_etag_match"
|
|
|
|
expect(versions_etag.binread).to eq(previous_content)
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs full update when range is ignored" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
# Initial install creates the cached versions file and etag file
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
versions = compact_index_cache_path.join(
|
|
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
|
|
)
|
|
# Modify the cached file. The ranged request will be based on this but,
|
|
# in this test, the range is ignored so this gets overwritten, allowing install.
|
|
versions.write "ruining this file"
|
|
|
|
bundle :install, artifice: "compact_index_range_ignored"
|
|
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs partial update with a non-empty range" do
|
|
build_repo4 do
|
|
build_gem "myrack", "0.9.1"
|
|
end
|
|
|
|
# Initial install creates the cached versions file
|
|
install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
update_repo4 do
|
|
build_gem "myrack", "1.0.0"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_partial_update", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs partial update while local cache is updated by another process" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack'
|
|
G
|
|
|
|
# Create a partial cache versions file
|
|
versions = compact_index_cache_path.join(
|
|
"localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions"
|
|
)
|
|
versions.dirname.mkpath
|
|
versions.write("created_at")
|
|
|
|
bundle :install, artifice: "compact_index_concurrent_download"
|
|
|
|
expect(versions.read).to start_with("created_at")
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs a partial update that fails digest check, then a full update" do
|
|
build_repo4 do
|
|
build_gem "myrack", "0.9.1"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
update_repo4 do
|
|
build_gem "myrack", "1.0.0"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_partial_update_bad_digest", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no digest" do
|
|
build_repo4 do
|
|
build_gem "myrack", "0.9.1"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
update_repo4 do
|
|
build_gem "myrack", "1.0.0"
|
|
end
|
|
|
|
install_gemfile <<-G, artifice: "compact_index_partial_update_no_digest_not_incremental", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
expect(the_bundle).to include_gems "myrack 1.0.0"
|
|
end
|
|
|
|
it "performs full update of compact index info cache if range is not satisfiable" do
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '0.9.1'
|
|
G
|
|
|
|
bundle :install, artifice: "compact_index"
|
|
|
|
cache_path = compact_index_cache_path.join("localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5")
|
|
|
|
# We must remove the etag so that we don't ignore the range and get a 304 Not Modified.
|
|
myrack_info_etag_path = File.join(cache_path, "info-etags", "myrack-92f3313ce5721296f14445c3a6b9c073")
|
|
File.unlink(myrack_info_etag_path) if File.exist?(myrack_info_etag_path)
|
|
|
|
myrack_info_path = File.join(cache_path, "info", "myrack")
|
|
expected_myrack_info_content = File.read(myrack_info_path)
|
|
|
|
# Modify the cache files to make the range not satisfiable
|
|
File.open(myrack_info_path, "a") {|f| f << "0.9.2 |checksum:c55b525b421fd833a93171ad3d7f04528ca8e87d99ac273f8933038942a5888c" }
|
|
|
|
# Update the Gemfile so the next install does its normal things
|
|
gemfile <<-G
|
|
source "#{source_uri}"
|
|
gem 'myrack', '1.0.0'
|
|
G
|
|
|
|
# The cache files now being longer means the requested range is going to be not satisfiable
|
|
# Bundler must end up requesting the whole file to fix things up.
|
|
bundle :install, artifice: "compact_index_range_not_satisfiable"
|
|
|
|
resulting_myrack_info_content = File.read(myrack_info_path)
|
|
|
|
expect(resulting_myrack_info_content).to eq(expected_myrack_info_content)
|
|
end
|
|
|
|
it "fails gracefully when the source URI has an invalid scheme" do
|
|
install_gemfile <<-G, raise_on_error: false
|
|
source "htps://rubygems.org"
|
|
gem "myrack"
|
|
G
|
|
expect(exitstatus).to eq(15)
|
|
expect(err).to end_with(<<-E.strip)
|
|
The request uri `htps://index.rubygems.org/versions` has an invalid scheme (`htps`). Did you mean `http` or `https`?
|
|
E
|
|
end
|
|
|
|
describe "checksum validation" do
|
|
before do
|
|
lockfile <<-L
|
|
GEM
|
|
remote: #{source_uri}
|
|
specs:
|
|
myrack (1.0.0)
|
|
|
|
PLATFORMS
|
|
ruby
|
|
|
|
DEPENDENCIES
|
|
#{checksums_section}
|
|
BUNDLED WITH
|
|
#{Bundler::VERSION}
|
|
L
|
|
end
|
|
|
|
it "handles checksums from the server in base64" do
|
|
api_checksum = checksum_digest(gem_repo1, "myrack", "1.0.0")
|
|
myrack_checksum = [[api_checksum].pack("H*")].pack("m0")
|
|
install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => myrack_checksum }
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
expect(out).to include("Fetching gem metadata from #{source_uri}")
|
|
expect(the_bundle).to include_gems("myrack 1.0.0")
|
|
end
|
|
|
|
it "raises when the checksum does not match" do
|
|
install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", raise_on_error: false
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
|
|
gem_path = default_cache_path.dirname.join("myrack-1.0.0.gem")
|
|
|
|
expect(exitstatus).to eq(37)
|
|
expect(err).to eq <<~E.strip
|
|
Bundler found mismatched checksums. This is a potential security risk.
|
|
myrack (1.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222
|
|
from the API at http://localgemserver.test/
|
|
#{checksum_to_lock(gem_repo1, "myrack", "1.0.0")}
|
|
from the gem at #{gem_path}
|
|
|
|
If you trust the API at http://localgemserver.test/, to resolve this issue you can:
|
|
1. remove the gem at #{gem_path}
|
|
2. run `bundle install`
|
|
|
|
To ignore checksum security warnings, disable checksum validation with
|
|
`bundle config set --local disable_checksum_validation true`
|
|
E
|
|
end
|
|
|
|
it "raises when the checksum is the wrong length" do
|
|
install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", env: { "BUNDLER_SPEC_MYRACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, verbose: true, raise_on_error: false
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
expect(exitstatus).to eq(14)
|
|
expect(err).to include('Invalid checksum for myrack-0.9.1: "checksum!" is not a valid SHA256 hex or base64 digest')
|
|
end
|
|
|
|
it "does not raise when disable_checksum_validation is set" do
|
|
bundle "config set disable_checksum_validation true"
|
|
install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum"
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
end
|
|
end
|
|
|
|
it "works when cache dir is world-writable" do
|
|
install_gemfile <<-G, artifice: "compact_index"
|
|
File.umask(0000)
|
|
source "#{source_uri}"
|
|
gem "myrack"
|
|
G
|
|
end
|
|
|
|
it "doesn't explode when the API dependencies are wrong" do
|
|
install_gemfile <<-G, artifice: "compact_index_wrong_dependencies", env: { "DEBUG" => "true" }, raise_on_error: false
|
|
source "#{source_uri}"
|
|
gem "rails"
|
|
G
|
|
deps = [Gem::Dependency.new("rake", "= #{rake_version}"),
|
|
Gem::Dependency.new("actionpack", "= 2.3.2"),
|
|
Gem::Dependency.new("activerecord", "= 2.3.2"),
|
|
Gem::Dependency.new("actionmailer", "= 2.3.2"),
|
|
Gem::Dependency.new("activeresource", "= 2.3.2")]
|
|
expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has corrupted API dependencies")
|
|
expect(err).to include(<<-E.strip)
|
|
Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API (#{deps.map(&:to_s).join(", ")}).
|
|
Running `bundle update rails` should fix the problem.
|
|
E
|
|
end
|
|
|
|
it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
|
|
install_gemfile <<-G, artifice: "compact_index"
|
|
source "https://gem.repo1"
|
|
source "#{source_uri}" do
|
|
gem "rails"
|
|
gem "activemerchant"
|
|
end
|
|
G
|
|
gem_command "uninstall activemerchant"
|
|
bundle "update rails", artifice: "compact_index"
|
|
count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS
|
|
expect(lockfile.scan(/activemerchant \(/).size).to eq(count)
|
|
end
|
|
|
|
it "handles an API that does not provide checksums info (undocumented, support may get removed)" do
|
|
install_gemfile <<-G, artifice: "compact_index_no_checksums"
|
|
source "https://gem.repo1"
|
|
gem "rake"
|
|
G
|
|
end
|
|
end
|