diff options
Diffstat (limited to 'lib/bundler/settings.rb')
| -rw-r--r-- | lib/bundler/settings.rb | 300 |
1 files changed, 225 insertions, 75 deletions
diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 1c669491f6..fd77c2f7fc 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -7,48 +7,44 @@ module Bundler 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 - 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 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 + inline + lockfile_checksums + no_build_extension no_install + no_install_plugin no_prune - only_update_to_newer_versions - path_relative_to_cwd path.system plugins prefer_patch - print_only_version_number - setup_makes_kernel_gem_public silence_deprecations silence_root_warning - suppress_install_using_messages - unlock_source_unlocks_spec update_requires_all_flag - use_gem_version_promoter_for_major_updates + verbose ].freeze NUMBER_KEYS = %w[ + cooldown jobs redirect retry @@ -57,10 +53,31 @@ 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 = { "BUNDLE_SILENCE_DEPRECATIONS" => false, "BUNDLE_DISABLE_VERSION_CHECK" => true, @@ -68,30 +85,45 @@ module Bundler "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) - @env_config = ENV.to_h.select {|key, _value| key =~ /\ABUNDLE_.+/ } + @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 = configs.values.map {|config| config[key] }.compact.first + + 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 - set_local(key, value) - end + temporary(key => value) + value end def set_command_option_if_given(key, value) @@ -100,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 @@ -123,17 +155,22 @@ module Bundler end def all - keys = @temporary.keys | @global_config.keys | @local_config.keys | @env_config.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 @@ -141,7 +178,7 @@ module Bundler def mirror_for(uri) if uri.is_a?(String) require_relative "vendored_uri" - uri = Bundler::URI(uri) + uri = Gem::URI(uri) end gem_mirrors.for(uri.to_s).uri @@ -173,25 +210,32 @@ module Bundler locations = [] if value = @temporary[key] - locations << "Set for the current command: #{converted_value(value, exposed_key).inspect}" + locations << "Set for the current command: #{printable_value(value, exposed_key).inspect}" end if value = @local_config[key] - locations << "Set for your local app (#{local_config_file}): #{converted_value(value, exposed_key).inspect}" + locations << "Set for your local app (#{local_config_file}): #{printable_value(value, exposed_key).inspect}" end if value = @env_config[key] - locations << "Set via #{key}: #{converted_value(value, exposed_key).inspect}" + locations << "Set via #{key}: #{printable_value(value, exposed_key).inspect}" end if value = @global_config[key] - locations << "Set for the current user (#{global_config_file}): #{converted_value(value, exposed_key).inspect}" + 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 + 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 configs.each do |_level, settings| @@ -203,7 +247,9 @@ module Bundler return Path.new(path, system_path) end - Path.new(nil, false) + path = "vendor/bundle" if self[:deployment] + + Path.new(path, false) end Path = Struct.new(:explicit_path, :system_path) do @@ -216,7 +262,7 @@ module Bundler def use_system_gems? return true if system_path return false if explicit_path - !Bundler.feature_flag.default_install_uses_path? + !Bundler.feature_flag.bundler_5_mode? end def base_path @@ -253,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 @@ -267,6 +307,10 @@ 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_config, @global_config].each do |settings| @@ -277,20 +321,18 @@ module Bundler 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 def configs - { - :temporary => @temporary, - :local => @local_config, - :env => @env_config, - :global => @global_config, - :default => DEFAULT_CONFIG, + @configs ||= { + temporary: @temporary, + local: @local_config, + env: @env_config, + global: @global_config, + default: DEFAULT_CONFIG, } end @@ -311,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 @@ -324,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) @@ -343,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) @@ -356,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_relative "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? @@ -377,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("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 @@ -398,23 +484,58 @@ module Bundler SharedHelpers.filesystem_access(config_file, :read) do |file| valid_file = file.exist? && !file.size.zero? return {} unless valid_file - require_relative "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.freeze + /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? @@ -425,13 +546,42 @@ module Bundler uri = $2 suffix = $3 end - uri = "#{uri}/" unless uri.end_with?("/") + uri = URINormalizer.normalize_suffix(uri) require_relative "vendored_uri" - uri = Bundler::URI(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 |
