diff options
author | Samuel Giddins <segiddins@segiddins.me> | 2017-08-01 15:41:21 -0500 |
---|---|---|
committer | Samuel Giddins <segiddins@segiddins.me> | 2017-08-01 16:07:28 -0500 |
commit | 02ed02ff784f7168d29e5633a094013199db4d46 (patch) | |
tree | 3e35f2a836347d728e2102d42490f62b5faeca46 | |
parent | b3f33fdfe128ebe1266a1e34a453a5c2cd46a4c2 (diff) | |
download | bundler-02ed02ff784f7168d29e5633a094013199db4d46.tar.gz |
WIP
-rw-r--r-- | lib/bundler/definition.rb | 41 | ||||
-rw-r--r-- | lib/bundler/dependency.rb | 12 | ||||
-rw-r--r-- | lib/bundler/lazy_specification.rb | 7 | ||||
-rw-r--r-- | lib/bundler/lockfile_generator.rb | 36 | ||||
-rw-r--r-- | lib/bundler/lockfile_parser.rb | 144 | ||||
-rw-r--r-- | lib/bundler/source_list.rb | 9 |
6 files changed, 196 insertions, 53 deletions
diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index a91f7e2a28..a84f4c3b5d 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -34,14 +34,27 @@ module Bundler raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file? + parsed_lockfile = nil if lockfile && File.exist?(lockfile) - # parsed_lockfile = LockfileParser.new(Bundler.read_file(lockfile)) - # use_lockfile = parsed_lockfile.gemfile_checksums.reduce do |file, checksum| - # p file, checksum - # end + parsed_lockfile = LockfileParser.new(Bundler.read_file(lockfile)) + changed_gemfiles = parsed_lockfile.gemfiles.values.reject(&:unchanged?) + if !changed_gemfiles.empty? + warn "skipping lf since #{changed_gemfiles} changed" + elsif unlock != {} + warn "skipping lf since unlocking" + elsif parsed_lockfile.gemfiles.empty? + warn "skipping lf since no locked gemfiles" + else + sources = parsed_lockfile.sources.dup + aggregate = sources.delete(parsed_lockfile.aggregate_source) + sources = SourceList.from_sources(sources) + aggregate.remotes.each {|r| sources.global_rubygems_source = r } if aggregate + # warn "using lockfile" + return Definition.new(parsed_lockfile, parsed_lockfile.dependencies.values, sources, {}, parsed_lockfile.ruby_version, parsed_lockfile.optional_groups, parsed_lockfile.gemfiles.keys) + end end - Dsl.evaluate(gemfile, lockfile, unlock) + Dsl.evaluate(gemfile, parsed_lockfile, unlock) end # @@ -74,18 +87,15 @@ module Bundler @ruby_version = ruby_version @gemfiles = gemfiles - @lockfile = lockfile - @lockfile_contents = String.new - @locked_bundler_version = nil - @locked_ruby_version = nil + @locked_gems = lockfile + @lockfile = lockfile && lockfile.lockfile + @lockfile_contents = lockfile && lockfile.lockfile_contents || String.new + @locked_bundler_version = lockfile && lockfile.bundler_version + @locked_ruby_version = lockfile && lockfile.ruby_version + @locked_platforms = lockfile && lockfile.platforms || [] - if lockfile && File.exist?(lockfile) - @lockfile_contents = Bundler.read_file(lockfile) - @locked_gems = LockfileParser.new(@lockfile_contents) - @locked_platforms = @locked_gems.platforms + if lockfile @platforms = @locked_platforms.dup - @locked_bundler_version = @locked_gems.bundler_version - @locked_ruby_version = @locked_gems.ruby_version if unlock != true @locked_deps = @locked_gems.dependencies @@ -104,7 +114,6 @@ module Bundler @locked_deps = {} @locked_specs = SpecSet.new([]) @locked_sources = [] - @locked_platforms = [] end @unlock[:gems] ||= [] diff --git a/lib/bundler/dependency.rb b/lib/bundler/dependency.rb index f943c6530a..2edd4ffa63 100644 --- a/lib/bundler/dependency.rb +++ b/lib/bundler/dependency.rb @@ -84,13 +84,23 @@ module Bundler @autorequire = nil @groups = Array(options["group"] || :default).map(&:to_sym) @source = options["source"] - @platforms = Array(options["platforms"]) + @platforms = Array(options["platforms"]).map(&:to_sym) @env = options["env"] @should_include = options.fetch("should_include", true) @autorequire = Array(options["require"] || []) if options.key?("require") end + def options_to_lock + { + "env" => @env, + "group" => @groups, + "platforms" => @platforms, + "require" => @autorequire, + "source" => @source, + }.reject {|_, v| Array(v).empty? } + end + # Returns the platforms this dependency is valid for, in the same order as # passed in the `valid_platforms` parameter def gem_platforms(valid_platforms) diff --git a/lib/bundler/lazy_specification.rb b/lib/bundler/lazy_specification.rb index 821d249d96..8e8f242ee9 100644 --- a/lib/bundler/lazy_specification.rb +++ b/lib/bundler/lazy_specification.rb @@ -62,7 +62,12 @@ module Bundler dependencies.sort_by(&:to_s).uniq.each do |dep| next if dep.type == :development - out << " #{dep.to_lock(true)}\n" + out << " #{dep.name}" + unless dep.requirement.none? + reqs = dep.requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse + out << " (#{reqs.join(", ")})" + end + out << "\n" end out diff --git a/lib/bundler/lockfile_generator.rb b/lib/bundler/lockfile_generator.rb index a065298486..324b39324b 100644 --- a/lib/bundler/lockfile_generator.rb +++ b/lib/bundler/lockfile_generator.rb @@ -20,6 +20,7 @@ module Bundler add_platforms add_dependencies add_section("OPTIONAL GROUPS", definition.optional_groups) + add_gemfiles add_locked_ruby_version add_bundled_with @@ -61,11 +62,31 @@ module Bundler handled = [] definition.dependencies.sort_by(&:to_s).each do |dep| next if handled.include?(dep.name) - out << dep.to_lock + out << " #{dep.name}" + unless dep.requirement.none? + reqs = dep.requirement.requirements.map {|o, v| "#{o} #{v}" }.sort.reverse + out << " (#{reqs.join(", ")})" + end + out << "!" if dep.source + out << "\n" + add_value(dep.options_to_lock, 4) handled << dep.name end end + def add_gemfiles + return unless SharedHelpers.md5_available? + gemfiles = {} + definition.gemfiles.each do |file| + md5 = Digest::MD5.file(file).hexdigest + if file.to_s.start_with?(Bundler.root.to_s) + file = file.relative_path_from(Bundler.root) + end + gemfiles[file] = "md5 #{md5}" + end + add_section("GEMFILE CHECKSUMS", gemfiles) + end + def add_locked_ruby_version return unless locked_ruby_version = definition.locked_ruby_version add_section("RUBY VERSION", locked_ruby_version.to_s) @@ -77,17 +98,24 @@ module Bundler def add_section(name, value) out << "\n#{name}\n" + add_value(value, 2) + end + + def add_value(value, indentation) + indent = " " * indentation case value when Array value.map(&:to_s).sort.each do |val| - out << " #{val}\n" + out << "#{indent}#{val}\n" end when Hash value.to_a.sort_by {|k, _| k.to_s }.each do |key, val| - out << " #{key}: #{val}\n" + Array(val).sort.each do |v| + out << "#{indent}#{key}: #{v}\n" + end end when String - out << " #{value}\n" + out << "#{indent} #{value}\n" else raise ArgumentError, "#{value.inspect} can't be serialized in a lockfile" end diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index ff706fca1d..8c23d1bae2 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -12,7 +12,20 @@ module Bundler class LockfileParser - attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version + LockedGemfile = Struct.new(:path, :checksum, :digest) do + def unchanged? + return unless path.file? + calculate_checksum == checksum + end + + def calculate_checksum + case digest + when "md5" then SharedHelpers.md5_available? && Digest::MD5.file(path).hexdigest + end.to_s + end + end + + attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version, :optional_groups, :gemfiles, :lockfile_contents, :lockfile BUNDLED = "BUNDLED WITH".freeze DEPENDENCIES = "DEPENDENCIES".freeze @@ -22,8 +35,10 @@ module Bundler GEM = "GEM".freeze PATH = "PATH".freeze PLUGIN = "PLUGIN SOURCE".freeze + GROUPS = "OPTIONAL GROUPS".freeze + GEMFILES = "GEMFILE CHECKSUMS".freeze SPECS = " specs:".freeze - OPTIONS = /^ ([a-z]+): (.*)$/i + OPTIONS = /^ {2}+([a-z]+): (.*)$/i SOURCE = [GIT, GEM, PATH, PLUGIN].freeze SECTIONS_BY_VERSION_INTRODUCED = { @@ -33,6 +48,7 @@ module Bundler Gem::Version.create("1.10".dup) => [BUNDLED].freeze, Gem::Version.create("1.12".dup) => [RUBY].freeze, Gem::Version.create("1.13".dup) => [PLUGIN].freeze, + Gem::Version.create("2.0".dup) => [GROUPS, GEMFILES].freeze, }.freeze KNOWN_SECTIONS = SECTIONS_BY_VERSION_INTRODUCED.values.flatten.freeze @@ -58,12 +74,16 @@ module Bundler attributes end - def initialize(lockfile) + def initialize(lockfile, path = nil) @platforms = [] @sources = [] @dependencies = {} @state = nil @specs = {} + @gemfiles = {} + @optional_groups = [] + @lockfile_contents = lockfile + @lockfile = path @rubygems_aggregate = Source::Rubygems.new @@ -72,24 +92,34 @@ module Bundler "Run `git checkout HEAD -- #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` first to get a clean lock." end - lockfile.split(/(?:\r?\n)+/).each do |line| - if SOURCE.include?(line) - @state = :source - parse_source(line) - elsif line == DEPENDENCIES + lockfile.split(/(?:\r?\n)/).each do |line| + case line + when DEPENDENCIES @state = :dependency - elsif line == PLATFORMS + when PLATFORMS @state = :platform - elsif line == RUBY + when RUBY @state = :ruby - elsif line == BUNDLED + when BUNDLED @state = :bundled_with - elsif line =~ /^[^\s]/ + when GROUPS + @state = :optional_group + when GEMFILES + @state = :gemfile + when "" + send("finalize_#{@state}") if @state + when *SOURCE # rubocop:disable Performance/CaseWhenSplat + @state = :source + parse_source(line) + when /^[^\s]/ @state = nil - elsif @state - send("parse_#{@state}", line) + else + send("parse_#{@state}", line) if @state end + # puts "#{@state.to_s.rjust(15)} => #{line}" end + send("finalize_#{@state}") if @state + @sources << @rubygems_aggregate unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? @specs = @specs.values.sort_by(&:identifier) warn_for_outdated_bundler_version @@ -116,6 +146,16 @@ module Bundler end end + def aggregate_source + return @aggregate_source unless Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + pinned_sources = @dependencies.values.map(&:source).uniq + @sources.find do |s| + next unless s.is_a?(Source::Rubygems) + next if pinned_sources.include?(s) + true + end + end + private TYPES = { @@ -155,19 +195,9 @@ module Bundler @current_source = Plugin.source_from_lock(@opts) @sources << @current_source end + @opts = {} when OPTIONS - value = $2 - value = true if value == "true" - value = false if value == "false" - - key = $1 - - if @opts[key] - @opts[key] = Array(@opts[key]) - @opts[key] << value - else - @opts[key] = value - end + parse_opts($1, $2) when *SOURCE @current_source = nil @opts = {} @@ -177,6 +207,20 @@ module Bundler end end + def finalize_source; end + + def parse_opts(key, value) + value = true if value == "true" + value = false if value == "false" + + if @opts[key] + @opts[key] = Array(@opts[key]) + @opts[key] << value + else + @opts[key] = value + end + end + space = / / NAME_VERSION = / ^(#{space}{2}|#{space}{4}|#{space}{6})(?!#{space}) # Exactly 2, 4, or 6 spaces at the start of the line @@ -188,6 +232,10 @@ module Bundler /xo def parse_dependency(line) + return parse_opts($1, $2) if line =~ OPTIONS + + finalize_dependency + return unless line =~ NAME_VERSION spaces = $1 return unless spaces.size == 2 @@ -195,11 +243,16 @@ module Bundler version = $3 pinned = $5 - version = version.split(",").map(&:strip) if version + @dep_name = name + @dep_version = version && version.split(",").map(&:strip) + @dep_pinned = pinned + end - dep = Bundler::Dependency.new(name, version) + def finalize_dependency + return unless defined?(@dep_name) && @dep_name + dep = Bundler::Dependency.new(@dep_name, @dep_version, @opts) - if pinned && dep.name != "bundler" + if @dep_pinned && dep.name != "bundler" spec = @specs.find {|_, v| v.name == dep.name } dep.source = spec.last.source if spec @@ -207,11 +260,12 @@ module Bundler # to use in the case that there are no gemspecs present. A fake # gemspec is created based on the version set on the dependency # TODO: Use the version from the spec instead of from the dependency - if version && version.size == 1 && version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path) - dep.source.name = name + if @dep_version && @dep_version.size == 1 && @dep_version.first =~ /^\s*= (.+)\s*$/ && dep.source.is_a?(Bundler::Source::Path) + dep.source.name = @dep_name dep.source.version = $1 end end + @opts = {} @dependencies[dep.name] = dep end @@ -239,18 +293,46 @@ module Bundler end end + def finalize_spec; end + def parse_platform(line) @platforms << Gem::Platform.new($1) if line =~ /^ (.*)$/ end + def finalize_platform; end + def parse_bundled_with(line) line = line.strip return unless Gem::Version.correct?(line) @bundler_version = Gem::Version.create(line) end + def finalize_bundled_with; end + def parse_ruby(line) @ruby_version = line.strip end + + def finalize_ruby; end + + def parse_optional_group(line) + @optional_groups << line.strip + end + + def finalize_optional_group; end + + def parse_gemfile(line) + return unless line =~ OPTIONS + parse_opts($1, $2) + end + + def finalize_gemfile + @opts.each do |file, checksum| + digest, sum = checksum.split(" ", 2) + file = Pathname(file).expand_path(Bundler.root) + @gemfiles[file] = LockedGemfile.new(file, sum, digest) + end + @opts = {} + end end end diff --git a/lib/bundler/source_list.rb b/lib/bundler/source_list.rb index dd5a07afd7..61598c128f 100644 --- a/lib/bundler/source_list.rb +++ b/lib/bundler/source_list.rb @@ -18,6 +18,15 @@ module Bundler @metadata_source = Source::Metadata.new end + def self.from_sources(sources) + source_list = new + sources.each do |source| + list = source_list.send(:source_list_for, source) + source_list.send(:add_source_to_list, source, list) + end + source_list + end + def add_path_source(options = {}) if options["gemspec"] add_source_to_list Source::Gemspec.new(options), path_sources |