diff options
-rw-r--r-- | doc/playbooks/RELEASING.md (renamed from doc/development/RELEASING.md) | 4 | ||||
-rw-r--r-- | doc/playbooks/TEAM_CHANGES.md | 42 | ||||
-rw-r--r-- | lib/bundler.rb | 4 | ||||
-rw-r--r-- | lib/bundler/definition.rb | 4 | ||||
-rw-r--r-- | lib/bundler/env.rb | 2 | ||||
-rw-r--r-- | lib/bundler/resolver.rb | 38 | ||||
-rw-r--r-- | lib/bundler/resolver/spec_group.rb | 4 | ||||
-rw-r--r-- | lib/bundler/source/metadata.rb | 6 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/base.rb | 2 | ||||
-rw-r--r-- | lib/bundler/vendor/thor/lib/thor/util.rb | 2 | ||||
-rw-r--r-- | lib/bundler/version_ranges.rb | 56 | ||||
-rw-r--r-- | spec/bundler/version_ranges_spec.rb | 3 | ||||
-rw-r--r-- | spec/install/gemfile_spec.rb | 29 | ||||
-rw-r--r-- | spec/install/gems/resolving_spec.rb | 9 | ||||
-rw-r--r-- | spec/resolver/basic_spec.rb | 4 | ||||
-rw-r--r-- | spec/runtime/setup_spec.rb | 2 | ||||
-rw-r--r-- | spec/support/indexes.rb | 4 |
17 files changed, 180 insertions, 35 deletions
diff --git a/doc/development/RELEASING.md b/doc/playbooks/RELEASING.md index afa689eaad..58a6196fef 100644 --- a/doc/development/RELEASING.md +++ b/doc/playbooks/RELEASING.md @@ -1,6 +1,6 @@ # Releasing -Bundler users [Semantic Versioning](https://semver.org/). +Bundler uses [Semantic Versioning](https://semver.org/). _Note: In the documentation listed below, the *current* minor version number is 1.11 and the *next* minor version number is 1.12_ @@ -55,7 +55,7 @@ The `rake release:patch` command will automatically handle cherry-picking, and i Bundler maintains a list of changes present in each version in the `CHANGELOG.md` file. Entries should not be added in pull requests, but are rather written by the Bundler maintainers in the [bundler-changelog repo](https://github.com/bundler/bundler-changelog). -That reposity tracks changes by pull requests, with each entry having an associated version, +That repository tracks changes by pull requests, with each entry having an associated version, PR, section, author(s), issue(s) closed, and message. Ensure that repo has been updated with all new PRs before releasing a new version, diff --git a/doc/playbooks/TEAM_CHANGES.md b/doc/playbooks/TEAM_CHANGES.md new file mode 100644 index 0000000000..4fd86b5cd1 --- /dev/null +++ b/doc/playbooks/TEAM_CHANGES.md @@ -0,0 +1,42 @@ +# Team changes + +This file documents how to add and remove team members. For the rules governing adding and removing team members, see [POLICIES][policies]. + +## Adding a new team member + +Interested in adding someone to the team? Here's the process. + +1. An existing team member nominates a potential team member to the rest of the team. +2. The existing team reaches consensus about whether to invite the potential member. +3. The nominator asks the potential member if they would like to join the team. +4. The nominator also sends the candidate a link to [POLICIES][policies] as an orientation for being on the team. +5. If the potential member accepts: + - Invite them to the maintainers Slack channel + - Add them to the [maintainers team][org_team] on GitHub + - Add them to the [Team page][team] on bundler.io, in the [maintainers list][maintainers] + - Add them to the [list of team members][list] in `contributors.rake` + - Add them to the authors list in `bundler.gemspec` + - Add them to the owners list on RubyGems.org by running + ``` + $ gem owner -a EMAIL bundler + ``` + + +## Removing a team member + +When the conditions in [POLICIES](https://github.com/bundler/bundler/blob/master/doc/POLICIES.md#maintainer-team-guidelines) are met, or when team members choose to retire, here's how to remove someone from the team. + +- Remove them from the owners list on RubyGems.org by running + ``` + $ gem owner -r EMAIL bundler + ``` +- Remove their entry on the [Team page][team] on bundler.io, in the [maintainers list][maintainers] +- Remove them from the [list of team members][list] in `contributors.rake` +- Remove them from the [maintainers team][org_team] on GitHub +- Remove them from the maintainers Slack channel + +[policies]: https://github.com/bundler/bundler/blob/master/doc/POLICIES.md#bundler-policies +[org_team]: https://github.com/orgs/bundler/teams/maintainers/members +[team]: https://bundler.io/contributors.html +[maintainers]: https://github.com/bundler/bundler-site/blob/02483d3f79f243774722b3fc18a471ca77b1c424/source/contributors.html.haml#L25 +[list]: https://github.com/bundler/bundler-site/blob/02483d3f79f243774722b3fc18a471ca77b1c424/lib/tasks/contributors.rake#L8 diff --git a/lib/bundler.rb b/lib/bundler.rb index 6b62ac9b1c..5da316ec4d 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -424,7 +424,7 @@ EOF def read_file(file) SharedHelpers.filesystem_access(file, :read) do - File.open(file, "rb", &:read) + File.open(file, "r:UTF-8", &:read) end end @@ -445,7 +445,7 @@ EOF def load_gemspec_uncached(file, validate = false) path = Pathname.new(file) - contents = File.open(file, "r:UTF-8", &:read) + contents = read_file(file) spec = if contents.start_with?("---") # YAML header eval_yaml_gemspec(path, contents) else diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index df8ee1f54b..3964c07fb5 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -855,8 +855,8 @@ module Bundler concat_ruby_version_requirements(locked_ruby_version_object) unless @unlock[:ruby] end [ - Dependency.new("ruby\0", ruby_versions), - Dependency.new("rubygems\0", Gem::VERSION), + Dependency.new("Ruby\0", ruby_versions), + Dependency.new("RubyGems\0", Gem::VERSION), ] end end diff --git a/lib/bundler/env.rb b/lib/bundler/env.rb index b91a345cee..923a14de0c 100644 --- a/lib/bundler/env.rb +++ b/lib/bundler/env.rb @@ -61,7 +61,7 @@ module Bundler end def self.read_file(filename) - File.read(filename.to_s).strip + Bundler.read_file(filename.to_s).strip rescue Errno::ENOENT "<No #{filename} found>" rescue RuntimeError => e diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index 8872b861be..a6c26ffc6b 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -302,10 +302,23 @@ module Bundler end def version_conflict_message(e) + # only show essential conflicts, if possible + conflicts = e.conflicts.dup + conflicts.delete_if do |_name, conflict| + deps = conflict.requirement_trees.map(&:last).flatten(1) + !Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) + end + e = Molinillo::VersionConflict.new(conflicts, e.specification_provider) unless conflicts.empty? + + solver_name = "Bundler" + possibility_type = "gem" e.message_with_trees( - :solver_name => "Bundler", - :possibility_type => "gem", + :solver_name => solver_name, + :possibility_type => possibility_type, :reduce_trees => lambda do |trees| + # called first, because we want to reduce the amount of work required to find maximal empty sets + trees = trees.uniq {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } + # bail out if tree size is too big for Array#combination to make any sense return trees if trees.size > 15 maximal = 1.upto(trees.size).map do |size| @@ -313,10 +326,8 @@ module Bundler end.flatten(1).select do |deps| Bundler::VersionRanges.empty?(*Bundler::VersionRanges.for_many(deps.map(&:requirement))) end.min_by(&:size) - trees.reject! {|t| !maximal.include?(t.last) } if maximal - trees = trees.sort_by {|t| t.flatten.map(&:to_s) } - trees.uniq! {|t| t.flatten.map {|dep| [dep.name, dep.requirement] } } + trees.reject! {|t| !maximal.include?(t.last) } if maximal trees.sort_by {|t| t.reverse.map(&:name) } end, @@ -351,7 +362,11 @@ module Bundler [] end.compact.map(&:to_s).uniq.sort - o << "Could not find gem '#{SharedHelpers.pretty_dependency(conflict.requirement)}'" + metadata_requirement = name.end_with?("\0") + + o << "Could not find gem '" unless metadata_requirement + o << SharedHelpers.pretty_dependency(conflict.requirement) + o << "'" unless metadata_requirement if conflict.requirement_trees.first.size > 1 o << ", which is required by " o << "gem '#{SharedHelpers.pretty_dependency(conflict.requirement_trees.first[-2])}'," @@ -360,12 +375,21 @@ module Bundler o << if relevant_sources.empty? "in any of the sources.\n" + elsif metadata_requirement + "is not available in #{relevant_sources.join(" or ")}" else "in any of the relevant sources:\n #{relevant_sources * "\n "}\n" end end end, - :version_for_spec => lambda {|spec| spec.version } + :version_for_spec => lambda {|spec| spec.version }, + :incompatible_version_message_for_conflict => lambda do |name, _conflict| + if name.end_with?("\0") + %(#{solver_name} found conflicting requirements for the #{name} version:) + else + %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":) + end + end ) end diff --git a/lib/bundler/resolver/spec_group.rb b/lib/bundler/resolver/spec_group.rb index 34d043aed7..119f63b5c8 100644 --- a/lib/bundler/resolver/spec_group.rb +++ b/lib/bundler/resolver/spec_group.rb @@ -94,10 +94,10 @@ module Bundler return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification) dependencies = [] if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("ruby\0", spec.required_ruby_version), platform) + dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform) end if !spec.required_rubygems_version.nil? && !spec.required_rubygems_version.none? - dependencies << DepProxy.new(Gem::Dependency.new("rubygems\0", spec.required_rubygems_version), platform) + dependencies << DepProxy.new(Gem::Dependency.new("RubyGems\0", spec.required_rubygems_version), platform) end dependencies end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 93909002c7..ff8861c710 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -5,8 +5,10 @@ module Bundler class Metadata < Source def specs @specs ||= Index.build do |idx| - idx << Gem::Specification.new("ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) - idx << Gem::Specification.new("rubygems\0", Gem::VERSION) + idx << Gem::Specification.new("Ruby\0", RubyVersion.system.to_gem_version_with_patchlevel) + idx << Gem::Specification.new("RubyGems\0", Gem::VERSION) do |s| + s.required_rubygems_version = Gem::Requirement.default + end idx << Gem::Specification.new do |s| s.name = "bundler" diff --git a/lib/bundler/vendor/thor/lib/thor/base.rb b/lib/bundler/vendor/thor/lib/thor/base.rb index 9bd1077170..7e41dc226b 100644 --- a/lib/bundler/vendor/thor/lib/thor/base.rb +++ b/lib/bundler/vendor/thor/lib/thor/base.rb @@ -113,7 +113,7 @@ class Bundler::Thor end # Whenever a class inherits from Bundler::Thor or Bundler::Thor::Group, we should track the - # class and the file on Bundler::Thor::Base. This is the method responsable for it. + # class and the file on Bundler::Thor::Base. This is the method responsible for it. # def register_klass_file(klass) #:nodoc: file = caller[1].match(/(.*):\d+/)[1] diff --git a/lib/bundler/vendor/thor/lib/thor/util.rb b/lib/bundler/vendor/thor/lib/thor/util.rb index 5d03177a28..0fe7d4d859 100644 --- a/lib/bundler/vendor/thor/lib/thor/util.rb +++ b/lib/bundler/vendor/thor/lib/thor/util.rb @@ -27,7 +27,7 @@ class Bundler::Thor end # Receives a constant and converts it to a Bundler::Thor namespace. Since Bundler::Thor - # commands can be added to a sandbox, this method is also responsable for + # commands can be added to a sandbox, this method is also responsible for # removing the sandbox namespace. # # This method should not be used in general because it's used to deal with diff --git a/lib/bundler/version_ranges.rb b/lib/bundler/version_ranges.rb index ec25716cde..12a956d6a0 100644 --- a/lib/bundler/version_ranges.rb +++ b/lib/bundler/version_ranges.rb @@ -5,11 +5,42 @@ module Bundler NEq = Struct.new(:version) ReqR = Struct.new(:left, :right) class ReqR - Endpoint = Struct.new(:version, :inclusive) + Endpoint = Struct.new(:version, :inclusive) do + def <=>(other) + if version.equal?(INFINITY) + return 0 if other.version.equal?(INFINITY) + return 1 + elsif other.version.equal?(INFINITY) + return -1 + end + + comp = version <=> other.version + return comp unless comp.zero? + + if inclusive && !other.inclusive + 1 + elsif !inclusive && other.inclusive + -1 + else + 0 + end + end + end + def to_s "#{left.inclusive ? "[" : "("}#{left.version}, #{right.version}#{right.inclusive ? "]" : ")"}" end - INFINITY = Object.new.freeze + INFINITY = begin + inf = Object.new + def inf.to_s + "∞" + end + def inf.<=>(other) + return 0 if other.equal?(self) + 1 + end + inf.freeze + end ZERO = Gem::Version.new("0.a") def cover?(v) @@ -32,6 +63,15 @@ module Bundler left.version == right.version end + def <=>(other) + return -1 if other.equal?(INFINITY) + + comp = left <=> other.left + return comp unless comp.zero? + + right <=> other.right + end + UNIVERSAL = ReqR.new(ReqR::Endpoint.new(Gem::Version.new("0.a"), true), ReqR::Endpoint.new(ReqR::INFINITY, false)).freeze end @@ -57,7 +97,7 @@ module Bundler end.uniq ranges, neqs = ranges.partition {|r| !r.is_a?(NEq) } - [ranges.sort_by {|range| [range.left.version, range.left.inclusive ? 0 : 1] }, neqs.map(&:version)] + [ranges.sort, neqs.map(&:version)] end def self.empty?(ranges, neqs) @@ -66,8 +106,14 @@ module Bundler next false if curr_range.single? && neqs.include?(curr_range.left.version) next curr_range if last_range.right.version == ReqR::INFINITY case last_range.right.version <=> curr_range.left.version - when 1 then next curr_range - when 0 then next(last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) && curr_range) + # higher + when 1 then next ReqR.new(curr_range.left, last_range.right) + # equal + when 0 + if last_range.right.inclusive && curr_range.left.inclusive && !neqs.include?(curr_range.left.version) + ReqR.new(curr_range.left, [curr_range.right, last_range.right].max) + end + # lower when -1 then next false end end diff --git a/spec/bundler/version_ranges_spec.rb b/spec/bundler/version_ranges_spec.rb index ccbb9285d5..bca044b0c0 100644 --- a/spec/bundler/version_ranges_spec.rb +++ b/spec/bundler/version_ranges_spec.rb @@ -25,9 +25,12 @@ RSpec.describe Bundler::VersionRanges do include_examples "empty?", false, ">= 1.0.0", "< 2.0.0" include_examples "empty?", false, "~> 1" include_examples "empty?", false, "~> 2.0", "~> 2.1" + include_examples "empty?", true, ">= 4.1.0", "< 5.0", "= 5.2.1" + include_examples "empty?", true, "< 5.0", "< 5.3", "< 6.0", "< 6", "= 5.2.0", "> 2", ">= 3.0", ">= 3.1", ">= 3.2", ">= 4.0.0", ">= 4.1.0", ">= 4.2.0", ">= 4.2", ">= 4" include_examples "empty?", true, "!= 1", "< 2", "> 2" include_examples "empty?", true, "!= 1", "<= 1", ">= 1" include_examples "empty?", true, "< 2", "> 2" + include_examples "empty?", true, "< 2", "> 2", "= 2" include_examples "empty?", true, "= 1", "!= 1" include_examples "empty?", true, "= 1", "= 2" include_examples "empty?", true, "= 1", "~> 2" diff --git a/spec/install/gemfile_spec.rb b/spec/install/gemfile_spec.rb index 45bc232901..ad254a87e9 100644 --- a/spec/install/gemfile_spec.rb +++ b/spec/install/gemfile_spec.rb @@ -112,4 +112,33 @@ RSpec.describe "bundle install" do end end end + + context "with a Gemfile containing non-US-ASCII characters" do + it "reads the Gemfile with the UTF-8 encoding by default" do + skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9" + + install_gemfile <<-G + str = "Il était une fois ..." + puts "The source encoding is: " + str.encoding.name + G + + expect(out).to include("The source encoding is: UTF-8") + expect(out).not_to include("The source encoding is: ASCII-8BIT") + expect(out).to include("Bundle complete!") + end + + it "respects the magic encoding comment" do + skip "Ruby 1.8 has no encodings" if RUBY_VERSION < "1.9" + + # NOTE: This works thanks to #eval interpreting the magic encoding comment + install_gemfile <<-G + # encoding: iso-8859-1 + str = "Il #{"\xE9".b}tait une fois ..." + puts "The source encoding is: " + str.encoding.name + G + + expect(out).to include("The source encoding is: ISO-8859-1") + expect(out).to include("Bundle complete!") + end + end end diff --git a/spec/install/gems/resolving_spec.rb b/spec/install/gems/resolving_spec.rb index e58f32836c..f581522c71 100644 --- a/spec/install/gems/resolving_spec.rb +++ b/spec/install/gems/resolving_spec.rb @@ -142,15 +142,14 @@ RSpec.describe "bundle install with install-time dependencies" do expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000") nice_error = strip_whitespace(<<-E).strip - Bundler could not find compatible versions for gem "ruby\0": + Bundler found conflicting requirements for the Ruby\0 version: In Gemfile: - ruby\0 (#{error_message_requirement}) + Ruby\0 (#{error_message_requirement}) require_ruby was resolved to 1.0, which depends on - ruby\0 (> 9000) + Ruby\0 (> 9000) - Could not find gem 'ruby\0 (> 9000)', which is required by gem 'require_ruby', in any of the relevant sources: - the local ruby installation + Ruby\0 (> 9000), which is required by gem 'require_ruby', is not available in the local ruby installation E expect(last_command.bundler_err).to end_with(nice_error) end diff --git a/spec/resolver/basic_spec.rb b/spec/resolver/basic_spec.rb index 6d2dea2fb3..a2d5e13377 100644 --- a/spec/resolver/basic_spec.rb +++ b/spec/resolver/basic_spec.rb @@ -157,10 +157,10 @@ Bundler could not find compatible versions for gem "a": s.required_ruby_version = "~> 2.0.0" end - gem "ruby\0", "1.8.7" + gem "Ruby\0", "1.8.7" end dep "foo" - dep "ruby\0", "1.8.7" + dep "Ruby\0", "1.8.7" deps = [] @deps.each do |d| diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index cd87971238..7fec55c4c3 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -881,7 +881,7 @@ end end end - it "should succesfully require 'bundler/setup'" do + it "should successfully require 'bundler/setup'" do install_gemfile "" ENV["GEM_PATH"] = symlinked_gem_home.path diff --git a/spec/support/indexes.rb b/spec/support/indexes.rb index c56d6145a7..5f6c515735 100644 --- a/spec/support/indexes.rb +++ b/spec/support/indexes.rb @@ -76,7 +76,7 @@ module Spec gem "rack-mount", %w[0.4 0.5 0.5.1 0.5.2 0.6] # --- Pre-release support - gem "rubygems\0", ["1.3.2"] + gem "RubyGems\0", ["1.3.2"] # --- Rails versions "1.2.3 2.2.3 2.3.5 3.0.0.beta 3.0.0.beta1" do |version| @@ -413,7 +413,7 @@ module Spec gem("b", %w[0.9.0 1.5.0 2.0.0.pre]) # --- Pre-release support - gem "rubygems\0", ["1.3.2"] + gem "RubyGems\0", ["1.3.2"] end end end |