From 1478b3525977447dbf06135dbdf6d9978aa637b6 Mon Sep 17 00:00:00 2001 From: Sutou Kouhei Date: Wed, 8 Jan 2020 06:53:52 +0900 Subject: Fix ruby version parsing in compact index We can't use the same logic as "dependencies" for "requirements". Because "," in "requirements" isn't escaped as "&". --- lib/bundler/compact_index_client/cache.rb | 18 +-- lib/bundler/compact_index_client/gem_parser.rb | 66 ++++++++ .../compact_index_client/gem_parser_spec.rb | 174 +++++++++++++++++++++ 3 files changed, 245 insertions(+), 13 deletions(-) create mode 100644 lib/bundler/compact_index_client/gem_parser.rb create mode 100644 spec/bundler/compact_index_client/gem_parser_spec.rb diff --git a/lib/bundler/compact_index_client/cache.rb b/lib/bundler/compact_index_client/cache.rb index f6105d3bb3..af3522e609 100644 --- a/lib/bundler/compact_index_client/cache.rb +++ b/lib/bundler/compact_index_client/cache.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require_relative "gem_parser" + module Bundler class CompactIndexClient class Cache @@ -92,19 +94,9 @@ module Bundler header ? lines[header + 1..-1] : lines end - def parse_gem(string) - version_and_platform, rest = string.split(" ", 2) - version, platform = version_and_platform.split("-", 2) - dependencies, requirements = rest.split("|", 2).map {|s| s.split(",") } if rest - dependencies = dependencies ? dependencies.map {|d| parse_dependency(d) } : [] - requirements = requirements ? requirements.map {|r| parse_dependency(r) } : [] - [version, platform, dependencies, requirements] - end - - def parse_dependency(string) - dependency = string.split(":") - dependency[-1] = dependency[-1].split("&") if dependency.size > 1 - dependency + def parse_gem(line) + @dependency_parser ||= GemParser.new + @dependency_parser.parse(line) end def info_roots diff --git a/lib/bundler/compact_index_client/gem_parser.rb b/lib/bundler/compact_index_client/gem_parser.rb new file mode 100644 index 0000000000..117da64ce6 --- /dev/null +++ b/lib/bundler/compact_index_client/gem_parser.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module Bundler + class CompactIndexClient + class GemParser + def parse(line) + version_and_platform, rest = line.split(" ", 2) + version, platform = version_and_platform.split("-", 2) + dependencies, requirements = rest.split("|", 2) if rest + dependencies = dependencies ? parse_dependencies(dependencies) : [] + requirements = requirements ? parse_requirements(requirements) : [] + [version, platform, dependencies, requirements] + end + + private + + def parse_dependencies(raw_dependencies) + raw_dependencies.split(",").map {|d| parse_dependency(d) } + end + + def parse_dependency(raw_dependency) + dependency = raw_dependency.split(":") + dependency[-1] = dependency[-1].split("&") if dependency.size > 1 + dependency + end + + # Parse the following format: + # + # line = "checksum:#{checksum}" + # line << ",ruby:#{ruby_version}" if ruby_version && ruby_version != ">= 0" + # line << ",rubygems:#{rubygems_version}" if rubygems_version && rubygems_version != ">= 0" + # + # See compact_index/gem_version.rb for details. + # + # We can't use parse_dependencies for requirements because "," in + # ruby_version and rubygems_version isn't escaped as "&". For example, + # "checksum:XXX,ruby:>=2.2, < 2.7.dev" can't be parsed as expected. + def parse_requirements(raw_requirements) + requirements = [] + checksum = raw_requirements.match(/\A(checksum):([^,]+)/) + if checksum + requirements << [checksum[1], [checksum[2]]] + raw_requirements = checksum.post_match + if raw_requirements.start_with?(",") + raw_requirements = raw_requirements[1..-1] + end + end + rubygems = raw_requirements.match(/(rubygems):(.+)\z/) + if rubygems + raw_requirements = rubygems.pre_match + if raw_requirements.start_with?(",") + raw_requirements = raw_requirements[1..-1] + end + end + ruby = raw_requirements.match(/\A(ruby):(.+)\z/) + if ruby + requirements << [ruby[1], ruby[2].split(/\s*,\s*/)] + end + if rubygems + requirements << [rubygems[1], rubygems[2].split(/\s*,\s*/)] + end + requirements + end + end + end +end diff --git a/spec/bundler/compact_index_client/gem_parser_spec.rb b/spec/bundler/compact_index_client/gem_parser_spec.rb new file mode 100644 index 0000000000..5f5305426f --- /dev/null +++ b/spec/bundler/compact_index_client/gem_parser_spec.rb @@ -0,0 +1,174 @@ +# frozen_string_literal: true + +require "bundler/compact_index_client/gem_parser" + +RSpec.describe Bundler::CompactIndexClient::GemParser do + def parse(line) + parser = Bundler::CompactIndexClient::GemParser.new + parser.parse(line) + end + + context "platform" do + it "existent" do + checksum = "d5956d2bcb509af2cd07c90d9e5fdb331be8845a75bfd823a31c147b52cff471" + line = "1.11.3-java |checksum:#{checksum}" + expected = [ + "1.11.3", + "java", + [], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + + it "nonexistent" do + checksum = "6da2eb3c4867e64df28d3e0b1008422dfacda7c046f9a8f3c56c52505b195e81" + line = "1.11.3 |checksum:#{checksum}" + expected = [ + "1.11.3", + nil, + [], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + end + + context "dependencies" do + it "nothing" do + checksum = "6da2eb3c4867e64df28d3e0b1008422dfacda7c046f9a8f3c56c52505b195e81" + line = "1.11.3 |checksum:#{checksum}" + expected = [ + "1.11.3", + nil, + [], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + + it "one" do + checksum = "5f0b378d12ab5665e2b6a1525274de97350238963002583cf088dae988527647" + line = "0.3.2 bones:>= 2.4.2|checksum:#{checksum}" + expected = [ + "0.3.2", + nil, + [ + ["bones", [">= 2.4.2"]], + ], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + + it "multiple" do + checksum = "199e892ada86c44d1f2e110b822d5da46b52fa2cbd2f00d89695b4cf610f9927" + line = "3.1.2 native-package-installer:>= 0,pkg-config:>= 0|checksum:#{checksum}" + expected = [ + "3.1.2", + nil, + [ + ["native-package-installer", [">= 0"]], + ["pkg-config", [">= 0"]], + ], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + + context "version" do + it "multiple" do + checksum = "1ec894b8090cb2c9393153552be2f3b6b1975265cbc1e0a3c6b28ebfea7e76a1" + line = "3.1.5 multi_json:< 1.3&>= 1.0|checksum:#{checksum}" + expected = [ + "3.1.5", + nil, + [ + ["multi_json", ["< 1.3", ">= 1.0"]], + ], + [ + ["checksum", [checksum]], + ], + ] + expect(parse(line)).to eq expected + end + end + end + + context "requirements" do + context "ruby" do + it "one version" do + checksum ="6da2eb3c4867e64df28d3e0b1008422dfacda7c046f9a8f3c56c52505b195e81" + line = "1.11.3 |checksum:#{checksum},ruby:>= 2.0" + expected = [ + "1.11.3", + nil, + [], + [ + ["checksum", [checksum]], + ["ruby", [">= 2.0"]], + ], + ] + expect(parse(line)).to eq expected + end + + it "multiple versions" do + checksum = "99e4845796c8dec1c3fc80dc772860a01633b33291bd7534007f5c7724f0b876" + line = "1.11.3-x86-mingw32 |checksum:#{checksum},ruby:>= 2.2, < 2.7.dev" + expected = [ + "1.11.3", + "x86-mingw32", + [], + [ + ["checksum", [checksum]], + ["ruby", [">= 2.2", "< 2.7.dev"]], + ], + ] + expect(parse(line)).to eq expected + end + + it "with rubygems" do + checksum = "7a82b358f00da749b01f8c84df8e8eb21c1bc389740aab9a2bf4ce59894564ac" + line = "1.9.23.pre1 |checksum:#{checksum},ruby:>= 1.9, < 2.7.dev,rubygems:> 1.3.1" + expected = [ + "1.9.23.pre1", + nil, + [], + [ + ["checksum", [checksum]], + ["ruby", [">= 1.9", "< 2.7.dev"]], + ["rubygems", ["> 1.3.1"]], + ], + ] + expect(parse(line)).to eq expected + end + end + + context "rubygems" do + it "existent" do + checksum = "91ddb4c1b5482a4aff957f6733e282ce2767b2d3051138e0203e39d6df4eba10" + line = "1.0.12.pre |checksum:#{checksum},rubygems:> 1.3.1" + expected = [ + "1.0.12.pre", + nil, + [], + [ + ["checksum", [checksum]], + ["rubygems", ["> 1.3.1"]], + ], + ] + expect(parse(line)).to eq expected + end + end + end +end -- cgit v1.2.1