Skip to content

Commit

Permalink
Merge RubyGems 3.2.2 and Bundler 2.2.2
Browse files Browse the repository at this point in the history
  • Loading branch information
hsbt committed Dec 18, 2020
1 parent 34f0606 commit 0e40cc9
Show file tree
Hide file tree
Showing 16 changed files with 175 additions and 40 deletions.
19 changes: 14 additions & 5 deletions lib/bundler/cli/install.rb
Expand Up @@ -152,18 +152,27 @@ def normalize_groups

check_for_group_conflicts_in_cli_options

Bundler.settings.set_command_option :with, nil if options[:with] == []
Bundler.settings.set_command_option :without, nil if options[:without] == []

with = options.fetch(:with, [])
with |= Bundler.settings[:with].map(&:to_s)
with -= options[:without] if options[:without]
with = nil if options[:with] == []

without = options.fetch(:without, [])
without |= Bundler.settings[:without].map(&:to_s)
without -= options[:with] if options[:with]
without = nil if options[:without] == []

Bundler.settings.set_command_option :without, without
Bundler.settings.set_command_option :with, with
options[:with] = with
options[:without] = without

unless Bundler.settings[:without] == options[:without] && Bundler.settings[:with] == options[:with]
# need to nil them out first to get around validation for backwards compatibility
Bundler.settings.set_command_option :without, nil
Bundler.settings.set_command_option :with, nil
Bundler.settings.set_command_option :without, options[:without] - options[:with]
Bundler.settings.set_command_option :with, options[:with]
end
end

def normalize_settings
Expand All @@ -190,7 +199,7 @@ def normalize_settings

Bundler.settings.set_command_option_if_given :clean, options["clean"]

normalize_groups if options[:without] || options[:with]
normalize_groups

options[:force] = options[:redownload]
end
Expand Down
2 changes: 2 additions & 0 deletions lib/bundler/resolver.rb
Expand Up @@ -155,6 +155,8 @@ def search_for(dependency_proxy)
search.each do |sg|
next unless sg.for?(platform)
sg_all_platforms = sg.copy_for(self.class.sort_platforms(@platforms).reverse)
next unless sg_all_platforms

selected_sgs << sg_all_platforms

next if sg_all_platforms.activated_platforms == [Gem::Platform::RUBY]
Expand Down
2 changes: 1 addition & 1 deletion lib/bundler/version.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: false

module Bundler
VERSION = "2.2.1".freeze
VERSION = "2.2.2".freeze

def self.bundler_major_version
@bundler_major_version ||= VERSION.split(".").first.to_i
Expand Down
2 changes: 1 addition & 1 deletion lib/rubygems.rb
Expand Up @@ -8,7 +8,7 @@
require 'rbconfig'

module Gem
VERSION = "3.2.1".freeze
VERSION = "3.2.2".freeze
end

# Must be first since it unloads the prelude from 1.9.2
Expand Down
1 change: 0 additions & 1 deletion lib/rubygems/commands/owner_command.rb
Expand Up @@ -105,7 +105,6 @@ def send_owner_request(method, name, owner)
rubygems_api_request method, "api/v1/gems/#{name}/owners", scope: get_owner_scope(method: method) do |request|
request.set_form_data 'email' => owner
request.add_field "Authorization", api_key
request.add_field "OTP", options[:otp] if options[:otp]
end
end

Expand Down
1 change: 0 additions & 1 deletion lib/rubygems/commands/push_command.rb
Expand Up @@ -91,7 +91,6 @@ def send_push_request(name, args)
request.add_field "Content-Length", request.body.size
request.add_field "Content-Type", "application/octet-stream"
request.add_field "Authorization", api_key
request.add_field "OTP", options[:otp] if options[:otp]
end
end

Expand Down
1 change: 0 additions & 1 deletion lib/rubygems/commands/yank_command.rb
Expand Up @@ -74,7 +74,6 @@ def yank_api_request(method, version, platform, api)
name = get_one_gem_name
response = rubygems_api_request(method, api, host, scope: get_yank_scope) do |request|
request.add_field("Authorization", api_key)
request.add_field("OTP", options[:otp]) if options[:otp]

