summaryrefslogtreecommitdiff
path: root/lib/bundler/settings.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bundler/settings.rb')
-rw-r--r--lib/bundler/settings.rb427
1 files changed, 286 insertions, 141 deletions
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb
index f33e9453be..fd77c2f7fc 100644
--- a/lib/bundler/settings.rb
+++ b/lib/bundler/settings.rb
@@ -1,61 +1,51 @@
# frozen_string_literal: true
-require "uri"
-
module Bundler
class Settings
- autoload :Mirror, "bundler/mirror"
- autoload :Mirrors, "bundler/mirror"
- autoload :Validator, "bundler/settings/validator"
+ autoload :Mirror, File.expand_path("mirror", __dir__)
+ autoload :Mirrors, File.expand_path("mirror", __dir__)
+ autoload :Validator, File.expand_path("settings/validator", __dir__)
BOOL_KEYS = %w[
- allow_bundler_dependency_conflicts
- allow_deployment_source_credential_changes
- allow_offline_install
- auto_clean_without_path
auto_install
cache_all
cache_all_platforms
- cache_command_is_package
- console_command
- default_install_uses_path
+ clean
deployment
- deployment_means_frozen
disable_checksum_validation
disable_exec_load
disable_local_branch_check
- disable_multisource
+ disable_local_revision_check
disable_shared_gems
disable_version_check
- error_on_stderr
force_ruby_platform
- forget_cli_options
frozen
+ gem.changelog
gem.coc
gem.mit
+ gem.bundle
+ git.allow_insecure
global_gem_cache
ignore_messages
init_gems_rb
- list_command
- lockfile_uses_separate_rubygems_sources
- major_deprecations
+ inline
+ lockfile_checksums
+ no_build_extension
no_install
+ no_install_plugin
no_prune
- only_update_to_newer_versions
path.system
plugins
- prefer_gems_rb
- print_only_version_number
- setup_makes_kernel_gem_public
+ prefer_patch
+ silence_deprecations
silence_root_warning
- skip_default_git_sources
- specific_platform
- suppress_install_using_messages
- unlock_source_unlocks_spec
update_requires_all_flag
+ verbose
].freeze
NUMBER_KEYS = %w[
+ cooldown
+ jobs
redirect
retry
ssl_verify_mode
@@ -63,56 +53,77 @@ module Bundler
].freeze
ARRAY_KEYS = %w[
+ only
with
without
].freeze
+ STRING_KEYS = %w[
+ bin
+ cache_path
+ console
+ default_cli_command
+ gem.ci
+ gem.github_username
+ gem.linter
+ gem.rubocop
+ gem.test
+ gemfile
+ lockfile
+ path
+ shebang
+ simulate_version
+ system_bindir
+ trust-policy
+ version
+ ].freeze
+
DEFAULT_CONFIG = {
- :disable_version_check => true,
- :redirect => 5,
- :retry => 3,
- :timeout => 10,
+ "BUNDLE_SILENCE_DEPRECATIONS" => false,
+ "BUNDLE_DISABLE_VERSION_CHECK" => true,
+ "BUNDLE_PREFER_PATCH" => false,
+ "BUNDLE_REDIRECT" => 5,
+ "BUNDLE_RETRY" => 3,
+ "BUNDLE_TIMEOUT" => 10,
+ "BUNDLE_VERSION" => "lockfile",
+ "BUNDLE_LOCKFILE_CHECKSUMS" => true,
+ "BUNDLE_CACHE_ALL" => true,
+ "BUNDLE_PLUGINS" => true,
+ "BUNDLE_GLOBAL_GEM_CACHE" => false,
+ "BUNDLE_UPDATE_REQUIRES_ALL_FLAG" => false,
}.freeze
def initialize(root = nil)
@root = root
@local_config = load_config(local_config_file)
+ @local_root = root || Pathname.new(".bundle").expand_path
+
+ @env_config = ENV.to_h
+ @env_config.select! {|key, _value| key.start_with?("BUNDLE_") }
+ @env_config.delete("BUNDLE_")
+
@global_config = load_config(global_config_file)
@temporary = {}
+
+ @key_cache = {}
end
def [](name)
key = key_for(name)
- value = @temporary.fetch(key) do
- @local_config.fetch(key) do
- ENV.fetch(key) do
- @global_config.fetch(key) do
- DEFAULT_CONFIG.fetch(name) do
- nil
- end end end end end
+
+ value = nil
+ configs.each do |_, config|
+ value = config[key]
+ next if value.nil?
+ break
+ end
converted_value(value, name)
end
def set_command_option(key, value)
- if Bundler.feature_flag.forget_cli_options?
- temporary(key => value)
- value
- else
- command = if value.nil?
- "bundle config --delete #{key}"
- else
- "bundle config #{key} #{Array(value).join(":")}"
- end
-
- Bundler::SharedHelpers.major_deprecation 2,\
- "flags passed to commands " \
- "will no longer be automatically remembered. Instead please set flags " \
- "you want remembered between commands using `bundle config " \
- "<setting name> <setting value>`, i.e. `#{command}`"
-
- set_local(key, value)
- end
+ temporary(key => value)
+ value
end
def set_command_option_if_given(key, value)
@@ -121,7 +132,7 @@ module Bundler
end
def set_local(key, value)
- local_config_file || raise(GemfileNotFound, "Could not locate Gemfile")
+ local_config_file = @local_root.join("config")
set_key(key, value, @local_config, local_config_file)
end
@@ -144,25 +155,32 @@ module Bundler
end
def all
- env_keys = ENV.keys.grep(/\ABUNDLE_.+/)
-
- keys = @temporary.keys | @global_config.keys | @local_config.keys | env_keys
+ keys = @temporary.keys.union(@global_config.keys, @local_config.keys, @env_config.keys)
- keys.map do |key|
- key.sub(/^BUNDLE_/, "").gsub(/__/, ".").downcase
- end
+ keys.map! do |key|
+ key = key.delete_prefix("BUNDLE_")
+ key.gsub!("___", "-")
+ key.gsub!("__", ".")
+ key.downcase!
+ key
+ end.sort!
+ keys
end
def local_overrides
repos = {}
all.each do |k|
- repos[$'] = self[k] if k =~ /^local\./
+ repos[k.delete_prefix("local.")] = self[k] if k.start_with?("local.")
end
repos
end
def mirror_for(uri)
- uri = URI(uri.to_s) unless uri.is_a?(URI)
+ if uri.is_a?(String)
+ require_relative "vendored_uri"
+ uri = Gem::URI(uri)
+ end
+
gem_mirrors.for(uri.to_s).uri
end
@@ -179,13 +197,11 @@ module Bundler
def locations(key)
key = key_for(key)
- locations = {}
- locations[:temporary] = @temporary[key] if @temporary.key?(key)
- locations[:local] = @local_config[key] if @local_config.key?(key)
- locations[:env] = ENV[key] if ENV[key]
- locations[:global] = @global_config[key] if @global_config.key?(key)
- locations[:default] = DEFAULT_CONFIG[key] if DEFAULT_CONFIG.key?(key)
- locations
+ configs.keys.inject({}) do |partial_locations, level|
+ value_on_level = configs[level][key]
+ partial_locations[level] = value_on_level unless value_on_level.nil?
+ partial_locations
+ end
end
def pretty_values_for(exposed_key)
@@ -193,50 +209,60 @@ module Bundler
locations = []
- if @temporary.key?(key)
- locations << "Set for the current command: #{converted_value(@temporary[key], exposed_key).inspect}"
+ if value = @temporary[key]
+ locations << "Set for the current command: #{printable_value(value, exposed_key).inspect}"
end
- if @local_config.key?(key)
- locations << "Set for your local app (#{local_config_file}): #{converted_value(@local_config[key], exposed_key).inspect}"
+ if value = @local_config[key]
+ locations << "Set for your local app (#{local_config_file}): #{printable_value(value, exposed_key).inspect}"
end
- if value = ENV[key]
- locations << "Set via #{key}: #{converted_value(value, exposed_key).inspect}"
+ if value = @env_config[key]
+ locations << "Set via #{key}: #{printable_value(value, exposed_key).inspect}"
end
- if @global_config.key?(key)
- locations << "Set for the current user (#{global_config_file}): #{converted_value(@global_config[key], exposed_key).inspect}"
+ if value = @global_config[key]
+ locations << "Set for the current user (#{global_config_file}): #{printable_value(value, exposed_key).inspect}"
end
return ["You have not configured a value for `#{exposed_key}`"] if locations.empty?
locations
end
- # for legacy reasons, the ruby scope isnt appended when the setting comes from ENV or the global config,
- # nor do we respect :disable_shared_gems
+ def processor_count
+ require "etc"
+ Etc.nprocessors
+ rescue StandardError
+ 1
+ end
+
+ # for legacy reasons, in Bundler 2, we do not respect :disable_shared_gems
def path
- key = key_for(:path)
- path = ENV[key] || @global_config[key]
- if path && !@temporary.key?(key) && !@local_config.key?(key)
- return Path.new(path, false, false, false)
+ configs.each do |_level, settings|
+ path = value_for("path", settings)
+ path_system = value_for("path.system", settings)
+ disabled_shared_gems = value_for("disable_shared_gems", settings)
+ next if path.nil? && path_system.nil? && disabled_shared_gems.nil?
+ system_path = path_system || (disabled_shared_gems == false)
+ return Path.new(path, system_path)
end
- system_path = self["path.system"] || (self[:disable_shared_gems] == false)
- Path.new(self[:path], true, system_path, Bundler.feature_flag.default_install_uses_path?)
+ path = "vendor/bundle" if self[:deployment]
+
+ Path.new(path, false)
end
- Path = Struct.new(:explicit_path, :append_ruby_scope, :system_path, :default_install_uses_path) do
+ Path = Struct.new(:explicit_path, :system_path) do
def path
path = base_path
- path = File.join(path, Bundler.ruby_scope) if append_ruby_scope && !use_system_gems?
+ path = File.join(path, Bundler.ruby_scope) unless use_system_gems?
path
end
def use_system_gems?
return true if system_path
return false if explicit_path
- !default_install_uses_path
+ !Bundler.feature_flag.bundler_5_mode?
end
def base_path
@@ -246,6 +272,20 @@ module Bundler
path
end
+ def base_path_relative_to_pwd
+ base_path = Pathname.new(self.base_path)
+ expanded_base_path = base_path.expand_path(Bundler.root)
+ relative_path = expanded_base_path.relative_path_from(Pathname.pwd)
+ if relative_path.to_s.start_with?("..")
+ relative_path = base_path if base_path.absolute?
+ else
+ relative_path = Pathname.new(File.join(".", relative_path))
+ end
+ relative_path
+ rescue ArgumentError
+ expanded_base_path
+ end
+
def validate!
return unless explicit_path && system_path
path = Bundler.settings.pretty_values_for(:path)
@@ -259,12 +299,6 @@ module Bundler
end
end
- def allow_sudo?
- key = key_for(:path)
- path_configured = @temporary.key?(key) || @local_config.key?(key)
- !path_configured
- end
-
def ignore_config?
ENV["BUNDLE_IGNORE_CONFIG"]
end
@@ -273,22 +307,38 @@ module Bundler
@app_cache_path ||= self[:cache_path] || "vendor/cache"
end
+ def installation_parallelization
+ self[:jobs] || processor_count
+ end
+
def validate!
all.each do |raw_key|
- [@local_config, ENV, @global_config].each do |settings|
- value = converted_value(settings[key_for(raw_key)], raw_key)
- Validator.validate!(raw_key, value, settings.to_hash.dup)
+ [@local_config, @env_config, @global_config].each do |settings|
+ value = value_for(raw_key, settings)
+ Validator.validate!(raw_key, value, settings.dup)
end
end
end
def key_for(key)
- key = Settings.normalize_uri(key).to_s if key.is_a?(String) && /https?:/ =~ key
- key = key.to_s.gsub(".", "__").upcase
- "BUNDLE_#{key}"
+ @key_cache[key] ||= self.class.key_for(key)
end
- private
+ private
+
+ def configs
+ @configs ||= {
+ temporary: @temporary,
+ local: @local_config,
+ env: @env_config,
+ global: @global_config,
+ default: DEFAULT_CONFIG,
+ }
+ end
+
+ def value_for(name, config)
+ converted_value(config[key_for(name)], name)
+ end
def parent_setting_for(name)
split_specific_setting_for(name)[0]
@@ -303,12 +353,20 @@ module Bundler
end
def is_bool(name)
- BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
+ name = self.class.key_to_s(name)
+ BOOL_KEYS.include?(name) || BOOL_KEYS.include?(parent_setting_for(name))
+ end
+
+ def is_string(name)
+ name = self.class.key_to_s(name)
+ STRING_KEYS.include?(name) || name.start_with?("local.") || name.start_with?("mirror.") || name.start_with?("build.")
end
def to_bool(value)
case value
- when nil, /\A(false|f|no|n|0|)\z/i, false
+ when String
+ value.match?(/\A(false|f|no|n|0|)\z/i) ? false : true
+ when nil, false
false
else
true
@@ -316,16 +374,24 @@ module Bundler
end
def is_num(key)
- NUMBER_KEYS.include?(key.to_s)
+ NUMBER_KEYS.include?(self.class.key_to_s(key))
end
def is_array(key)
- ARRAY_KEYS.include?(key.to_s)
+ ARRAY_KEYS.include?(self.class.key_to_s(key))
+ end
+
+ def is_credential(key)
+ key == "gem.push_key"
+ end
+
+ def is_userinfo(value)
+ value.include?(":")
end
def to_array(value)
return [] unless value
- value.split(":").map(&:to_sym)
+ value.tr(" ", ":").split(":").map(&:to_sym)
end
def array_to_s(array)
@@ -335,7 +401,7 @@ module Bundler
end
def set_key(raw_key, value, hash, file)
- raw_key = raw_key.to_s
+ raw_key = self.class.key_to_s(raw_key)
value = array_to_s(value) if is_array(raw_key)
key = key_for(raw_key)
@@ -348,14 +414,19 @@ module Bundler
Validator.validate!(raw_key, converted_value(value, raw_key), hash)
return unless file
+
+ SharedHelpers.filesystem_access(file.dirname, :create) do |p|
+ FileUtils.mkdir_p(p)
+ end
+
SharedHelpers.filesystem_access(file) do |p|
- FileUtils.mkdir_p(p.dirname)
- require "bundler/yaml_serializer"
- p.open("w") {|f| f.write(YAMLSerializer.dump(hash)) }
+ p.open("w") {|f| f.write(serializer_class.dump(hash)) }
end
end
def converted_value(value, key)
+ key = self.class.key_to_s(key)
+
if is_array(key)
to_array(value)
elsif value.nil?
@@ -369,15 +440,38 @@ module Bundler
end
end
+ def printable_value(value, key)
+ converted = converted_value(value, key)
+ return converted unless converted.is_a?(String)
+
+ if is_string(key)
+ converted
+ elsif is_credential(key)
+ "[REDACTED]"
+ elsif is_userinfo(converted)
+ username, pass = converted.split(":", 2)
+
+ if pass == "x-oauth-basic"
+ username = "[REDACTED]"
+ else
+ pass = "[REDACTED]"
+ end
+
+ [username, pass].join(":")
+ else
+ converted
+ end
+ end
+
def global_config_file
if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty?
Pathname.new(ENV["BUNDLE_CONFIG"])
- else
- begin
- Bundler.user_bundle_path.join("config")
- rescue PermissionError, GenericSystemCallError
- nil
- end
+ elsif ENV["BUNDLE_USER_CONFIG"] && !ENV["BUNDLE_USER_CONFIG"].empty?
+ Pathname.new(ENV["BUNDLE_USER_CONFIG"])
+ elsif ENV["BUNDLE_USER_HOME"] && !ENV["BUNDLE_USER_HOME"].empty?
+ Pathname.new(ENV["BUNDLE_USER_HOME"]).join("config")
+ elsif Bundler.rubygems.user_home && !Bundler.rubygems.user_home.empty?
+ Pathname.new(Bundler.rubygems.user_home).join(".bundle/config")
end
end
@@ -385,43 +479,64 @@ module Bundler
Pathname.new(@root).join("config") if @root
end
- CONFIG_REGEX = %r{ # rubocop:disable Style/RegexpLiteral
- ^
- (BUNDLE_.+):\s # the key
- (?: !\s)? # optional exclamation mark found with ruby 1.9.3
- (['"]?) # optional opening quote
- (.* # contents of the value
- (?: # optionally, up until the next key
- (\n(?!BUNDLE).+)*
- )
- )
- \2 # matching closing quote
- $
- }xo
-
def load_config(config_file)
return {} if !config_file || ignore_config?
SharedHelpers.filesystem_access(config_file, :read) do |file|
valid_file = file.exist? && !file.size.zero?
return {} unless valid_file
- require "bundler/yaml_serializer"
- YAMLSerializer.load file.read
+ (serializer_class.load(file.read) || {}).inject({}) do |config, (k, v)|
+ k = k.dup
+ k << "/" if /https?:/i.match?(k) && !k.end_with?("/", "__#{FALLBACK_TIMEOUT_URI_OPTION.upcase}")
+ k.gsub!(".", "__")
+
+ unless k.start_with?("#")
+ if k.include?("-")
+ Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
+ "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
+
+ # string hash keys are frozen
+ k = k.gsub("-", "___")
+ end
+
+ config[k] = v
+ end
+
+ config
+ end
end
end
- PER_URI_OPTIONS = %w[
- fallback_timeout
- ].freeze
+ def serializer_class
+ require "rubygems/yaml_serializer"
+ Gem::YAMLSerializer
+ rescue LoadError
+ # TODO: Remove this when RubyGems 3.4 is EOL
+ require_relative "yaml_serializer"
+ YAMLSerializer
+ end
+
+ FALLBACK_TIMEOUT_URI_OPTION = "fallback_timeout"
NORMALIZE_URI_OPTIONS_PATTERN =
/
\A
(\w+\.)? # optional prefix key
(https?.*?) # URI
- (\.#{Regexp.union(PER_URI_OPTIONS)})? # optional suffix key
+ (\.#{FALLBACK_TIMEOUT_URI_OPTION})? # optional suffix key
\z
/ix
+ def self.key_for(key)
+ key = key_to_s(key)
+ key = normalize_uri(key) if key.start_with?("http", "mirror.http")
+ key = key.gsub(".", "__")
+ key.gsub!("-", "___")
+ key.upcase!
+
+ key.gsub(/\A([ #]*)/, '\1BUNDLE_')
+ end
+
# TODO: duplicates Rubygems#normalize_uri
# TODO: is this the correct place to validate mirror URIs?
def self.normalize_uri(uri)
@@ -431,12 +546,42 @@ module Bundler
uri = $2
suffix = $3
end
- uri = "#{uri}/" unless uri.end_with?("/")
- uri = URI(uri)
+ uri = URINormalizer.normalize_suffix(uri)
+ require_relative "vendored_uri"
+ uri = Gem::URI(uri)
unless uri.absolute?
raise ArgumentError, format("Gem sources must be absolute. You provided '%s'.", uri)
end
"#{prefix}#{uri}#{suffix}"
end
+
+ # This is a hot method, so avoid respond_to? checks on every invocation
+ if :read.respond_to?(:name)
+ def self.key_to_s(key)
+ case key
+ when String
+ key
+ when Symbol
+ key.name
+ when Gem::URI::HTTP
+ key.to_s
+ else
+ raise ArgumentError, "Invalid key: #{key.inspect}"
+ end
+ end
+ else
+ def self.key_to_s(key)
+ case key
+ when String
+ key
+ when Symbol
+ key.to_s
+ when Gem::URI::HTTP
+ key.to_s
+ else
+ raise ArgumentError, "Invalid key: #{key.inspect}"
+ end
+ end
+ end
end
end