data = {
'gem_name' => name,
Expand Down
2 changes: 1 addition & 1 deletion lib/rubygems/ext/rake_builder.rb
Expand Up @@ -19,7 +19,7 @@ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_di
rake = rake.shellsplit
else
begin
rake = [Gem.ruby, "-I#{File.expand_path("..", __dir__)}", "-rrubygems", Gem.bin_path('rake', 'rake')]
rake = [Gem.ruby, "-I#{File.expand_path("../..", __dir__)}", "-rrubygems", Gem.bin_path('rake', 'rake')]
rescue Gem::Exception
rake = [Gem.default_exec_format % 'rake']
end
Expand Down
35 changes: 20 additions & 15 deletions lib/rubygems/gemcutter_utilities.rb
Expand Up @@ -94,20 +94,16 @@ def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scop
end

uri = URI.parse "#{self.host}/#{path}"

request_method = Net::HTTP.const_get method.to_s.capitalize
response = Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
response = request_with_otp(method, uri, &block)

if mfa_unauthorized?(response)
response = Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
req.add_field "OTP", get_otp
block.call(req)
end
ask_otp
response = request_with_otp(method, uri, &block)
end

if api_key_forbidden?(response)
update_scope(scope)
Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
request_with_otp(method, uri, &block)
else
response
end
Expand All @@ -117,11 +113,6 @@ def mfa_unauthorized?(response)
response.kind_of?(Net::HTTPUnauthorized) && response.body.start_with?('You have enabled multifactor authentication')
end

def get_otp
say 'You have enabled multi-factor authentication. Please enter OTP code.'
ask 'Code: '
end

def update_scope(scope)
sign_in_host = self.host
pretty_host = pretty_host(sign_in_host)
Expand All @@ -135,7 +126,7 @@ def update_scope(scope)
response = rubygems_api_request(:put, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
request.basic_auth email, password
request.add_field "OTP", options[:otp] if options[:otp]
request["OTP"] = options[:otp] if options[:otp]
request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params))
end

Expand Down Expand Up @@ -168,7 +159,7 @@ def sign_in(sign_in_host = nil, scope: nil)
response = rubygems_api_request(:post, "api/v1/api_key",
sign_in_host, scope: scope) do |request|
request.basic_auth email, password
request.add_field "OTP", options[:otp] if options[:otp]
request["OTP"] = options[:otp] if options[:otp]
request.body = URI.encode_www_form({ name: key_name }.merge(scope_params))
end

Expand Down Expand Up @@ -229,6 +220,20 @@ def set_api_key(host, key)

private

def request_with_otp(method, uri, &block)
request_method = Net::HTTP.const_get method.to_s.capitalize

Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
req["OTP"] = options[:otp] if options[:otp]
block.call(req)
end
end

def ask_otp
say 'You have enabled multi-factor authentication. Please enter OTP code.'
options[:otp] = ask 'Code: '
end

def pretty_host(host)
if Gem::DEFAULT_HOST == host
'RubyGems.org'
Expand Down
11 changes: 6 additions & 5 deletions lib/rubygems/test_utilities.rb
Expand Up @@ -38,7 +38,7 @@ def initialize
@paths = []
end

def find_data(path, nargs = 3)
def find_data(path)
return Gem.read_binary path.path if URI === path and 'file' == path.scheme

if URI === path and "URI::#{path.scheme.upcase}" != path.class.name
Expand All @@ -54,10 +54,11 @@ def find_data(path, nargs = 3)
raise Gem::RemoteFetcher::FetchError.new("no data for #{path}", path)
end

data = @data[path]

data.flatten! and return data.shift(nargs) if data.respond_to?(:flatten!)
data
if @data[path].kind_of?(Array) && @data[path].first.kind_of?(Array)
@data[path].shift
else
@data[path]
end
end

def fetch_path(path, mtime = nil, head = false)
Expand Down
8 changes: 4 additions & 4 deletions spec/bundler/commands/console_spec.rb
Expand Up @@ -55,10 +55,10 @@ def __pry__

it "uses IRB as default console" do
bundle "console" do |input, _, _|
input.puts("__method__")
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include(":irb_binding")
expect(out).to include("(irb)")
end

it "starts another REPL if configured as such" do
Expand All @@ -80,10 +80,10 @@ def __pry__
# make sure pry isn't there

bundle "console" do |input, _, _|
input.puts("__method__")
input.puts("__FILE__")
input.puts("exit")
end
expect(out).to include(":irb_binding")
expect(out).to include("(irb)")
end

it "doesn't load any other groups" do
Expand Down
41 changes: 41 additions & 0 deletions spec/bundler/commands/lock_spec.rb
Expand Up @@ -342,6 +342,47 @@ def read_lockfile(file = "Gemfile.lock")
G
end

it "doesn't crash when an update candidate doesn't have any matching platform" do
build_repo4 do
build_gem "libv8", "8.4.255.0"
build_gem "libv8", "8.4.255.0" do |s|
s.platform = "x86_64-darwin-19"
end

build_gem "libv8", "15.0.71.48.1beta2" do |s|
s.platform = "x86_64-linux"
end
end

gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "libv8"
G

lockfile <<-G
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
libv8 (8.4.255.0)
libv8 (8.4.255.0-x86_64-darwin-19)
PLATFORMS
ruby
x86_64-darwin-19
DEPENDENCIES
libv8
BUNDLED WITH
#{Bundler::VERSION}
G

simulate_platform(Gem::Platform.new("x86_64-darwin-19")) { bundle "lock --update" }

expect(out).to match(/Writing lockfile to.+Gemfile\.lock/)
end

context "when an update is available" do
let(:repo) { gem_repo2 }

Expand Down
20 changes: 18 additions & 2 deletions spec/bundler/install/gemfile/groups_spec.rb
Expand Up @@ -91,15 +91,31 @@
expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
end

it "respects global `without` configuration, but does not save it locally" do
bundle "config without emo"
it "respects global `without` configuration, and saves it locally", :bundler => "< 3" do
bundle "config set without emo"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
bundle "config list"
expect(out).to include("Set for your local app (#{bundled_app(".bundle/config")}): [:emo]")
expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]")
end

it "respects global `without` configuration, but does not save it locally", :bundler => "3" do
bundle "config set without emo"
bundle :install
expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
bundle "config list"
expect(out).not_to include("Set for your local app (#{bundled_app(".bundle/config")}): [:emo]")
expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]")
end

it "allows running application where groups where configured by a different user", :bundler => "< 3" do
bundle "config set without emo"
bundle :install
bundle "exec ruby -e 'puts 42'", :env => { "BUNDLE_USER_HOME" => tmp("new_home").to_s }
expect(out).to include("42")
end

it "does not install gems from the excluded group" do
bundle "config --local without emo"
bundle :install
Expand Down
2 changes: 1 addition & 1 deletion spec/bundler/realworld/fixtures/warbler/Gemfile.lock
Expand Up @@ -6,7 +6,7 @@ PATH
GEM
remote: https://rubygems.org/
specs:
jruby-jars (9.2.11.1)
jruby-jars (9.2.14.0)
jruby-rack (1.1.21)
rake (13.0.1)
rubyzip (1.3.0)
Expand Down
43 changes: 41 additions & 2 deletions test/rubygems/test_gem_commands_push_command.rb
Expand Up @@ -404,11 +404,13 @@ def test_otp_verified_failure
assert_equal '111111', @fetcher.last_request['OTP']
end

def test_sending_gem_unathorized_api_key
def test_sending_gem_unathorized_api_key_with_mfa_enabled
response_mfa_enabled = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_forbidden = "The API key doesn't have access"
response_success = 'Successfully registered gem: freewill (1.0.0)'

@fetcher.data["#{@host}/api/v1/gems"] = [
[response_mfa_enabled, 401, 'Unauthorized'],
[response_forbidden, 403, 'Forbidden'],
[response_success, 200, "OK"],
]
Expand All @@ -417,17 +419,54 @@ def test_sending_gem_unathorized_api_key
@cmd.instance_variable_set :@host, @host
@cmd.instance_variable_set :@scope, :push_rubygem

@ui = Gem::MockGemUi.new "some@mail.com\npass\n"
@ui = Gem::MockGemUi.new "11111\nsome@mail.com\npass\n"
use_ui @ui do
@cmd.send_gem(@path)
end

mfa_notice = "You have enabled multi-factor authentication. Please enter OTP code."
access_notice = "The existing key doesn't have access of push_rubygem on https://rubygems.example. Please sign in to update access."
assert_match mfa_notice, @ui.output
assert_match access_notice, @ui.output
assert_match "Email:", @ui.output
assert_match "Password:", @ui.output
assert_match "Added push_rubygem scope to the existing API key", @ui.output
assert_match response_success, @ui.output
assert_equal '11111', @fetcher.last_request['OTP']
end

def test_sending_gem_with_no_local_creds
Gem.configuration.rubygems_api_key = nil

response_mfa_enabled = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."
response_success = 'Successfully registered gem: freewill (1.0.0)'

@fetcher.data["#{@host}/api/v1/gems"] = [
[response_success, 200, "OK"],
]

@fetcher.data["#{@host}/api/v1/api_key"] = [
[response_mfa_enabled, 401, 'Unauthorized'],
["", 200, "OK"],
]

@cmd.instance_variable_set :@scope, :push_rubygem
@cmd.options[:args] = [@path]
@cmd.options[:host] = @host

@ui = Gem::MockGemUi.new "some@mail.com\npass\n11111\n"
use_ui @ui do
@cmd.execute
end

mfa_notice = "You have enabled multi-factor authentication. Please enter OTP code."
assert_match mfa_notice, @ui.output
assert_match "Enter your https://rubygems.example credentials.", @ui.output
assert_match "Email:", @ui.output
assert_match "Password:", @ui.output
assert_match "Signed in with API key:", @ui.output
assert_match response_success, @ui.output
assert_equal '11111', @fetcher.last_request['OTP']
end

private
Expand Down

0 comments on commit 0e40cc9

Please sign in to comment.